1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.uwb.discovery;
17 
18 import android.annotation.IntRange;
19 import android.util.Log;
20 
21 import androidx.annotation.NonNull;
22 
23 import com.android.server.uwb.discovery.info.AdminErrorMessage;
24 import com.android.server.uwb.discovery.info.AdminErrorMessage.ErrorType;
25 import com.android.server.uwb.discovery.info.AdminEventMessage;
26 import com.android.server.uwb.discovery.info.AdminEventMessage.EventType;
27 import com.android.server.uwb.discovery.info.FiraConnectorMessage;
28 import com.android.server.uwb.discovery.info.FiraConnectorMessage.InstructionCode;
29 import com.android.server.uwb.discovery.info.FiraConnectorMessage.MessageType;
30 
31 import java.nio.ByteBuffer;
32 
33 /** Abstract class for Transport Provider */
34 public abstract class TransportProvider implements Transport {
35     private static final String TAG = TransportProvider.class.getSimpleName();
36 
37     public enum TerminationReason {
38         /** Disconnection of the remote GATT service. */
39         REMOTE_DISCONNECTED,
40         /** remote GATT service discovery failure. */
41         SERVICE_DISCOVERY_FAILURE,
42         /** Characterstic read failure */
43         CHARACTERSTIC_READ_FAILURE,
44         /** Characterstic write failure */
45         CHARACTERSTIC_WRITE_FAILURE,
46         /** Descriptor write failure */
47         DESCRIPTOR_WRITE_FAILURE,
48         /** Remote device message error */
49         REMOTE_DEVICE_MESSAGE_ERROR,
50         /** Remote device SECID error */
51         REMOTE_DEVICE_SECID_ERROR,
52     }
53 
54     /** Callback for listening to transport events. */
55     public interface TransportCallback {
56 
57         /** Called when the transport started processing. */
onProcessingStarted()58         void onProcessingStarted();
59 
60         /** Called when the transport stopped processing. */
onProcessingStopped()61         void onProcessingStopped();
62 
63         /**
64          * Called when the transport terminated the connection due to an unrecoverable errors.
65          *
66          * @param reason indicates the termination reason.
67          */
onTerminated(TerminationReason reason)68         void onTerminated(TerminationReason reason);
69     }
70 
71     /**
72      * administrative SECID shall be exposed on each CS implementation at all times. It shall be
73      * marked as static.
74      */
75     public static final int ADMIN_SECID = 1;
76 
77     private DataReceiver mDataReceiver;
78 
79     /** Assigned SECID value (unsigned integer in the range 2..127, values 0 and 1 are reserved). */
80     private int mSecid = 2;
81 
82     /**
83      * Remote device SECID value (unsigned integer in the range 2..127, values 0 and 1 are
84      * reserved).
85      */
86     private int mDestinationSecid = 2;
87 
88     /** Wraps Fira Connector Message byte array and the associated SECID. */
89     public static class MessagePacket {
90         public final int secid;
91         public ByteBuffer messageBytes;
92 
MessagePacket(int secid, ByteBuffer messageBytes)93         public MessagePacket(int secid, ByteBuffer messageBytes) {
94             this.secid = secid;
95             this.messageBytes = messageBytes;
96         }
97     }
98 
TransportProvider(@ntRangefrom = 2, to = 127) int secid)99     protected TransportProvider(@IntRange(from = 2, to = 127) int secid) {
100         mSecid = secid;
101     }
102 
103     /**
104      * Set the Destination SECID on the Remote device.
105      *
106      * @param secid 7-bit secure component ID.
107      */
setDestinationSecid(@ntRangefrom = 2, to = 127) int secid)108     public void setDestinationSecid(@IntRange(from = 2, to = 127) int secid) {
109         mDestinationSecid = secid;
110     }
111 
112     @Override
sendData( MessageType messageType, @NonNull byte[] data, SendingDataCallback sendingDataCallback)113     public void sendData(
114             MessageType messageType,
115             @NonNull byte[] data,
116             SendingDataCallback sendingDataCallback) {
117         if (sendMessage(
118                 mDestinationSecid,
119                 new FiraConnectorMessage(
120                         messageType,
121                         /*Default instrcution code for message exchange.*/
122                         InstructionCode.DATA_EXCHANGE,
123                         data))) {
124             sendingDataCallback.onSuccess();
125         } else {
126             sendingDataCallback.onFailure();
127         }
128     }
129 
130     @Override
registerDataReceiver(DataReceiver dataReceiver)131     public void registerDataReceiver(DataReceiver dataReceiver) {
132         if (mDataReceiver != null) {
133             Log.w(TAG, "Already has a registered data receiver.");
134             return;
135         }
136         mDataReceiver = dataReceiver;
137     }
138 
139     @Override
unregisterDataReceiver()140     public void unregisterDataReceiver() {
141         mDataReceiver = null;
142     }
143 
144     /* Indicates whether the server has started.
145      */
146     protected boolean mStarted = false;
147 
148     /**
149      * Checks if the server has started.
150      *
151      * @return indicates if the server has started.
152      */
isStarted()153     public boolean isStarted() {
154         return mStarted;
155     }
156 
157     /**
158      * Starts the transport.
159      *
160      * @return indicates if successfully started.
161      */
start()162     public boolean start() {
163         if (isStarted()) {
164             Log.i(TAG, "Transport already started.");
165             return false;
166         }
167         return true;
168     }
169 
170     /**
171      * Stops the transport.
172      *
173      * @return indicates if successfully stopped.
174      */
stop()175     public boolean stop() {
176         if (!isStarted()) {
177             Log.i(TAG, "Transport already stopped.");
178             return false;
179         }
180         return true;
181     }
182 
183     /**
184      * Send a FiRa connector message to the remote device through the transport.
185      *
186      * @param secid destination SECID on remote device.
187      * @param message message to be send.
188      * @return indicates if successfully started.
189      */
sendMessage(int secid, FiraConnectorMessage message)190     public abstract boolean sendMessage(int secid, FiraConnectorMessage message);
191 
192     /**
193      * Called when the client received a new FiRa connector message from the remote device.
194      *
195      * @param secid destination SECID on this device.
196      * @param message FiRa connector message.
197      */
onMessageReceived(int secid, FiraConnectorMessage message)198     protected void onMessageReceived(int secid, FiraConnectorMessage message) {
199         if (secid == ADMIN_SECID) {
200             processAdminMessage(message);
201             return;
202         }
203         if (secid != mSecid) {
204             Log.w(
205                     TAG,
206                     "onMessageReceived rejected due to invalid SECID. Expect:"
207                             + mSecid
208                             + " Received:"
209                             + secid);
210             sentAdminErrorMessage(ErrorType.SECID_INVALID);
211             return;
212         }
213         if (mDataReceiver != null) {
214             mDataReceiver.onDataReceived(message.payload);
215         }
216     }
217 
218     /**
219      * Send a FiRa OOB administrative Error message to the administrative SECID on the remote
220      * device.
221      *
222      * @param errorType ErrorType of the message.
223      */
sentAdminErrorMessage(ErrorType errorType)224     protected void sentAdminErrorMessage(ErrorType errorType) {
225         if (!sendMessage(ADMIN_SECID, new AdminErrorMessage(errorType))) {
226             Log.w(TAG, "sentAdminErrorMessage with ErrorType:" + errorType + " failed.");
227         }
228     }
229 
230     /**
231      * Send a FiRa OOB administrative Event message to the administrative SECID on the remote
232      * device.
233      *
234      * @param eventType EventType of the message.
235      * @param additionalData additional data associated with the event.
236      */
sentAdminEventMessage(EventType eventType, byte[] additionalData)237     protected void sentAdminEventMessage(EventType eventType, byte[] additionalData) {
238         if (!sendMessage(ADMIN_SECID, new AdminEventMessage(eventType, additionalData))) {
239             Log.w(TAG, "sentAdminEventMessage with EventType:" + eventType + " failed.");
240         }
241     }
242 
243     /**
244      * Process FiRa OOB administrative message from the remote device.
245      *
246      * @param message FiRa connector message.
247      */
processAdminMessage(FiraConnectorMessage message)248     private void processAdminMessage(FiraConnectorMessage message) {
249         if (AdminErrorMessage.isAdminErrorMessage(message)) {
250             AdminErrorMessage errorMessage = AdminErrorMessage.convertToAdminErrorMessage(message);
251             Log.w(TAG, "Received AdminErrorMessage:" + errorMessage);
252             switch (errorMessage.errorType) {
253                 case DATA_PACKET_LENGTH_OVERFLOW:
254                 case MESSAGE_LENGTH_OVERFLOW:
255                 case TOO_MANY_CONCURRENT_FRAGMENTED_MESSAGE_SESSIONS:
256                     terminateOnError(TerminationReason.REMOTE_DEVICE_MESSAGE_ERROR);
257                     break;
258                 case SECID_INVALID:
259                 case SECID_INVALID_FOR_RESPONSE:
260                 case SECID_BUSY:
261                 case SECID_PROTOCOL_ERROR:
262                 case SECID_INTERNAL_ERROR:
263                     terminateOnError(TerminationReason.REMOTE_DEVICE_SECID_ERROR);
264                     break;
265             }
266         } else if (AdminEventMessage.isAdminEventMessage(message)) {
267             AdminEventMessage eventMessage = AdminEventMessage.convertToAdminEventMessage(message);
268             Log.w(TAG, "Received AdminEventMessage:" + eventMessage);
269             switch (eventMessage.eventType) {
270                 case CAPABILITIES_CHANGED:
271                     // No-op since this is only applicatble for CS with the role of GATT Server,
272                     // which isn't mandated by FiRa.
273                     break;
274             }
275         } else {
276             Log.e(TAG, "Invalid Admin FiraConnectorMessage received:" + message);
277         }
278     }
279 
280     /**
281      * Terminates the transport provider.
282      *
283      * @param reason reason for the termination.
284      */
terminateOnError(TerminationReason reason)285     protected abstract void terminateOnError(TerminationReason reason);
286 }
287