BluetoothGatt.java revision bbb35c0ba6e6c80a44af45b1a891394b532e001e
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.os.Handler;
20import android.os.ParcelUuid;
21import android.os.RemoteException;
22import android.util.Log;
23
24import java.util.ArrayList;
25import java.util.List;
26import java.util.UUID;
27
28/**
29 * Public API for the Bluetooth GATT Profile.
30 *
31 * <p>This class provides Bluetooth GATT functionality to enable communication
32 * with Bluetooth Smart or Smart Ready devices.
33 *
34 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
35 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
36 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
37 * scan process.
38 */
39public final class BluetoothGatt implements BluetoothProfile {
40    private static final String TAG = "BluetoothGatt";
41    private static final boolean DBG = true;
42    private static final boolean VDBG = false;
43
44    private IBluetoothGatt mService;
45    private volatile BluetoothGattCallback mCallback;
46    private Handler mHandler;
47    private int mClientIf;
48    private BluetoothDevice mDevice;
49    private boolean mAutoConnect;
50    private int mAuthRetryState;
51    private int mConnState;
52    private final Object mStateLock = new Object();
53    private Boolean mDeviceBusy = false;
54    private int mTransport;
55    private int mPhy;
56    private boolean mOpportunistic;
57
58    private static final int AUTH_RETRY_STATE_IDLE = 0;
59    private static final int AUTH_RETRY_STATE_NO_MITM = 1;
60    private static final int AUTH_RETRY_STATE_MITM = 2;
61
62    private static final int CONN_STATE_IDLE = 0;
63    private static final int CONN_STATE_CONNECTING = 1;
64    private static final int CONN_STATE_CONNECTED = 2;
65    private static final int CONN_STATE_DISCONNECTING = 3;
66    private static final int CONN_STATE_CLOSED = 4;
67
68    private List<BluetoothGattService> mServices;
69
70    /** A GATT operation completed successfully */
71    public static final int GATT_SUCCESS = 0;
72
73    /** GATT read operation is not permitted */
74    public static final int GATT_READ_NOT_PERMITTED = 0x2;
75
76    /** GATT write operation is not permitted */
77    public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
78
79    /** Insufficient authentication for a given operation */
80    public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
81
82    /** The given request is not supported */
83    public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
84
85    /** Insufficient encryption for a given operation */
86    public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
87
88    /** A read or write operation was requested with an invalid offset */
89    public static final int GATT_INVALID_OFFSET = 0x7;
90
91    /** A write operation exceeds the maximum length of the attribute */
92    public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
93
94    /** A remote device connection is congested. */
95    public static final int GATT_CONNECTION_CONGESTED = 0x8f;
96
97    /** A GATT operation failed, errors other than the above */
98    public static final int GATT_FAILURE = 0x101;
99
100    /**
101     * Connection paramter update - Use the connection paramters recommended by the
102     * Bluetooth SIG. This is the default value if no connection parameter update
103     * is requested.
104     */
105    public static final int CONNECTION_PRIORITY_BALANCED = 0;
106
107    /**
108     * Connection paramter update - Request a high priority, low latency connection.
109     * An application should only request high priority connection paramters to transfer
110     * large amounts of data over LE quickly. Once the transfer is complete, the application
111     * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
112     * to reduce energy use.
113     */
114    public static final int CONNECTION_PRIORITY_HIGH = 1;
115
116    /** Connection paramter update - Request low power, reduced data rate connection parameters. */
117    public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
118
119    /**
120     * No authentication required.
121     *
122     * @hide
123     */
124    /*package*/ static final int AUTHENTICATION_NONE = 0;
125
126    /**
127     * Authentication requested; no man-in-the-middle protection required.
128     *
129     * @hide
130     */
131    /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
132
133    /**
134     * Authentication with man-in-the-middle protection requested.
135     *
136     * @hide
137     */
138    /*package*/ static final int AUTHENTICATION_MITM = 2;
139
140    /**
141     * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
142     */
143    private final IBluetoothGattCallback mBluetoothGattCallback =
144            new IBluetoothGattCallback.Stub() {
145                /**
146                 * Application interface registered - app is ready to go
147                 * @hide
148                 */
149                @Override
150                public void onClientRegistered(int status, int clientIf) {
151                    if (DBG) {
152                        Log.d(TAG, "onClientRegistered() - status=" + status
153                                + " clientIf=" + clientIf);
154                    }
155                    if (VDBG) {
156                        synchronized (mStateLock) {
157                            if (mConnState != CONN_STATE_CONNECTING) {
158                                Log.e(TAG, "Bad connection state: " + mConnState);
159                            }
160                        }
161                    }
162                    mClientIf = clientIf;
163                    if (status != GATT_SUCCESS) {
164                        runOrQueueCallback(new Runnable() {
165                            @Override
166                            public void run() {
167                                final BluetoothGattCallback callback = mCallback;
168                                if (callback != null) {
169                                    callback.onConnectionStateChange(BluetoothGatt.this,
170                                            GATT_FAILURE,
171                                            BluetoothProfile.STATE_DISCONNECTED);
172                                }
173                            }
174                        });
175
176                        synchronized (mStateLock) {
177                            mConnState = CONN_STATE_IDLE;
178                        }
179                        return;
180                    }
181                    try {
182                        mService.clientConnect(mClientIf, mDevice.getAddress(),
183                                !mAutoConnect, mTransport, mOpportunistic,
184                                mPhy); // autoConnect is inverse of "isDirect"
185                    } catch (RemoteException e) {
186                        Log.e(TAG, "", e);
187                    }
188                }
189
190                /**
191                 * Phy update callback
192                 * @hide
193                 */
194                @Override
195                public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
196                    if (DBG) {
197                        Log.d(TAG, "onPhyUpdate() - status=" + status
198                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
199                    }
200                    if (!address.equals(mDevice.getAddress())) {
201                        return;
202                    }
203
204                    runOrQueueCallback(new Runnable() {
205                        @Override
206                        public void run() {
207                            final BluetoothGattCallback callback = mCallback;
208                            if (callback != null) {
209                                callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
210                            }
211                        }
212                    });
213                }
214
215                /**
216                 * Phy read callback
217                 * @hide
218                 */
219                @Override
220                public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
221                    if (DBG) {
222                        Log.d(TAG, "onPhyRead() - status=" + status
223                                + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
224                    }
225                    if (!address.equals(mDevice.getAddress())) {
226                        return;
227                    }
228
229                    runOrQueueCallback(new Runnable() {
230                        @Override
231                        public void run() {
232                            final BluetoothGattCallback callback = mCallback;
233                            if (callback != null) {
234                                callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
235                            }
236                        }
237                    });
238                }
239
240                /**
241                 * Client connection state changed
242                 * @hide
243                 */
244                @Override
245                public void onClientConnectionState(int status, int clientIf,
246                        boolean connected, String address) {
247                    if (DBG) {
248                        Log.d(TAG, "onClientConnectionState() - status=" + status
249                                + " clientIf=" + clientIf + " device=" + address);
250                    }
251                    if (!address.equals(mDevice.getAddress())) {
252                        return;
253                    }
254                    int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
255                            BluetoothProfile.STATE_DISCONNECTED;
256
257                    runOrQueueCallback(new Runnable() {
258                        @Override
259                        public void run() {
260                            final BluetoothGattCallback callback = mCallback;
261                            if (callback != null) {
262                                callback.onConnectionStateChange(BluetoothGatt.this, status,
263                                        profileState);
264                            }
265                        }
266                    });
267
268                    synchronized (mStateLock) {
269                        if (connected) {
270                            mConnState = CONN_STATE_CONNECTED;
271                        } else {
272                            mConnState = CONN_STATE_IDLE;
273                        }
274                    }
275
276                    synchronized (mDeviceBusy) {
277                        mDeviceBusy = false;
278                    }
279                }
280
281                /**
282                 * Remote search has been completed.
283                 * The internal object structure should now reflect the state
284                 * of the remote device database. Let the application know that
285                 * we are done at this point.
286                 * @hide
287                 */
288                @Override
289                public void onSearchComplete(String address, List<BluetoothGattService> services,
290                        int status) {
291                    if (DBG) {
292                        Log.d(TAG,
293                                "onSearchComplete() = Device=" + address + " Status=" + status);
294                    }
295                    if (!address.equals(mDevice.getAddress())) {
296                        return;
297                    }
298
299                    for (BluetoothGattService s : services) {
300                        //services we receive don't have device set properly.
301                        s.setDevice(mDevice);
302                    }
303
304                    mServices.addAll(services);
305
306                    // Fix references to included services, as they doesn't point to right objects.
307                    for (BluetoothGattService fixedService : mServices) {
308                        ArrayList<BluetoothGattService> includedServices =
309                                new ArrayList(fixedService.getIncludedServices());
310                        fixedService.getIncludedServices().clear();
311
312                        for (BluetoothGattService brokenRef : includedServices) {
313                            BluetoothGattService includedService = getService(mDevice,
314                                    brokenRef.getUuid(), brokenRef.getInstanceId());
315                            if (includedService != null) {
316                                fixedService.addIncludedService(includedService);
317                            } else {
318                                Log.e(TAG, "Broken GATT database: can't find included service.");
319                            }
320                        }
321                    }
322
323                    runOrQueueCallback(new Runnable() {
324                        @Override
325                        public void run() {
326                            final BluetoothGattCallback callback = mCallback;
327                            if (callback != null) {
328                                callback.onServicesDiscovered(BluetoothGatt.this, status);
329                            }
330                        }
331                    });
332                }
333
334                /**
335                 * Remote characteristic has been read.
336                 * Updates the internal value.
337                 * @hide
338                 */
339                @Override
340                public void onCharacteristicRead(String address, int status, int handle,
341                        byte[] value) {
342                    if (VDBG) {
343                        Log.d(TAG, "onCharacteristicRead() - Device=" + address
344                                + " handle=" + handle + " Status=" + status);
345                    }
346
347                    if (!address.equals(mDevice.getAddress())) {
348                        return;
349                    }
350
351                    synchronized (mDeviceBusy) {
352                        mDeviceBusy = false;
353                    }
354
355                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
356                            || status == GATT_INSUFFICIENT_ENCRYPTION)
357                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
358                        try {
359                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
360                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
361                            mService.readCharacteristic(mClientIf, address, handle, authReq);
362                            mAuthRetryState++;
363                            return;
364                        } catch (RemoteException e) {
365                            Log.e(TAG, "", e);
366                        }
367                    }
368
369                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
370
371                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
372                            handle);
373                    if (characteristic == null) {
374                        Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
375                        return;
376                    }
377
378                    runOrQueueCallback(new Runnable() {
379                        @Override
380                        public void run() {
381                            final BluetoothGattCallback callback = mCallback;
382                            if (callback != null) {
383                                if (status == 0) characteristic.setValue(value);
384                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
385                                        status);
386                            }
387                        }
388                    });
389                }
390
391                /**
392                 * Characteristic has been written to the remote device.
393                 * Let the app know how we did...
394                 * @hide
395                 */
396                @Override
397                public void onCharacteristicWrite(String address, int status, int handle) {
398                    if (VDBG) {
399                        Log.d(TAG, "onCharacteristicWrite() - Device=" + address
400                                + " handle=" + handle + " Status=" + status);
401                    }
402
403                    if (!address.equals(mDevice.getAddress())) {
404                        return;
405                    }
406
407                    synchronized (mDeviceBusy) {
408                        mDeviceBusy = false;
409                    }
410
411                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
412                            handle);
413                    if (characteristic == null) return;
414
415                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
416                            || status == GATT_INSUFFICIENT_ENCRYPTION)
417                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
418                        try {
419                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
420                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
421                            mService.writeCharacteristic(mClientIf, address, handle,
422                                    characteristic.getWriteType(), authReq,
423                                    characteristic.getValue());
424                            mAuthRetryState++;
425                            return;
426                        } catch (RemoteException e) {
427                            Log.e(TAG, "", e);
428                        }
429                    }
430
431                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
432
433                    runOrQueueCallback(new Runnable() {
434                        @Override
435                        public void run() {
436                            final BluetoothGattCallback callback = mCallback;
437                            if (callback != null) {
438                                callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
439                                        status);
440                            }
441                        }
442                    });
443                }
444
445                /**
446                 * Remote characteristic has been updated.
447                 * Updates the internal value.
448                 * @hide
449                 */
450                @Override
451                public void onNotify(String address, int handle, byte[] value) {
452                    if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
453
454                    if (!address.equals(mDevice.getAddress())) {
455                        return;
456                    }
457
458                    BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
459                            handle);
460                    if (characteristic == null) return;
461
462                    runOrQueueCallback(new Runnable() {
463                        @Override
464                        public void run() {
465                            final BluetoothGattCallback callback = mCallback;
466                            if (callback != null) {
467                                characteristic.setValue(value);
468                                callback.onCharacteristicChanged(BluetoothGatt.this,
469                                        characteristic);
470                            }
471                        }
472                    });
473                }
474
475                /**
476                 * Descriptor has been read.
477                 * @hide
478                 */
479                @Override
480                public void onDescriptorRead(String address, int status, int handle, byte[] value) {
481                    if (VDBG) {
482                        Log.d(TAG,
483                                "onDescriptorRead() - Device=" + address + " handle=" + handle);
484                    }
485
486                    if (!address.equals(mDevice.getAddress())) {
487                        return;
488                    }
489
490                    synchronized (mDeviceBusy) {
491                        mDeviceBusy = false;
492                    }
493
494                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
495                    if (descriptor == null) return;
496
497
498                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
499                            || status == GATT_INSUFFICIENT_ENCRYPTION)
500                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
501                        try {
502                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
503                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
504                            mService.readDescriptor(mClientIf, address, handle, authReq);
505                            mAuthRetryState++;
506                            return;
507                        } catch (RemoteException e) {
508                            Log.e(TAG, "", e);
509                        }
510                    }
511
512                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
513
514                    runOrQueueCallback(new Runnable() {
515                        @Override
516                        public void run() {
517                            final BluetoothGattCallback callback = mCallback;
518                            if (callback != null) {
519                                if (status == 0) descriptor.setValue(value);
520                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
521                            }
522                        }
523                    });
524                }
525
526                /**
527                 * Descriptor write operation complete.
528                 * @hide
529                 */
530                @Override
531                public void onDescriptorWrite(String address, int status, int handle) {
532                    if (VDBG) {
533                        Log.d(TAG,
534                                "onDescriptorWrite() - Device=" + address + " handle=" + handle);
535                    }
536
537                    if (!address.equals(mDevice.getAddress())) {
538                        return;
539                    }
540
541                    synchronized (mDeviceBusy) {
542                        mDeviceBusy = false;
543                    }
544
545                    BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
546                    if (descriptor == null) return;
547
548                    if ((status == GATT_INSUFFICIENT_AUTHENTICATION
549                            || status == GATT_INSUFFICIENT_ENCRYPTION)
550                            && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
551                        try {
552                            final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
553                                    ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
554                            mService.writeDescriptor(mClientIf, address, handle,
555                                    authReq, descriptor.getValue());
556                            mAuthRetryState++;
557                            return;
558                        } catch (RemoteException e) {
559                            Log.e(TAG, "", e);
560                        }
561                    }
562
563                    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
564
565                    runOrQueueCallback(new Runnable() {
566                        @Override
567                        public void run() {
568                            final BluetoothGattCallback callback = mCallback;
569                            if (callback != null) {
570                                callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
571                            }
572                        }
573                    });
574                }
575
576                /**
577                 * Prepared write transaction completed (or aborted)
578                 * @hide
579                 */
580                @Override
581                public void onExecuteWrite(String address, int status) {
582                    if (VDBG) {
583                        Log.d(TAG, "onExecuteWrite() - Device=" + address
584                                + " status=" + status);
585                    }
586                    if (!address.equals(mDevice.getAddress())) {
587                        return;
588                    }
589
590                    synchronized (mDeviceBusy) {
591                        mDeviceBusy = false;
592                    }
593
594                    runOrQueueCallback(new Runnable() {
595                        @Override
596                        public void run() {
597                            final BluetoothGattCallback callback = mCallback;
598                            if (callback != null) {
599                                callback.onReliableWriteCompleted(BluetoothGatt.this, status);
600                            }
601                        }
602                    });
603                }
604
605                /**
606                 * Remote device RSSI has been read
607                 * @hide
608                 */
609                @Override
610                public void onReadRemoteRssi(String address, int rssi, int status) {
611                    if (VDBG) {
612                        Log.d(TAG, "onReadRemoteRssi() - Device=" + address
613                                + " rssi=" + rssi + " status=" + status);
614                    }
615                    if (!address.equals(mDevice.getAddress())) {
616                        return;
617                    }
618                    runOrQueueCallback(new Runnable() {
619                        @Override
620                        public void run() {
621                            final BluetoothGattCallback callback = mCallback;
622                            if (callback != null) {
623                                callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
624                            }
625                        }
626                    });
627                }
628
629                /**
630                 * Callback invoked when the MTU for a given connection changes
631                 * @hide
632                 */
633                @Override
634                public void onConfigureMTU(String address, int mtu, int status) {
635                    if (DBG) {
636                        Log.d(TAG, "onConfigureMTU() - Device=" + address
637                                + " mtu=" + mtu + " status=" + status);
638                    }
639                    if (!address.equals(mDevice.getAddress())) {
640                        return;
641                    }
642
643                    runOrQueueCallback(new Runnable() {
644                        @Override
645                        public void run() {
646                            final BluetoothGattCallback callback = mCallback;
647                            if (callback != null) {
648                                callback.onMtuChanged(BluetoothGatt.this, mtu, status);
649                            }
650                        }
651                    });
652                }
653
654                /**
655                 * Callback invoked when the given connection is updated
656                 * @hide
657                 */
658                @Override
659                public void onConnectionUpdated(String address, int interval, int latency,
660                        int timeout, int status) {
661                    if (DBG) {
662                        Log.d(TAG, "onConnectionUpdated() - Device=" + address
663                                + " interval=" + interval + " latency=" + latency
664                                + " timeout=" + timeout + " status=" + status);
665                    }
666                    if (!address.equals(mDevice.getAddress())) {
667                        return;
668                    }
669
670                    runOrQueueCallback(new Runnable() {
671                        @Override
672                        public void run() {
673                            final BluetoothGattCallback callback = mCallback;
674                            if (callback != null) {
675                                callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
676                                        timeout, status);
677                            }
678                        }
679                    });
680                }
681            };
682
683    /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
684            int transport, boolean opportunistic, int phy) {
685        mService = iGatt;
686        mDevice = device;
687        mTransport = transport;
688        mPhy = phy;
689        mOpportunistic = opportunistic;
690        mServices = new ArrayList<BluetoothGattService>();
691
692        mConnState = CONN_STATE_IDLE;
693        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
694    }
695
696    /**
697     * Close this Bluetooth GATT client.
698     *
699     * Application should call this method as early as possible after it is done with
700     * this GATT client.
701     */
702    public void close() {
703        if (DBG) Log.d(TAG, "close()");
704
705        unregisterApp();
706        mConnState = CONN_STATE_CLOSED;
707        mAuthRetryState = AUTH_RETRY_STATE_IDLE;
708    }
709
710    /**
711     * Returns a service by UUID, instance and type.
712     *
713     * @hide
714     */
715    /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
716            int instanceId) {
717        for (BluetoothGattService svc : mServices) {
718            if (svc.getDevice().equals(device)
719                    && svc.getInstanceId() == instanceId
720                    && svc.getUuid().equals(uuid)) {
721                return svc;
722            }
723        }
724        return null;
725    }
726
727
728    /**
729     * Returns a characteristic with id equal to instanceId.
730     *
731     * @hide
732     */
733    /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
734            int instanceId) {
735        for (BluetoothGattService svc : mServices) {
736            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
737                if (charac.getInstanceId() == instanceId) {
738                    return charac;
739                }
740            }
741        }
742        return null;
743    }
744
745    /**
746     * Returns a descriptor with id equal to instanceId.
747     *
748     * @hide
749     */
750    /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
751        for (BluetoothGattService svc : mServices) {
752            for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
753                for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
754                    if (desc.getInstanceId() == instanceId) {
755                        return desc;
756                    }
757                }
758            }
759        }
760        return null;
761    }
762
763    /**
764     * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
765     * immediately if no Handler was provided.
766     */
767    private void runOrQueueCallback(final Runnable cb) {
768        if (mHandler == null) {
769            try {
770                cb.run();
771            } catch (Exception ex) {
772                Log.w(TAG, "Unhandled exception in callback", ex);
773            }
774        } else {
775            mHandler.post(cb);
776        }
777    }
778
779    /**
780     * Register an application callback to start using GATT.
781     *
782     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
783     * is used to notify success or failure if the function returns true.
784     *
785     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
786     *
787     * @param callback GATT callback handler that will receive asynchronous callbacks.
788     * @return If true, the callback will be called to notify success or failure, false on immediate
789     * error
790     */
791    private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
792        if (DBG) Log.d(TAG, "registerApp()");
793        if (mService == null) return false;
794
795        mCallback = callback;
796        mHandler = handler;
797        UUID uuid = UUID.randomUUID();
798        if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
799
800        try {
801            mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
802        } catch (RemoteException e) {
803            Log.e(TAG, "", e);
804            return false;
805        }
806
807        return true;
808    }
809
810    /**
811     * Unregister the current application and callbacks.
812     */
813    private void unregisterApp() {
814        if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
815        if (mService == null || mClientIf == 0) return;
816
817        try {
818            mCallback = null;
819            mService.unregisterClient(mClientIf);
820            mClientIf = 0;
821        } catch (RemoteException e) {
822            Log.e(TAG, "", e);
823        }
824    }
825
826    /**
827     * Initiate a connection to a Bluetooth GATT capable device.
828     *
829     * <p>The connection may not be established right away, but will be
830     * completed when the remote device is available. A
831     * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
832     * invoked when the connection state changes as a result of this function.
833     *
834     * <p>The autoConnect parameter determines whether to actively connect to
835     * the remote device, or rather passively scan and finalize the connection
836     * when the remote device is in range/available. Generally, the first ever
837     * connection to a device should be direct (autoConnect set to false) and
838     * subsequent connections to known devices should be invoked with the
839     * autoConnect parameter set to true.
840     *
841     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
842     *
843     * @param device Remote device to connect to
844     * @param autoConnect Whether to directly connect to the remote device (false) or to
845     * automatically connect as soon as the remote device becomes available (true).
846     * @return true, if the connection attempt was initiated successfully
847     */
848    /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
849            Handler handler) {
850        if (DBG) {
851            Log.d(TAG,
852                    "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
853        }
854        synchronized (mStateLock) {
855            if (mConnState != CONN_STATE_IDLE) {
856                throw new IllegalStateException("Not idle");
857            }
858            mConnState = CONN_STATE_CONNECTING;
859        }
860
861        mAutoConnect = autoConnect;
862
863        if (!registerApp(callback, handler)) {
864            synchronized (mStateLock) {
865                mConnState = CONN_STATE_IDLE;
866            }
867            Log.e(TAG, "Failed to register callback");
868            return false;
869        }
870
871        // The connection will continue in the onClientRegistered callback
872        return true;
873    }
874
875    /**
876     * Disconnects an established connection, or cancels a connection attempt
877     * currently in progress.
878     *
879     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
880     */
881    public void disconnect() {
882        if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
883        if (mService == null || mClientIf == 0) return;
884
885        try {
886            mService.clientDisconnect(mClientIf, mDevice.getAddress());
887        } catch (RemoteException e) {
888            Log.e(TAG, "", e);
889        }
890    }
891
892    /**
893     * Connect back to remote device.
894     *
895     * <p>This method is used to re-connect to a remote device after the
896     * connection has been dropped. If the device is not in range, the
897     * re-connection will be triggered once the device is back in range.
898     *
899     * @return true, if the connection attempt was initiated successfully
900     */
901    public boolean connect() {
902        try {
903            mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
904                    mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
905            return true;
906        } catch (RemoteException e) {
907            Log.e(TAG, "", e);
908            return false;
909        }
910    }
911
912    /**
913     * Set the preferred connection PHY for this app. Please note that this is just a
914     * recommendation, whether the PHY change will happen depends on other applications preferences,
915     * local and remote controller capabilities. Controller can override these settings.
916     * <p>
917     * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
918     * if no PHY change happens. It is also triggered when remote device updates the PHY.
919     *
920     * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
921     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
922     * BluetoothDevice#PHY_LE_CODED_MASK}.
923     * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
924     * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
925     * BluetoothDevice#PHY_LE_CODED_MASK}.
926     * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
927     * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
928     * {@link BluetoothDevice#PHY_OPTION_S8}
929     */
930    public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
931        try {
932            mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
933                    phyOptions);
934        } catch (RemoteException e) {
935            Log.e(TAG, "", e);
936        }
937    }
938
939    /**
940     * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
941     * in {@link BluetoothGattCallback#onPhyRead}
942     */
943    public void readPhy() {
944        try {
945            mService.clientReadPhy(mClientIf, mDevice.getAddress());
946        } catch (RemoteException e) {
947            Log.e(TAG, "", e);
948        }
949    }
950
951    /**
952     * Return the remote bluetooth device this GATT client targets to
953     *
954     * @return remote bluetooth device
955     */
956    public BluetoothDevice getDevice() {
957        return mDevice;
958    }
959
960    /**
961     * Discovers services offered by a remote device as well as their
962     * characteristics and descriptors.
963     *
964     * <p>This is an asynchronous operation. Once service discovery is completed,
965     * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
966     * triggered. If the discovery was successful, the remote services can be
967     * retrieved using the {@link #getServices} function.
968     *
969     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
970     *
971     * @return true, if the remote service discovery has been started
972     */
973    public boolean discoverServices() {
974        if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
975        if (mService == null || mClientIf == 0) return false;
976
977        mServices.clear();
978
979        try {
980            mService.discoverServices(mClientIf, mDevice.getAddress());
981        } catch (RemoteException e) {
982            Log.e(TAG, "", e);
983            return false;
984        }
985
986        return true;
987    }
988
989    /**
990     * Discovers a service by UUID. This is exposed only for passing PTS tests.
991     * It should never be used by real applications. The service is not searched
992     * for characteristics and descriptors, or returned in any callback.
993     *
994     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
995     *
996     * @return true, if the remote service discovery has been started
997     * @hide
998     */
999    public boolean discoverServiceByUuid(UUID uuid) {
1000        if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1001        if (mService == null || mClientIf == 0) return false;
1002
1003        mServices.clear();
1004
1005        try {
1006            mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1007        } catch (RemoteException e) {
1008            Log.e(TAG, "", e);
1009            return false;
1010        }
1011        return true;
1012    }
1013
1014    /**
1015     * Returns a list of GATT services offered by the remote device.
1016     *
1017     * <p>This function requires that service discovery has been completed
1018     * for the given device.
1019     *
1020     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1021     *
1022     * @return List of services on the remote device. Returns an empty list if service discovery has
1023     * not yet been performed.
1024     */
1025    public List<BluetoothGattService> getServices() {
1026        List<BluetoothGattService> result =
1027                new ArrayList<BluetoothGattService>();
1028
1029        for (BluetoothGattService service : mServices) {
1030            if (service.getDevice().equals(mDevice)) {
1031                result.add(service);
1032            }
1033        }
1034
1035        return result;
1036    }
1037
1038    /**
1039     * Returns a {@link BluetoothGattService}, if the requested UUID is
1040     * supported by the remote device.
1041     *
1042     * <p>This function requires that service discovery has been completed
1043     * for the given device.
1044     *
1045     * <p>If multiple instances of the same service (as identified by UUID)
1046     * exist, the first instance of the service is returned.
1047     *
1048     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1049     *
1050     * @param uuid UUID of the requested service
1051     * @return BluetoothGattService if supported, or null if the requested service is not offered by
1052     * the remote device.
1053     */
1054    public BluetoothGattService getService(UUID uuid) {
1055        for (BluetoothGattService service : mServices) {
1056            if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
1057                return service;
1058            }
1059        }
1060
1061        return null;
1062    }
1063
1064    /**
1065     * Reads the requested characteristic from the associated remote device.
1066     *
1067     * <p>This is an asynchronous operation. The result of the read operation
1068     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1069     * callback.
1070     *
1071     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1072     *
1073     * @param characteristic Characteristic to read from the remote device
1074     * @return true, if the read operation was initiated successfully
1075     */
1076    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
1077        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
1078            return false;
1079        }
1080
1081        if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
1082        if (mService == null || mClientIf == 0) return false;
1083
1084        BluetoothGattService service = characteristic.getService();
1085        if (service == null) return false;
1086
1087        BluetoothDevice device = service.getDevice();
1088        if (device == null) return false;
1089
1090        synchronized (mDeviceBusy) {
1091            if (mDeviceBusy) return false;
1092            mDeviceBusy = true;
1093        }
1094
1095        try {
1096            mService.readCharacteristic(mClientIf, device.getAddress(),
1097                    characteristic.getInstanceId(), AUTHENTICATION_NONE);
1098        } catch (RemoteException e) {
1099            Log.e(TAG, "", e);
1100            mDeviceBusy = false;
1101            return false;
1102        }
1103
1104        return true;
1105    }
1106
1107    /**
1108     * Reads the characteristic using its UUID from the associated remote device.
1109     *
1110     * <p>This is an asynchronous operation. The result of the read operation
1111     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1112     * callback.
1113     *
1114     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1115     *
1116     * @param uuid UUID of characteristic to read from the remote device
1117     * @return true, if the read operation was initiated successfully
1118     * @hide
1119     */
1120    public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1121        if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1122        if (mService == null || mClientIf == 0) return false;
1123
1124        synchronized (mDeviceBusy) {
1125            if (mDeviceBusy) return false;
1126            mDeviceBusy = true;
1127        }
1128
1129        try {
1130            mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
1131                    new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
1132        } catch (RemoteException e) {
1133            Log.e(TAG, "", e);
1134            mDeviceBusy = false;
1135            return false;
1136        }
1137
1138        return true;
1139    }
1140
1141
1142    /**
1143     * Writes a given characteristic and its values to the associated remote device.
1144     *
1145     * <p>Once the write operation has been completed, the
1146     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
1147     * reporting the result of the operation.
1148     *
1149     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1150     *
1151     * @param characteristic Characteristic to write on the remote device
1152     * @return true, if the write operation was initiated successfully
1153     */
1154    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1155        if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
1156                && (characteristic.getProperties()
1157                & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
1158            return false;
1159        }
1160
1161        if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
1162        if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
1163
1164        BluetoothGattService service = characteristic.getService();
1165        if (service == null) return false;
1166
1167        BluetoothDevice device = service.getDevice();
1168        if (device == null) return false;
1169
1170        synchronized (mDeviceBusy) {
1171            if (mDeviceBusy) return false;
1172            mDeviceBusy = true;
1173        }
1174
1175        try {
1176            mService.writeCharacteristic(mClientIf, device.getAddress(),
1177                    characteristic.getInstanceId(), characteristic.getWriteType(),
1178                    AUTHENTICATION_NONE, characteristic.getValue());
1179        } catch (RemoteException e) {
1180            Log.e(TAG, "", e);
1181            mDeviceBusy = false;
1182            return false;
1183        }
1184
1185        return true;
1186    }
1187
1188    /**
1189     * Reads the value for a given descriptor from the associated remote device.
1190     *
1191     * <p>Once the read operation has been completed, the
1192     * {@link BluetoothGattCallback#onDescriptorRead} callback is
1193     * triggered, signaling the result of the operation.
1194     *
1195     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1196     *
1197     * @param descriptor Descriptor value to read from the remote device
1198     * @return true, if the read operation was initiated successfully
1199     */
1200    public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
1201        if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
1202        if (mService == null || mClientIf == 0) return false;
1203
1204        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1205        if (characteristic == null) return false;
1206
1207        BluetoothGattService service = characteristic.getService();
1208        if (service == null) return false;
1209
1210        BluetoothDevice device = service.getDevice();
1211        if (device == null) return false;
1212
1213        synchronized (mDeviceBusy) {
1214            if (mDeviceBusy) return false;
1215            mDeviceBusy = true;
1216        }
1217
1218        try {
1219            mService.readDescriptor(mClientIf, device.getAddress(),
1220                    descriptor.getInstanceId(), AUTHENTICATION_NONE);
1221        } catch (RemoteException e) {
1222            Log.e(TAG, "", e);
1223            mDeviceBusy = false;
1224            return false;
1225        }
1226
1227        return true;
1228    }
1229
1230    /**
1231     * Write the value of a given descriptor to the associated remote device.
1232     *
1233     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1234     * triggered to report the result of the write operation.
1235     *
1236     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1237     *
1238     * @param descriptor Descriptor to write to the associated remote device
1239     * @return true, if the write operation was initiated successfully
1240     */
1241    public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1242        if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1243        if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
1244
1245        BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1246        if (characteristic == null) return false;
1247
1248        BluetoothGattService service = characteristic.getService();
1249        if (service == null) return false;
1250
1251        BluetoothDevice device = service.getDevice();
1252        if (device == null) return false;
1253
1254        synchronized (mDeviceBusy) {
1255            if (mDeviceBusy) return false;
1256            mDeviceBusy = true;
1257        }
1258
1259        try {
1260            mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
1261                    AUTHENTICATION_NONE, descriptor.getValue());
1262        } catch (RemoteException e) {
1263            Log.e(TAG, "", e);
1264            mDeviceBusy = false;
1265            return false;
1266        }
1267
1268        return true;
1269    }
1270
1271    /**
1272     * Initiates a reliable write transaction for a given remote device.
1273     *
1274     * <p>Once a reliable write transaction has been initiated, all calls
1275     * to {@link #writeCharacteristic} are sent to the remote device for
1276     * verification and queued up for atomic execution. The application will
1277     * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1278     * in response to every {@link #writeCharacteristic} call and is responsible
1279     * for verifying if the value has been transmitted accurately.
1280     *
1281     * <p>After all characteristics have been queued up and verified,
1282     * {@link #executeReliableWrite} will execute all writes. If a characteristic
1283     * was not written correctly, calling {@link #abortReliableWrite} will
1284     * cancel the current transaction without commiting any values on the
1285     * remote device.
1286     *
1287     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1288     *
1289     * @return true, if the reliable write transaction has been initiated
1290     */
1291    public boolean beginReliableWrite() {
1292        if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
1293        if (mService == null || mClientIf == 0) return false;
1294
1295        try {
1296            mService.beginReliableWrite(mClientIf, mDevice.getAddress());
1297        } catch (RemoteException e) {
1298            Log.e(TAG, "", e);
1299            return false;
1300        }
1301
1302        return true;
1303    }
1304
1305    /**
1306     * Executes a reliable write transaction for a given remote device.
1307     *
1308     * <p>This function will commit all queued up characteristic write
1309     * operations for a given remote device.
1310     *
1311     * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1312     * invoked to indicate whether the transaction has been executed correctly.
1313     *
1314     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1315     *
1316     * @return true, if the request to execute the transaction has been sent
1317     */
1318    public boolean executeReliableWrite() {
1319        if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
1320        if (mService == null || mClientIf == 0) return false;
1321
1322        synchronized (mDeviceBusy) {
1323            if (mDeviceBusy) return false;
1324            mDeviceBusy = true;
1325        }
1326
1327        try {
1328            mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
1329        } catch (RemoteException e) {
1330            Log.e(TAG, "", e);
1331            mDeviceBusy = false;
1332            return false;
1333        }
1334
1335        return true;
1336    }
1337
1338    /**
1339     * Cancels a reliable write transaction for a given device.
1340     *
1341     * <p>Calling this function will discard all queued characteristic write
1342     * operations for a given remote device.
1343     *
1344     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1345     */
1346    public void abortReliableWrite() {
1347        if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
1348        if (mService == null || mClientIf == 0) return;
1349
1350        try {
1351            mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
1352        } catch (RemoteException e) {
1353            Log.e(TAG, "", e);
1354        }
1355    }
1356
1357    /**
1358     * @deprecated Use {@link #abortReliableWrite()}
1359     */
1360    @Deprecated
1361    public void abortReliableWrite(BluetoothDevice mDevice) {
1362        abortReliableWrite();
1363    }
1364
1365    /**
1366     * Enable or disable notifications/indications for a given characteristic.
1367     *
1368     * <p>Once notifications are enabled for a characteristic, a
1369     * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1370     * triggered if the remote device indicates that the given characteristic
1371     * has changed.
1372     *
1373     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1374     *
1375     * @param characteristic The characteristic for which to enable notifications
1376     * @param enable Set to true to enable notifications/indications
1377     * @return true, if the requested notification status was set successfully
1378     */
1379    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1380            boolean enable) {
1381        if (DBG) {
1382            Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1383                    + " enable: " + enable);
1384        }
1385        if (mService == null || mClientIf == 0) return false;
1386
1387        BluetoothGattService service = characteristic.getService();
1388        if (service == null) return false;
1389
1390        BluetoothDevice device = service.getDevice();
1391        if (device == null) return false;
1392
1393        try {
1394            mService.registerForNotification(mClientIf, device.getAddress(),
1395                    characteristic.getInstanceId(), enable);
1396        } catch (RemoteException e) {
1397            Log.e(TAG, "", e);
1398            return false;
1399        }
1400
1401        return true;
1402    }
1403
1404    /**
1405     * Clears the internal cache and forces a refresh of the services from the
1406     * remote device.
1407     *
1408     * @hide
1409     */
1410    public boolean refresh() {
1411        if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
1412        if (mService == null || mClientIf == 0) return false;
1413
1414        try {
1415            mService.refreshDevice(mClientIf, mDevice.getAddress());
1416        } catch (RemoteException e) {
1417            Log.e(TAG, "", e);
1418            return false;
1419        }
1420
1421        return true;
1422    }
1423
1424    /**
1425     * Read the RSSI for a connected remote device.
1426     *
1427     * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1428     * invoked when the RSSI value has been read.
1429     *
1430     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1431     *
1432     * @return true, if the RSSI value has been requested successfully
1433     */
1434    public boolean readRemoteRssi() {
1435        if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
1436        if (mService == null || mClientIf == 0) return false;
1437
1438        try {
1439            mService.readRemoteRssi(mClientIf, mDevice.getAddress());
1440        } catch (RemoteException e) {
1441            Log.e(TAG, "", e);
1442            return false;
1443        }
1444
1445        return true;
1446    }
1447
1448    /**
1449     * Request an MTU size used for a given connection.
1450     *
1451     * <p>When performing a write request operation (write without response),
1452     * the data sent is truncated to the MTU size. This function may be used
1453     * to request a larger MTU size to be able to send more data at once.
1454     *
1455     * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
1456     * whether this operation was successful.
1457     *
1458     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1459     *
1460     * @return true, if the new MTU value has been requested successfully
1461     */
1462    public boolean requestMtu(int mtu) {
1463        if (DBG) {
1464            Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1465                    + " mtu: " + mtu);
1466        }
1467        if (mService == null || mClientIf == 0) return false;
1468
1469        try {
1470            mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1471        } catch (RemoteException e) {
1472            Log.e(TAG, "", e);
1473            return false;
1474        }
1475
1476        return true;
1477    }
1478
1479    /**
1480     * Request a connection parameter update.
1481     *
1482     * <p>This function will send a connection parameter update request to the
1483     * remote device.
1484     *
1485     * @param connectionPriority Request a specific connection priority. Must be one of {@link
1486     * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1487     * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1488     * @throws IllegalArgumentException If the parameters are outside of their specified range.
1489     */
1490    public boolean requestConnectionPriority(int connectionPriority) {
1491        if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1492                || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
1493            throw new IllegalArgumentException("connectionPriority not within valid range");
1494        }
1495
1496        if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
1497        if (mService == null || mClientIf == 0) return false;
1498
1499        try {
1500            mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1501        } catch (RemoteException e) {
1502            Log.e(TAG, "", e);
1503            return false;
1504        }
1505
1506        return true;
1507    }
1508
1509    /**
1510     * Request an LE connection parameter update.
1511     *
1512     * <p>This function will send an LE connection parameters update request to the remote device.
1513     *
1514     * @return true, if the request is send to the Bluetooth stack.
1515     * @hide
1516     */
1517    public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
1518                                             int slaveLatency, int supervisionTimeout,
1519                                             int minConnectionEventLen, int maxConnectionEventLen) {
1520        if (DBG) {
1521            Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
1522                        + ")" + (1.25 * minConnectionInterval)
1523                        + "msec, max=(" + maxConnectionInterval + ")"
1524                        + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
1525                        + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
1526                        + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
1527        }
1528        if (mService == null || mClientIf == 0) return false;
1529
1530        try {
1531            mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
1532                                        minConnectionInterval, maxConnectionInterval,
1533                                        slaveLatency, supervisionTimeout,
1534                                        minConnectionEventLen, maxConnectionEventLen);
1535        } catch (RemoteException e) {
1536            Log.e(TAG, "", e);
1537            return false;
1538        }
1539
1540        return true;
1541    }
1542
1543    /**
1544     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1545     * with {@link BluetoothProfile#GATT} as argument
1546     *
1547     * @throws UnsupportedOperationException
1548     */
1549    @Override
1550    public int getConnectionState(BluetoothDevice device) {
1551        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
1552    }
1553
1554    /**
1555     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1556     * with {@link BluetoothProfile#GATT} as argument
1557     *
1558     * @throws UnsupportedOperationException
1559     */
1560    @Override
1561    public List<BluetoothDevice> getConnectedDevices() {
1562        throw new UnsupportedOperationException(
1563                "Use BluetoothManager#getConnectedDevices instead.");
1564    }
1565
1566    /**
1567     * Not supported - please use
1568     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1569     * with {@link BluetoothProfile#GATT} as first argument
1570     *
1571     * @throws UnsupportedOperationException
1572     */
1573    @Override
1574    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1575        throw new UnsupportedOperationException(
1576                "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
1577    }
1578}
1579