• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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 android.bluetooth;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
21 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.RequiresNoPermission;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SuppressLint;
28 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
29 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
30 import android.content.AttributionSource;
31 import android.os.IBinder;
32 import android.os.ParcelUuid;
33 import android.os.RemoteException;
34 import android.util.Log;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.UUID;
41 
42 /**
43  * Public API for the Bluetooth GATT Profile server role.
44  *
45  * <p>This class provides Bluetooth GATT server role functionality, allowing applications to create
46  * Bluetooth Smart services and characteristics.
47  *
48  * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service via IPC. Use
49  * {@link BluetoothManager#openGattServer} to get an instance of this class.
50  */
51 public final class BluetoothGattServer implements BluetoothProfile {
52     private static final String TAG = BluetoothGattServer.class.getSimpleName();
53 
54     private static final boolean DBG = true;
55     private static final boolean VDBG = false;
56 
57     private final IBluetoothGatt mService;
58     private final BluetoothAdapter mAdapter;
59     private final AttributionSource mAttributionSource;
60 
61     private BluetoothGattServerCallback mCallback;
62 
63     private final Object mServerIfLock = new Object();
64     private int mServerIf;
65     private final int mTransport;
66     private BluetoothGattService mPendingService;
67     private final List<BluetoothGattService> mServices;
68 
69     private static final int CALLBACK_REG_TIMEOUT = 10000;
70     // Max length of an attribute value, defined in gatt_api.h
71     private static final int GATT_MAX_ATTR_LEN = 512;
72 
73     /** Bluetooth GATT interface callbacks */
74     @SuppressLint("AndroidFrameworkBluetoothPermission")
75     private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
76             new IBluetoothGattServerCallback.Stub() {
77                 /**
78                  * Application interface registered - app is ready to go
79                  *
80                  * @hide
81                  */
82                 @Override
83                 public void onServerRegistered(int status, int serverIf) {
84                     if (DBG) {
85                         Log.d(
86                                 TAG,
87                                 "onServerRegistered() - status="
88                                         + status
89                                         + " serverIf="
90                                         + serverIf);
91                     }
92                     synchronized (mServerIfLock) {
93                         if (mCallback != null) {
94                             mServerIf = serverIf;
95                             mServerIfLock.notify();
96                         } else {
97                             // registration timeout
98                             Log.e(TAG, "onServerRegistered: mCallback is null");
99                         }
100                     }
101                 }
102 
103                 /**
104                  * Server connection state changed
105                  *
106                  * @hide
107                  */
108                 @Override
109                 public void onServerConnectionState(
110                         int status, int serverIf, boolean connected, String address) {
111                     if (DBG) {
112                         Log.d(
113                                 TAG,
114                                 "onServerConnectionState() - status="
115                                         + status
116                                         + " serverIf="
117                                         + serverIf
118                                         + " connected="
119                                         + connected
120                                         + " device="
121                                         + BluetoothUtils.toAnonymizedAddress(address));
122                     }
123                     try {
124                         mCallback.onConnectionStateChange(
125                                 mAdapter.getRemoteDevice(address),
126                                 status,
127                                 connected ? STATE_CONNECTED : STATE_DISCONNECTED);
128                     } catch (Exception ex) {
129                         Log.w(TAG, "Unhandled exception in callback", ex);
130                     }
131                 }
132 
133                 /**
134                  * Service has been added
135                  *
136                  * @hide
137                  */
138                 @Override
139                 public void onServiceAdded(int status, BluetoothGattService service) {
140                     if (DBG) {
141                         Log.d(
142                                 TAG,
143                                 "onServiceAdded() - handle="
144                                         + service.getInstanceId()
145                                         + " uuid="
146                                         + service.getUuid()
147                                         + " status="
148                                         + status);
149                     }
150 
151                     if (mPendingService == null) {
152                         return;
153                     }
154 
155                     BluetoothGattService tmp = mPendingService;
156                     mPendingService = null;
157 
158                     // Rewrite newly assigned handles to existing service.
159                     tmp.setInstanceId(service.getInstanceId());
160                     List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
161                     List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
162                     for (int i = 0; i < svc_chars.size(); i++) {
163                         BluetoothGattCharacteristic temp_char = temp_chars.get(i);
164                         BluetoothGattCharacteristic svc_char = svc_chars.get(i);
165 
166                         temp_char.setInstanceId(svc_char.getInstanceId());
167 
168                         List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
169                         List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
170                         for (int j = 0; j < svc_descs.size(); j++) {
171                             temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
172                         }
173                     }
174 
175                     mServices.add(tmp);
176 
177                     try {
178                         mCallback.onServiceAdded((int) status, tmp);
179                     } catch (Exception ex) {
180                         Log.w(TAG, "Unhandled exception in callback", ex);
181                     }
182                 }
183 
184                 /**
185                  * Remote client characteristic read request.
186                  *
187                  * @hide
188                  */
189                 @Override
190                 public void onCharacteristicReadRequest(
191                         String address, int transId, int offset, boolean isLong, int handle) {
192                     if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
193 
194                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
195                     BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
196                     if (characteristic == null) {
197                         Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
198                         return;
199                     }
200 
201                     try {
202                         mCallback.onCharacteristicReadRequest(
203                                 device, transId, offset, characteristic);
204                     } catch (Exception ex) {
205                         Log.w(TAG, "Unhandled exception in callback", ex);
206                     }
207                 }
208 
209                 /**
210                  * Remote client descriptor read request.
211                  *
212                  * @hide
213                  */
214                 @Override
215                 public void onDescriptorReadRequest(
216                         String address, int transId, int offset, boolean isLong, int handle) {
217                     if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
218 
219                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
220                     BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
221                     if (descriptor == null) {
222                         Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
223                         return;
224                     }
225 
226                     try {
227                         mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
228                     } catch (Exception ex) {
229                         Log.w(TAG, "Unhandled exception in callback", ex);
230                     }
231                 }
232 
233                 /**
234                  * Remote client characteristic write request.
235                  *
236                  * @hide
237                  */
238                 @Override
239                 public void onCharacteristicWriteRequest(
240                         String address,
241                         int transId,
242                         int offset,
243                         int length,
244                         boolean isPrep,
245                         boolean needRsp,
246                         int handle,
247                         byte[] value) {
248                     if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
249 
250                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
251                     BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
252                     if (characteristic == null) {
253                         Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
254                         return;
255                     }
256 
257                     try {
258                         mCallback.onCharacteristicWriteRequest(
259                                 device, transId, characteristic, isPrep, needRsp, offset, value);
260                     } catch (Exception ex) {
261                         Log.w(TAG, "Unhandled exception in callback", ex);
262                     }
263                 }
264 
265                 /**
266                  * Remote client descriptor write request.
267                  *
268                  * @hide
269                  */
270                 @Override
271                 public void onDescriptorWriteRequest(
272                         String address,
273                         int transId,
274                         int offset,
275                         int length,
276                         boolean isPrep,
277                         boolean needRsp,
278                         int handle,
279                         byte[] value) {
280                     if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
281 
282                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
283                     BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
284                     if (descriptor == null) {
285                         Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
286                         return;
287                     }
288 
289                     try {
290                         mCallback.onDescriptorWriteRequest(
291                                 device, transId, descriptor, isPrep, needRsp, offset, value);
292                     } catch (Exception ex) {
293                         Log.w(TAG, "Unhandled exception in callback", ex);
294                     }
295                 }
296 
297                 /**
298                  * Execute pending writes.
299                  *
300                  * @hide
301                  */
302                 @Override
303                 public void onExecuteWrite(String address, int transId, boolean execWrite) {
304                     if (DBG) {
305                         Log.d(
306                                 TAG,
307                                 "onExecuteWrite() - "
308                                         + "device="
309                                         + BluetoothUtils.toAnonymizedAddress(address)
310                                         + ", transId="
311                                         + transId
312                                         + "execWrite="
313                                         + execWrite);
314                     }
315 
316                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
317                     if (device == null) return;
318 
319                     try {
320                         mCallback.onExecuteWrite(device, transId, execWrite);
321                     } catch (Exception ex) {
322                         Log.w(TAG, "Unhandled exception in callback", ex);
323                     }
324                 }
325 
326                 /**
327                  * A notification/indication has been sent.
328                  *
329                  * @hide
330                  */
331                 @Override
332                 public void onNotificationSent(String address, int status) {
333                     if (VDBG) {
334                         Log.d(
335                                 TAG,
336                                 "onNotificationSent() - "
337                                         + "device="
338                                         + BluetoothUtils.toAnonymizedAddress(address)
339                                         + ", status="
340                                         + status);
341                     }
342 
343                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
344                     if (device == null) return;
345 
346                     try {
347                         mCallback.onNotificationSent(device, status);
348                     } catch (Exception ex) {
349                         Log.w(TAG, "Unhandled exception: " + ex);
350                     }
351                 }
352 
353                 /**
354                  * The MTU for a connection has changed
355                  *
356                  * @hide
357                  */
358                 @Override
359                 public void onMtuChanged(String address, int mtu) {
360                     if (DBG) {
361                         Log.d(
362                                 TAG,
363                                 "onMtuChanged() - "
364                                         + "device="
365                                         + BluetoothUtils.toAnonymizedAddress(address)
366                                         + ", mtu="
367                                         + mtu);
368                     }
369 
370                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
371                     if (device == null) return;
372 
373                     try {
374                         mCallback.onMtuChanged(device, mtu);
375                     } catch (Exception ex) {
376                         Log.w(TAG, "Unhandled exception: " + ex);
377                     }
378                 }
379 
380                 /**
381                  * The PHY for a connection was updated
382                  *
383                  * @hide
384                  */
385                 @Override
386                 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
387                     if (DBG) {
388                         Log.d(
389                                 TAG,
390                                 "onPhyUpdate() - "
391                                         + "device="
392                                         + BluetoothUtils.toAnonymizedAddress(address)
393                                         + ", txPHy="
394                                         + txPhy
395                                         + ", rxPHy="
396                                         + rxPhy);
397                     }
398 
399                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
400                     if (device == null) return;
401 
402                     try {
403                         mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
404                     } catch (Exception ex) {
405                         Log.w(TAG, "Unhandled exception: " + ex);
406                     }
407                 }
408 
409                 /**
410                  * The PHY for a connection was read
411                  *
412                  * @hide
413                  */
414                 @Override
415                 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
416                     if (DBG) {
417                         Log.d(
418                                 TAG,
419                                 "onPhyUpdate() - "
420                                         + "device="
421                                         + BluetoothUtils.toAnonymizedAddress(address)
422                                         + ", txPHy="
423                                         + txPhy
424                                         + ", rxPHy="
425                                         + rxPhy);
426                     }
427 
428                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
429                     if (device == null) return;
430 
431                     try {
432                         mCallback.onPhyRead(device, txPhy, rxPhy, status);
433                     } catch (Exception ex) {
434                         Log.w(TAG, "Unhandled exception: " + ex);
435                     }
436                 }
437 
438                 /**
439                  * Callback invoked when the given connection is updated
440                  *
441                  * @hide
442                  */
443                 @Override
444                 public void onConnectionUpdated(
445                         String address, int interval, int latency, int timeout, int status) {
446                     if (DBG) {
447                         Log.d(
448                                 TAG,
449                                 "onConnectionUpdated() - Device="
450                                         + BluetoothUtils.toAnonymizedAddress(address)
451                                         + " interval="
452                                         + interval
453                                         + " latency="
454                                         + latency
455                                         + " timeout="
456                                         + timeout
457                                         + " status="
458                                         + status);
459                     }
460                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
461                     if (device == null) return;
462 
463                     try {
464                         mCallback.onConnectionUpdated(device, interval, latency, timeout, status);
465                     } catch (Exception ex) {
466                         Log.w(TAG, "Unhandled exception: " + ex);
467                     }
468                 }
469 
470                 /**
471                  * Callback invoked when the given connection's subrate parameters are changed
472                  *
473                  * @hide
474                  */
475                 @Override
476                 public void onSubrateChange(
477                         String address,
478                         int subrateFactor,
479                         int latency,
480                         int contNum,
481                         int timeout,
482                         int status) {
483                     if (DBG) {
484                         Log.d(
485                                 TAG,
486                                 "onSubrateChange() - "
487                                         + "Device="
488                                         + BluetoothUtils.toAnonymizedAddress(address)
489                                         + ", subrateFactor="
490                                         + subrateFactor
491                                         + ", latency="
492                                         + latency
493                                         + ", contNum="
494                                         + contNum
495                                         + ", timeout="
496                                         + timeout
497                                         + ", status="
498                                         + status);
499                     }
500                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
501                     if (device == null) {
502                         return;
503                     }
504 
505                     try {
506                         mCallback.onSubrateChange(
507                                 device, subrateFactor, latency, contNum, timeout, status);
508                     } catch (Exception ex) {
509                         Log.w(TAG, "Unhandled exception: " + ex);
510                     }
511                 }
512             };
513 
514     /** Create a BluetoothGattServer proxy object. */
BluetoothGattServer( IBluetoothGatt iGatt, int transport, BluetoothAdapter adapter)515     /* package */ BluetoothGattServer(
516             IBluetoothGatt iGatt, int transport, BluetoothAdapter adapter) {
517         mService = iGatt;
518         mAdapter = adapter;
519         mAttributionSource = adapter.getAttributionSource();
520         mCallback = null;
521         mServerIf = 0;
522         mTransport = transport;
523         mServices = new ArrayList<BluetoothGattService>();
524     }
525 
526     /**
527      * Get the identifier of the BluetoothGattServer, or 0 if it is closed
528      *
529      * @hide
530      */
531     @RequiresNoPermission
getServerIf()532     public int getServerIf() {
533         return mServerIf;
534     }
535 
536     /**
537      * Returns a characteristic with given handle.
538      *
539      * @hide
540      */
getCharacteristicByHandle(int handle)541     /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
542         for (BluetoothGattService svc : mServices) {
543             for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
544                 if (charac.getInstanceId() == handle) {
545                     return charac;
546                 }
547             }
548         }
549         return null;
550     }
551 
552     /**
553      * Returns a descriptor with given handle.
554      *
555      * @hide
556      */
getDescriptorByHandle(int handle)557     /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
558         for (BluetoothGattService svc : mServices) {
559             for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
560                 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
561                     if (desc.getInstanceId() == handle) {
562                         return desc;
563                     }
564                 }
565             }
566         }
567         return null;
568     }
569 
570     /** @hide */
571     @Override
572     @RequiresNoPermission
onServiceConnected(IBinder service)573     public void onServiceConnected(IBinder service) {}
574 
575     /** @hide */
576     @Override
577     @RequiresNoPermission
onServiceDisconnected()578     public void onServiceDisconnected() {}
579 
580     /** @hide */
581     @Override
582     @RequiresNoPermission
getAdapter()583     public BluetoothAdapter getAdapter() {
584         return mAdapter;
585     }
586 
587     /**
588      * Close this GATT server instance.
589      *
590      * <p>Application should call this method as early as possible after it is done with this GATT
591      * server.
592      */
593     @RequiresBluetoothConnectPermission
594     @RequiresPermission(BLUETOOTH_CONNECT)
close()595     public void close() {
596         if (DBG) Log.d(TAG, "close()");
597         unregisterCallback();
598     }
599 
600     /**
601      * Register an application callback to start using GattServer.
602      *
603      * <p>This is an asynchronous call. The callback is used to notify success or failure if the
604      * function returns true.
605      *
606      * @param callback GATT callback handler that will receive asynchronous callbacks.
607      * @return true, the callback will be called to notify success or failure, false on immediate
608      *     error
609      */
610     @RequiresLegacyBluetoothPermission
611     @RequiresBluetoothConnectPermission
612     @RequiresPermission(BLUETOOTH_CONNECT)
registerCallback(BluetoothGattServerCallback callback)613     /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
614         return registerCallback(callback, false);
615     }
616 
617     /**
618      * Register an application callback to start using GattServer.
619      *
620      * <p>This is an asynchronous call. The callback is used to notify success or failure if the
621      * function returns true.
622      *
623      * @param callback GATT callback handler that will receive asynchronous callbacks.
624      * @param eattSupport indicates if server can use eatt
625      * @return true, the callback will be called to notify success or failure, false on immediate
626      *     error
627      * @hide
628      */
629     @RequiresLegacyBluetoothPermission
630     @RequiresBluetoothConnectPermission
631     @RequiresPermission(BLUETOOTH_CONNECT)
632     @SuppressWarnings("WaitNotInLoop") // TODO(b/314811467)
registerCallback( BluetoothGattServerCallback callback, boolean eattSupport)633     /*package*/ boolean registerCallback(
634             BluetoothGattServerCallback callback, boolean eattSupport) {
635         if (DBG) Log.d(TAG, "registerCallback()");
636         if (mService == null) {
637             Log.e(TAG, "GATT service not available");
638             return false;
639         }
640         UUID uuid = UUID.randomUUID();
641         if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
642 
643         synchronized (mServerIfLock) {
644             if (mCallback != null) {
645                 Log.e(TAG, "App can register callback only once");
646                 return false;
647             }
648 
649             mCallback = callback;
650             try {
651                 mService.registerServer(
652                         new ParcelUuid(uuid),
653                         mBluetoothGattServerCallback,
654                         eattSupport,
655                         mAttributionSource);
656             } catch (RemoteException e) {
657                 Log.e(TAG, "", e);
658                 mCallback = null;
659                 return false;
660             }
661 
662             try {
663                 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
664             } catch (InterruptedException e) {
665                 Log.e(TAG, "" + e);
666                 mCallback = null;
667             }
668 
669             if (mServerIf == 0) {
670                 mCallback = null;
671                 return false;
672             } else {
673                 return true;
674             }
675         }
676     }
677 
678     /** Unregister the current application and callbacks. */
679     @RequiresBluetoothConnectPermission
680     @RequiresPermission(BLUETOOTH_CONNECT)
unregisterCallback()681     private void unregisterCallback() {
682         if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
683         if (mService == null || mServerIf == 0) return;
684 
685         try {
686             mCallback = null;
687             mService.unregisterServer(mServerIf, mAttributionSource);
688             mServerIf = 0;
689         } catch (RemoteException e) {
690             Log.e(TAG, "", e);
691         }
692     }
693 
694     /**
695      * Returns a service by UUID, instance and type.
696      *
697      * @hide
698      */
getService(UUID uuid, int instanceId, int type)699     /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
700         for (BluetoothGattService svc : mServices) {
701             if (svc.getType() == type
702                     && svc.getInstanceId() == instanceId
703                     && svc.getUuid().equals(uuid)) {
704                 return svc;
705             }
706         }
707         return null;
708     }
709 
710     /**
711      * Initiate a connection to a Bluetooth GATT capable device.
712      *
713      * <p>The connection may not be established right away, but will be completed when the remote
714      * device is available. A {@link BluetoothGattServerCallback#onConnectionStateChange} callback
715      * will be invoked when the connection state changes as a result of this function.
716      *
717      * <p>The autoConnect parameter determines whether to actively connect to the remote device, or
718      * rather passively scan and finalize the connection when the remote device is in
719      * range/available. Generally, the first ever connection to a device should be direct
720      * (autoConnect set to false) and subsequent connections to known devices should be invoked with
721      * the autoConnect parameter set to true.
722      *
723      * @param autoConnect Whether to directly connect to the remote device (false) or to
724      *     automatically connect as soon as the remote device becomes available (true).
725      * @return true, if the connection attempt was initiated successfully
726      */
727     @RequiresLegacyBluetoothPermission
728     @RequiresBluetoothConnectPermission
729     @RequiresPermission(BLUETOOTH_CONNECT)
connect(BluetoothDevice device, boolean autoConnect)730     public boolean connect(BluetoothDevice device, boolean autoConnect) {
731         if (DBG) {
732             Log.d(TAG, "connect() - device: " + device + ", auto: " + autoConnect);
733         }
734         if (mService == null || mServerIf == 0) return false;
735 
736         try {
737             // autoConnect is inverse of "isDirect"
738             mService.serverConnect(
739                     mServerIf,
740                     device.getAddress(),
741                     device.getAddressType(),
742                     !autoConnect,
743                     mTransport,
744                     mAttributionSource);
745         } catch (RemoteException e) {
746             Log.e(TAG, "", e);
747             return false;
748         }
749 
750         return true;
751     }
752 
753     /**
754      * Disconnects an established connection, or cancels a connection attempt currently in progress.
755      *
756      * @param device Remote device
757      */
758     @RequiresLegacyBluetoothPermission
759     @RequiresBluetoothConnectPermission
760     @RequiresPermission(BLUETOOTH_CONNECT)
cancelConnection(BluetoothDevice device)761     public void cancelConnection(BluetoothDevice device) {
762         if (DBG) Log.d(TAG, "cancelConnection() - device: " + device);
763         if (mService == null || mServerIf == 0) return;
764 
765         try {
766             mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource);
767         } catch (RemoteException e) {
768             Log.e(TAG, "", e);
769         }
770     }
771 
772     /**
773      * Set the preferred connection PHY for this app. Please note that this is just a
774      * recommendation, whether the PHY change will happen depends on other applications preferences,
775      * local and remote controller capabilities. Controller can override these settings.
776      *
777      * <p>{@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this
778      * call, even if no PHY change happens. It is also triggered when remote device updates the PHY.
779      *
780      * @param device The remote device to send this response to
781      * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
782      *     BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
783      *     BluetoothDevice#PHY_LE_CODED_MASK}.
784      * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
785      *     BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
786      *     BluetoothDevice#PHY_LE_CODED_MASK}.
787      * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
788      *     of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2}
789      *     or {@link BluetoothDevice#PHY_OPTION_S8}
790      */
791     @RequiresBluetoothConnectPermission
792     @RequiresPermission(BLUETOOTH_CONNECT)
setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions)793     public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
794         try {
795             mService.serverSetPreferredPhy(
796                     mServerIf, device.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource);
797         } catch (RemoteException e) {
798             Log.e(TAG, "", e);
799         }
800     }
801 
802     /**
803      * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
804      * in {@link BluetoothGattServerCallback#onPhyRead}
805      *
806      * @param device The remote device to send this response to
807      */
808     @RequiresBluetoothConnectPermission
809     @RequiresPermission(BLUETOOTH_CONNECT)
readPhy(BluetoothDevice device)810     public void readPhy(BluetoothDevice device) {
811         try {
812             mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource);
813         } catch (RemoteException e) {
814             Log.e(TAG, "", e);
815         }
816     }
817 
818     /**
819      * Send a response to a read or write request to a remote device.
820      *
821      * <p>This function must be invoked in when a remote read/write request is received by one of
822      * these callback methods:
823      *
824      * <ul>
825      *   <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
826      *   <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
827      *   <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
828      *   <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
829      * </ul>
830      *
831      * @param device The remote device to send this response to
832      * @param requestId The ID of the request that was received with the callback
833      * @param status The status of the request to be sent to the remote devices
834      * @param offset Value offset for partial read/write response
835      * @param value The value of the attribute that was read/written (optional)
836      */
837     @RequiresLegacyBluetoothPermission
838     @RequiresBluetoothConnectPermission
839     @RequiresPermission(BLUETOOTH_CONNECT)
sendResponse( BluetoothDevice device, int requestId, int status, int offset, byte[] value)840     public boolean sendResponse(
841             BluetoothDevice device, int requestId, int status, int offset, byte[] value) {
842         if (VDBG) Log.d(TAG, "sendResponse() - device: " + device);
843         if (mService == null || mServerIf == 0) return false;
844 
845         try {
846             mService.sendResponse(
847                     mServerIf,
848                     device.getAddress(),
849                     requestId,
850                     status,
851                     offset,
852                     value,
853                     mAttributionSource);
854         } catch (RemoteException e) {
855             Log.e(TAG, "", e);
856             return false;
857         }
858         return true;
859     }
860 
861     /**
862      * Send a notification or indication that a local characteristic has been updated.
863      *
864      * <p>A notification or indication is sent to the remote device to signal that the
865      * characteristic has been updated. This function should be invoked for every client that
866      * requests notifications/indications by writing to the "Client Configuration" descriptor for
867      * the given characteristic.
868      *
869      * @param device The remote device to receive the notification/indication
870      * @param characteristic The local characteristic that has been updated
871      * @param confirm true to request confirmation from the client (indication), false to send a
872      *     notification
873      * @return true, if the notification has been triggered successfully
874      * @throws IllegalArgumentException if the characteristic value or service is null
875      * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice,
876      *     BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe.
877      */
878     @Deprecated
879     @RequiresLegacyBluetoothPermission
880     @RequiresBluetoothConnectPermission
881     @RequiresPermission(BLUETOOTH_CONNECT)
notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm)882     public boolean notifyCharacteristicChanged(
883             BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) {
884         return notifyCharacteristicChanged(
885                         device, characteristic, confirm, characteristic.getValue())
886                 == BluetoothStatusCodes.SUCCESS;
887     }
888 
889     /** @hide */
890     @Retention(RetentionPolicy.SOURCE)
891     @IntDef(
892             value = {
893                 BluetoothStatusCodes.SUCCESS,
894                 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
895                 BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
896                 BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
897                 BluetoothStatusCodes.ERROR_UNKNOWN
898             })
899     public @interface NotifyCharacteristicReturnValues {}
900 
901     /**
902      * Send a notification or indication that a local characteristic has been updated.
903      *
904      * <p>A notification or indication is sent to the remote device to signal that the
905      * characteristic has been updated. This function should be invoked for every client that
906      * requests notifications/indications by writing to the "Client Configuration" descriptor for
907      * the given characteristic.
908      *
909      * @param device the remote device to receive the notification/indication
910      * @param characteristic the local characteristic that has been updated
911      * @param confirm {@code true} to request confirmation from the client (indication) or {@code
912      *     false} to send a notification
913      * @param value the characteristic value
914      * @return whether the notification has been triggered successfully
915      * @throws IllegalArgumentException if the characteristic value or service is null
916      */
917     @RequiresLegacyBluetoothPermission
918     @RequiresBluetoothConnectPermission
919     @RequiresPermission(BLUETOOTH_CONNECT)
920     @NotifyCharacteristicReturnValues
notifyCharacteristicChanged( @onNull BluetoothDevice device, @NonNull BluetoothGattCharacteristic characteristic, boolean confirm, @NonNull byte[] value)921     public int notifyCharacteristicChanged(
922             @NonNull BluetoothDevice device,
923             @NonNull BluetoothGattCharacteristic characteristic,
924             boolean confirm,
925             @NonNull byte[] value) {
926         if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device);
927         if (mService == null || mServerIf == 0) {
928             return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
929         }
930 
931         if (characteristic == null) {
932             throw new IllegalArgumentException("characteristic must not be null");
933         }
934         if (device == null) {
935             throw new IllegalArgumentException("device must not be null");
936         }
937         if (value.length > GATT_MAX_ATTR_LEN) {
938             throw new IllegalArgumentException(
939                     "notification should not be longer than max length of an attribute value");
940         }
941         BluetoothGattService service = characteristic.getService();
942         if (service == null) {
943             throw new IllegalArgumentException("Characteristic must have a non-null service");
944         }
945         if (value == null) {
946             throw new IllegalArgumentException("Characteristic value must not be null");
947         }
948 
949         try {
950             return mService.sendNotification(
951                     mServerIf,
952                     device.getAddress(),
953                     characteristic.getInstanceId(),
954                     confirm,
955                     value,
956                     mAttributionSource);
957         } catch (RemoteException e) {
958             Log.e(TAG, "", e);
959             throw e.rethrowAsRuntimeException();
960         }
961     }
962 
963     /**
964      * Add a service to the list of services to be hosted.
965      *
966      * <p>Once a service has been added to the list, the service and its included characteristics
967      * will be provided by the local device.
968      *
969      * <p>If the local device has already exposed services when this function is called, a service
970      * update notification will be sent to all clients.
971      *
972      * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate whether this
973      * service has been added successfully. Do not add another service before this callback.
974      *
975      * @param service Service to be added to the list of services provided by this device.
976      * @return true, if the request to add service has been initiated
977      */
978     @RequiresLegacyBluetoothPermission
979     @RequiresBluetoothConnectPermission
980     @RequiresPermission(BLUETOOTH_CONNECT)
addService(BluetoothGattService service)981     public boolean addService(BluetoothGattService service) {
982         if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
983         if (mService == null || mServerIf == 0) return false;
984 
985         mPendingService = service;
986 
987         try {
988             mService.addService(mServerIf, service, mAttributionSource);
989         } catch (RemoteException e) {
990             Log.e(TAG, "", e);
991             return false;
992         }
993 
994         return true;
995     }
996 
997     /**
998      * Removes a service from the list of services to be provided.
999      *
1000      * @param service Service to be removed.
1001      * @return true, if the service has been removed
1002      */
1003     @RequiresLegacyBluetoothPermission
1004     @RequiresBluetoothConnectPermission
1005     @RequiresPermission(BLUETOOTH_CONNECT)
removeService(BluetoothGattService service)1006     public boolean removeService(BluetoothGattService service) {
1007         if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
1008         if (mService == null || mServerIf == 0) return false;
1009 
1010         BluetoothGattService intService =
1011                 getService(service.getUuid(), service.getInstanceId(), service.getType());
1012         if (intService == null) return false;
1013 
1014         try {
1015             mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource);
1016             mServices.remove(intService);
1017         } catch (RemoteException e) {
1018             Log.e(TAG, "", e);
1019             return false;
1020         }
1021 
1022         return true;
1023     }
1024 
1025     /** Remove all services from the list of provided services. */
1026     @RequiresLegacyBluetoothPermission
1027     @RequiresBluetoothConnectPermission
1028     @RequiresPermission(BLUETOOTH_CONNECT)
clearServices()1029     public void clearServices() {
1030         if (DBG) Log.d(TAG, "clearServices()");
1031         if (mService == null || mServerIf == 0) return;
1032 
1033         try {
1034             mService.clearServices(mServerIf, mAttributionSource);
1035             mServices.clear();
1036         } catch (RemoteException e) {
1037             Log.e(TAG, "", e);
1038         }
1039     }
1040 
1041     /**
1042      * Returns a list of GATT services offered by this device.
1043      *
1044      * <p>An application must call {@link #addService} to add a service to the list of services
1045      * offered by this device.
1046      *
1047      * @return List of services. Returns an empty list if no services have been added yet.
1048      */
1049     @RequiresLegacyBluetoothPermission
1050     @RequiresNoPermission
getServices()1051     public List<BluetoothGattService> getServices() {
1052         return mServices;
1053     }
1054 
1055     /**
1056      * Returns a {@link BluetoothGattService} from the list of services offered by this device.
1057      *
1058      * <p>If multiple instances of the same service (as identified by UUID) exist, the first
1059      * instance of the service is returned.
1060      *
1061      * @param uuid UUID of the requested service
1062      * @return BluetoothGattService if supported, or null if the requested service is not offered by
1063      *     this device.
1064      */
1065     @RequiresLegacyBluetoothPermission
1066     @RequiresNoPermission
getService(UUID uuid)1067     public BluetoothGattService getService(UUID uuid) {
1068         for (BluetoothGattService service : mServices) {
1069             if (service.getUuid().equals(uuid)) {
1070                 return service;
1071             }
1072         }
1073 
1074         return null;
1075     }
1076 
1077     /**
1078      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with {@link
1079      * BluetoothProfile#GATT} as argument
1080      *
1081      * @throws UnsupportedOperationException on every call
1082      */
1083     @Override
1084     @RequiresNoPermission
getConnectionState(BluetoothDevice device)1085     public int getConnectionState(BluetoothDevice device) {
1086         throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
1087     }
1088 
1089     /**
1090      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with {@link
1091      * BluetoothProfile#GATT} as argument
1092      *
1093      * @throws UnsupportedOperationException on every call
1094      */
1095     @Override
1096     @RequiresNoPermission
getConnectedDevices()1097     public List<BluetoothDevice> getConnectedDevices() {
1098         throw new UnsupportedOperationException(
1099                 "Use BluetoothManager#getConnectedDevices instead.");
1100     }
1101 
1102     /**
1103      * Not supported - please use {@link BluetoothManager#getDevicesMatchingConnectionStates(int,
1104      * int[])} with {@link BluetoothProfile#GATT} as first argument
1105      *
1106      * @throws UnsupportedOperationException on every call
1107      */
1108     @Override
1109     @RequiresNoPermission
getDevicesMatchingConnectionStates(int[] states)1110     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1111         throw new UnsupportedOperationException(
1112                 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
1113     }
1114 }
1115