• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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