blob: 1e12025497fdbb9ca2c0564867a3c142cec37f83 [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
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
19
20import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothDevice;
22import android.bluetooth.BluetoothProfile;
23import android.bluetooth.BluetoothProfile.ServiceListener;
24import android.bluetooth.IBluetoothManager;
25import android.bluetooth.IBluetoothStateChangeCallback;
26
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.Intent;
30import android.content.ServiceConnection;
31import android.os.IBinder;
32import android.os.ParcelUuid;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.util.Log;
36
37import java.util.ArrayList;
38import java.util.List;
39import java.util.UUID;
40
41/**
42 * Public API for the Bluetooth Gatt Profile.
43 *
44 * <p>This class provides Bluetooth Gatt functionality to enable communication
45 * with Bluetooth Smart or Smart Ready devices.
46 *
47 * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service
48 * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
49 * BluetoothGatt proxy object.
50 *
51 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
52 * and call {@link #registerApp} to register your application. Gatt capable
53 * devices can be discovered using the {@link #startScan} function or the
54 * regular Bluetooth device discovery process.
55 * @hide
56 */
57public final class BluetoothGatt implements BluetoothProfile {
58 private static final String TAG = "BluetoothGatt";
59 private static final boolean DBG = true;
60
61 private Context mContext;
62 private ServiceListener mServiceListener;
63 private BluetoothAdapter mAdapter;
64 private IBluetoothGatt mService;
65 private BluetoothGattCallback mCallback;
66 private int mClientIf;
67 private boolean mAuthRetry = false;
68
69 private List<BluetoothGattService> mServices;
70
71 /** A Gatt operation completed successfully */
72 public static final int GATT_SUCCESS = 0;
73
74 /** Gatt read operation is not permitted */
75 public static final int GATT_READ_NOT_PERMITTED = 0x2;
76
77 /** Gatt write operation is not permitted */
78 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
79
80 /** Insufficient authentication for a given operation */
81 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
82
83 /** The given request is not supported */
84 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
85
86 /** Insufficient encryption for a given operation */
87 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
88
89 /** A read or write operation was requested with an invalid offset */
90 public static final int GATT_INVALID_OFFSET = 0x7;
91
92 /** A write operation exceeds the maximum length of the attribute */
93 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
94
95 /**
96 * No authentication required.
97 * @hide
98 */
99 /*package*/ static final int AUTHENTICATION_NONE = 0;
100
101 /**
102 * Authentication requested; no man-in-the-middle protection required.
103 * @hide
104 */
105 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
106
107 /**
108 * Authentication with man-in-the-middle protection requested.
109 * @hide
110 */
111 /*package*/ static final int AUTHENTICATION_MITM = 2;
112
113 /**
114 * Bluetooth state change handlers
115 */
116 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
117 new IBluetoothStateChangeCallback.Stub() {
118 public void onBluetoothStateChange(boolean up) {
119 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
120 if (!up) {
121 if (DBG) Log.d(TAG,"Unbinding service...");
122 synchronized (mConnection) {
123 mService = null;
124 mContext.unbindService(mConnection);
125 }
126 } else {
127 synchronized (mConnection) {
128 if (mService == null) {
129 if (DBG) Log.d(TAG,"Binding service...");
130 if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()),
131 mConnection, 0)) {
132 Log.e(TAG, "Could not bind to Bluetooth GATT Service");
133 }
134 }
135 }
136 }
137 }
138 };
139
140 /**
141 * Service binder handling
142 */
143 private ServiceConnection mConnection = new ServiceConnection() {
144 public void onServiceConnected(ComponentName className, IBinder service) {
145 if (DBG) Log.d(TAG, "Proxy object connected");
146 mService = IBluetoothGatt.Stub.asInterface(service);
147 ServiceListener serviceListener = mServiceListener;
148 if (serviceListener != null) {
149 serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this);
150 }
151 }
152 public void onServiceDisconnected(ComponentName className) {
153 if (DBG) Log.d(TAG, "Proxy object disconnected");
154 mService = null;
155 ServiceListener serviceListener = mServiceListener;
156 if (serviceListener != null) {
157 serviceListener.onServiceDisconnected(BluetoothProfile.GATT);
158 }
159 }
160 };
161
162 /**
163 * Bluetooth GATT interface callbacks
164 */
165 private final IBluetoothGattCallback mBluetoothGattCallback =
166 new IBluetoothGattCallback.Stub() {
167 /**
168 * Application interface registered - app is ready to go
169 * @hide
170 */
171 public void onClientRegistered(int status, int clientIf) {
172 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
173 + " clientIf=" + clientIf);
174 mClientIf = clientIf;
175 try {
176 mCallback.onAppRegistered(status);
177 } catch (Exception ex) {
178 Log.w(TAG, "Unhandled exception: " + ex);
179 }
180 }
181
182 /**
183 * Client connection state changed
184 * @hide
185 */
186 public void onClientConnectionState(int status, int clientIf,
187 boolean connected, String address) {
188 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
189 + " clientIf=" + clientIf + " device=" + address);
190 try {
191 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
192 connected ? BluetoothProfile.STATE_CONNECTED
193 : BluetoothProfile.STATE_DISCONNECTED);
194 } catch (Exception ex) {
195 Log.w(TAG, "Unhandled exception: " + ex);
196 }
197 }
198
199 /**
200 * Callback reporting an LE scan result.
201 * @hide
202 */
203 public void onScanResult(String address, int rssi, byte[] advData) {
204 if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
205
206 try {
207 mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData);
208 } catch (Exception ex) {
209 Log.w(TAG, "Unhandled exception: " + ex);
210 }
211 }
212
213 /**
214 * A new GATT service has been discovered.
215 * The service is added to the internal list and the search
216 * continues.
217 * @hide
218 */
219 public void onGetService(String address, int srvcType,
220 int srvcInstId, ParcelUuid srvcUuid) {
221 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
222 BluetoothDevice device = mAdapter.getRemoteDevice(address);
223 mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(),
224 srvcInstId, srvcType));
225 }
226
227 /**
228 * An included service has been found durig GATT discovery.
229 * The included service is added to the respective parent.
230 * @hide
231 */
232 public void onGetIncludedService(String address, int srvcType,
233 int srvcInstId, ParcelUuid srvcUuid,
234 int inclSrvcType, int inclSrvcInstId,
235 ParcelUuid inclSrvcUuid) {
236 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
237 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
238
239 BluetoothDevice device = mAdapter.getRemoteDevice(address);
240 BluetoothGattService service = getService(device,
241 srvcUuid.getUuid(), srvcInstId, srvcType);
242 BluetoothGattService includedService = getService(device,
243 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
244
245 if (service != null && includedService != null) {
246 service.addIncludedService(includedService);
247 }
248 }
249
250 /**
251 * A new GATT characteristic has been discovered.
252 * Add the new characteristic to the relevant service and continue
253 * the remote device inspection.
254 * @hide
255 */
256 public void onGetCharacteristic(String address, int srvcType,
257 int srvcInstId, ParcelUuid srvcUuid,
258 int charInstId, ParcelUuid charUuid,
259 int charProps) {
260 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
261 charUuid);
262
263 BluetoothDevice device = mAdapter.getRemoteDevice(address);
264 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
265 srvcInstId, srvcType);
266 if (service != null) {
267 service.addCharacteristic(new BluetoothGattCharacteristic(
268 service, charUuid.getUuid(), charInstId, charProps, 0));
269 }
270 }
271
272 /**
273 * A new GATT descriptor has been discovered.
274 * Finally, add the descriptor to the related characteristic.
275 * This should conclude the remote device update.
276 * @hide
277 */
278 public void onGetDescriptor(String address, int srvcType,
279 int srvcInstId, ParcelUuid srvcUuid,
280 int charInstId, ParcelUuid charUuid,
281 ParcelUuid descUuid) {
282 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
283
284 BluetoothDevice device = mAdapter.getRemoteDevice(address);
285 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
286 srvcInstId, srvcType);
287 if (service == null) return;
288
289 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
290 charUuid.getUuid());
291 if (characteristic == null) return;
292
293 characteristic.addDescriptor(new BluetoothGattDescriptor(
294 characteristic, descUuid.getUuid(), 0));
295 }
296
297 /**
298 * Remote search has been completed.
299 * The internal object structure should now reflect the state
300 * of the remote device database. Let the application know that
301 * we are done at this point.
302 * @hide
303 */
304 public void onSearchComplete(String address, int status) {
305 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
306 BluetoothDevice device = mAdapter.getRemoteDevice(address);
307 try {
308 mCallback.onServicesDiscovered(device, status);
309 } catch (Exception ex) {
310 Log.w(TAG, "Unhandled exception: " + ex);
311 }
312 }
313
314 /**
315 * Remote characteristic has been read.
316 * Updates the internal value.
317 * @hide
318 */
319 public void onCharacteristicRead(String address, int status, int srvcType,
320 int srvcInstId, ParcelUuid srvcUuid,
321 int charInstId, ParcelUuid charUuid, byte[] value) {
322 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
323 + " UUID=" + charUuid + " Status=" + status);
324
325 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
326 || status == GATT_INSUFFICIENT_ENCRYPTION)
327 && mAuthRetry == false) {
328 try {
329 mAuthRetry = true;
330 mService.readCharacteristic(mClientIf, address,
331 srvcType, srvcInstId, srvcUuid,
332 charInstId, charUuid, AUTHENTICATION_MITM);
333 return;
334 } catch (RemoteException e) {
335 Log.e(TAG,"",e);
336 }
337 }
338
339 mAuthRetry = false;
340
341 BluetoothDevice device = mAdapter.getRemoteDevice(address);
342 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
343 srvcInstId, srvcType);
344 if (service == null) return;
345
346 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
347 charUuid.getUuid(), charInstId);
348 if (characteristic == null) return;
349
350 if (status == 0) characteristic.setValue(value);
351
352 try {
353 mCallback.onCharacteristicRead(characteristic, status);
354 } catch (Exception ex) {
355 Log.w(TAG, "Unhandled exception: " + ex);
356 }
357 }
358
359 /**
360 * Characteristic has been written to the remote device.
361 * Let the app know how we did...
362 * @hide
363 */
364 public void onCharacteristicWrite(String address, int status, int srvcType,
365 int srvcInstId, ParcelUuid srvcUuid,
366 int charInstId, ParcelUuid charUuid) {
367 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
368 + " UUID=" + charUuid + " Status=" + status);
369
370 BluetoothDevice device = mAdapter.getRemoteDevice(address);
371 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
372 srvcInstId, srvcType);
373 if (service == null) return;
374
375 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
376 charUuid.getUuid(), charInstId);
377 if (characteristic == null) return;
378
379 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
380 || status == GATT_INSUFFICIENT_ENCRYPTION)
381 && mAuthRetry == false) {
382 try {
383 mAuthRetry = true;
384 mService.writeCharacteristic(mClientIf, address,
385 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
386 characteristic.getWriteType(), AUTHENTICATION_MITM,
387 characteristic.getValue());
388 return;
389 } catch (RemoteException e) {
390 Log.e(TAG,"",e);
391 }
392 }
393
394 mAuthRetry = false;
395
396 try {
397 mCallback.onCharacteristicWrite(characteristic, status);
398 } catch (Exception ex) {
399 Log.w(TAG, "Unhandled exception: " + ex);
400 }
401 }
402
403 /**
404 * Remote characteristic has been updated.
405 * Updates the internal value.
406 * @hide
407 */
408 public void onNotify(String address, int srvcType,
409 int srvcInstId, ParcelUuid srvcUuid,
410 int charInstId, ParcelUuid charUuid,
411 byte[] value) {
412 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
413
414 BluetoothDevice device = mAdapter.getRemoteDevice(address);
415 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
416 srvcInstId, srvcType);
417 if (service == null) return;
418
419 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
420 charUuid.getUuid(), charInstId);
421 if (characteristic == null) return;
422
423 characteristic.setValue(value);
424
425 try {
426 mCallback.onCharacteristicChanged(characteristic);
427 } catch (Exception ex) {
428 Log.w(TAG, "Unhandled exception: " + ex);
429 }
430 }
431
432 /**
433 * Descriptor has been read.
434 * @hide
435 */
436 public void onDescriptorRead(String address, int status, int srvcType,
437 int srvcInstId, ParcelUuid srvcUuid,
438 int charInstId, ParcelUuid charUuid,
439 ParcelUuid descrUuid, byte[] value) {
440 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
441
442 BluetoothDevice device = mAdapter.getRemoteDevice(address);
443 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
444 srvcInstId, srvcType);
445 if (service == null) return;
446
447 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
448 charUuid.getUuid(), charInstId);
449 if (characteristic == null) return;
450
451 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
452 descrUuid.getUuid());
453 if (descriptor == null) return;
454
455 if (status == 0) descriptor.setValue(value);
456
457 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
458 || status == GATT_INSUFFICIENT_ENCRYPTION)
459 && mAuthRetry == false) {
460 try {
461 mAuthRetry = true;
462 mService.readDescriptor(mClientIf, address,
463 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
464 descrUuid, AUTHENTICATION_MITM);
465 } catch (RemoteException e) {
466 Log.e(TAG,"",e);
467 }
468 }
469
470 mAuthRetry = true;
471
472 try {
473 mCallback.onDescriptorRead(descriptor, status);
474 } catch (Exception ex) {
475 Log.w(TAG, "Unhandled exception: " + ex);
476 }
477 }
478
479 /**
480 * Descriptor write operation complete.
481 * @hide
482 */
483 public void onDescriptorWrite(String address, int status, int srvcType,
484 int srvcInstId, ParcelUuid srvcUuid,
485 int charInstId, ParcelUuid charUuid,
486 ParcelUuid descrUuid) {
487 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
488
489 BluetoothDevice device = mAdapter.getRemoteDevice(address);
490 BluetoothGattService service = getService(device, srvcUuid.getUuid(),
491 srvcInstId, srvcType);
492 if (service == null) return;
493
494 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
495 charUuid.getUuid(), charInstId);
496 if (characteristic == null) return;
497
498 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
499 descrUuid.getUuid());
500 if (descriptor == null) return;
501
502 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
503 || status == GATT_INSUFFICIENT_ENCRYPTION)
504 && mAuthRetry == false) {
505 try {
506 mAuthRetry = true;
507 mService.writeDescriptor(mClientIf, address,
508 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
509 descrUuid, characteristic.getWriteType(),
510 AUTHENTICATION_MITM, descriptor.getValue());
511 } catch (RemoteException e) {
512 Log.e(TAG,"",e);
513 }
514 }
515
516 mAuthRetry = false;
517
518 try {
519 mCallback.onDescriptorWrite(descriptor, status);
520 } catch (Exception ex) {
521 Log.w(TAG, "Unhandled exception: " + ex);
522 }
523 }
524
525 /**
526 * Prepared write transaction completed (or aborted)
527 * @hide
528 */
529 public void onExecuteWrite(String address, int status) {
530 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
531 + " status=" + status);
532 BluetoothDevice device = mAdapter.getRemoteDevice(address);
533 try {
534 mCallback.onReliableWriteCompleted(device, status);
535 } catch (Exception ex) {
536 Log.w(TAG, "Unhandled exception: " + ex);
537 }
538 }
539
540 /**
541 * Remote device RSSI has been read
542 * @hide
543 */
544 public void onReadRemoteRssi(String address, int rssi, int status) {
545 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
546 " rssi=" + rssi + " status=" + status);
547 BluetoothDevice device = mAdapter.getRemoteDevice(address);
548 try {
549 mCallback.onReadRemoteRssi(device, rssi, status);
550 } catch (Exception ex) {
551 Log.w(TAG, "Unhandled exception: " + ex);
552 }
553 }
554 };
555
556 /**
557 * Create a BluetoothGatt proxy object.
558 */
559 /*package*/ BluetoothGatt(Context context, ServiceListener l) {
560 mContext = context;
561 mServiceListener = l;
562 mAdapter = BluetoothAdapter.getDefaultAdapter();
563 mServices = new ArrayList<BluetoothGattService>();
564
565 IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
566 if (b != null) {
567 IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
568 try {
569 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
570 } catch (RemoteException re) {
571 Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
572 }
573 } else {
574 Log.e(TAG, "Unable to get BluetoothManager interface.");
575 throw new RuntimeException("BluetoothManager inactive");
576 }
577
578 //Bind to the service only if the Bluetooth is ON
579 if(mAdapter.isEnabled()){
580 if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
581 Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
582 }
583 }
584 }
585
586 /**
587 * Close the connection to the gatt service.
588 */
589 /*package*/ void close() {
590 if (DBG) Log.d(TAG, "close()");
591
592 unregisterApp();
593 mServiceListener = null;
594
595 IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
596 if (b != null) {
597 IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
598 try {
599 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
600 } catch (RemoteException re) {
601 Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
602 }
603 }
604
605 synchronized (mConnection) {
606 if (mService != null) {
607 mService = null;
608 mContext.unbindService(mConnection);
609 }
610 }
611 }
612
613 /**
614 * Returns a service by UUID, instance and type.
615 * @hide
616 */
617 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
618 int instanceId, int type) {
619 for(BluetoothGattService svc : mServices) {
620 if (svc.getDevice().equals(device) &&
621 svc.getType() == type &&
622 svc.getInstanceId() == instanceId &&
623 svc.getUuid().equals(uuid)) {
624 return svc;
625 }
626 }
627 return null;
628 }
629
630
631 /**
632 * Register an application callback to start using Gatt.
633 *
634 * <p>This is an asynchronous call. The callback is used to notify
635 * success or failure if the function returns true.
636 *
637 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
638 *
639 * @param callback Gatt callback handler that will receive asynchronous
640 * callbacks.
641 * @return true, if application was successfully registered.
642 */
643 public boolean registerApp(BluetoothGattCallback callback) {
644 if (DBG) Log.d(TAG, "registerApp()");
645 if (mService == null) return false;
646
647 mCallback = callback;
648 UUID uuid = UUID.randomUUID();
649 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
650
651 try {
652 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
653 } catch (RemoteException e) {
654 Log.e(TAG,"",e);
655 return false;
656 }
657
658 return true;
659 }
660
661 /**
662 * Unregister the current application and callbacks.
663 */
664 public void unregisterApp() {
665 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
666 if (mService == null || mClientIf == 0) return;
667
668 try {
669 mCallback = null;
670 mService.unregisterClient(mClientIf);
671 mClientIf = 0;
672 } catch (RemoteException e) {
673 Log.e(TAG,"",e);
674 }
675 }
676
677 /**
678 * Starts a scan for Bluetooth LE devices.
679 *
680 * <p>Results of the scan are reported using the
681 * {@link BluetoothGattCallback#onScanResult} callback.
682 *
683 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
684 *
685 * @return true, if the scan was started successfully
686 */
687 public boolean startScan() {
688 if (DBG) Log.d(TAG, "startScan()");
689 if (mService == null || mClientIf == 0) return false;
690
691 try {
692 mService.startScan(mClientIf, false);
693 } catch (RemoteException e) {
694 Log.e(TAG,"",e);
695 return false;
696 }
697
698 return true;
699 }
700
701 /**
702 * Starts a scan for Bluetooth LE devices, looking for devices that
703 * advertise given services.
704 *
705 * <p>Devices which advertise all specified services are reported using the
706 * {@link BluetoothGattCallback#onScanResult} callback.
707 *
708 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
709 *
710 * @param serviceUuids Array of services to look for
711 * @return true, if the scan was started successfully
712 */
713 public boolean startScan(UUID[] serviceUuids) {
714 if (DBG) Log.d(TAG, "startScan() - with UUIDs");
715 if (mService == null || mClientIf == 0) return false;
716
717 try {
718 ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
719 for(int i = 0; i != uuids.length; ++i) {
720 uuids[i] = new ParcelUuid(serviceUuids[i]);
721 }
722 mService.startScanWithUuids(mClientIf, false, uuids);
723 } catch (RemoteException e) {
724 Log.e(TAG,"",e);
725 return false;
726 }
727
728 return true;
729 }
730
731 /**
732 * Stops an ongoing Bluetooth LE device scan.
733 *
734 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
735 */
736 public void stopScan() {
737 if (DBG) Log.d(TAG, "stopScan()");
738 if (mService == null || mClientIf == 0) return;
739
740 try {
741 mService.stopScan(mClientIf, false);
742 } catch (RemoteException e) {
743 Log.e(TAG,"",e);
744 }
745 }
746
747 /**
748 * Initiate a connection to a Bluetooth Gatt capable device.
749 *
750 * <p>The connection may not be established right away, but will be
751 * completed when the remote device is available. A
752 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
753 * invoked when the connection state changes as a result of this function.
754 *
755 * <p>The autoConnect paramter determines whether to actively connect to
756 * the remote device, or rather passively scan and finalize the connection
757 * when the remote device is in range/available. Generally, the first ever
758 * connection to a device should be direct (autoConnect set to false) and
759 * subsequent connections to known devices should be invoked with the
760 * autoConnect parameter set to false.
761 *
762 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
763 *
764 * @param device Remote device to connect to
765 * @param autoConnect Whether to directly connect to the remote device (false)
766 * or to automatically connect as soon as the remote
767 * device becomes available (true).
768 * @return true, if the connection attempt was initiated successfully
769 */
770 public boolean connect(BluetoothDevice device, boolean autoConnect) {
771 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
772 if (mService == null || mClientIf == 0) return false;
773
774 try {
775 mService.clientConnect(mClientIf, device.getAddress(),
776 autoConnect ? false : true); // autoConnect is inverse of "isDirect"
777 } catch (RemoteException e) {
778 Log.e(TAG,"",e);
779 return false;
780 }
781
782 return true;
783 }
784
785 /**
786 * Disconnects an established connection, or cancels a connection attempt
787 * currently in progress.
788 *
789 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
790 *
791 * @param device Remote device
792 */
793 public void cancelConnection(BluetoothDevice device) {
794 if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress());
795 if (mService == null || mClientIf == 0) return;
796
797 try {
798 mService.clientDisconnect(mClientIf, device.getAddress());
799 } catch (RemoteException e) {
800 Log.e(TAG,"",e);
801 }
802 }
803
804 /**
805 * Discovers services offered by a remote device as well as their
806 * characteristics and descriptors.
807 *
808 * <p>This is an asynchronous operation. Once service discovery is completed,
809 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
810 * triggered. If the discovery was successful, the remote services can be
811 * retrieved using the {@link #getServices} function.
812 *
813 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
814 *
815 * @param device Remote device to explore
816 * @return true, if the remote service discovery has been started
817 */
818 public boolean discoverServices(BluetoothDevice device) {
819 if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress());
820 if (mService == null || mClientIf == 0) return false;
821
822 mServices.clear();
823
824 try {
825 mService.discoverServices(mClientIf, device.getAddress());
826 } catch (RemoteException e) {
827 Log.e(TAG,"",e);
828 return false;
829 }
830
831 return true;
832 }
833
834 /**
835 * Returns a list of GATT services offered by the remote device.
836 *
837 * <p>This function requires that service discovery has been completed
838 * for the given device.
839 *
840 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
841 *
842 * @param device Remote device
843 * @return List of services on the remote device. Returns an empty list
844 * if service discovery has not yet been performed.
845 */
846 public List<BluetoothGattService> getServices(BluetoothDevice device) {
847 List<BluetoothGattService> result =
848 new ArrayList<BluetoothGattService>();
849
850 for (BluetoothGattService service : mServices) {
851 if (service.getDevice().equals(device)) {
852 result.add(service);
853 }
854 }
855
856 return result;
857 }
858
859 /**
860 * Returns a {@link BluetoothGattService}, if the requested UUID is
861 * supported by the remote device.
862 *
863 * <p>This function requires that service discovery has been completed
864 * for the given device.
865 *
866 * <p>If multiple instances of the same service (as identified by UUID)
867 * exist, the first instance of the service is returned.
868 *
869 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
870 *
871 * @param device Remote device
872 * @param uuid UUID of the requested service
873 * @return BluetoothGattService if supported, or null if the requested
874 * service is not offered by the remote device.
875 */
876 public BluetoothGattService getService(BluetoothDevice device, UUID uuid) {
877 for (BluetoothGattService service : mServices) {
878 if (service.getDevice().equals(device) &&
879 service.getUuid().equals(uuid)) {
880 return service;
881 }
882 }
883
884 return null;
885 }
886
887 /**
888 * Reads the requested characteristic from the associated remote device.
889 *
890 * <p>This is an asynchronous operation. The result of the read operation
891 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
892 * callback.
893 *
894 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
895 *
896 * @param characteristic Characteristic to read from the remote device
897 * @return true, if the read operation was initiated successfully
898 */
899 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
900 if ((characteristic.getProperties() &
901 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
902
903 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
904 if (mService == null || mClientIf == 0) return false;
905
906 BluetoothGattService service = characteristic.getService();
907 if (service == null) return false;
908
909 BluetoothDevice device = service.getDevice();
910 if (device == null) return false;
911
912 try {
913 mService.readCharacteristic(mClientIf, device.getAddress(),
914 service.getType(), service.getInstanceId(),
915 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
916 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
917 } catch (RemoteException e) {
918 Log.e(TAG,"",e);
919 return false;
920 }
921
922 return true;
923 }
924
925 /**
926 * Writes a given characteristic and it's values to the associated remote
927 * device.
928 *
929 * <p>Once the write operation has been completed, the
930 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
931 * reporting the result of the operation.
932 *
933 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
934 *
935 * @param characteristic Characteristic to write on the remote device
936 * @return true, if the write operation was initiated successfully
937 */
938 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
939 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
940 && (characteristic.getProperties() &
941 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
942
943 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
944 if (mService == null || mClientIf == 0) return false;
945
946 BluetoothGattService service = characteristic.getService();
947 if (service == null) return false;
948
949 BluetoothDevice device = service.getDevice();
950 if (device == null) return false;
951
952 try {
953 mService.writeCharacteristic(mClientIf, device.getAddress(),
954 service.getType(), service.getInstanceId(),
955 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
956 new ParcelUuid(characteristic.getUuid()),
957 characteristic.getWriteType(), AUTHENTICATION_NONE,
958 characteristic.getValue());
959 } catch (RemoteException e) {
960 Log.e(TAG,"",e);
961 return false;
962 }
963
964 return true;
965 }
966
967 /**
968 * Reads the value for a given descriptor from the associated remote device.
969 *
970 * <p>Once the read operation has been completed, the
971 * {@link BluetoothGattCallback#onDescriptorRead} callback is
972 * triggered, signaling the result of the operation.
973 *
974 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
975 *
976 * @param descriptor Descriptor value to read from the remote device
977 * @return true, if the read operation was initiated successfully
978 */
979 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
980 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
981 if (mService == null || mClientIf == 0) return false;
982
983 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
984 if (characteristic == null) return false;
985
986 BluetoothGattService service = characteristic.getService();
987 if (service == null) return false;
988
989 BluetoothDevice device = service.getDevice();
990 if (device == null) return false;
991
992 try {
993 mService.readDescriptor(mClientIf, device.getAddress(),
994 service.getType(), service.getInstanceId(),
995 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
996 new ParcelUuid(characteristic.getUuid()),
997 new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE);
998 } catch (RemoteException e) {
999 Log.e(TAG,"",e);
1000 return false;
1001 }
1002
1003 return true;
1004 }
1005
1006 /**
1007 * Write the value of a given descriptor to the associated remote device.
1008 *
1009 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1010 * triggered to report the result of the write operation.
1011 *
1012 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1013 *
1014 * @param descriptor Descriptor to write to the associated remote device
1015 * @return true, if the write operation was initiated successfully
1016 */
1017 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1018 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1019 if (mService == null || mClientIf == 0) return false;
1020
1021 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1022 if (characteristic == null) return false;
1023
1024 BluetoothGattService service = characteristic.getService();
1025 if (service == null) return false;
1026
1027 BluetoothDevice device = service.getDevice();
1028 if (device == null) return false;
1029
1030 try {
1031 mService.writeDescriptor(mClientIf, device.getAddress(),
1032 service.getType(), service.getInstanceId(),
1033 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1034 new ParcelUuid(characteristic.getUuid()),
1035 new ParcelUuid(descriptor.getUuid()),
1036 characteristic.getWriteType(), AUTHENTICATION_NONE,
1037 descriptor.getValue());
1038 } catch (RemoteException e) {
1039 Log.e(TAG,"",e);
1040 return false;
1041 }
1042
1043 return true;
1044 }
1045
1046 /**
1047 * Initiates a reliable write transaction for a given remote device.
1048 *
1049 * <p>Once a reliable write transaction has been initiated, all calls
1050 * to {@link #writeCharacteristic} are sent to the remote device for
1051 * verification and queued up for atomic execution. The application will
1052 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1053 * in response to every {@link #writeCharacteristic} call and is responsible
1054 * for verifying if the value has been transmitted accurately.
1055 *
1056 * <p>After all characteristics have been queued up and verified,
1057 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1058 * was not written correctly, calling {@link #abortReliableWrite} will
1059 * cancel the current transaction without commiting any values on the
1060 * remote device.
1061 *
1062 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1063 *
1064 * @param device Remote device
1065 * @return true, if the reliable write transaction has been initiated
1066 */
1067 public boolean beginReliableWrite(BluetoothDevice device) {
1068 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress());
1069 if (mService == null || mClientIf == 0) return false;
1070
1071 try {
1072 mService.beginReliableWrite(mClientIf, device.getAddress());
1073 } catch (RemoteException e) {
1074 Log.e(TAG,"",e);
1075 return false;
1076 }
1077
1078 return true;
1079 }
1080
1081 /**
1082 * Executes a reliable write transaction for a given remote device.
1083 *
1084 * <p>This function will commit all queued up characteristic write
1085 * operations for a given remote device.
1086 *
1087 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1088 * invoked to indicate whether the transaction has been executed correctly.
1089 *
1090 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1091 *
1092 * @param device Remote device
1093 * @return true, if the request to execute the transaction has been sent
1094 */
1095 public boolean executeReliableWrite(BluetoothDevice device) {
1096 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress());
1097 if (mService == null || mClientIf == 0) return false;
1098
1099 try {
1100 mService.endReliableWrite(mClientIf, device.getAddress(), true);
1101 } catch (RemoteException e) {
1102 Log.e(TAG,"",e);
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 /**
1110 * Cancels a reliable write transaction for a given device.
1111 *
1112 * <p>Calling this function will discard all queued characteristic write
1113 * operations for a given remote device.
1114 *
1115 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1116 *
1117 * @param device Remote device
1118 */
1119 public void abortReliableWrite(BluetoothDevice device) {
1120 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress());
1121 if (mService == null || mClientIf == 0) return;
1122
1123 try {
1124 mService.endReliableWrite(mClientIf, device.getAddress(), false);
1125 } catch (RemoteException e) {
1126 Log.e(TAG,"",e);
1127 }
1128 }
1129
1130 /**
1131 * Enable or disable notifications/indications for a given characteristic.
1132 *
1133 * <p>Once notifications are enabled for a characteristic, a
1134 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1135 * triggered if the remote device indicates that the given characteristic
1136 * has changed.
1137 *
1138 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1139 *
1140 * @param characteristic The characteristic for which to enable notifications
1141 * @param enable Set to true to enable notifications/indications
1142 * @return true, if the requested notification status was set successfully
1143 */
1144 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1145 boolean enable) {
1146 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1147 + " enable: " + enable);
1148 if (mService == null || mClientIf == 0) return false;
1149
1150 BluetoothGattService service = characteristic.getService();
1151 if (service == null) return false;
1152
1153 BluetoothDevice device = service.getDevice();
1154 if (device == null) return false;
1155
1156 try {
1157 mService.registerForNotification(mClientIf, device.getAddress(),
1158 service.getType(), service.getInstanceId(),
1159 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1160 new ParcelUuid(characteristic.getUuid()),
1161 enable);
1162 } catch (RemoteException e) {
1163 Log.e(TAG,"",e);
1164 return false;
1165 }
1166
1167 return true;
1168 }
1169
1170 /**
1171 * Clears the internal cache and forces a refresh of the services from the
1172 * remote device.
1173 * @hide
1174 */
1175 public boolean refresh(BluetoothDevice device) {
1176 if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress());
1177 if (mService == null || mClientIf == 0) return false;
1178
1179 try {
1180 mService.refreshDevice(mClientIf, device.getAddress());
1181 } catch (RemoteException e) {
1182 Log.e(TAG,"",e);
1183 return false;
1184 }
1185
1186 return true;
1187 }
1188
1189 /**
1190 * Read the RSSI for a connected remote device.
1191 *
1192 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1193 * invoked when the RSSI value has been read.
1194 *
1195 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1196 *
1197 * @param device Remote device
1198 * @return true, if the RSSI value has been requested successfully
1199 */
1200 public boolean readRemoteRssi(BluetoothDevice device) {
1201 if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress());
1202 if (mService == null || mClientIf == 0) return false;
1203
1204 try {
1205 mService.readRemoteRssi(mClientIf, device.getAddress());
1206 } catch (RemoteException e) {
1207 Log.e(TAG,"",e);
1208 return false;
1209 }
1210
1211 return true;
1212 }
1213
1214 /**
1215 * Get the current connection state of the profile.
1216 *
1217 * <p>This is not specific to any application configuration but represents
1218 * the connection state of the local Bluetooth adapter for this profile.
1219 * This can be used by applications like status bar which would just like
1220 * to know the state of the local adapter.
1221 *
1222 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1223 *
1224 * @param device Remote bluetooth device.
1225 * @return State of the profile connection. One of
1226 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
1227 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
1228 */
1229 @Override
1230 public int getConnectionState(BluetoothDevice device) {
1231 if (DBG) Log.d(TAG,"getConnectionState()");
1232 if (mService == null) return STATE_DISCONNECTED;
1233
1234 List<BluetoothDevice> connectedDevices = getConnectedDevices();
1235 for(BluetoothDevice connectedDevice : connectedDevices) {
1236 if (device.equals(connectedDevice)) {
1237 return STATE_CONNECTED;
1238 }
1239 }
1240
1241 return STATE_DISCONNECTED;
1242 }
1243
1244 /**
1245 * Get connected devices for the Gatt profile.
1246 *
1247 * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
1248 *
1249 * <p>This is not specific to any application configuration but represents
1250 * the connection state of the local Bluetooth adapter for this profile.
1251 * This can be used by applications like status bar which would just like
1252 * to know the state of the local adapter.
1253 *
1254 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1255 *
1256 * @return List of devices. The list will be empty on error.
1257 */
1258 @Override
1259 public List<BluetoothDevice> getConnectedDevices() {
1260 if (DBG) Log.d(TAG,"getConnectedDevices");
1261
1262 List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
1263 if (mService == null) return connectedDevices;
1264
1265 try {
1266 connectedDevices = mService.getDevicesMatchingConnectionStates(
1267 new int[] { BluetoothProfile.STATE_CONNECTED });
1268 } catch (RemoteException e) {
1269 Log.e(TAG,"",e);
1270 }
1271
1272 return connectedDevices;
1273 }
1274
1275 /**
1276 * Get a list of devices that match any of the given connection
1277 * states.
1278 *
1279 * <p> If none of the devices match any of the given states,
1280 * an empty list will be returned.
1281 *
1282 * <p>This is not specific to any application configuration but represents
1283 * the connection state of the local Bluetooth adapter for this profile.
1284 * This can be used by applications like status bar which would just like
1285 * to know the state of the local adapter.
1286 *
1287 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1288 *
1289 * @param states Array of states. States can be one of
1290 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
1291 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
1292 * @return List of devices. The list will be empty on error.
1293 */
1294 @Override
1295 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1296 if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
1297
1298 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
1299 if (mService == null) return devices;
1300
1301 try {
1302 devices = mService.getDevicesMatchingConnectionStates(states);
1303 } catch (RemoteException e) {
1304 Log.e(TAG,"",e);
1305 }
1306
1307 return devices;
1308 }
1309}