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