1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.libraries.testing.deviceshadower.internal.bluetooth; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothGatt; 21 import android.bluetooth.IBluetoothGatt; 22 import android.bluetooth.IBluetoothGattCallback; 23 import android.bluetooth.IBluetoothGattServerCallback; 24 import android.bluetooth.le.AdvertiseData; 25 import android.bluetooth.le.AdvertiseSettings; 26 import android.bluetooth.le.ScanFilter; 27 import android.bluetooth.le.ScanSettings; 28 import android.os.ParcelUuid; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.libraries.testing.deviceshadower.internal.DeviceShadowEnvironmentImpl; 32 import com.android.libraries.testing.deviceshadower.internal.bluetooth.GattDelegate.ReadCharacteristicRequest; 33 import com.android.libraries.testing.deviceshadower.internal.bluetooth.GattDelegate.ReadDescriptorRequest; 34 import com.android.libraries.testing.deviceshadower.internal.bluetooth.GattDelegate.Request; 35 import com.android.libraries.testing.deviceshadower.internal.common.NamedRunnable; 36 import com.android.libraries.testing.deviceshadower.internal.utils.Logger; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 /** 42 * Implementation of IBluetoothGatt. 43 */ 44 public class IBluetoothGattImpl implements IBluetoothGatt { 45 46 private static final Logger LOGGER = Logger.create("IBluetoothGattImpl"); 47 private GattDelegate.Service mCurrentService; 48 private GattDelegate.Characteristic mCurrentCharacteristic; 49 50 @Override startScan( int appIf, boolean isServer, ScanSettings settings, List<ScanFilter> filters, List<?> scanStorages, String callingPackage)51 public void startScan( 52 int appIf, 53 boolean isServer, 54 ScanSettings settings, 55 List<ScanFilter> filters, 56 List<?> scanStorages, 57 String callingPackage) { 58 localGattDelegate().startScan(appIf, settings, filters); 59 } 60 61 @Override startScan( int appIf, boolean isServer, ScanSettings settings, List<ScanFilter> filters, List<?> scanStorages)62 public void startScan( 63 int appIf, 64 boolean isServer, 65 ScanSettings settings, 66 List<ScanFilter> filters, 67 List<?> scanStorages) { 68 startScan(appIf, isServer, settings, filters, scanStorages, "" /* callingPackage */); 69 } 70 71 @Override stopScan(int appIf, boolean isServer)72 public void stopScan(int appIf, boolean isServer) { 73 localGattDelegate().stopScan(appIf); 74 } 75 76 @Override startMultiAdvertising( int appIf, AdvertiseData advertiseData, AdvertiseData scanResponse, AdvertiseSettings settings)77 public void startMultiAdvertising( 78 int appIf, 79 AdvertiseData advertiseData, 80 AdvertiseData scanResponse, 81 AdvertiseSettings settings) { 82 localGattDelegate().startMultiAdvertising(appIf, advertiseData, scanResponse, settings); 83 } 84 85 @Override stopMultiAdvertising(int appIf)86 public void stopMultiAdvertising(int appIf) { 87 localGattDelegate().stopMultiAdvertising(appIf); 88 } 89 90 @Override 91 @SuppressWarnings("FutureReturnValueIgnored") registerClient(ParcelUuid appId, final IBluetoothGattCallback callback)92 public void registerClient(ParcelUuid appId, final IBluetoothGattCallback callback) { 93 final int clientIf = localGattDelegate().registerClient(callback); 94 NamedRunnable onClientRegistered = 95 NamedRunnable.create( 96 "ClientGatt.onClientRegistered=" + clientIf, 97 () -> { 98 callback.onClientRegistered(BluetoothGatt.GATT_SUCCESS, clientIf); 99 }); 100 101 DeviceShadowEnvironmentImpl.runOnService(localAddress(), onClientRegistered); 102 } 103 104 @Override unregisterClient(int clientIf)105 public void unregisterClient(int clientIf) { 106 localGattDelegate().unregisterClient(clientIf); 107 } 108 109 @Override 110 @SuppressWarnings("FutureReturnValueIgnored") clientConnect( final int clientIf, final String serverAddress, boolean isDirect, int transport)111 public void clientConnect( 112 final int clientIf, final String serverAddress, boolean isDirect, int transport) { 113 // TODO(b/200231384): implement auto connect. 114 String clientAddress = localAddress(); 115 int serverIf = remoteGattDelegate(serverAddress).getServerIf(); 116 boolean success = remoteGattDelegate(serverAddress).connect(clientAddress); 117 if (!success) { 118 LOGGER.i(String.format("clientConnect failed: %s connect %s", serverAddress, 119 clientAddress)); 120 return; 121 } 122 123 DeviceShadowEnvironmentImpl.runOnService( 124 clientAddress, 125 newClientConnectionStateChangeRunnable(clientIf, true, serverAddress)); 126 127 DeviceShadowEnvironmentImpl.runOnService( 128 serverAddress, 129 newServerConnectionStateChangeRunnable(serverIf, true, clientAddress)); 130 } 131 132 @Override 133 @SuppressWarnings("FutureReturnValueIgnored") clientDisconnect(final int clientIf, final String serverAddress)134 public void clientDisconnect(final int clientIf, final String serverAddress) { 135 final String clientAddress = localAddress(); 136 remoteGattDelegate(serverAddress).disconnect(clientAddress); 137 int serverIf = remoteGattDelegate(serverAddress).getServerIf(); 138 139 140 DeviceShadowEnvironmentImpl.runOnService( 141 clientAddress, 142 newClientConnectionStateChangeRunnable(clientIf, false, serverAddress)); 143 144 DeviceShadowEnvironmentImpl.runOnService( 145 serverAddress, 146 newServerConnectionStateChangeRunnable(serverIf, false, clientAddress)); 147 } 148 149 @Override discoverServices(int clientIf, String serverAddress)150 public void discoverServices(int clientIf, String serverAddress) { 151 final IBluetoothGattCallback callback = localGattDelegate().getClientCallback(clientIf); 152 if (callback == null) { 153 return; 154 } 155 for (GattDelegate.Service service : remoteGattDelegate(serverAddress).getServices()) { 156 callback.onGetService(serverAddress, 0 /*srvcType*/, 0 /*srvcInstId*/, 157 service.getUuid()); 158 159 for (GattDelegate.Characteristic characteristic : service.getCharacteristics()) { 160 callback.onGetCharacteristic( 161 serverAddress, 162 0 /*srvcType*/, 163 0 /*srvcInstId*/, 164 service.getUuid(), 165 0 /*charInstId*/, 166 characteristic.getUuid(), 167 characteristic.getProperties()); 168 for (GattDelegate.Descriptor descriptor : characteristic.getDescriptors()) { 169 callback.onGetDescriptor( 170 serverAddress, 171 0 /*srvcType*/, 172 0 /*srvcInstId*/, 173 service.getUuid(), 174 0 /*charInstId*/, 175 characteristic.getUuid(), 176 0 /*descrInstId*/, 177 descriptor.getUuid()); 178 } 179 } 180 } 181 182 callback.onSearchComplete(serverAddress, BluetoothGatt.GATT_SUCCESS); 183 } 184 185 @Override 186 @SuppressWarnings("FutureReturnValueIgnored") readCharacteristic( final int clientIf, final String serverAddress, final int srvcType, final int srvcInstId, final ParcelUuid srvcId, final int charInstId, final ParcelUuid charId, final int authReq)187 public void readCharacteristic( 188 final int clientIf, 189 final String serverAddress, 190 final int srvcType, 191 final int srvcInstId, 192 final ParcelUuid srvcId, 193 final int charInstId, 194 final ParcelUuid charId, 195 final int authReq) { 196 // TODO(b/200231384): implement authReq. 197 final String clientAddress = localAddress(); 198 localGattDelegate() 199 .setLastRequest( 200 new ReadCharacteristicRequest(srvcType, srvcInstId, srvcId, charInstId, 201 charId)); 202 203 NamedRunnable serverOnCharacteristicReadRequest = 204 NamedRunnable.create( 205 "ServerGatt.onCharacteristicReadRequest", 206 () -> { 207 int serverIf = localGattDelegate().getServerIf(); 208 IBluetoothGattServerCallback callback = 209 localGattDelegate().getServerCallback(serverIf); 210 if (callback != null) { 211 callback.onCharacteristicReadRequest( 212 clientAddress, 213 0 /*transId*/, 214 0 /*offset*/, 215 false /*isLong*/, 216 0 /*srvcType*/, 217 srvcInstId, 218 srvcId, 219 charInstId, 220 charId); 221 } 222 }); 223 224 DeviceShadowEnvironmentImpl.runOnService(serverAddress, serverOnCharacteristicReadRequest); 225 } 226 227 @Override 228 @SuppressWarnings("FutureReturnValueIgnored") writeCharacteristic( final int clientIf, final String serverAddress, final int srvcType, final int srvcInstId, final ParcelUuid srvcId, final int charInstId, final ParcelUuid charId, final int writeType, final int authReq, final byte[] value)229 public void writeCharacteristic( 230 final int clientIf, 231 final String serverAddress, 232 final int srvcType, 233 final int srvcInstId, 234 final ParcelUuid srvcId, 235 final int charInstId, 236 final ParcelUuid charId, 237 final int writeType, 238 final int authReq, 239 final byte[] value) { 240 // TODO(b/200231384): implement write with response needed. 241 remoteGattDelegate(serverAddress).getService(srvcId).getCharacteristic(charId) 242 .setValue(value); 243 final String clientAddress = localAddress(); 244 245 NamedRunnable clientOnCharacteristicWrite = 246 NamedRunnable.create( 247 "ClientGatt.onCharacteristicWrite", 248 () -> { 249 IBluetoothGattCallback callback = localGattDelegate().getClientCallback( 250 clientIf); 251 if (callback != null) { 252 callback.onCharacteristicWrite( 253 serverAddress, 254 BluetoothGatt.GATT_SUCCESS, 255 0 /*srvcType*/, 256 srvcInstId, 257 srvcId, 258 charInstId, 259 charId); 260 } 261 }); 262 263 NamedRunnable onCharacteristicWriteRequest = 264 NamedRunnable.create( 265 "ServerGatt.onCharacteristicWriteRequest", 266 () -> { 267 int serverIf = localGattDelegate().getServerIf(); 268 IBluetoothGattServerCallback callback = 269 localGattDelegate().getServerCallback(serverIf); 270 if (callback != null) { 271 callback.onCharacteristicWriteRequest( 272 clientAddress, 273 0 /*transId*/, 274 0 /*offset*/, 275 value.length, 276 false /*isPrep*/, 277 false /*needRsp*/, 278 0 /*srvcType*/, 279 srvcInstId, 280 srvcId, 281 charInstId, 282 charId, 283 value); 284 } 285 }); 286 287 DeviceShadowEnvironmentImpl.runOnService(clientAddress, clientOnCharacteristicWrite); 288 289 DeviceShadowEnvironmentImpl.runOnService(serverAddress, onCharacteristicWriteRequest); 290 } 291 292 @Override 293 @SuppressWarnings("FutureReturnValueIgnored") readDescriptor( final int clientIf, final String serverAddress, final int srvcType, final int srvcInstId, final ParcelUuid srvcId, final int charInstId, final ParcelUuid charId, final int descrInstId, final ParcelUuid descrId, final int authReq)294 public void readDescriptor( 295 final int clientIf, 296 final String serverAddress, 297 final int srvcType, 298 final int srvcInstId, 299 final ParcelUuid srvcId, 300 final int charInstId, 301 final ParcelUuid charId, 302 final int descrInstId, 303 final ParcelUuid descrId, 304 final int authReq) { 305 final String clientAddress = localAddress(); 306 localGattDelegate() 307 .setLastRequest( 308 new ReadDescriptorRequest( 309 srvcType, srvcInstId, srvcId, charInstId, charId, descrInstId, 310 descrId)); 311 312 NamedRunnable serverOnDescriptorReadRequest = 313 NamedRunnable.create( 314 "ServerGatt.onDescriptorReadRequest", 315 () -> { 316 int serverIf = localGattDelegate().getServerIf(); 317 IBluetoothGattServerCallback callback = 318 localGattDelegate().getServerCallback(serverIf); 319 if (callback != null) { 320 callback.onDescriptorReadRequest( 321 clientAddress, 322 0 /*transId*/, 323 0 /*offset*/, 324 false /*isLong*/, 325 0 /*srvcType*/, 326 srvcInstId, 327 srvcId, 328 charInstId, 329 charId, 330 descrId); 331 } 332 }); 333 334 DeviceShadowEnvironmentImpl.runOnService(serverAddress, serverOnDescriptorReadRequest); 335 } 336 337 @Override 338 @SuppressWarnings("FutureReturnValueIgnored") writeDescriptor( final int clientIf, final String serverAddress, final int srvcType, final int srvcInstId, final ParcelUuid srvcId, final int charInstId, final ParcelUuid charId, final int descrInstId, final ParcelUuid descrId, final int writeType, final int authReq, final byte[] value)339 public void writeDescriptor( 340 final int clientIf, 341 final String serverAddress, 342 final int srvcType, 343 final int srvcInstId, 344 final ParcelUuid srvcId, 345 final int charInstId, 346 final ParcelUuid charId, 347 final int descrInstId, 348 final ParcelUuid descrId, 349 final int writeType, 350 final int authReq, 351 final byte[] value) { 352 // TODO(b/200231384): implement write with response needed. 353 remoteGattDelegate(serverAddress) 354 .getService(srvcId) 355 .getCharacteristic(charId) 356 .getDescriptor(descrId) 357 .setValue(value); 358 final String clientAddress = localAddress(); 359 360 NamedRunnable serverOnDescriptorWriteRequest = 361 NamedRunnable.create( 362 "ServerGatt.onDescriptorWriteRequest", 363 () -> { 364 int serverIf = localGattDelegate().getServerIf(); 365 IBluetoothGattServerCallback callback = 366 localGattDelegate().getServerCallback(serverIf); 367 if (callback != null) { 368 callback.onDescriptorWriteRequest( 369 clientAddress, 370 0 /*transId*/, 371 0 /*offset*/, 372 value.length, 373 false /*isPrep*/, 374 false /*needRsp*/, 375 0 /*srvcType*/, 376 srvcInstId, 377 srvcId, 378 charInstId, 379 charId, 380 descrId, 381 value); 382 } 383 }); 384 385 NamedRunnable clientOnDescriptorWrite = 386 NamedRunnable.create( 387 "ClientGatt.onDescriptorWrite", 388 () -> { 389 IBluetoothGattCallback callback = localGattDelegate().getClientCallback( 390 clientIf); 391 if (callback != null) { 392 callback.onDescriptorWrite( 393 serverAddress, 394 BluetoothGatt.GATT_SUCCESS, 395 0 /*srvcType*/, 396 srvcInstId, 397 srvcId, 398 charInstId, 399 charId, 400 descrInstId, 401 descrId); 402 } 403 }); 404 405 DeviceShadowEnvironmentImpl.runOnService(serverAddress, serverOnDescriptorWriteRequest); 406 407 DeviceShadowEnvironmentImpl.runOnService(clientAddress, clientOnDescriptorWrite); 408 } 409 410 @Override registerForNotification( int clientIf, String remoteAddress, int srvcType, int srvcInstId, ParcelUuid srvcId, int charInstId, ParcelUuid charId, boolean enable)411 public void registerForNotification( 412 int clientIf, 413 String remoteAddress, 414 int srvcType, 415 int srvcInstId, 416 ParcelUuid srvcId, 417 int charInstId, 418 ParcelUuid charId, 419 boolean enable) { 420 remoteGattDelegate(remoteAddress) 421 .getService(srvcId) 422 .getCharacteristic(charId) 423 .registerNotification(localAddress(), clientIf); 424 } 425 426 @Override 427 @SuppressWarnings("FutureReturnValueIgnored") registerServer(ParcelUuid appId, final IBluetoothGattServerCallback callback)428 public void registerServer(ParcelUuid appId, final IBluetoothGattServerCallback callback) { 429 // TODO(b/200231384): support multiple serverIf. 430 final int serverIf = localGattDelegate().registerServer(callback); 431 NamedRunnable serverOnRegistered = 432 NamedRunnable.create( 433 "ServerGatt.onServerRegistered", 434 () -> { 435 callback.onServerRegistered(BluetoothGatt.GATT_SUCCESS, serverIf); 436 }); 437 438 DeviceShadowEnvironmentImpl.runOnService(localAddress(), serverOnRegistered); 439 } 440 441 @Override unregisterServer(int serverIf)442 public void unregisterServer(int serverIf) { 443 localGattDelegate().unregisterServer(serverIf); 444 } 445 446 @Override 447 @SuppressWarnings("FutureReturnValueIgnored") serverConnect( final int serverIf, final String clientAddress, boolean isDirect, int transport)448 public void serverConnect( 449 final int serverIf, final String clientAddress, boolean isDirect, int transport) { 450 // TODO(b/200231384): implement isDirect and transport. 451 boolean success = localGattDelegate().connect(clientAddress); 452 final String serverAddress = localAddress(); 453 if (!success) { 454 return; 455 } 456 int clientIf = remoteGattDelegate(clientAddress).getClientIf(); 457 458 DeviceShadowEnvironmentImpl.runOnService( 459 serverAddress, 460 newServerConnectionStateChangeRunnable(serverIf, true, clientAddress)); 461 462 DeviceShadowEnvironmentImpl.runOnService( 463 clientAddress, 464 newClientConnectionStateChangeRunnable(clientIf, true, serverAddress)); 465 } 466 467 @Override 468 @SuppressWarnings("FutureReturnValueIgnored") serverDisconnect(final int serverIf, final String clientAddress)469 public void serverDisconnect(final int serverIf, final String clientAddress) { 470 localGattDelegate().disconnect(clientAddress); 471 String serverAddress = localAddress(); 472 int clientIf = remoteGattDelegate(clientAddress).getClientIf(); 473 474 DeviceShadowEnvironmentImpl.runOnService( 475 serverAddress, 476 newServerConnectionStateChangeRunnable(serverIf, false, clientAddress)); 477 478 DeviceShadowEnvironmentImpl.runOnService( 479 clientAddress, 480 newClientConnectionStateChangeRunnable(clientIf, false, serverAddress)); 481 } 482 483 @Override beginServiceDeclaration( int serverIf, int srvcType, int srvcInstId, int minHandles, ParcelUuid srvcId, boolean advertisePreferred)484 public void beginServiceDeclaration( 485 int serverIf, 486 int srvcType, 487 int srvcInstId, 488 int minHandles, 489 ParcelUuid srvcId, 490 boolean advertisePreferred) { 491 // TODO(b/200231384): support different service type, instanceId, advertisePreferred. 492 mCurrentService = localGattDelegate().addService(srvcId); 493 } 494 495 @Override addIncludedService(int serverIf, int srvcType, int srvcInstId, ParcelUuid srvcId)496 public void addIncludedService(int serverIf, int srvcType, int srvcInstId, ParcelUuid srvcId) { 497 // TODO(b/200231384): implement this. 498 } 499 500 @Override addCharacteristic(int serverIf, ParcelUuid charId, int properties, int permissions)501 public void addCharacteristic(int serverIf, ParcelUuid charId, int properties, 502 int permissions) { 503 mCurrentCharacteristic = mCurrentService.addCharacteristic(charId, properties, permissions); 504 } 505 506 @Override addDescriptor(int serverIf, ParcelUuid descId, int permissions)507 public void addDescriptor(int serverIf, ParcelUuid descId, int permissions) { 508 mCurrentCharacteristic.addDescriptor(descId, permissions); 509 } 510 511 @Override endServiceDeclaration(int serverIf)512 public void endServiceDeclaration(int serverIf) { 513 // TODO(b/200231384): choose correct srvc type and inst id. 514 IBluetoothGattServerCallback callback = localGattDelegate().getServerCallback(serverIf); 515 if (callback != null) { 516 callback.onServiceAdded( 517 BluetoothGatt.GATT_SUCCESS, 0 /*srvcType*/, 0 /*srvcInstId*/, 518 mCurrentService.getUuid()); 519 } 520 mCurrentService = null; 521 } 522 523 @Override removeService(int serverIf, int srvcType, int srvcInstId, ParcelUuid srvcId)524 public void removeService(int serverIf, int srvcType, int srvcInstId, ParcelUuid srvcId) { 525 // TODO(b/200231384): implement remove service. 526 // localGattDelegate().removeService(srvcId); 527 } 528 529 @Override clearServices(int serverIf)530 public void clearServices(int serverIf) { 531 // TODO(b/200231384): support multiple serverIf. 532 // localGattDelegate().clearService(); 533 } 534 535 @Override 536 @SuppressWarnings("FutureReturnValueIgnored") sendResponse( int serverIf, String clientAddress, int requestId, int status, int offset, byte[] value)537 public void sendResponse( 538 int serverIf, String clientAddress, int requestId, int status, int offset, 539 byte[] value) { 540 // TODO(b/200231384): implement more operations. 541 String serverAddress = localAddress(); 542 543 DeviceShadowEnvironmentImpl.runOnService( 544 clientAddress, 545 NamedRunnable.create( 546 "ClientGatt.receiveResponse", 547 () -> { 548 IBluetoothGattCallback callback = 549 localGattDelegate().getClientCallback( 550 localGattDelegate().getClientIf()); 551 if (callback != null) { 552 Request request = localGattDelegate().getLastRequest(); 553 localGattDelegate().setLastRequest(null); 554 if (request != null) { 555 if (request instanceof ReadCharacteristicRequest) { 556 callback.onCharacteristicRead( 557 serverAddress, 558 status, 559 request.mSrvcType, 560 request.mSrvcInstId, 561 request.mSrvcId, 562 request.mCharInstId, 563 request.mCharId, 564 value); 565 } else if (request instanceof ReadDescriptorRequest) { 566 ReadDescriptorRequest readDescriptorRequest = 567 (ReadDescriptorRequest) request; 568 callback.onDescriptorRead( 569 serverAddress, 570 status, 571 readDescriptorRequest.mSrvcType, 572 readDescriptorRequest.mSrvcInstId, 573 readDescriptorRequest.mSrvcId, 574 readDescriptorRequest.mCharInstId, 575 readDescriptorRequest.mCharId, 576 readDescriptorRequest.mDescrInstId, 577 readDescriptorRequest.mDescrId, 578 value); 579 } 580 } 581 } 582 })); 583 } 584 585 @Override 586 @SuppressWarnings("FutureReturnValueIgnored") sendNotification( final int serverIf, final String address, final int srvcType, final int srvcInstId, final ParcelUuid srvcId, final int charInstId, final ParcelUuid charId, boolean confirm, final byte[] value)587 public void sendNotification( 588 final int serverIf, 589 final String address, 590 final int srvcType, 591 final int srvcInstId, 592 final ParcelUuid srvcId, 593 final int charInstId, 594 final ParcelUuid charId, 595 boolean confirm, 596 final byte[] value) { 597 GattDelegate.Characteristic characteristic = 598 localGattDelegate().getService(srvcId).getCharacteristic(charId); 599 characteristic.setValue(value); 600 final String serverAddress = localAddress(); 601 for (final String clientAddress : characteristic.getNotifyClients()) { 602 NamedRunnable clientOnNotify = 603 NamedRunnable.create( 604 "ClientGatt.onNotify", 605 () -> { 606 int clientIf = localGattDelegate().getClientIf(); 607 IBluetoothGattCallback callback = 608 localGattDelegate().getClientCallback(clientIf); 609 if (callback != null) { 610 callback.onNotify( 611 serverAddress, srvcType, srvcInstId, srvcId, charInstId, 612 charId, value); 613 } 614 }); 615 616 DeviceShadowEnvironmentImpl.runOnService(clientAddress, clientOnNotify); 617 } 618 619 NamedRunnable serverOnNotificationSent = 620 NamedRunnable.create( 621 "ServerGatt.onNotificationSent", 622 () -> { 623 IBluetoothGattServerCallback callback = 624 localGattDelegate().getServerCallback(serverIf); 625 if (callback != null) { 626 callback.onNotificationSent(address, BluetoothGatt.GATT_SUCCESS); 627 } 628 }); 629 630 DeviceShadowEnvironmentImpl.runOnService(serverAddress, serverOnNotificationSent); 631 } 632 633 @Override 634 @SuppressWarnings("FutureReturnValueIgnored") configureMTU(int clientIf, String address, int mtu)635 public void configureMTU(int clientIf, String address, int mtu) { 636 final String clientAddress = localAddress(); 637 638 NamedRunnable clientSetMtu = 639 NamedRunnable.create( 640 "ClientGatt.setMtu", 641 () -> { 642 localGattDelegate().clientSetMtu(clientIf, mtu, address); 643 }); 644 NamedRunnable serverSetMtu = 645 NamedRunnable.create( 646 "ServerGatt.setMtu", 647 () -> { 648 int serverIf = localGattDelegate().getServerIf(); 649 localGattDelegate().serverSetMtu(serverIf, mtu, clientAddress); 650 }); 651 652 DeviceShadowEnvironmentImpl.runOnService(clientAddress, clientSetMtu); 653 654 DeviceShadowEnvironmentImpl.runOnService(address, serverSetMtu); 655 } 656 657 @Override connectionParameterUpdate(int clientIf, String address, int connectionPriority)658 public void connectionParameterUpdate(int clientIf, String address, int connectionPriority) { 659 // TODO(b/200231384): Implement. 660 } 661 662 @Override disconnectAll()663 public void disconnectAll() { 664 } 665 666 @Override getDevicesMatchingConnectionStates(int[] states)667 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 668 return new ArrayList<>(); 669 } 670 671 @VisibleForTesting remoteGattDelegate(String address)672 static GattDelegate remoteGattDelegate(String address) { 673 return DeviceShadowEnvironmentImpl.getBlueletImpl(address).getGattDelegate(); 674 } 675 localGattDelegate()676 private static GattDelegate localGattDelegate() { 677 return DeviceShadowEnvironmentImpl.getLocalBlueletImpl().getGattDelegate(); 678 } 679 localAddress()680 private static String localAddress() { 681 return DeviceShadowEnvironmentImpl.getLocalBlueletImpl().address; 682 } 683 newClientConnectionStateChangeRunnable( final int clientIf, final boolean isConnected, final String serverAddress)684 private static NamedRunnable newClientConnectionStateChangeRunnable( 685 final int clientIf, final boolean isConnected, final String serverAddress) { 686 return NamedRunnable.create( 687 "ClientGatt.clientConnectionStateChange", 688 () -> { 689 localGattDelegate() 690 .clientConnectionStateChange( 691 BluetoothGatt.GATT_SUCCESS, clientIf, isConnected, 692 serverAddress); 693 }); 694 } 695 newServerConnectionStateChangeRunnable( final int serverIf, final boolean isConnected, final String clientAddress)696 private static NamedRunnable newServerConnectionStateChangeRunnable( 697 final int serverIf, final boolean isConnected, final String clientAddress) { 698 return NamedRunnable.create( 699 "ServerGatt.serverConnectionStateChange", 700 () -> { 701 localGattDelegate() 702 .serverConnectionStateChange( 703 BluetoothGatt.GATT_SUCCESS, serverIf, isConnected, 704 clientAddress); 705 }); 706 } 707 } 708