BluetoothGattServer.java revision 55d19e495e2b3cd744724f2d12c399217e3c565c
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
17package android.bluetooth;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothProfile;
22import android.content.Context;
23import android.os.ParcelUuid;
24import android.os.RemoteException;
25import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.List;
29import java.util.UUID;
30
31/**
32 * Public API for the Bluetooth GATT Profile server role.
33 *
34 * <p>This class provides Bluetooth GATT server role functionality,
35 * allowing applications to create Bluetooth Smart services and
36 * characteristics.
37 *
38 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
39 * via IPC.  Use {@link BluetoothManager#openGattServer} to get an instance
40 * of this class.
41 */
42public final class BluetoothGattServer implements BluetoothProfile {
43    private static final String TAG = "BluetoothGattServer";
44    private static final boolean DBG = true;
45    private static final boolean VDBG = false;
46
47    private final Context mContext;
48    private BluetoothAdapter mAdapter;
49    private IBluetoothGatt mService;
50    private BluetoothGattServerCallback mCallback;
51
52    private Object mServerIfLock = new Object();
53    private int mServerIf;
54    private int mTransport;
55    private List<BluetoothGattService> mServices;
56
57    private static final int CALLBACK_REG_TIMEOUT = 10000;
58
59    /**
60     * Bluetooth GATT interface callbacks
61     */
62    private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
63        new IBluetoothGattServerCallback.Stub() {
64            /**
65             * Application interface registered - app is ready to go
66             * @hide
67             */
68            public void onServerRegistered(int status, int serverIf) {
69                if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
70                    + " serverIf=" + serverIf);
71                synchronized(mServerIfLock) {
72                    if (mCallback != null) {
73                        mServerIf = serverIf;
74                        mServerIfLock.notify();
75                    } else {
76                        // registration timeout
77                        Log.e(TAG, "onServerRegistered: mCallback is null");
78                    }
79                }
80            }
81
82            /**
83             * Callback reporting an LE scan result.
84             * @hide
85             */
86            public void onScanResult(String address, int rssi, byte[] advData) {
87                if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
88                // no op
89            }
90
91            /**
92             * Server connection state changed
93             * @hide
94             */
95            public void onServerConnectionState(int status, int serverIf,
96                                                boolean connected, String address) {
97                if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
98                    + " serverIf=" + serverIf + " device=" + address);
99                try {
100                    mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
101                                                      connected ? BluetoothProfile.STATE_CONNECTED :
102                                                      BluetoothProfile.STATE_DISCONNECTED);
103                } catch (Exception ex) {
104                    Log.w(TAG, "Unhandled exception in callback", ex);
105                }
106            }
107
108            /**
109             * Service has been added
110             * @hide
111             */
112            public void onServiceAdded(int status, int srvcType,
113                                       int srvcInstId, ParcelUuid srvcId) {
114                UUID srvcUuid = srvcId.getUuid();
115                if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid
116                    + "status=" + status);
117
118                BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
119                if (service == null) return;
120
121                try {
122                    mCallback.onServiceAdded((int)status, service);
123                } catch (Exception ex) {
124                    Log.w(TAG, "Unhandled exception in callback", ex);
125                }
126            }
127
128            /**
129             * Remote client characteristic read request.
130             * @hide
131             */
132            public void onCharacteristicReadRequest(String address, int transId,
133                            int offset, boolean isLong, int srvcType, int srvcInstId,
134                            ParcelUuid srvcId, int charInstId, ParcelUuid charId) {
135                UUID srvcUuid = srvcId.getUuid();
136                UUID charUuid = charId.getUuid();
137                if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - "
138                    + "service=" + srvcUuid + ", characteristic=" + charUuid);
139
140                BluetoothDevice device = mAdapter.getRemoteDevice(address);
141                BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
142                if (service == null) return;
143
144                BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
145                if (characteristic == null) return;
146
147                try {
148                    mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
149                } catch (Exception ex) {
150                    Log.w(TAG, "Unhandled exception in callback", ex);
151                }
152            }
153
154            /**
155             * Remote client descriptor read request.
156             * @hide
157             */
158            public void onDescriptorReadRequest(String address, int transId,
159                            int offset, boolean isLong, int srvcType, int srvcInstId,
160                            ParcelUuid srvcId, int charInstId, ParcelUuid charId,
161                            ParcelUuid descrId) {
162                UUID srvcUuid = srvcId.getUuid();
163                UUID charUuid = charId.getUuid();
164                UUID descrUuid = descrId.getUuid();
165                if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - "
166                    + "service=" + srvcUuid + ", characteristic=" + charUuid
167                    + "descriptor=" + descrUuid);
168
169                BluetoothDevice device = mAdapter.getRemoteDevice(address);
170                BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
171                if (service == null) return;
172
173                BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
174                if (characteristic == null) return;
175
176                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
177                if (descriptor == null) return;
178
179                try {
180                    mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
181                } catch (Exception ex) {
182                    Log.w(TAG, "Unhandled exception in callback", ex);
183                }
184            }
185
186            /**
187             * Remote client characteristic write request.
188             * @hide
189             */
190            public void onCharacteristicWriteRequest(String address, int transId,
191                            int offset, int length, boolean isPrep, boolean needRsp,
192                            int srvcType, int srvcInstId, ParcelUuid srvcId,
193                            int charInstId, ParcelUuid charId, byte[] value) {
194                UUID srvcUuid = srvcId.getUuid();
195                UUID charUuid = charId.getUuid();
196                if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - "
197                    + "service=" + srvcUuid + ", characteristic=" + charUuid);
198
199                BluetoothDevice device = mAdapter.getRemoteDevice(address);
200                BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
201                if (service == null) return;
202
203                BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
204                if (characteristic == null) return;
205
206                try {
207                    mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
208                                                           isPrep, needRsp, offset, value);
209                } catch (Exception ex) {
210                    Log.w(TAG, "Unhandled exception in callback", ex);
211                }
212
213            }
214
215            /**
216             * Remote client descriptor write request.
217             * @hide
218             */
219            public void onDescriptorWriteRequest(String address, int transId,
220                            int offset, int length, boolean isPrep, boolean needRsp,
221                            int srvcType, int srvcInstId, ParcelUuid srvcId,
222                            int charInstId, ParcelUuid charId, ParcelUuid descrId,
223                            byte[] value) {
224                UUID srvcUuid = srvcId.getUuid();
225                UUID charUuid = charId.getUuid();
226                UUID descrUuid = descrId.getUuid();
227                if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - "
228                    + "service=" + srvcUuid + ", characteristic=" + charUuid
229                    + "descriptor=" + descrUuid);
230
231                BluetoothDevice device = mAdapter.getRemoteDevice(address);
232
233                BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
234                if (service == null) return;
235
236                BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
237                if (characteristic == null) return;
238
239                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
240                if (descriptor == null) return;
241
242                try {
243                    mCallback.onDescriptorWriteRequest(device, transId, descriptor,
244                                                       isPrep, needRsp, offset, value);
245                } catch (Exception ex) {
246                    Log.w(TAG, "Unhandled exception in callback", ex);
247                }
248            }
249
250            /**
251             * Execute pending writes.
252             * @hide
253             */
254            public void onExecuteWrite(String address, int transId,
255                                       boolean execWrite) {
256                if (DBG) Log.d(TAG, "onExecuteWrite() - "
257                    + "device=" + address + ", transId=" + transId
258                    + "execWrite=" + execWrite);
259
260                BluetoothDevice device = mAdapter.getRemoteDevice(address);
261                if (device == null) return;
262
263                try {
264                    mCallback.onExecuteWrite(device, transId, execWrite);
265                } catch (Exception ex) {
266                    Log.w(TAG, "Unhandled exception in callback", ex);
267                }
268            }
269
270            /**
271             * A notification/indication has been sent.
272             * @hide
273             */
274            public void onNotificationSent(String address, int status) {
275                if (VDBG) Log.d(TAG, "onNotificationSent() - "
276                    + "device=" + address + ", status=" + status);
277
278                BluetoothDevice device = mAdapter.getRemoteDevice(address);
279                if (device == null) return;
280
281                try {
282                    mCallback.onNotificationSent(device, status);
283                } catch (Exception ex) {
284                    Log.w(TAG, "Unhandled exception: " + ex);
285                }
286            }
287
288            /**
289             * Callback indicating the remote device connection is congested.
290             * @hide
291             */
292            public void onConnectionCongested(String address, boolean congested) {
293                if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
294                        + " congested=" + congested);
295
296                BluetoothDevice device = mAdapter.getRemoteDevice(address);
297                if (device == null) return;
298
299                try {
300                    mCallback.onConnectionCongested(device, congested);
301                } catch (Exception ex) {
302                    Log.w(TAG, "Unhandled exception in callback", ex);
303                }
304            }
305        };
306
307    /**
308     * Create a BluetoothGattServer proxy object.
309     */
310    /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) {
311        mContext = context;
312        mService = iGatt;
313        mAdapter = BluetoothAdapter.getDefaultAdapter();
314        mCallback = null;
315        mServerIf = 0;
316        mTransport = transport;
317        mServices = new ArrayList<BluetoothGattService>();
318    }
319
320    /**
321     * Close this GATT server instance.
322     *
323     * Application should call this method as early as possible after it is done with
324     * this GATT server.
325     */
326    public void close() {
327        if (DBG) Log.d(TAG, "close()");
328        unregisterCallback();
329    }
330
331    /**
332     * Register an application callback to start using GattServer.
333     *
334     * <p>This is an asynchronous call. The callback is used to notify
335     * success or failure if the function returns true.
336     *
337     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
338     *
339     * @param callback GATT callback handler that will receive asynchronous
340     *                 callbacks.
341     * @return true, the callback will be called to notify success or failure,
342     *         false on immediate error
343     */
344    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
345        if (DBG) Log.d(TAG, "registerCallback()");
346        if (mService == null) {
347            Log.e(TAG, "GATT service not available");
348            return false;
349        }
350        UUID uuid = UUID.randomUUID();
351        if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
352
353        synchronized(mServerIfLock) {
354            if (mCallback != null) {
355                Log.e(TAG, "App can register callback only once");
356                return false;
357            }
358
359            mCallback = callback;
360            try {
361                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
362            } catch (RemoteException e) {
363                Log.e(TAG,"",e);
364                mCallback = null;
365                return false;
366            }
367
368            try {
369                mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
370            } catch (InterruptedException e) {
371                Log.e(TAG, "" + e);
372                mCallback = null;
373            }
374
375            if (mServerIf == 0) {
376                mCallback = null;
377                return false;
378            } else {
379                return true;
380            }
381        }
382    }
383
384    /**
385     * Unregister the current application and callbacks.
386     */
387    private void unregisterCallback() {
388        if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
389        if (mService == null || mServerIf == 0) return;
390
391        try {
392            mCallback = null;
393            mService.unregisterServer(mServerIf);
394            mServerIf = 0;
395        } catch (RemoteException e) {
396            Log.e(TAG,"",e);
397        }
398    }
399
400    /**
401     * Returns a service by UUID, instance and type.
402     * @hide
403     */
404    /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
405        for(BluetoothGattService svc : mServices) {
406            if (svc.getType() == type &&
407                svc.getInstanceId() == instanceId &&
408                svc.getUuid().equals(uuid)) {
409                return svc;
410            }
411        }
412        return null;
413    }
414
415    /**
416     * Initiate a connection to a Bluetooth GATT capable device.
417     *
418     * <p>The connection may not be established right away, but will be
419     * completed when the remote device is available. A
420     * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
421     * invoked when the connection state changes as a result of this function.
422     *
423     * <p>The autoConnect paramter determines whether to actively connect to
424     * the remote device, or rather passively scan and finalize the connection
425     * when the remote device is in range/available. Generally, the first ever
426     * connection to a device should be direct (autoConnect set to false) and
427     * subsequent connections to known devices should be invoked with the
428     * autoConnect parameter set to true.
429     *
430     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
431     *
432     * @param autoConnect Whether to directly connect to the remote device (false)
433     *                    or to automatically connect as soon as the remote
434     *                    device becomes available (true).
435     * @return true, if the connection attempt was initiated successfully
436     */
437    public boolean connect(BluetoothDevice device, boolean autoConnect) {
438        if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
439        if (mService == null || mServerIf == 0) return false;
440
441        try {
442            mService.serverConnect(mServerIf, device.getAddress(),
443                               autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
444        } catch (RemoteException e) {
445            Log.e(TAG,"",e);
446            return false;
447        }
448
449        return true;
450    }
451
452    /**
453     * Disconnects an established connection, or cancels a connection attempt
454     * currently in progress.
455     *
456     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
457     *
458     * @param device Remote device
459     */
460    public void cancelConnection(BluetoothDevice device) {
461        if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
462        if (mService == null || mServerIf == 0) return;
463
464        try {
465            mService.serverDisconnect(mServerIf, device.getAddress());
466        } catch (RemoteException e) {
467            Log.e(TAG,"",e);
468        }
469    }
470
471    /**
472     * Send a response to a read or write request to a remote device.
473     *
474     * <p>This function must be invoked in when a remote read/write request
475     * is received by one of these callback methods:
476     *
477     * <ul>
478     *      <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
479     *      <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
480     *      <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
481     *      <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
482     * </ul>
483     *
484     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
485     *
486     * @param device The remote device to send this response to
487     * @param requestId The ID of the request that was received with the callback
488     * @param status The status of the request to be sent to the remote devices
489     * @param offset Value offset for partial read/write response
490     * @param value The value of the attribute that was read/written (optional)
491     */
492    public boolean sendResponse(BluetoothDevice device, int requestId,
493                                int status, int offset, byte[] value) {
494        if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
495        if (mService == null || mServerIf == 0) return false;
496
497        try {
498            mService.sendResponse(mServerIf, device.getAddress(), requestId,
499                                  status, offset, value);
500        } catch (RemoteException e) {
501            Log.e(TAG,"",e);
502            return false;
503        }
504        return true;
505    }
506
507    /**
508     * Send a notification or indication that a local characteristic has been
509     * updated.
510     *
511     * <p>A notification or indication is sent to the remote device to signal
512     * that the characteristic has been updated. This function should be invoked
513     * for every client that requests notifications/indications by writing
514     * to the "Client Configuration" descriptor for the given characteristic.
515     *
516     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
517     *
518     * @param device The remote device to receive the notification/indication
519     * @param characteristic The local characteristic that has been updated
520     * @param confirm true to request confirmation from the client (indication),
521     *                false to send a notification
522     * @return true, if the notification has been triggered successfully
523     */
524    public boolean notifyCharacteristicChanged(BluetoothDevice device,
525                    BluetoothGattCharacteristic characteristic, boolean confirm) {
526        if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
527        if (mService == null || mServerIf == 0) return false;
528
529        BluetoothGattService service = characteristic.getService();
530        if (service == null) return false;
531
532        try {
533            mService.sendNotification(mServerIf, device.getAddress(),
534                    service.getType(), service.getInstanceId(),
535                    new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
536                    new ParcelUuid(characteristic.getUuid()), confirm,
537                    characteristic.getValue());
538        } catch (RemoteException e) {
539            Log.e(TAG,"",e);
540            return false;
541        }
542
543        return true;
544    }
545
546    /**
547     * Add a service to the list of services to be hosted.
548     *
549     * <p>Once a service has been addded to the the list, the service and its
550     * included characteristics will be provided by the local device.
551     *
552     * <p>If the local device has already exposed services when this function
553     * is called, a service update notification will be sent to all clients.
554     *
555     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
556     *
557     * @param service Service to be added to the list of services provided
558     *                by this device.
559     * @return true, if the service has been added successfully
560     */
561    public boolean addService(BluetoothGattService service) {
562        if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
563        if (mService == null || mServerIf == 0) return false;
564
565        mServices.add(service);
566
567        try {
568            mService.beginServiceDeclaration(mServerIf, service.getType(),
569                service.getInstanceId(), service.getHandles(),
570                new ParcelUuid(service.getUuid()), service.isAdvertisePreferred());
571
572            List<BluetoothGattService> includedServices = service.getIncludedServices();
573            for (BluetoothGattService includedService : includedServices) {
574                mService.addIncludedService(mServerIf,
575                    includedService.getType(),
576                    includedService.getInstanceId(),
577                    new ParcelUuid(includedService.getUuid()));
578            }
579
580            List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
581            for (BluetoothGattCharacteristic characteristic : characteristics) {
582                int permission = ((characteristic.getKeySize() - 7) << 12)
583                                    + characteristic.getPermissions();
584                mService.addCharacteristic(mServerIf,
585                    new ParcelUuid(characteristic.getUuid()),
586                    characteristic.getProperties(), permission);
587
588                List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
589                for (BluetoothGattDescriptor descriptor: descriptors) {
590                    permission = ((characteristic.getKeySize() - 7) << 12)
591                                        + descriptor.getPermissions();
592                    mService.addDescriptor(mServerIf,
593                        new ParcelUuid(descriptor.getUuid()), permission);
594                }
595            }
596
597            mService.endServiceDeclaration(mServerIf);
598        } catch (RemoteException e) {
599            Log.e(TAG,"",e);
600            return false;
601        }
602
603        return true;
604    }
605
606    /**
607     * Removes a service from the list of services to be provided.
608     *
609     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
610     *
611     * @param service Service to be removed.
612     * @return true, if the service has been removed
613     */
614    public boolean removeService(BluetoothGattService service) {
615        if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
616        if (mService == null || mServerIf == 0) return false;
617
618        BluetoothGattService intService = getService(service.getUuid(),
619                                service.getInstanceId(), service.getType());
620        if (intService == null) return false;
621
622        try {
623            mService.removeService(mServerIf, service.getType(),
624                service.getInstanceId(), new ParcelUuid(service.getUuid()));
625            mServices.remove(intService);
626        } catch (RemoteException e) {
627            Log.e(TAG,"",e);
628            return false;
629        }
630
631        return true;
632    }
633
634    /**
635     * Remove all services from the list of provided services.
636     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
637     */
638    public void clearServices() {
639        if (DBG) Log.d(TAG, "clearServices()");
640        if (mService == null || mServerIf == 0) return;
641
642        try {
643            mService.clearServices(mServerIf);
644            mServices.clear();
645        } catch (RemoteException e) {
646            Log.e(TAG,"",e);
647        }
648    }
649
650    /**
651     * Returns a list of GATT services offered by this device.
652     *
653     * <p>An application must call {@link #addService} to add a serice to the
654     * list of services offered by this device.
655     *
656     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
657     *
658     * @return List of services. Returns an empty list
659     *         if no services have been added yet.
660     */
661    public List<BluetoothGattService> getServices() {
662        return mServices;
663    }
664
665    /**
666     * Returns a {@link BluetoothGattService} from the list of services offered
667     * by this device.
668     *
669     * <p>If multiple instances of the same service (as identified by UUID)
670     * exist, the first instance of the service is returned.
671     *
672     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
673     *
674     * @param uuid UUID of the requested service
675     * @return BluetoothGattService if supported, or null if the requested
676     *         service is not offered by this device.
677     */
678    public BluetoothGattService getService(UUID uuid) {
679        for (BluetoothGattService service : mServices) {
680            if (service.getUuid().equals(uuid)) {
681                return service;
682            }
683        }
684
685        return null;
686    }
687
688
689    /**
690     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
691     * with {@link BluetoothProfile#GATT} as argument
692     *
693     * @throws UnsupportedOperationException
694     */
695    @Override
696    public int getConnectionState(BluetoothDevice device) {
697        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
698    }
699
700    /**
701     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
702     * with {@link BluetoothProfile#GATT} as argument
703     *
704     * @throws UnsupportedOperationException
705     */
706    @Override
707    public List<BluetoothDevice> getConnectedDevices() {
708        throw new UnsupportedOperationException
709            ("Use BluetoothManager#getConnectedDevices instead.");
710    }
711
712    /**
713     * Not supported - please use
714     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
715     * with {@link BluetoothProfile#GATT} as first argument
716     *
717     * @throws UnsupportedOperationException
718     */
719    @Override
720    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
721        throw new UnsupportedOperationException
722            ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
723    }
724}
725