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