1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.tbs;
19 
20 import static android.bluetooth.BluetoothDevice.METADATA_GTBS_CCCD;
21 
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothGatt;
25 import android.bluetooth.BluetoothGattCharacteristic;
26 import android.bluetooth.BluetoothGattDescriptor;
27 import android.bluetooth.BluetoothGattServerCallback;
28 import android.bluetooth.BluetoothGattService;
29 import android.bluetooth.BluetoothProfile;
30 import android.content.Context;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.ParcelUuid;
34 import android.util.Log;
35 
36 import com.android.bluetooth.BluetoothEventLogger;
37 import com.android.bluetooth.Utils;
38 import com.android.bluetooth.btservice.AdapterService;
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 import java.io.ByteArrayOutputStream;
43 import java.nio.ByteBuffer;
44 import java.nio.ByteOrder;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashMap;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Objects;
51 import java.util.UUID;
52 
53 public class TbsGatt {
54 
55     private static final String TAG = "TbsGatt";
56 
57     private static final String UUID_PREFIX = "0000";
58     private static final String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb";
59 
60     /* TBS assigned uuid's */
61     @VisibleForTesting static final UUID UUID_TBS = makeUuid("184B");
62     @VisibleForTesting public static final UUID UUID_GTBS = makeUuid("184C");
63     @VisibleForTesting static final UUID UUID_BEARER_PROVIDER_NAME = makeUuid("2BB3");
64     @VisibleForTesting static final UUID UUID_BEARER_UCI = makeUuid("2BB4");
65     @VisibleForTesting static final UUID UUID_BEARER_TECHNOLOGY = makeUuid("2BB5");
66     @VisibleForTesting static final UUID UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST = makeUuid("2BB6");
67     @VisibleForTesting static final UUID UUID_BEARER_LIST_CURRENT_CALLS = makeUuid("2BB9");
68     @VisibleForTesting static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA");
69     @VisibleForTesting static final UUID UUID_STATUS_FLAGS = makeUuid("2BBB");
70     @VisibleForTesting static final UUID UUID_CALL_STATE = makeUuid("2BBD");
71     @VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT = makeUuid("2BBE");
72 
73     @VisibleForTesting
74     static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF");
75 
76     @VisibleForTesting static final UUID UUID_TERMINATION_REASON = makeUuid("2BC0");
77     @VisibleForTesting static final UUID UUID_INCOMING_CALL = makeUuid("2BC1");
78     @VisibleForTesting static final UUID UUID_CALL_FRIENDLY_NAME = makeUuid("2BC2");
79 
80     @VisibleForTesting
81     static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = makeUuid("2902");
82 
83     @VisibleForTesting static final int STATUS_FLAG_INBAND_RINGTONE_ENABLED = 0x0001;
84     @VisibleForTesting static final int STATUS_FLAG_SILENT_MODE_ENABLED = 0x0002;
85 
86     @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001;
87     @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002;
88 
89     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00;
90     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01;
91     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02;
92     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03;
93     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04;
94     @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05;
95 
96     @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00;
97 
98     @VisibleForTesting
99     public static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01;
100 
101     @VisibleForTesting
102     public static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02;
103 
104     @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03;
105     @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04;
106     @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_LACK_OF_RESOURCES = 0x05;
107 
108     @VisibleForTesting
109     public static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06;
110 
111     private final Object mPendingGattOperationsLock = new Object();
112     private final Context mContext;
113     private final GattCharacteristic mBearerProviderNameCharacteristic;
114     private final GattCharacteristic mBearerUciCharacteristic;
115     private final GattCharacteristic mBearerTechnologyCharacteristic;
116     private final GattCharacteristic mBearerUriSchemesSupportedListCharacteristic;
117     private final GattCharacteristic mBearerListCurrentCallsCharacteristic;
118     private final GattCharacteristic mContentControlIdCharacteristic;
119     private final GattCharacteristic mStatusFlagsCharacteristic;
120     private final GattCharacteristic mCallStateCharacteristic;
121     private final CallControlPointCharacteristic mCallControlPointCharacteristic;
122     private final GattCharacteristic mCallControlPointOptionalOpcodesCharacteristic;
123     private final GattCharacteristic mTerminationReasonCharacteristic;
124     private final GattCharacteristic mIncomingCallCharacteristic;
125     private final GattCharacteristic mCallFriendlyNameCharacteristic;
126     private boolean mSilentMode = false;
127     private Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>();
128 
129     @GuardedBy("mPendingGattOperationsLock")
130     private Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>();
131 
132     private BluetoothGattServerProxy mBluetoothGattServer;
133     private Handler mHandler;
134     private Callback mCallback;
135     private AdapterService mAdapterService;
136     private HashMap<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues;
137     private TbsService mTbsService;
138 
139     private static final int LOG_NB_EVENTS = 200;
140     private BluetoothEventLogger mEventLogger = null;
141 
tbsUuidToString(UUID uuid)142     private static String tbsUuidToString(UUID uuid) {
143         if (uuid.equals(UUID_BEARER_PROVIDER_NAME)) {
144             return "BEARER_PROVIDER_NAME";
145         } else if (uuid.equals(UUID_BEARER_UCI)) {
146             return "BEARER_UCI";
147         } else if (uuid.equals(UUID_BEARER_TECHNOLOGY)) {
148             return "BEARER_TECHNOLOGY";
149         } else if (uuid.equals(UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST)) {
150             return "BEARER_URI_SCHEMES_SUPPORTED_LIST";
151         } else if (uuid.equals(UUID_BEARER_LIST_CURRENT_CALLS)) {
152             return "BEARER_LIST_CURRENT_CALLS";
153         } else if (uuid.equals(UUID_CONTENT_CONTROL_ID)) {
154             return "CONTENT_CONTROL_ID";
155         } else if (uuid.equals(UUID_STATUS_FLAGS)) {
156             return "STATUS_FLAGS";
157         } else if (uuid.equals(UUID_CALL_STATE)) {
158             return "CALL_STATE";
159         } else if (uuid.equals(UUID_CALL_CONTROL_POINT)) {
160             return "CALL_CONTROL_POINT";
161         } else if (uuid.equals(UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES)) {
162             return "CALL_CONTROL_POINT_OPTIONAL_OPCODES";
163         } else if (uuid.equals(UUID_TERMINATION_REASON)) {
164             return "TERMINATION_REASON";
165         } else if (uuid.equals(UUID_INCOMING_CALL)) {
166             return "INCOMING_CALL";
167         } else if (uuid.equals(UUID_CALL_FRIENDLY_NAME)) {
168             return "CALL_FRIENDLY_NAME";
169         } else if (uuid.equals(UUID_CLIENT_CHARACTERISTIC_CONFIGURATION)) {
170             return "CLIENT_CHARACTERISTIC_CONFIGURATION";
171         } else {
172             return "UNKNOWN(" + uuid + ")";
173         }
174     }
175 
176     public abstract static class Callback {
177 
onServiceAdded(boolean success)178         public abstract void onServiceAdded(boolean success);
179 
onCallControlPointRequest( BluetoothDevice device, int opcode, byte[] args)180         public abstract void onCallControlPointRequest(
181                 BluetoothDevice device, int opcode, byte[] args);
182 
183         /**
184          * Check if device has enabled inband ringtone
185          *
186          * @param device device which is checked for inband ringtone availability
187          * @return {@code true} if enabled, {@code false} otherwise
188          */
isInbandRingtoneEnabled(BluetoothDevice device)189         public abstract boolean isInbandRingtoneEnabled(BluetoothDevice device);
190     }
191 
192     private static class GattOpContext {
193         public enum Operation {
194             READ_CHARACTERISTIC,
195             WRITE_CHARACTERISTIC,
196             READ_DESCRIPTOR,
197             WRITE_DESCRIPTOR,
198         }
199 
GattOpContext( Operation operation, int requestId, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)200         GattOpContext(
201                 Operation operation,
202                 int requestId,
203                 BluetoothGattCharacteristic characteristic,
204                 BluetoothGattDescriptor descriptor,
205                 boolean preparedWrite,
206                 boolean responseNeeded,
207                 int offset,
208                 byte[] value) {
209             mOperation = operation;
210             mRequestId = requestId;
211             mCharacteristic = characteristic;
212             mDescriptor = descriptor;
213             mPreparedWrite = preparedWrite;
214             mResponseNeeded = responseNeeded;
215             mOffset = offset;
216             mValue = value;
217         }
218 
GattOpContext( Operation operation, int requestId, BluetoothGattCharacteristic characteristic, BluetoothGattDescriptor descriptor)219         GattOpContext(
220                 Operation operation,
221                 int requestId,
222                 BluetoothGattCharacteristic characteristic,
223                 BluetoothGattDescriptor descriptor) {
224             mOperation = operation;
225             mRequestId = requestId;
226             mCharacteristic = characteristic;
227             mDescriptor = descriptor;
228             mPreparedWrite = false;
229             mResponseNeeded = false;
230             mOffset = 0;
231             mValue = null;
232         }
233 
234         public Operation mOperation;
235         public int mRequestId;
236         public BluetoothGattCharacteristic mCharacteristic;
237         public BluetoothGattDescriptor mDescriptor;
238         public boolean mPreparedWrite;
239         public boolean mResponseNeeded;
240         public int mOffset;
241         public byte[] mValue;
242     }
243 
TbsGatt(TbsService tbsService)244     TbsGatt(TbsService tbsService) {
245         mContext = tbsService;
246         mAdapterService =
247                 Objects.requireNonNull(
248                         AdapterService.getAdapterService(),
249                         "AdapterService shouldn't be null when creating TbsGatt");
250 
251         mAdapterService.registerBluetoothStateCallback(
252                 mContext.getMainExecutor(), mBluetoothStateChangeCallback);
253 
254         mBearerProviderNameCharacteristic =
255                 new GattCharacteristic(
256                         UUID_BEARER_PROVIDER_NAME,
257                         BluetoothGattCharacteristic.PROPERTY_READ
258                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
259                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
260         mBearerUciCharacteristic =
261                 new GattCharacteristic(
262                         UUID_BEARER_UCI,
263                         BluetoothGattCharacteristic.PROPERTY_READ,
264                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
265         mBearerTechnologyCharacteristic =
266                 new GattCharacteristic(
267                         UUID_BEARER_TECHNOLOGY,
268                         BluetoothGattCharacteristic.PROPERTY_READ
269                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
270                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
271         mBearerUriSchemesSupportedListCharacteristic =
272                 new GattCharacteristic(
273                         UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST,
274                         BluetoothGattCharacteristic.PROPERTY_READ
275                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
276                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
277         mBearerListCurrentCallsCharacteristic =
278                 new GattCharacteristic(
279                         UUID_BEARER_LIST_CURRENT_CALLS,
280                         BluetoothGattCharacteristic.PROPERTY_READ
281                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
282                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
283         mContentControlIdCharacteristic =
284                 new GattCharacteristic(
285                         UUID_CONTENT_CONTROL_ID,
286                         BluetoothGattCharacteristic.PROPERTY_READ,
287                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
288         mStatusFlagsCharacteristic =
289                 new GattCharacteristic(
290                         UUID_STATUS_FLAGS,
291                         BluetoothGattCharacteristic.PROPERTY_READ
292                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
293                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
294         mCallStateCharacteristic =
295                 new GattCharacteristic(
296                         UUID_CALL_STATE,
297                         BluetoothGattCharacteristic.PROPERTY_READ
298                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
299                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
300         mCallControlPointCharacteristic = new CallControlPointCharacteristic();
301         mCallControlPointOptionalOpcodesCharacteristic =
302                 new GattCharacteristic(
303                         UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES,
304                         BluetoothGattCharacteristic.PROPERTY_READ,
305                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
306         mTerminationReasonCharacteristic =
307                 new GattCharacteristic(
308                         UUID_TERMINATION_REASON, BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0);
309         mIncomingCallCharacteristic =
310                 new GattCharacteristic(
311                         UUID_INCOMING_CALL,
312                         BluetoothGattCharacteristic.PROPERTY_READ
313                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
314                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
315         mCallFriendlyNameCharacteristic =
316                 new GattCharacteristic(
317                         UUID_CALL_FRIENDLY_NAME,
318                         BluetoothGattCharacteristic.PROPERTY_READ
319                                 | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
320                         BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
321 
322         mTbsService = tbsService;
323         mBluetoothGattServer = null;
324     }
325 
326     @VisibleForTesting
setBluetoothGattServerForTesting(BluetoothGattServerProxy proxy)327     void setBluetoothGattServerForTesting(BluetoothGattServerProxy proxy) {
328         mBluetoothGattServer = proxy;
329     }
330 
init( int ccid, String uci, List<String> uriSchemes, boolean isLocalHoldOpcodeSupported, boolean isJoinOpcodeSupported, String providerName, int technology, Callback callback)331     public boolean init(
332             int ccid,
333             String uci,
334             List<String> uriSchemes,
335             boolean isLocalHoldOpcodeSupported,
336             boolean isJoinOpcodeSupported,
337             String providerName,
338             int technology,
339             Callback callback) {
340         mCccDescriptorValues = new HashMap<>();
341         mBearerProviderNameCharacteristic.setValue(providerName);
342         mBearerTechnologyCharacteristic.setValue(new byte[] {(byte) (technology & 0xFF)});
343         mBearerUciCharacteristic.setValue(uci);
344         setBearerUriSchemesSupportedList(uriSchemes);
345         mContentControlIdCharacteristic.setValue(ccid, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
346         setCallControlPointOptionalOpcodes(isLocalHoldOpcodeSupported, isJoinOpcodeSupported);
347         mStatusFlagsCharacteristic.setValue(0, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
348         mCallback = callback;
349         mHandler = new Handler(Looper.getMainLooper());
350 
351         if (mBluetoothGattServer == null) {
352             mBluetoothGattServer = new BluetoothGattServerProxy(mContext);
353         }
354 
355         if (!mBluetoothGattServer.open(mGattServerCallback)) {
356             Log.e(TAG, " Could not open Gatt server");
357             return false;
358         }
359 
360         BluetoothGattService gattService =
361                 new BluetoothGattService(UUID_GTBS, BluetoothGattService.SERVICE_TYPE_PRIMARY);
362         gattService.addCharacteristic(mBearerProviderNameCharacteristic);
363         gattService.addCharacteristic(mBearerUciCharacteristic);
364         gattService.addCharacteristic(mBearerTechnologyCharacteristic);
365         gattService.addCharacteristic(mBearerUriSchemesSupportedListCharacteristic);
366         gattService.addCharacteristic(mBearerListCurrentCallsCharacteristic);
367         gattService.addCharacteristic(mContentControlIdCharacteristic);
368         gattService.addCharacteristic(mStatusFlagsCharacteristic);
369         gattService.addCharacteristic(mCallStateCharacteristic);
370         gattService.addCharacteristic(mCallControlPointCharacteristic);
371         gattService.addCharacteristic(mCallControlPointOptionalOpcodesCharacteristic);
372         gattService.addCharacteristic(mTerminationReasonCharacteristic);
373         gattService.addCharacteristic(mIncomingCallCharacteristic);
374         gattService.addCharacteristic(mCallFriendlyNameCharacteristic);
375 
376         mEventLogger =
377                 new BluetoothEventLogger(
378                         LOG_NB_EVENTS, TAG + " instance (CCID= " + ccid + ") event log");
379         mEventLogger.add("Initializing");
380 
381         return mBluetoothGattServer.addService(gattService);
382     }
383 
cleanup()384     public void cleanup() {
385         mAdapterService.unregisterBluetoothStateCallback(mBluetoothStateChangeCallback);
386 
387         if (mBluetoothGattServer == null) {
388             return;
389         }
390         mBluetoothGattServer.close();
391         mBluetoothGattServer = null;
392     }
393 
getContext()394     public Context getContext() {
395         return mContext;
396     }
397 
removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device)398     private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) {
399         List<ParcelUuid> uuidList;
400         byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);
401 
402         if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
403             uuidList = new ArrayList<ParcelUuid>();
404         } else {
405             uuidList = new ArrayList<>(Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd)));
406 
407             if (!uuidList.contains(charUuid)) {
408                 Log.d(
409                         TAG,
410                         "Characteristic CCCD can't be removed (not cached): "
411                                 + charUuid.toString());
412                 return;
413             }
414         }
415 
416         uuidList.remove(charUuid);
417 
418         if (!device.setMetadata(
419                 METADATA_GTBS_CCCD, Utils.uuidsToByteArray(uuidList.toArray(new ParcelUuid[0])))) {
420             Log.e(TAG, "Can't set CCCD for GTBS characteristic UUID: " + charUuid + ", (remove)");
421         }
422     }
423 
addUuidToMetadata(ParcelUuid charUuid, BluetoothDevice device)424     private void addUuidToMetadata(ParcelUuid charUuid, BluetoothDevice device) {
425         List<ParcelUuid> uuidList;
426         byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);
427 
428         if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
429             uuidList = new ArrayList<ParcelUuid>();
430         } else {
431             uuidList = new ArrayList<>(Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd)));
432 
433             if (uuidList.contains(charUuid)) {
434                 Log.d(TAG, "Characteristic CCCD already add: " + charUuid.toString());
435                 return;
436             }
437         }
438 
439         uuidList.add(charUuid);
440 
441         if (!device.setMetadata(
442                 METADATA_GTBS_CCCD, Utils.uuidsToByteArray(uuidList.toArray(new ParcelUuid[0])))) {
443             Log.e(TAG, "Can't set CCCD for GTBS characteristic UUID: " + charUuid + ", (add)");
444         }
445     }
446 
447     @VisibleForTesting
setCcc(BluetoothDevice device, UUID charUuid, byte[] value)448     void setCcc(BluetoothDevice device, UUID charUuid, byte[] value) {
449         HashMap<UUID, Short> characteristicCcc = mCccDescriptorValues.get(device);
450         if (characteristicCcc == null) {
451             characteristicCcc = new HashMap<>();
452             mCccDescriptorValues.put(device, characteristicCcc);
453         }
454 
455         characteristicCcc.put(
456                 charUuid, ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getShort());
457 
458         Log.d(
459                 TAG,
460                 "setCcc, device: "
461                         + device.getAddress()
462                         + ", UUID: "
463                         + charUuid
464                         + ", value: "
465                         + characteristicCcc.get(charUuid));
466     }
467 
getCccBytes(BluetoothDevice device, UUID charUuid)468     private byte[] getCccBytes(BluetoothDevice device, UUID charUuid) {
469         Map<UUID, Short> characteristicCcc = mCccDescriptorValues.get(device);
470         if (characteristicCcc != null) {
471             ByteBuffer bb = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN);
472             Short ccc = characteristicCcc.get(charUuid);
473             if (ccc != null) {
474                 bb.putShort(characteristicCcc.get(charUuid));
475                 return bb.array();
476             }
477         }
478 
479         return BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
480     }
481 
482     /** Class that handles GATT characteristic notifications */
483     private class BluetoothGattCharacteristicNotifier {
setSubscriptionConfiguration( BluetoothDevice device, UUID uuid, byte[] configuration)484         public int setSubscriptionConfiguration(
485                 BluetoothDevice device, UUID uuid, byte[] configuration) {
486             setCcc(device, uuid, configuration);
487 
488             return BluetoothGatt.GATT_SUCCESS;
489         }
490 
getSubscriptionConfiguration(BluetoothDevice device, UUID uuid)491         public byte[] getSubscriptionConfiguration(BluetoothDevice device, UUID uuid) {
492             return getCccBytes(device, uuid);
493         }
494 
isSubscribed(BluetoothDevice device, UUID uuid)495         public boolean isSubscribed(BluetoothDevice device, UUID uuid) {
496             return Arrays.equals(
497                     getCccBytes(device, uuid), BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
498         }
499 
notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value)500         private void notifyCharacteristicChanged(
501                 BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) {
502             if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return;
503             if (value == null) return;
504             if (mBluetoothGattServer != null) {
505                 mBluetoothGattServer.notifyCharacteristicChanged(
506                         device, characteristic, false, value);
507             }
508         }
509 
notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic)510         private void notifyCharacteristicChanged(
511                 BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
512             if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return;
513 
514             if (mBluetoothGattServer != null) {
515                 mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
516             }
517         }
518 
notifyWithValue( BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value)519         public void notifyWithValue(
520                 BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) {
521             if (isSubscribed(device, characteristic.getUuid())) {
522                 notifyCharacteristicChanged(device, characteristic, value);
523             }
524         }
525 
notify(BluetoothDevice device, BluetoothGattCharacteristic characteristic)526         public void notify(BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
527             if (isSubscribed(device, characteristic.getUuid())) {
528                 notifyCharacteristicChanged(device, characteristic);
529             }
530         }
531 
notifyAll(BluetoothGattCharacteristic characteristic)532         public void notifyAll(BluetoothGattCharacteristic characteristic) {
533             for (BluetoothDevice device : mCccDescriptorValues.keySet()) {
534                 notify(device, characteristic);
535             }
536         }
537     }
538 
539     /** Wrapper class for BluetoothGattCharacteristic */
540     private class GattCharacteristic extends BluetoothGattCharacteristic {
541 
542         protected BluetoothGattCharacteristicNotifier mNotifier;
543 
GattCharacteristic(UUID uuid, int properties, int permissions)544         public GattCharacteristic(UUID uuid, int properties, int permissions) {
545             super(uuid, properties, permissions);
546             if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
547                 mNotifier = new BluetoothGattCharacteristicNotifier();
548                 addDescriptor(new ClientCharacteristicConfigurationDescriptor());
549             } else {
550                 mNotifier = null;
551             }
552         }
553 
getSubscriptionConfiguration(BluetoothDevice device, UUID uuid)554         public byte[] getSubscriptionConfiguration(BluetoothDevice device, UUID uuid) {
555             return mNotifier.getSubscriptionConfiguration(device, uuid);
556         }
557 
setSubscriptionConfiguration( BluetoothDevice device, UUID uuid, byte[] configuration)558         public int setSubscriptionConfiguration(
559                 BluetoothDevice device, UUID uuid, byte[] configuration) {
560             return mNotifier.setSubscriptionConfiguration(device, uuid, configuration);
561         }
562 
isNotifiable()563         private boolean isNotifiable() {
564             return mNotifier != null;
565         }
566 
567         @Override
setValue(byte[] value)568         public boolean setValue(byte[] value) {
569             boolean success = super.setValue(value);
570             if (success && isNotifiable()) {
571                 mNotifier.notifyAll(this);
572             }
573 
574             return success;
575         }
576 
577         @Override
setValue(int value, int formatType, int offset)578         public boolean setValue(int value, int formatType, int offset) {
579             boolean success = super.setValue(value, formatType, offset);
580             if (success && isNotifiable()) {
581                 mNotifier.notifyAll(this);
582             }
583 
584             return success;
585         }
586 
587         @Override
setValue(String value)588         public boolean setValue(String value) {
589             boolean success = super.setValue(value);
590             if (success && isNotifiable()) {
591                 mNotifier.notifyAll(this);
592             }
593 
594             return success;
595         }
596 
setValueNoNotify(byte[] value)597         public boolean setValueNoNotify(byte[] value) {
598             return super.setValue(value);
599         }
600 
notifyWithValue(BluetoothDevice device, byte[] value)601         public boolean notifyWithValue(BluetoothDevice device, byte[] value) {
602             if (isNotifiable()) {
603                 mNotifier.notifyWithValue(device, this, value);
604                 return true;
605             }
606             return false;
607         }
608 
notify(BluetoothDevice device)609         public void notify(BluetoothDevice device) {
610             if (isNotifiable() && super.getValue() != null) {
611                 mNotifier.notify(device, this);
612             }
613         }
614 
clearValue(boolean notify)615         public boolean clearValue(boolean notify) {
616             boolean success = super.setValue(new byte[0]);
617             if (success && notify && isNotifiable()) {
618                 mNotifier.notifyAll(this);
619             }
620 
621             return success;
622         }
623 
handleWriteRequest( BluetoothDevice device, int requestId, boolean responseNeeded, byte[] value)624         public void handleWriteRequest(
625                 BluetoothDevice device, int requestId, boolean responseNeeded, byte[] value) {
626             if (responseNeeded) {
627                 mBluetoothGattServer.sendResponse(
628                         device, requestId, BluetoothGatt.GATT_FAILURE, 0, value);
629             }
630         }
631     }
632 
633     private class CallControlPointCharacteristic extends GattCharacteristic {
634 
CallControlPointCharacteristic()635         public CallControlPointCharacteristic() {
636             super(
637                     UUID_CALL_CONTROL_POINT,
638                     PROPERTY_WRITE | PROPERTY_WRITE_NO_RESPONSE | PROPERTY_NOTIFY,
639                     PERMISSION_WRITE_ENCRYPTED);
640         }
641 
642         @Override
handleWriteRequest( BluetoothDevice device, int requestId, boolean responseNeeded, byte[] value)643         public void handleWriteRequest(
644                 BluetoothDevice device, int requestId, boolean responseNeeded, byte[] value) {
645             int status;
646             if (value.length < 2) {
647                 // at least opcode is required and value is at least 1 byte
648                 status = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
649             } else {
650                 status = BluetoothGatt.GATT_SUCCESS;
651             }
652 
653             if (responseNeeded) {
654                 mBluetoothGattServer.sendResponse(device, requestId, status, 0, value);
655             }
656 
657             if (status != BluetoothGatt.GATT_SUCCESS) {
658                 return;
659             }
660 
661             int opcode = (int) value[0];
662             mCallback.onCallControlPointRequest(
663                     device, opcode, Arrays.copyOfRange(value, 1, value.length));
664         }
665 
setResult( BluetoothDevice device, int requestedOpcode, int callIndex, int requestResult)666         public void setResult(
667                 BluetoothDevice device, int requestedOpcode, int callIndex, int requestResult) {
668             byte[] value = new byte[3];
669             value[0] = (byte) (requestedOpcode);
670             value[1] = (byte) (callIndex);
671             value[2] = (byte) (requestResult);
672 
673             super.setValueNoNotify(value);
674 
675             // to avoid sending control point notification before write response
676             mHandler.post(() -> mNotifier.notify(device, this));
677         }
678     }
679 
680     private class ClientCharacteristicConfigurationDescriptor extends BluetoothGattDescriptor {
681 
ClientCharacteristicConfigurationDescriptor()682         ClientCharacteristicConfigurationDescriptor() {
683             super(
684                     UUID_CLIENT_CHARACTERISTIC_CONFIGURATION,
685                     PERMISSION_WRITE_ENCRYPTED | PERMISSION_READ_ENCRYPTED);
686         }
687 
getValue(BluetoothDevice device)688         public byte[] getValue(BluetoothDevice device) {
689             GattCharacteristic characteristic = (GattCharacteristic) getCharacteristic();
690             byte[] value =
691                     characteristic.getSubscriptionConfiguration(device, characteristic.getUuid());
692             if (value == null) {
693                 return BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
694             }
695 
696             return value;
697         }
698 
setValue(BluetoothDevice device, byte[] value)699         public int setValue(BluetoothDevice device, byte[] value) {
700             GattCharacteristic characteristic = (GattCharacteristic) getCharacteristic();
701             int properties = characteristic.getProperties();
702 
703             if (value.length != 2) {
704                 return BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
705 
706             } else if ((!Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
707                             && !Arrays.equals(
708                                     value, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
709                             && !Arrays.equals(
710                                     value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE))
711                     || ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0
712                             && Arrays.equals(
713                                     value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE))
714                     || ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == 0
715                             && Arrays.equals(
716                                     value, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE))) {
717                 return BluetoothGatt.GATT_FAILURE;
718             }
719 
720             if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
721                 addUuidToMetadata(new ParcelUuid(characteristic.getUuid()), device);
722             } else if (Arrays.equals(value, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
723                 removeUuidFromMetadata(new ParcelUuid(characteristic.getUuid()), device);
724             } else {
725                 Log.e(TAG, "Not handled CCC value: " + Arrays.toString(value));
726             }
727 
728             return characteristic.setSubscriptionConfiguration(
729                     device, characteristic.getUuid(), value);
730         }
731     }
732 
setBearerProviderName(String providerName)733     public boolean setBearerProviderName(String providerName) {
734         return mBearerProviderNameCharacteristic.setValue(providerName);
735     }
736 
setBearerTechnology(int technology)737     public boolean setBearerTechnology(int technology) {
738         return mBearerTechnologyCharacteristic.setValue(
739                 technology, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
740     }
741 
setBearerUriSchemesSupportedList(List<String> bearerUriSchemesSupportedList)742     public boolean setBearerUriSchemesSupportedList(List<String> bearerUriSchemesSupportedList) {
743         return mBearerUriSchemesSupportedListCharacteristic.setValue(
744                 String.join(",", bearerUriSchemesSupportedList));
745     }
746 
setCallState(Map<Integer, TbsCall> callsList)747     public boolean setCallState(Map<Integer, TbsCall> callsList) {
748         Log.d(TAG, "setCallState: callsList=" + callsList);
749         int i = 0;
750         byte[] value = new byte[callsList.size() * 3];
751         for (Map.Entry<Integer, TbsCall> entry : callsList.entrySet()) {
752             TbsCall call = entry.getValue();
753             value[i++] = (byte) (entry.getKey() & 0xff);
754             value[i++] = (byte) (call.getState() & 0xff);
755             value[i++] = (byte) (call.getFlags() & 0xff);
756         }
757 
758         return mCallStateCharacteristic.setValue(value);
759     }
760 
setBearerListCurrentCalls(Map<Integer, TbsCall> callsList)761     public boolean setBearerListCurrentCalls(Map<Integer, TbsCall> callsList) {
762         Log.d(TAG, "setBearerListCurrentCalls: callsList=" + callsList);
763         final int listItemLengthMax = Byte.MAX_VALUE;
764 
765         ByteArrayOutputStream stream = new ByteArrayOutputStream();
766         for (Map.Entry<Integer, TbsCall> entry : callsList.entrySet()) {
767             TbsCall call = entry.getValue();
768             if (call == null) {
769                 Log.w(TAG, "setBearerListCurrentCalls: call is null");
770                 continue;
771             }
772 
773             int uri_len = 0;
774             if (call.getUri() != null) {
775                 uri_len = call.getUri().getBytes().length;
776             }
777 
778             int listItemLength = Math.min(listItemLengthMax, 3 + uri_len);
779             stream.write((byte) (listItemLength & 0xff));
780             stream.write((byte) (entry.getKey() & 0xff));
781             stream.write((byte) (call.getState() & 0xff));
782             stream.write((byte) (call.getFlags() & 0xff));
783             if (uri_len > 0) {
784                 stream.write(call.getUri().getBytes(), 0, listItemLength - 3);
785             }
786         }
787 
788         return mBearerListCurrentCallsCharacteristic.setValue(stream.toByteArray());
789     }
790 
updateStatusFlags(BluetoothDevice device, int valueInt)791     private boolean updateStatusFlags(BluetoothDevice device, int valueInt) {
792         /* uint16_t */
793         byte[] value = new byte[2];
794         value[0] = (byte) (valueInt & 0xFF);
795         value[1] = (byte) ((valueInt >> 8) & 0xFF);
796         return mStatusFlagsCharacteristic.notifyWithValue(device, value);
797     }
798 
updateStatusFlagsInbandRingtone(BluetoothDevice device, boolean set)799     private boolean updateStatusFlagsInbandRingtone(BluetoothDevice device, boolean set) {
800         boolean entryExist = mStatusFlagValue.containsKey(device);
801         if (entryExist
802                 && (((mStatusFlagValue.get(device) & STATUS_FLAG_INBAND_RINGTONE_ENABLED) != 0)
803                         == set)) {
804             Log.i(TAG, "Silent mode already set for " + device);
805             return false;
806         }
807 
808         Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0;
809         valueInt ^= STATUS_FLAG_INBAND_RINGTONE_ENABLED;
810 
811         if (entryExist) {
812             mStatusFlagValue.replace(device, valueInt);
813         } else {
814             mStatusFlagValue.put(device, valueInt);
815         }
816         return updateStatusFlags(device, valueInt);
817     }
818 
updateStatusFlagsSilentMode(boolean set)819     private boolean updateStatusFlagsSilentMode(boolean set) {
820         mSilentMode = set;
821         for (BluetoothDevice device : mCccDescriptorValues.keySet()) {
822             boolean entryExist = mStatusFlagValue.containsKey(device);
823             if (entryExist
824                     && (((mStatusFlagValue.get(device) & STATUS_FLAG_SILENT_MODE_ENABLED) != 0)
825                             == set)) {
826                 Log.i(TAG, "Silent mode already set for " + device);
827                 continue;
828             }
829 
830             Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0;
831             valueInt ^= STATUS_FLAG_SILENT_MODE_ENABLED;
832 
833             if (entryExist) {
834                 mStatusFlagValue.replace(device, valueInt);
835             } else {
836                 mStatusFlagValue.put(device, valueInt);
837             }
838             updateStatusFlags(device, valueInt);
839         }
840         return true;
841     }
842 
843     /**
844      * Set inband ringtone for the device. When set, notification will be sent to given device.
845      *
846      * @param device device for which inband ringtone has been set
847      * @return true, when notification has been sent, false otherwise
848      */
setInbandRingtoneFlag(BluetoothDevice device)849     public boolean setInbandRingtoneFlag(BluetoothDevice device) {
850         return updateStatusFlagsInbandRingtone(device, true);
851     }
852 
853     /**
854      * Clear inband ringtone for the device. When set, notification will be sent to given device.
855      *
856      * @param device device for which inband ringtone has been cleared
857      * @return true, when notification has been sent, false otherwise
858      */
clearInbandRingtoneFlag(BluetoothDevice device)859     public boolean clearInbandRingtoneFlag(BluetoothDevice device) {
860         return updateStatusFlagsInbandRingtone(device, false);
861     }
862 
setSilentModeFlag()863     public boolean setSilentModeFlag() {
864         return updateStatusFlagsSilentMode(true);
865     }
866 
clearSilentModeFlag()867     public boolean clearSilentModeFlag() {
868         return updateStatusFlagsSilentMode(false);
869     }
870 
setCallControlPointOptionalOpcodes( boolean isLocalHoldOpcodeSupported, boolean isJoinOpcodeSupported)871     private void setCallControlPointOptionalOpcodes(
872             boolean isLocalHoldOpcodeSupported, boolean isJoinOpcodeSupported) {
873         int valueInt = 0;
874         if (isLocalHoldOpcodeSupported) {
875             valueInt |= CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD;
876         }
877         if (isJoinOpcodeSupported) {
878             valueInt |= CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN;
879         }
880 
881         byte[] value = new byte[2];
882         value[0] = (byte) (valueInt & 0xff);
883         value[1] = (byte) ((valueInt >> 8) & 0xff);
884 
885         mCallControlPointOptionalOpcodesCharacteristic.setValue(value);
886     }
887 
setTerminationReason(int callIndex, int terminationReason)888     public boolean setTerminationReason(int callIndex, int terminationReason) {
889         Log.d(
890                 TAG,
891                 "setTerminationReason: callIndex="
892                         + callIndex
893                         + " terminationReason="
894                         + terminationReason);
895         byte[] value = new byte[2];
896         value[0] = (byte) (callIndex & 0xff);
897         value[1] = (byte) (terminationReason & 0xff);
898 
899         return mTerminationReasonCharacteristic.setValue(value);
900     }
901 
getIncomingCallIndex()902     public Integer getIncomingCallIndex() {
903         byte[] value = mIncomingCallCharacteristic.getValue();
904         if (value == null || value.length == 0) {
905             return null;
906         }
907 
908         return (int) value[0];
909     }
910 
setIncomingCall(int callIndex, String uri)911     public boolean setIncomingCall(int callIndex, String uri) {
912         Log.d(TAG, "setIncomingCall: callIndex=" + callIndex + " uri=" + uri);
913         int uri_len = 0;
914         if (uri != null) {
915             uri_len = uri.length();
916         }
917 
918         byte[] value = new byte[uri_len + 1];
919         value[0] = (byte) (callIndex & 0xff);
920 
921         if (uri_len > 0) {
922             System.arraycopy(uri.getBytes(), 0, value, 1, uri_len);
923         }
924 
925         return mIncomingCallCharacteristic.setValue(value);
926     }
927 
clearIncomingCall()928     public boolean clearIncomingCall() {
929         Log.d(TAG, "clearIncomingCall");
930         return mIncomingCallCharacteristic.clearValue(false);
931     }
932 
setCallFriendlyName(int callIndex, String callFriendlyName)933     public boolean setCallFriendlyName(int callIndex, String callFriendlyName) {
934         Log.d(
935                 TAG,
936                 "setCallFriendlyName: callIndex="
937                         + callIndex
938                         + "callFriendlyName="
939                         + callFriendlyName);
940         byte[] value = new byte[callFriendlyName.length() + 1];
941         value[0] = (byte) (callIndex & 0xff);
942         System.arraycopy(callFriendlyName.getBytes(), 0, value, 1, callFriendlyName.length());
943 
944         return mCallFriendlyNameCharacteristic.setValue(value);
945     }
946 
getCallFriendlyNameIndex()947     public Integer getCallFriendlyNameIndex() {
948         byte[] value = mCallFriendlyNameCharacteristic.getValue();
949         if (value == null || value.length == 0) {
950             return null;
951         }
952 
953         return (int) value[0];
954     }
955 
clearFriendlyName()956     public boolean clearFriendlyName() {
957         Log.d(TAG, "clearFriendlyName");
958         return mCallFriendlyNameCharacteristic.clearValue(false);
959     }
960 
setCallControlPointResult( BluetoothDevice device, int requestedOpcode, int callIndex, int requestResult)961     public void setCallControlPointResult(
962             BluetoothDevice device, int requestedOpcode, int callIndex, int requestResult) {
963         Log.d(
964                 TAG,
965                 "setCallControlPointResult: device="
966                         + device
967                         + " requestedOpcode="
968                         + requestedOpcode
969                         + " callIndex="
970                         + callIndex
971                         + " requesuResult="
972                         + requestResult);
973         mCallControlPointCharacteristic.setResult(
974                 device, requestedOpcode, callIndex, requestResult);
975     }
976 
makeUuid(String uuid16)977     private static UUID makeUuid(String uuid16) {
978         return UUID.fromString(UUID_PREFIX + uuid16 + UUID_SUFFIX);
979     }
980 
restoreCccValuesForStoredDevices()981     private void restoreCccValuesForStoredDevices() {
982         BluetoothGattService gattService = mBluetoothGattServer.getService(UUID_GTBS);
983 
984         for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
985             byte[] gtbs_cccd = device.getMetadata(METADATA_GTBS_CCCD);
986 
987             if ((gtbs_cccd == null) || (gtbs_cccd.length == 0)) {
988                 return;
989             }
990 
991             List<ParcelUuid> uuidList = Arrays.asList(Utils.byteArrayToUuid(gtbs_cccd));
992 
993             /* Restore CCCD values for device */
994             for (ParcelUuid uuid : uuidList) {
995                 BluetoothGattCharacteristic characteristic =
996                         gattService.getCharacteristic(uuid.getUuid());
997                 if (characteristic == null) {
998                     Log.e(TAG, "Invalid UUID stored in metadata: " + uuid.toString());
999                     continue;
1000                 }
1001 
1002                 BluetoothGattDescriptor descriptor =
1003                         characteristic.getDescriptor(UUID_CLIENT_CHARACTERISTIC_CONFIGURATION);
1004                 if (descriptor == null) {
1005                     Log.e(TAG, "Invalid characteristic, does not include CCCD");
1006                     continue;
1007                 }
1008 
1009                 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
1010             }
1011         }
1012     }
1013 
1014     private final AdapterService.BluetoothStateCallback mBluetoothStateChangeCallback =
1015             new AdapterService.BluetoothStateCallback() {
1016                 public void onBluetoothStateChange(int prevState, int newState) {
1017                     Log.d(
1018                             TAG,
1019                             "onBluetoothStateChange: state="
1020                                     + BluetoothAdapter.nameForState(newState));
1021                     if (newState == BluetoothAdapter.STATE_ON) {
1022                         restoreCccValuesForStoredDevices();
1023                     }
1024                 }
1025             };
1026 
getDeviceAuthorization(BluetoothDevice device)1027     private int getDeviceAuthorization(BluetoothDevice device) {
1028         return mTbsService.getDeviceAuthorization(device);
1029     }
1030 
onRejectedAuthorizationGattOperation(BluetoothDevice device, GattOpContext op)1031     private void onRejectedAuthorizationGattOperation(BluetoothDevice device, GattOpContext op) {
1032         UUID charUuid =
1033                 (op.mCharacteristic != null
1034                         ? op.mCharacteristic.getUuid()
1035                         : (op.mDescriptor != null
1036                                 ? op.mDescriptor.getCharacteristic().getUuid()
1037                                 : null));
1038         mEventLogger.logw(
1039                 TAG,
1040                 "onRejectedAuthorizationGattOperation device: "
1041                         + device
1042                         + ", opcode= "
1043                         + op.mOperation
1044                         + ", characteristic= "
1045                         + (charUuid != null ? tbsUuidToString(charUuid) : "UNKNOWN"));
1046 
1047         switch (op.mOperation) {
1048             case READ_CHARACTERISTIC:
1049             case READ_DESCRIPTOR:
1050                 mBluetoothGattServer.sendResponse(
1051                         device,
1052                         op.mRequestId,
1053                         BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION,
1054                         op.mOffset,
1055                         null);
1056                 break;
1057             case WRITE_CHARACTERISTIC:
1058                 if (op.mResponseNeeded) {
1059                     mBluetoothGattServer.sendResponse(
1060                             device,
1061                             op.mRequestId,
1062                             BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION,
1063                             op.mOffset,
1064                             null);
1065                 } else {
1066                     // In case of control point operations we can send an application error code
1067                     if (op.mCharacteristic.getUuid().equals(UUID_CALL_CONTROL_POINT)) {
1068                         setCallControlPointResult(
1069                                 device,
1070                                 op.mOperation.ordinal(),
1071                                 0,
1072                                 TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE);
1073                     }
1074                 }
1075                 break;
1076             case WRITE_DESCRIPTOR:
1077                 if (op.mResponseNeeded) {
1078                     mBluetoothGattServer.sendResponse(
1079                             device,
1080                             op.mRequestId,
1081                             BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION,
1082                             op.mOffset,
1083                             null);
1084                 }
1085                 break;
1086 
1087             default:
1088                 break;
1089         }
1090     }
1091 
onUnauthorizedCharRead(BluetoothDevice device, GattOpContext op)1092     private void onUnauthorizedCharRead(BluetoothDevice device, GattOpContext op) {
1093         UUID charUuid = op.mCharacteristic.getUuid();
1094         boolean allowToReadRealValue = false;
1095         byte[] buffer = null;
1096 
1097         /* Allow only some informations to be disclosed at this stage. */
1098         if (charUuid.equals(UUID_BEARER_PROVIDER_NAME)) {
1099             ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
1100             bb.put("".getBytes());
1101             buffer = bb.array();
1102 
1103         } else if (charUuid.equals(UUID_BEARER_UCI)) {
1104             buffer = "E.164".getBytes();
1105 
1106         } else if (charUuid.equals(UUID_BEARER_TECHNOLOGY)) {
1107             allowToReadRealValue = true;
1108 
1109         } else if (charUuid.equals(UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST)) {
1110             ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
1111             bb.put("".getBytes());
1112             buffer = bb.array();
1113 
1114         } else if (charUuid.equals(UUID_BEARER_LIST_CURRENT_CALLS)) {
1115             ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
1116             bb.put("".getBytes());
1117             buffer = bb.array();
1118 
1119         } else if (charUuid.equals(UUID_CONTENT_CONTROL_ID)) {
1120             allowToReadRealValue = true;
1121 
1122         } else if (charUuid.equals(UUID_STATUS_FLAGS)) {
1123             allowToReadRealValue = true;
1124 
1125         } else if (charUuid.equals(UUID_CALL_STATE)) {
1126             ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
1127             bb.put("".getBytes());
1128             buffer = bb.array();
1129 
1130         } else if (charUuid.equals(UUID_CALL_CONTROL_POINT)) {
1131             // No read is available on this characteristic
1132 
1133         } else if (charUuid.equals(UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES)) {
1134             ByteBuffer bb = ByteBuffer.allocate(1).order(ByteOrder.LITTLE_ENDIAN);
1135             bb.put((byte) 0x00);
1136             buffer = bb.array();
1137 
1138         } else if (charUuid.equals(UUID_TERMINATION_REASON)) {
1139             // No read is available on this characteristic
1140 
1141         } else if (charUuid.equals(UUID_INCOMING_CALL)) {
1142             ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
1143             bb.put("".getBytes());
1144             buffer = bb.array();
1145 
1146         } else if (charUuid.equals(UUID_CALL_FRIENDLY_NAME)) {
1147             ByteBuffer bb = ByteBuffer.allocate(1).order(ByteOrder.LITTLE_ENDIAN);
1148             bb.put((byte) 0x00);
1149             buffer = bb.array();
1150         }
1151 
1152         if (allowToReadRealValue) {
1153             if (op.mCharacteristic.getValue() != null) {
1154                 buffer =
1155                         Arrays.copyOfRange(
1156                                 op.mCharacteristic.getValue(),
1157                                 op.mOffset,
1158                                 op.mCharacteristic.getValue().length);
1159             }
1160         }
1161 
1162         if (buffer != null) {
1163             mBluetoothGattServer.sendResponse(
1164                     device, op.mRequestId, BluetoothGatt.GATT_SUCCESS, op.mOffset, buffer);
1165         } else {
1166             mEventLogger.loge(
1167                     TAG, "Missing characteristic value for char: " + tbsUuidToString(charUuid));
1168             mBluetoothGattServer.sendResponse(
1169                     device,
1170                     op.mRequestId,
1171                     BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH,
1172                     op.mOffset,
1173                     buffer);
1174         }
1175     }
1176 
onUnauthorizedGattOperation(BluetoothDevice device, GattOpContext op)1177     private void onUnauthorizedGattOperation(BluetoothDevice device, GattOpContext op) {
1178         UUID charUuid =
1179                 (op.mCharacteristic != null
1180                         ? op.mCharacteristic.getUuid()
1181                         : (op.mDescriptor != null
1182                                 ? op.mDescriptor.getCharacteristic().getUuid()
1183                                 : null));
1184         mEventLogger.logw(
1185                 TAG,
1186                 "onUnauthorizedGattOperation device: "
1187                         + device
1188                         + ", opcode= "
1189                         + op.mOperation
1190                         + ", characteristic= "
1191                         + (charUuid != null ? tbsUuidToString(charUuid) : "UNKNOWN"));
1192 
1193         int status = BluetoothGatt.GATT_SUCCESS;
1194 
1195         switch (op.mOperation) {
1196                 /* Allow not yet authorized devices to subscribe for notifications */
1197             case READ_DESCRIPTOR:
1198                 byte[] value = getCccBytes(device, op.mDescriptor.getCharacteristic().getUuid());
1199                 if (value.length < op.mOffset) {
1200                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1201                 } else {
1202                     value = Arrays.copyOfRange(value, op.mOffset, value.length);
1203                     status = BluetoothGatt.GATT_SUCCESS;
1204                 }
1205 
1206                 mBluetoothGattServer.sendResponse(device, op.mRequestId, status, op.mOffset, value);
1207                 return;
1208             case WRITE_DESCRIPTOR:
1209                 if (op.mPreparedWrite) {
1210                     status = BluetoothGatt.GATT_FAILURE;
1211                 } else if (op.mOffset > 0) {
1212                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1213                 } else if (op.mValue.length != 2) {
1214                     status = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
1215                 } else {
1216                     status = BluetoothGatt.GATT_SUCCESS;
1217                     setCcc(device, op.mDescriptor.getCharacteristic().getUuid(), op.mValue);
1218                 }
1219 
1220                 if (op.mResponseNeeded) {
1221                     mBluetoothGattServer.sendResponse(
1222                             device, op.mRequestId, status, op.mOffset, op.mValue);
1223                 }
1224                 return;
1225             case READ_CHARACTERISTIC:
1226                 onUnauthorizedCharRead(device, op);
1227                 return;
1228             case WRITE_CHARACTERISTIC:
1229                 // store as pending operation
1230                 break;
1231             default:
1232                 break;
1233         }
1234 
1235         synchronized (mPendingGattOperationsLock) {
1236             List<GattOpContext> operations = mPendingGattOperations.get(device);
1237             if (operations == null) {
1238                 operations = new ArrayList<>();
1239                 mPendingGattOperations.put(device, operations);
1240             }
1241 
1242             operations.add(op);
1243             // Send authorization request for each device only for it's first GATT request
1244             if (operations.size() == 1) {
1245                 mTbsService.onDeviceUnauthorized(device);
1246             }
1247         }
1248     }
1249 
onAuthorizedGattOperation(BluetoothDevice device, GattOpContext op)1250     private void onAuthorizedGattOperation(BluetoothDevice device, GattOpContext op) {
1251         int status = BluetoothGatt.GATT_SUCCESS;
1252         ClientCharacteristicConfigurationDescriptor cccd;
1253         byte[] value;
1254 
1255         UUID charUuid =
1256                 (op.mCharacteristic != null
1257                         ? op.mCharacteristic.getUuid()
1258                         : (op.mDescriptor != null
1259                                 ? op.mDescriptor.getCharacteristic().getUuid()
1260                                 : null));
1261         mEventLogger.logd(
1262                 TAG,
1263                 "onAuthorizedGattOperation device: "
1264                         + device
1265                         + ", opcode= "
1266                         + op.mOperation
1267                         + ", characteristic= "
1268                         + (charUuid != null ? tbsUuidToString(charUuid) : "UNKNOWN"));
1269 
1270         switch (op.mOperation) {
1271             case READ_CHARACTERISTIC:
1272                 Log.d(TAG, "onCharacteristicReadRequest: device=" + device);
1273 
1274                 if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
1275                     onRejectedAuthorizationGattOperation(device, op);
1276                     return;
1277                 }
1278 
1279                 if (op.mCharacteristic.getUuid().equals(UUID_STATUS_FLAGS)) {
1280                     value = new byte[2];
1281                     int valueInt = mSilentMode ? STATUS_FLAG_SILENT_MODE_ENABLED : 0;
1282                     if (mStatusFlagValue.containsKey(device)) {
1283                         valueInt = mStatusFlagValue.get(device);
1284                     } else if (mCallback.isInbandRingtoneEnabled(device)) {
1285                         valueInt |= STATUS_FLAG_INBAND_RINGTONE_ENABLED;
1286                     }
1287                     value[0] = (byte) (valueInt & 0xFF);
1288                     value[1] = (byte) ((valueInt >> 8) & 0xFF);
1289                 } else {
1290                     GattCharacteristic gattCharacteristic = (GattCharacteristic) op.mCharacteristic;
1291                     value = gattCharacteristic.getValue();
1292                     if (value == null) {
1293                         value = new byte[0];
1294                     }
1295                 }
1296 
1297                 if (value.length < op.mOffset) {
1298                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1299                 } else {
1300                     value = Arrays.copyOfRange(value, op.mOffset, value.length);
1301                     status = BluetoothGatt.GATT_SUCCESS;
1302                 }
1303 
1304                 mBluetoothGattServer.sendResponse(device, op.mRequestId, status, op.mOffset, value);
1305                 break;
1306 
1307             case WRITE_CHARACTERISTIC:
1308                 Log.d(TAG, "onCharacteristicWriteRequest: device=" + device);
1309 
1310                 if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
1311                     onRejectedAuthorizationGattOperation(device, op);
1312                     return;
1313                 }
1314 
1315                 GattCharacteristic gattCharacteristic = (GattCharacteristic) op.mCharacteristic;
1316                 if (op.mPreparedWrite) {
1317                     status = BluetoothGatt.GATT_FAILURE;
1318                 } else if (op.mOffset > 0) {
1319                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1320                 } else {
1321                     gattCharacteristic.handleWriteRequest(
1322                             device, op.mRequestId, op.mResponseNeeded, op.mValue);
1323                     return;
1324                 }
1325 
1326                 if (op.mResponseNeeded) {
1327                     mBluetoothGattServer.sendResponse(
1328                             device, op.mRequestId, status, op.mOffset, op.mValue);
1329                 }
1330                 break;
1331 
1332             case READ_DESCRIPTOR:
1333                 Log.d(TAG, "onDescriptorReadRequest: device=" + device);
1334 
1335                 if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
1336                     onRejectedAuthorizationGattOperation(device, op);
1337                     return;
1338                 }
1339 
1340                 cccd = (ClientCharacteristicConfigurationDescriptor) op.mDescriptor;
1341                 value = cccd.getValue(device);
1342                 if (value.length < op.mOffset) {
1343                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1344                 } else {
1345                     value = Arrays.copyOfRange(value, op.mOffset, value.length);
1346                     status = BluetoothGatt.GATT_SUCCESS;
1347                 }
1348 
1349                 mBluetoothGattServer.sendResponse(device, op.mRequestId, status, op.mOffset, value);
1350                 break;
1351 
1352             case WRITE_DESCRIPTOR:
1353                 Log.d(TAG, "onDescriptorWriteRequest: device=" + device);
1354 
1355                 if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) {
1356                     onRejectedAuthorizationGattOperation(device, op);
1357                     return;
1358                 }
1359 
1360                 cccd = (ClientCharacteristicConfigurationDescriptor) op.mDescriptor;
1361                 if (op.mPreparedWrite) {
1362                     // TODO: handle prepareWrite
1363                     status = BluetoothGatt.GATT_FAILURE;
1364                 } else if (op.mOffset > 0) {
1365                     status = BluetoothGatt.GATT_INVALID_OFFSET;
1366                 } else if (op.mValue.length != 2) {
1367                     status = BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH;
1368                 } else {
1369                     status = cccd.setValue(device, op.mValue);
1370                 }
1371 
1372                 if (op.mResponseNeeded) {
1373                     mBluetoothGattServer.sendResponse(
1374                             device, op.mRequestId, status, op.mOffset, op.mValue);
1375                 }
1376                 break;
1377 
1378             default:
1379                 break;
1380         }
1381     }
1382 
getLocalCharacteristicWrapper(UUID uuid)1383     private GattCharacteristic getLocalCharacteristicWrapper(UUID uuid) {
1384         if (uuid.equals(UUID_BEARER_PROVIDER_NAME)) {
1385             return mBearerProviderNameCharacteristic;
1386         } else if (uuid.equals(UUID_BEARER_UCI)) {
1387             return mBearerUciCharacteristic;
1388         } else if (uuid.equals(UUID_BEARER_TECHNOLOGY)) {
1389             return mBearerTechnologyCharacteristic;
1390         } else if (uuid.equals(UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST)) {
1391             return mBearerUriSchemesSupportedListCharacteristic;
1392         } else if (uuid.equals(UUID_BEARER_LIST_CURRENT_CALLS)) {
1393             return mBearerListCurrentCallsCharacteristic;
1394         } else if (uuid.equals(UUID_CONTENT_CONTROL_ID)) {
1395             return mContentControlIdCharacteristic;
1396         } else if (uuid.equals(UUID_STATUS_FLAGS)) {
1397             return mStatusFlagsCharacteristic;
1398         } else if (uuid.equals(UUID_CALL_STATE)) {
1399             return mCallStateCharacteristic;
1400         } else if (uuid.equals(UUID_CALL_CONTROL_POINT)) {
1401             return mCallControlPointCharacteristic;
1402         } else if (uuid.equals(UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES)) {
1403             return mCallControlPointOptionalOpcodesCharacteristic;
1404         } else if (uuid.equals(UUID_TERMINATION_REASON)) {
1405             return mTerminationReasonCharacteristic;
1406         } else if (uuid.equals(UUID_INCOMING_CALL)) {
1407             return mIncomingCallCharacteristic;
1408         } else if (uuid.equals(UUID_CALL_FRIENDLY_NAME)) {
1409             return mCallFriendlyNameCharacteristic;
1410         }
1411 
1412         return null;
1413     }
1414 
1415     /**
1416      * Callback for TBS GATT instance about authorization change for device.
1417      *
1418      * @param device device for which authorization is changed
1419      */
onDeviceAuthorizationSet(BluetoothDevice device)1420     public void onDeviceAuthorizationSet(BluetoothDevice device) {
1421         int auth = getDeviceAuthorization(device);
1422         mEventLogger.logd(
1423                 TAG,
1424                 "onDeviceAuthorizationSet: device= "
1425                         + device
1426                         + ", authorization= "
1427                         + (auth == BluetoothDevice.ACCESS_ALLOWED
1428                                 ? "ALLOWED"
1429                                 : (auth == BluetoothDevice.ACCESS_REJECTED
1430                                         ? "REJECTED"
1431                                         : "UNKNOWN")));
1432         processPendingGattOperations(device);
1433 
1434         if (auth != BluetoothDevice.ACCESS_ALLOWED) {
1435             return;
1436         }
1437 
1438         BluetoothGattService gattService = mBluetoothGattServer.getService(UUID_GTBS);
1439         if (gattService != null) {
1440             List<BluetoothGattCharacteristic> characteristics = gattService.getCharacteristics();
1441             for (BluetoothGattCharacteristic characteristic : characteristics) {
1442                 GattCharacteristic wrapper =
1443                         getLocalCharacteristicWrapper(characteristic.getUuid());
1444                 if (wrapper != null) {
1445                     /* Value of status flags is not keep in the characteristic but in the
1446                      * mStatusFlagValue
1447                      */
1448                     if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) {
1449                         if (mStatusFlagValue.containsKey(device)) {
1450                             updateStatusFlags(device, mStatusFlagValue.get(device));
1451                         }
1452                     } else {
1453                         wrapper.notify(device);
1454                     }
1455                 }
1456             }
1457         }
1458     }
1459 
clearUnauthorizedGattOperationss(BluetoothDevice device)1460     private void clearUnauthorizedGattOperationss(BluetoothDevice device) {
1461         Log.d(TAG, "clearUnauthorizedGattOperationss device: " + device);
1462 
1463         synchronized (mPendingGattOperationsLock) {
1464             mPendingGattOperations.remove(device);
1465         }
1466     }
1467 
processPendingGattOperations(BluetoothDevice device)1468     private void processPendingGattOperations(BluetoothDevice device) {
1469         Log.d(TAG, "processPendingGattOperations device: " + device);
1470 
1471         synchronized (mPendingGattOperationsLock) {
1472             if (mPendingGattOperations.containsKey(device)) {
1473                 if (getDeviceAuthorization(device) == BluetoothDevice.ACCESS_ALLOWED) {
1474                     for (GattOpContext op : mPendingGattOperations.get(device)) {
1475                         onAuthorizedGattOperation(device, op);
1476                     }
1477                 } else {
1478                     for (GattOpContext op : mPendingGattOperations.get(device)) {
1479                         onRejectedAuthorizationGattOperation(device, op);
1480                     }
1481                 }
1482                 clearUnauthorizedGattOperationss(device);
1483             }
1484         }
1485     }
1486 
1487     /**
1488      * Callback to handle incoming requests to the GATT server. All read/write requests for
1489      * characteristics and descriptors are handled here.
1490      */
1491     @VisibleForTesting
1492     final BluetoothGattServerCallback mGattServerCallback =
1493             new BluetoothGattServerCallback() {
1494                 @Override
1495                 public void onConnectionStateChange(
1496                         BluetoothDevice device, int status, int newState) {
1497                     super.onConnectionStateChange(device, status, newState);
1498                     Log.d(TAG, "BluetoothGattServerCallback: onConnectionStateChange");
1499                     if (newState == BluetoothProfile.STATE_DISCONNECTED) {
1500                         clearUnauthorizedGattOperationss(device);
1501                     }
1502                 }
1503 
1504                 @Override
1505                 public void onServiceAdded(int status, BluetoothGattService service) {
1506                     Log.d(TAG, "onServiceAdded: status=" + status);
1507                     if (mCallback != null) {
1508                         mCallback.onServiceAdded(status == BluetoothGatt.GATT_SUCCESS);
1509                     }
1510 
1511                     restoreCccValuesForStoredDevices();
1512                 }
1513 
1514                 @Override
1515                 public void onCharacteristicReadRequest(
1516                         BluetoothDevice device,
1517                         int requestId,
1518                         int offset,
1519                         BluetoothGattCharacteristic characteristic) {
1520                     super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
1521                     Log.d(
1522                             TAG,
1523                             "BluetoothGattServerCallback: onCharacteristicReadRequest offset= "
1524                                     + offset
1525                                     + " entire value= "
1526                                     + Arrays.toString(characteristic.getValue()));
1527 
1528                     if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)
1529                             == 0) {
1530                         mBluetoothGattServer.sendResponse(
1531                                 device,
1532                                 requestId,
1533                                 BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED,
1534                                 offset,
1535                                 null);
1536                         return;
1537                     }
1538 
1539                     GattOpContext op =
1540                             new GattOpContext(
1541                                     GattOpContext.Operation.READ_CHARACTERISTIC,
1542                                     requestId,
1543                                     characteristic,
1544                                     null);
1545                     switch (getDeviceAuthorization(device)) {
1546                         case BluetoothDevice.ACCESS_REJECTED:
1547                             onRejectedAuthorizationGattOperation(device, op);
1548                             break;
1549                         case BluetoothDevice.ACCESS_UNKNOWN:
1550                             onUnauthorizedGattOperation(device, op);
1551                             break;
1552                         default:
1553                             onAuthorizedGattOperation(device, op);
1554                             break;
1555                     }
1556                 }
1557 
1558                 @Override
1559                 public void onCharacteristicWriteRequest(
1560                         BluetoothDevice device,
1561                         int requestId,
1562                         BluetoothGattCharacteristic characteristic,
1563                         boolean preparedWrite,
1564                         boolean responseNeeded,
1565                         int offset,
1566                         byte[] value) {
1567                     super.onCharacteristicWriteRequest(
1568                             device,
1569                             requestId,
1570                             characteristic,
1571                             preparedWrite,
1572                             responseNeeded,
1573                             offset,
1574                             value);
1575                     Log.d(TAG, "BluetoothGattServerCallback: " + "onCharacteristicWriteRequest");
1576 
1577                     if ((characteristic.getProperties()
1578                                     & BluetoothGattCharacteristic.PROPERTY_WRITE)
1579                             == 0) {
1580                         mBluetoothGattServer.sendResponse(
1581                                 device,
1582                                 requestId,
1583                                 BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED,
1584                                 offset,
1585                                 value);
1586                         return;
1587                     }
1588 
1589                     GattOpContext op =
1590                             new GattOpContext(
1591                                     GattOpContext.Operation.WRITE_CHARACTERISTIC,
1592                                     requestId,
1593                                     characteristic,
1594                                     null,
1595                                     preparedWrite,
1596                                     responseNeeded,
1597                                     offset,
1598                                     value);
1599                     switch (getDeviceAuthorization(device)) {
1600                         case BluetoothDevice.ACCESS_REJECTED:
1601                             onRejectedAuthorizationGattOperation(device, op);
1602                             break;
1603                         case BluetoothDevice.ACCESS_UNKNOWN:
1604                             onUnauthorizedGattOperation(device, op);
1605                             break;
1606                         default:
1607                             onAuthorizedGattOperation(device, op);
1608                             break;
1609                     }
1610                 }
1611 
1612                 @Override
1613                 public void onDescriptorReadRequest(
1614                         BluetoothDevice device,
1615                         int requestId,
1616                         int offset,
1617                         BluetoothGattDescriptor descriptor) {
1618                     super.onDescriptorReadRequest(device, requestId, offset, descriptor);
1619                     Log.d(TAG, "BluetoothGattServerCallback: " + "onDescriptorReadRequest");
1620 
1621                     if ((descriptor.getPermissions()
1622                                     & BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED)
1623                             == 0) {
1624                         mBluetoothGattServer.sendResponse(
1625                                 device,
1626                                 requestId,
1627                                 BluetoothGatt.GATT_READ_NOT_PERMITTED,
1628                                 offset,
1629                                 null);
1630                         return;
1631                     }
1632 
1633                     GattOpContext op =
1634                             new GattOpContext(
1635                                     GattOpContext.Operation.READ_DESCRIPTOR,
1636                                     requestId,
1637                                     null,
1638                                     descriptor);
1639                     switch (getDeviceAuthorization(device)) {
1640                         case BluetoothDevice.ACCESS_REJECTED:
1641                             onRejectedAuthorizationGattOperation(device, op);
1642                             break;
1643                         case BluetoothDevice.ACCESS_UNKNOWN:
1644                             onUnauthorizedGattOperation(device, op);
1645                             break;
1646                         default:
1647                             onAuthorizedGattOperation(device, op);
1648                             break;
1649                     }
1650                 }
1651 
1652                 @Override
1653                 public void onDescriptorWriteRequest(
1654                         BluetoothDevice device,
1655                         int requestId,
1656                         BluetoothGattDescriptor descriptor,
1657                         boolean preparedWrite,
1658                         boolean responseNeeded,
1659                         int offset,
1660                         byte[] value) {
1661                     super.onDescriptorWriteRequest(
1662                             device,
1663                             requestId,
1664                             descriptor,
1665                             preparedWrite,
1666                             responseNeeded,
1667                             offset,
1668                             value);
1669                     Log.d(TAG, "BluetoothGattServerCallback: " + "onDescriptorWriteRequest");
1670 
1671                     if ((descriptor.getPermissions()
1672                                     & BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED)
1673                             == 0) {
1674                         mBluetoothGattServer.sendResponse(
1675                                 device,
1676                                 requestId,
1677                                 BluetoothGatt.GATT_WRITE_NOT_PERMITTED,
1678                                 offset,
1679                                 value);
1680                         return;
1681                     }
1682 
1683                     GattOpContext op =
1684                             new GattOpContext(
1685                                     GattOpContext.Operation.WRITE_DESCRIPTOR,
1686                                     requestId,
1687                                     null,
1688                                     descriptor,
1689                                     preparedWrite,
1690                                     responseNeeded,
1691                                     offset,
1692                                     value);
1693                     switch (getDeviceAuthorization(device)) {
1694                         case BluetoothDevice.ACCESS_REJECTED:
1695                             onRejectedAuthorizationGattOperation(device, op);
1696                             break;
1697                         case BluetoothDevice.ACCESS_UNKNOWN:
1698                             onUnauthorizedGattOperation(device, op);
1699                             break;
1700                         default:
1701                             onAuthorizedGattOperation(device, op);
1702                             break;
1703                     }
1704                 }
1705             };
1706 
dump(StringBuilder sb)1707     public void dump(StringBuilder sb) {
1708         sb.append("\n\tSilent mode: " + mSilentMode);
1709 
1710         for (Map.Entry<BluetoothDevice, HashMap<UUID, Short>> deviceEntry :
1711                 mCccDescriptorValues.entrySet()) {
1712             sb.append("\n\tCCC states for device: " + deviceEntry.getKey());
1713             for (Map.Entry<UUID, Short> entry : deviceEntry.getValue().entrySet()) {
1714                 sb.append(
1715                         "\n\t\tCharacteristic: "
1716                                 + tbsUuidToString(entry.getKey())
1717                                 + ", value: "
1718                                 + Utils.cccIntToStr(entry.getValue()));
1719             }
1720         }
1721 
1722         if (mEventLogger != null) {
1723             sb.append("\n\n");
1724             mEventLogger.dump(sb);
1725         }
1726     }
1727 }
1728