Initial version of BLE support for Bluedroid
The API classes are hidden for now. Will unhide after API console
approval.
Change-Id: I8283dd562fd6189fdd15c866ef2efb8bbdbc4109
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
old mode 100755
new mode 100644
index 6367e16..1bbfb5d
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1136,8 +1136,9 @@
/**
* Get the profile proxy object associated with the profile.
*
- * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or
- * {@link BluetoothProfile#A2DP}. Clients must implements
+ * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
+ * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT},
+ * or {@link BluetoothProfile#GATT_SERVER}. Clients must implements
* {@link BluetoothProfile.ServiceListener} to get notified of
* the connection status and to get the proxy object.
*
@@ -1166,6 +1167,12 @@
} else if (profile == BluetoothProfile.HEALTH) {
BluetoothHealth health = new BluetoothHealth(context, listener);
return true;
+ } else if (profile == BluetoothProfile.GATT) {
+ BluetoothGatt gatt = new BluetoothGatt(context, listener);
+ return true;
+ } else if (profile == BluetoothProfile.GATT_SERVER) {
+ BluetoothGattServer gattServer = new BluetoothGattServer(context, listener);
+ return true;
} else {
return false;
}
@@ -1206,6 +1213,14 @@
BluetoothHealth health = (BluetoothHealth)proxy;
health.close();
break;
+ case BluetoothProfile.GATT:
+ BluetoothGatt gatt = (BluetoothGatt)proxy;
+ gatt.close();
+ break;
+ case BluetoothProfile.GATT_SERVER:
+ BluetoothGattServer gattServer = (BluetoothGattServer)proxy;
+ gattServer.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
new file mode 100644
index 0000000..1e12025
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.IBluetoothStateChangeCallback;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Public API for the Bluetooth Gatt Profile.
+ *
+ * <p>This class provides Bluetooth Gatt functionality to enable communication
+ * with Bluetooth Smart or Smart Ready devices.
+ *
+ * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service
+ * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothGatt proxy object.
+ *
+ * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
+ * and call {@link #registerApp} to register your application. Gatt capable
+ * devices can be discovered using the {@link #startScan} function or the
+ * regular Bluetooth device discovery process.
+ * @hide
+ */
+public final class BluetoothGatt implements BluetoothProfile {
+ private static final String TAG = "BluetoothGatt";
+ private static final boolean DBG = true;
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private BluetoothAdapter mAdapter;
+ private IBluetoothGatt mService;
+ private BluetoothGattCallback mCallback;
+ private int mClientIf;
+ private boolean mAuthRetry = false;
+
+ private List<BluetoothGattService> mServices;
+
+ /** A Gatt operation completed successfully */
+ public static final int GATT_SUCCESS = 0;
+
+ /** Gatt read operation is not permitted */
+ public static final int GATT_READ_NOT_PERMITTED = 0x2;
+
+ /** Gatt write operation is not permitted */
+ public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
+
+ /** Insufficient authentication for a given operation */
+ public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
+
+ /** The given request is not supported */
+ public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
+
+ /** Insufficient encryption for a given operation */
+ public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
+
+ /** A read or write operation was requested with an invalid offset */
+ public static final int GATT_INVALID_OFFSET = 0x7;
+
+ /** A write operation exceeds the maximum length of the attribute */
+ public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
+
+ /**
+ * No authentication required.
+ * @hide
+ */
+ /*package*/ static final int AUTHENTICATION_NONE = 0;
+
+ /**
+ * Authentication requested; no man-in-the-middle protection required.
+ * @hide
+ */
+ /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
+
+ /**
+ * Authentication with man-in-the-middle protection requested.
+ * @hide
+ */
+ /*package*/ static final int AUTHENTICATION_MITM = 2;
+
+ /**
+ * Bluetooth state change handlers
+ */
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (DBG) Log.d(TAG,"Unbinding service...");
+ synchronized (mConnection) {
+ mService = null;
+ mContext.unbindService(mConnection);
+ }
+ } else {
+ synchronized (mConnection) {
+ if (mService == null) {
+ if (DBG) Log.d(TAG,"Binding service...");
+ if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()),
+ mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth GATT Service");
+ }
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Service binder handling
+ */
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ mService = IBluetoothGatt.Stub.asInterface(service);
+ ServiceListener serviceListener = mServiceListener;
+ if (serviceListener != null) {
+ serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this);
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ mService = null;
+ ServiceListener serviceListener = mServiceListener;
+ if (serviceListener != null) {
+ serviceListener.onServiceDisconnected(BluetoothProfile.GATT);
+ }
+ }
+ };
+
+ /**
+ * Bluetooth GATT interface callbacks
+ */
+ private final IBluetoothGattCallback mBluetoothGattCallback =
+ new IBluetoothGattCallback.Stub() {
+ /**
+ * Application interface registered - app is ready to go
+ * @hide
+ */
+ public void onClientRegistered(int status, int clientIf) {
+ if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
+ + " clientIf=" + clientIf);
+ mClientIf = clientIf;
+ try {
+ mCallback.onAppRegistered(status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Client connection state changed
+ * @hide
+ */
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
+ + " clientIf=" + clientIf + " device=" + address);
+ try {
+ mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
+ connected ? BluetoothProfile.STATE_CONNECTED
+ : BluetoothProfile.STATE_DISCONNECTED);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ * @hide
+ */
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+ try {
+ mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * A new GATT service has been discovered.
+ * The service is added to the internal list and the search
+ * continues.
+ * @hide
+ */
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType));
+ }
+
+ /**
+ * An included service has been found durig GATT discovery.
+ * The included service is added to the respective parent.
+ * @hide
+ */
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
+ + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device,
+ srvcUuid.getUuid(), srvcInstId, srvcType);
+ BluetoothGattService includedService = getService(device,
+ inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
+
+ if (service != null && includedService != null) {
+ service.addIncludedService(includedService);
+ }
+ }
+
+ /**
+ * A new GATT characteristic has been discovered.
+ * Add the new characteristic to the relevant service and continue
+ * the remote device inspection.
+ * @hide
+ */
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
+ charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service != null) {
+ service.addCharacteristic(new BluetoothGattCharacteristic(
+ service, charUuid.getUuid(), charInstId, charProps, 0));
+ }
+ }
+
+ /**
+ * A new GATT descriptor has been discovered.
+ * Finally, add the descriptor to the related characteristic.
+ * This should conclude the remote device update.
+ * @hide
+ */
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descUuid) {
+ if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid());
+ if (characteristic == null) return;
+
+ characteristic.addDescriptor(new BluetoothGattDescriptor(
+ characteristic, descUuid.getUuid(), 0));
+ }
+
+ /**
+ * Remote search has been completed.
+ * The internal object structure should now reflect the state
+ * of the remote device database. Let the application know that
+ * we are done at this point.
+ * @hide
+ */
+ public void onSearchComplete(String address, int status) {
+ if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ try {
+ mCallback.onServicesDiscovered(device, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote characteristic has been read.
+ * Updates the internal value.
+ * @hide
+ */
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
+ + " UUID=" + charUuid + " Status=" + status);
+
+ if ((status == GATT_INSUFFICIENT_AUTHENTICATION
+ || status == GATT_INSUFFICIENT_ENCRYPTION)
+ && mAuthRetry == false) {
+ try {
+ mAuthRetry = true;
+ mService.readCharacteristic(mClientIf, address,
+ srvcType, srvcInstId, srvcUuid,
+ charInstId, charUuid, AUTHENTICATION_MITM);
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ mAuthRetry = false;
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid(), charInstId);
+ if (characteristic == null) return;
+
+ if (status == 0) characteristic.setValue(value);
+
+ try {
+ mCallback.onCharacteristicRead(characteristic, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Characteristic has been written to the remote device.
+ * Let the app know how we did...
+ * @hide
+ */
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
+ + " UUID=" + charUuid + " Status=" + status);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid(), charInstId);
+ if (characteristic == null) return;
+
+ if ((status == GATT_INSUFFICIENT_AUTHENTICATION
+ || status == GATT_INSUFFICIENT_ENCRYPTION)
+ && mAuthRetry == false) {
+ try {
+ mAuthRetry = true;
+ mService.writeCharacteristic(mClientIf, address,
+ srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
+ characteristic.getWriteType(), AUTHENTICATION_MITM,
+ characteristic.getValue());
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ mAuthRetry = false;
+
+ try {
+ mCallback.onCharacteristicWrite(characteristic, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote characteristic has been updated.
+ * Updates the internal value.
+ * @hide
+ */
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid(), charInstId);
+ if (characteristic == null) return;
+
+ characteristic.setValue(value);
+
+ try {
+ mCallback.onCharacteristicChanged(characteristic);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Descriptor has been read.
+ * @hide
+ */
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid, byte[] value) {
+ if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid(), charInstId);
+ if (characteristic == null) return;
+
+ BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
+ descrUuid.getUuid());
+ if (descriptor == null) return;
+
+ if (status == 0) descriptor.setValue(value);
+
+ if ((status == GATT_INSUFFICIENT_AUTHENTICATION
+ || status == GATT_INSUFFICIENT_ENCRYPTION)
+ && mAuthRetry == false) {
+ try {
+ mAuthRetry = true;
+ mService.readDescriptor(mClientIf, address,
+ srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
+ descrUuid, AUTHENTICATION_MITM);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ mAuthRetry = true;
+
+ try {
+ mCallback.onDescriptorRead(descriptor, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Descriptor write operation complete.
+ * @hide
+ */
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid) {
+ if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid.getUuid(), charInstId);
+ if (characteristic == null) return;
+
+ BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
+ descrUuid.getUuid());
+ if (descriptor == null) return;
+
+ if ((status == GATT_INSUFFICIENT_AUTHENTICATION
+ || status == GATT_INSUFFICIENT_ENCRYPTION)
+ && mAuthRetry == false) {
+ try {
+ mAuthRetry = true;
+ mService.writeDescriptor(mClientIf, address,
+ srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
+ descrUuid, characteristic.getWriteType(),
+ AUTHENTICATION_MITM, descriptor.getValue());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ mAuthRetry = false;
+
+ try {
+ mCallback.onDescriptorWrite(descriptor, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Prepared write transaction completed (or aborted)
+ * @hide
+ */
+ public void onExecuteWrite(String address, int status) {
+ if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
+ + " status=" + status);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ try {
+ mCallback.onReliableWriteCompleted(device, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote device RSSI has been read
+ * @hide
+ */
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
+ " rssi=" + rssi + " status=" + status);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ try {
+ mCallback.onReadRemoteRssi(device, rssi, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothGatt proxy object.
+ */
+ /*package*/ BluetoothGatt(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mServices = new ArrayList<BluetoothGattService>();
+
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
+ if (b != null) {
+ IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
+ }
+ } else {
+ Log.e(TAG, "Unable to get BluetoothManager interface.");
+ throw new RuntimeException("BluetoothManager inactive");
+ }
+
+ //Bind to the service only if the Bluetooth is ON
+ if(mAdapter.isEnabled()){
+ if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
+ }
+ }
+ }
+
+ /**
+ * Close the connection to the gatt service.
+ */
+ /*package*/ void close() {
+ if (DBG) Log.d(TAG, "close()");
+
+ unregisterApp();
+ mServiceListener = null;
+
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
+ if (b != null) {
+ IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
+ }
+ }
+
+ synchronized (mConnection) {
+ if (mService != null) {
+ mService = null;
+ mContext.unbindService(mConnection);
+ }
+ }
+ }
+
+ /**
+ * Returns a service by UUID, instance and type.
+ * @hide
+ */
+ /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
+ int instanceId, int type) {
+ for(BluetoothGattService svc : mServices) {
+ if (svc.getDevice().equals(device) &&
+ svc.getType() == type &&
+ svc.getInstanceId() == instanceId &&
+ svc.getUuid().equals(uuid)) {
+ return svc;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Register an application callback to start using Gatt.
+ *
+ * <p>This is an asynchronous call. The callback is used to notify
+ * success or failure if the function returns true.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback Gatt callback handler that will receive asynchronous
+ * callbacks.
+ * @return true, if application was successfully registered.
+ */
+ public boolean registerApp(BluetoothGattCallback callback) {
+ if (DBG) Log.d(TAG, "registerApp()");
+ if (mService == null) return false;
+
+ mCallback = callback;
+ UUID uuid = UUID.randomUUID();
+ if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
+
+ try {
+ mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Unregister the current application and callbacks.
+ */
+ public void unregisterApp() {
+ if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
+ if (mService == null || mClientIf == 0) return;
+
+ try {
+ mCallback = null;
+ mService.unregisterClient(mClientIf);
+ mClientIf = 0;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices.
+ *
+ * <p>Results of the scan are reported using the
+ * {@link BluetoothGattCallback#onScanResult} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return true, if the scan was started successfully
+ */
+ public boolean startScan() {
+ if (DBG) Log.d(TAG, "startScan()");
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.startScan(mClientIf, false);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices, looking for devices that
+ * advertise given services.
+ *
+ * <p>Devices which advertise all specified services are reported using the
+ * {@link BluetoothGattCallback#onScanResult} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param serviceUuids Array of services to look for
+ * @return true, if the scan was started successfully
+ */
+ public boolean startScan(UUID[] serviceUuids) {
+ if (DBG) Log.d(TAG, "startScan() - with UUIDs");
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
+ for(int i = 0; i != uuids.length; ++i) {
+ uuids[i] = new ParcelUuid(serviceUuids[i]);
+ }
+ mService.startScanWithUuids(mClientIf, false, uuids);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Stops an ongoing Bluetooth LE device scan.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ */
+ public void stopScan() {
+ if (DBG) Log.d(TAG, "stopScan()");
+ if (mService == null || mClientIf == 0) return;
+
+ try {
+ mService.stopScan(mClientIf, false);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Initiate a connection to a Bluetooth Gatt capable device.
+ *
+ * <p>The connection may not be established right away, but will be
+ * completed when the remote device is available. A
+ * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
+ * invoked when the connection state changes as a result of this function.
+ *
+ * <p>The autoConnect paramter determines whether to actively connect to
+ * the remote device, or rather passively scan and finalize the connection
+ * when the remote device is in range/available. Generally, the first ever
+ * connection to a device should be direct (autoConnect set to false) and
+ * subsequent connections to known devices should be invoked with the
+ * autoConnect parameter set to false.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device to connect to
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @return true, if the connection attempt was initiated successfully
+ */
+ public boolean connect(BluetoothDevice device, boolean autoConnect) {
+ if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.clientConnect(mClientIf, device.getAddress(),
+ autoConnect ? false : true); // autoConnect is inverse of "isDirect"
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnects an established connection, or cancels a connection attempt
+ * currently in progress.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ */
+ public void cancelConnection(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return;
+
+ try {
+ mService.clientDisconnect(mClientIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Discovers services offered by a remote device as well as their
+ * characteristics and descriptors.
+ *
+ * <p>This is an asynchronous operation. Once service discovery is completed,
+ * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
+ * triggered. If the discovery was successful, the remote services can be
+ * retrieved using the {@link #getServices} function.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device to explore
+ * @return true, if the remote service discovery has been started
+ */
+ public boolean discoverServices(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return false;
+
+ mServices.clear();
+
+ try {
+ mService.discoverServices(mClientIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of GATT services offered by the remote device.
+ *
+ * <p>This function requires that service discovery has been completed
+ * for the given device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ * @return List of services on the remote device. Returns an empty list
+ * if service discovery has not yet been performed.
+ */
+ public List<BluetoothGattService> getServices(BluetoothDevice device) {
+ List<BluetoothGattService> result =
+ new ArrayList<BluetoothGattService>();
+
+ for (BluetoothGattService service : mServices) {
+ if (service.getDevice().equals(device)) {
+ result.add(service);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a {@link BluetoothGattService}, if the requested UUID is
+ * supported by the remote device.
+ *
+ * <p>This function requires that service discovery has been completed
+ * for the given device.
+ *
+ * <p>If multiple instances of the same service (as identified by UUID)
+ * exist, the first instance of the service is returned.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ * @param uuid UUID of the requested service
+ * @return BluetoothGattService if supported, or null if the requested
+ * service is not offered by the remote device.
+ */
+ public BluetoothGattService getService(BluetoothDevice device, UUID uuid) {
+ for (BluetoothGattService service : mServices) {
+ if (service.getDevice().equals(device) &&
+ service.getUuid().equals(uuid)) {
+ return service;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Reads the requested characteristic from the associated remote device.
+ *
+ * <p>This is an asynchronous operation. The result of the read operation
+ * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
+ * callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic Characteristic to read from the remote device
+ * @return true, if the read operation was initiated successfully
+ */
+ public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
+ if ((characteristic.getProperties() &
+ BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
+
+ if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
+ if (mService == null || mClientIf == 0) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ BluetoothDevice device = service.getDevice();
+ if (device == null) return false;
+
+ try {
+ mService.readCharacteristic(mClientIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Writes a given characteristic and it's values to the associated remote
+ * device.
+ *
+ * <p>Once the write operation has been completed, the
+ * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+ * reporting the result of the operation.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic Characteristic to write on the remote device
+ * @return true, if the write operation was initiated successfully
+ */
+ public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
+ && (characteristic.getProperties() &
+ BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
+
+ if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
+ if (mService == null || mClientIf == 0) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ BluetoothDevice device = service.getDevice();
+ if (device == null) return false;
+
+ try {
+ mService.writeCharacteristic(mClientIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()),
+ characteristic.getWriteType(), AUTHENTICATION_NONE,
+ characteristic.getValue());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Reads the value for a given descriptor from the associated remote device.
+ *
+ * <p>Once the read operation has been completed, the
+ * {@link BluetoothGattCallback#onDescriptorRead} callback is
+ * triggered, signaling the result of the operation.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param descriptor Descriptor value to read from the remote device
+ * @return true, if the read operation was initiated successfully
+ */
+ public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
+ if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
+ if (mService == null || mClientIf == 0) return false;
+
+ BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
+ if (characteristic == null) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ BluetoothDevice device = service.getDevice();
+ if (device == null) return false;
+
+ try {
+ mService.readDescriptor(mClientIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()),
+ new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Write the value of a given descriptor to the associated remote device.
+ *
+ * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
+ * triggered to report the result of the write operation.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param descriptor Descriptor to write to the associated remote device
+ * @return true, if the write operation was initiated successfully
+ */
+ public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+ if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
+ if (mService == null || mClientIf == 0) return false;
+
+ BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
+ if (characteristic == null) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ BluetoothDevice device = service.getDevice();
+ if (device == null) return false;
+
+ try {
+ mService.writeDescriptor(mClientIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()),
+ new ParcelUuid(descriptor.getUuid()),
+ characteristic.getWriteType(), AUTHENTICATION_NONE,
+ descriptor.getValue());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Initiates a reliable write transaction for a given remote device.
+ *
+ * <p>Once a reliable write transaction has been initiated, all calls
+ * to {@link #writeCharacteristic} are sent to the remote device for
+ * verification and queued up for atomic execution. The application will
+ * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
+ * in response to every {@link #writeCharacteristic} call and is responsible
+ * for verifying if the value has been transmitted accurately.
+ *
+ * <p>After all characteristics have been queued up and verified,
+ * {@link #executeReliableWrite} will execute all writes. If a characteristic
+ * was not written correctly, calling {@link #abortReliableWrite} will
+ * cancel the current transaction without commiting any values on the
+ * remote device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ * @return true, if the reliable write transaction has been initiated
+ */
+ public boolean beginReliableWrite(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.beginReliableWrite(mClientIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Executes a reliable write transaction for a given remote device.
+ *
+ * <p>This function will commit all queued up characteristic write
+ * operations for a given remote device.
+ *
+ * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
+ * invoked to indicate whether the transaction has been executed correctly.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ * @return true, if the request to execute the transaction has been sent
+ */
+ public boolean executeReliableWrite(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.endReliableWrite(mClientIf, device.getAddress(), true);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Cancels a reliable write transaction for a given device.
+ *
+ * <p>Calling this function will discard all queued characteristic write
+ * operations for a given remote device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ */
+ public void abortReliableWrite(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return;
+
+ try {
+ mService.endReliableWrite(mClientIf, device.getAddress(), false);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Enable or disable notifications/indications for a given characteristic.
+ *
+ * <p>Once notifications are enabled for a characteristic, a
+ * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
+ * triggered if the remote device indicates that the given characteristic
+ * has changed.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic The characteristic for which to enable notifications
+ * @param enable Set to true to enable notifications/indications
+ * @return true, if the requested notification status was set successfully
+ */
+ public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
+ boolean enable) {
+ if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
+ + " enable: " + enable);
+ if (mService == null || mClientIf == 0) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ BluetoothDevice device = service.getDevice();
+ if (device == null) return false;
+
+ try {
+ mService.registerForNotification(mClientIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()),
+ enable);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Clears the internal cache and forces a refresh of the services from the
+ * remote device.
+ * @hide
+ */
+ public boolean refresh(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.refreshDevice(mClientIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Read the RSSI for a connected remote device.
+ *
+ * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
+ * invoked when the RSSI value has been read.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ * @return true, if the RSSI value has been requested successfully
+ */
+ public boolean readRemoteRssi(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress());
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.readRemoteRssi(mClientIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the current connection state of the profile.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote bluetooth device.
+ * @return State of the profile connection. One of
+ * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) Log.d(TAG,"getConnectionState()");
+ if (mService == null) return STATE_DISCONNECTED;
+
+ List<BluetoothDevice> connectedDevices = getConnectedDevices();
+ for(BluetoothDevice connectedDevice : connectedDevices) {
+ if (device.equals(connectedDevice)) {
+ return STATE_CONNECTED;
+ }
+ }
+
+ return STATE_DISCONNECTED;
+ }
+
+ /**
+ * Get connected devices for the Gatt profile.
+ *
+ * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return List of devices. The list will be empty on error.
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) Log.d(TAG,"getConnectedDevices");
+
+ List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
+ if (mService == null) return connectedDevices;
+
+ try {
+ connectedDevices = mService.getDevicesMatchingConnectionStates(
+ new int[] { BluetoothProfile.STATE_CONNECTED });
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return connectedDevices;
+ }
+
+ /**
+ * Get a list of devices that match any of the given connection
+ * states.
+ *
+ * <p> If none of the devices match any of the given states,
+ * an empty list will be returned.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param states Array of states. States can be one of
+ * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
+ * @return List of devices. The list will be empty on error.
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
+
+ List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+ if (mService == null) return devices;
+
+ try {
+ devices = mService.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return devices;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
new file mode 100644
index 0000000..afa4539
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+import android.util.Log;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGatt} callbacks.
+ * @hide
+ */
+public abstract class BluetoothGattCallback {
+ /**
+ * Callback to inform change in registration state of the application.
+ *
+ * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
+ * was successfully registered.
+ */
+ public void onAppRegistered(int status) {
+ }
+
+ /**
+ * Callback reporting an LE device found during a device scan initiated
+ * by the {@link BluetoothGatt#startScan} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device as reported by the
+ * Bluetooth hardware. 0 if no RSSI value is available.
+ * @param scanRecord The content of the advertisement record offered by
+ * the remote device.
+ */
+ public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
+ }
+
+ /**
+ * Callback indicating when a remote device has been connected or disconnected.
+ *
+ * @param device Remote device that has been connected or disconnected.
+ * @param status Status of the connect or disconnect operation.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothDevice device, int status,
+ int newState) {
+ }
+
+ /**
+ * Callback invoked when the list of remote services, characteristics and
+ * descriptors for the remote device have been updated.
+ *
+ * @param device Remote device
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
+ * has been explored successfully.
+ */
+ public void onServicesDiscovered(BluetoothDevice device, int status) {
+ }
+
+ /**
+ * Callback reporting the result of a characteristic read operation.
+ *
+ * @param characteristic Characteristic that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully.
+ */
+ public void onCharacteristicRead(BluetoothGattCharacteristic characteristic,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a characteristic write operation.
+ *
+ * <p>If this callback is invoked while a reliable write transaction is
+ * in progress, the value of the characteristic represents the value
+ * reported by the remote device. An application should compare this
+ * value to the desired value to be written. If the values don't match,
+ * the application must abort the reliable write transaction.
+ *
+ * @param characteristic Characteristic that was written to the associated
+ * remote device.
+ * @param status The result of the write operation
+ */
+ public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic,
+ int status) {
+ }
+
+ /**
+ * Callback triggered as a result of a remote characteristic notification.
+ *
+ * @param characteristic Characteristic that has been updated as a result
+ * of a remote notification event.
+ */
+ public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * Callback reporting the result of a descriptor read operation.
+ *
+ * @param descriptor Descriptor that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully
+ */
+ public void onDescriptorRead(BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a descriptor write operation.
+ *
+ * @param descriptor Descriptor that was writte to the associated
+ * remote device.
+ * @param status The result of the write operation
+ */
+ public void onDescriptorWrite(BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback invoked when a reliable write transaction has been completed.
+ *
+ * @param device Remote device
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
+ * transaction was executed successfully
+ */
+ public void onReliableWriteCompleted(BluetoothDevice device, int status) {
+ }
+
+ /**
+ * Callback reporting the RSSI for a remote device connection.
+ *
+ * This callback is triggered in response to the
+ * {@link BluetoothGatt#readRemoteRssi} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device
+ * @param status 0 if the RSSI was read successfully
+ */
+ public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
new file mode 100644
index 0000000..18492ab
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Represents a Bluetooth Gatt Characteristic
+ * @hide
+ */
+public class BluetoothGattCharacteristic {
+
+ /**
+ * Characteristic proprty: Characteristic is broadcastable.
+ */
+ public static final int PROPERTY_BROADCAST = 0x01;
+
+ /**
+ * Characteristic property: Characteristic is readable.
+ */
+ public static final int PROPERTY_READ = 0x02;
+
+ /**
+ * Characteristic property: Characteristic can be written without response.
+ */
+ public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04;
+
+ /**
+ * Characteristic property: Characteristic can be written.
+ */
+ public static final int PROPERTY_WRITE = 0x08;
+
+ /**
+ * Characteristic property: Characteristic supports notification
+ */
+ public static final int PROPERTY_NOTIFY = 0x10;
+
+ /**
+ * Characteristic property: Characteristic supports indication
+ */
+ public static final int PROPERTY_INDICATE = 0x20;
+
+ /**
+ * Characteristic property: Characteristic supports write with signature
+ */
+ public static final int PROPERTY_SIGNED_WRITE = 0x40;
+
+ /**
+ * Characteristic property: Characteristic has extended properties
+ */
+ public static final int PROPERTY_EXTENDED_PROPS = 0x80;
+
+ /**
+ * Characteristic read permission
+ */
+ public static final int PERMISSION_READ = 0x01;
+
+ /**
+ * Characteristic permission: Allow encrypted read operations
+ */
+ public static final int PERMISSION_READ_ENCRYPTED = 0x02;
+
+ /**
+ * Characteristic permission: Allow reading with man-in-the-middle protection
+ */
+ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
+
+ /**
+ * Characteristic write permission
+ */
+ public static final int PERMISSION_WRITE = 0x10;
+
+ /**
+ * Characteristic permission: Allow encrypted writes
+ */
+ public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
+
+ /**
+ * Characteristic permission: Allow encrypted writes with man-in-the-middle
+ * protection
+ */
+ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
+
+ /**
+ * Characteristic permission: Allow signed write operations
+ */
+ public static final int PERMISSION_WRITE_SIGNED = 0x80;
+
+ /**
+ * Characteristic permission: Allow signed write operations with
+ * man-in-the-middle protection
+ */
+ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
+
+ /**
+ * Write characteristic, requesting acknoledgement by the remote device
+ */
+ public static final int WRITE_TYPE_DEFAULT = 0x02;
+
+ /**
+ * Wrtite characteristic without requiring a response by the remote device
+ */
+ public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
+
+ /**
+ * Write characteristic including and authenticated signature
+ */
+ public static final int WRITE_TYPE_SIGNED = 0x04;
+
+ /**
+ * Characteristic value format type uint8
+ */
+ public static final int FORMAT_UINT8 = 0x11;
+
+ /**
+ * Characteristic value format type uint16
+ */
+ public static final int FORMAT_UINT16 = 0x12;
+
+ /**
+ * Characteristic value format type uint32
+ */
+ public static final int FORMAT_UINT32 = 0x14;
+
+ /**
+ * Characteristic value format type sint8
+ */
+ public static final int FORMAT_SINT8 = 0x21;
+
+ /**
+ * Characteristic value format type sint16
+ */
+ public static final int FORMAT_SINT16 = 0x22;
+
+ /**
+ * Characteristic value format type sint32
+ */
+ public static final int FORMAT_SINT32 = 0x24;
+
+ /**
+ * Characteristic value format type sfloat (16-bit float)
+ */
+ public static final int FORMAT_SFLOAT = 0x32;
+
+ /**
+ * Characteristic value format type float (32-bit float)
+ */
+ public static final int FORMAT_FLOAT = 0x34;
+
+
+ /**
+ * The UUID of this characteristic.
+ * @hide
+ */
+ protected UUID mUuid;
+
+ /**
+ * Instance ID for this characteristic.
+ * @hide
+ */
+ protected int mInstance;
+
+ /**
+ * Characteristic properties.
+ * @hide
+ */
+ protected int mProperties;
+
+ /**
+ * Characteristic permissions.
+ * @hide
+ */
+ protected int mPermissions;
+
+ /**
+ * Key size (default = 16).
+ * @hide
+ */
+ protected int mKeySize = 16;
+
+ /**
+ * Write type for this characteristic.
+ * See WRITE_TYPE_* constants.
+ * @hide
+ */
+ protected int mWriteType;
+
+ /**
+ * Back-reference to the service this characteristic belongs to.
+ * @hide
+ */
+ protected BluetoothGattService mService;
+
+ /**
+ * The cached value of this characteristic.
+ * @hide
+ */
+ protected byte[] mValue;
+
+ /**
+ * List of descriptors included in this characteristic.
+ */
+ protected List<BluetoothGattDescriptor> mDescriptors;
+
+ /**
+ * Create a new BluetoothGattCharacteristic
+ * @hide
+ */
+ /*package*/ BluetoothGattCharacteristic(BluetoothGattService service,
+ UUID uuid, int instanceId,
+ int properties, int permissions) {
+ mUuid = uuid;
+ mInstance = instanceId;
+ mProperties = properties;
+ mPermissions = permissions;
+ mService = service;
+ mValue = null;
+ mDescriptors = new ArrayList<BluetoothGattDescriptor>();
+
+ if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) {
+ mWriteType = WRITE_TYPE_NO_RESPONSE;
+ } else {
+ mWriteType = WRITE_TYPE_DEFAULT;
+ }
+ }
+
+ /**
+ * Returns the deisred key size.
+ * @hide
+ */
+ /*package*/ int getKeySize() {
+ return mKeySize;
+ }
+
+ /**
+ * Add a descriptor to this characteristic
+ * @hide
+ */
+ /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) {
+ mDescriptors.add(descriptor);
+ }
+
+ /**
+ * Returns the service this characteristic belongs to.
+ * @return The asscociated service
+ */
+ public BluetoothGattService getService() {
+ return mService;
+ }
+
+ /**
+ * Returns the UUID of this characteristic
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return UUID of this characteristic
+ */
+ public UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns the instance ID for this characteristic.
+ *
+ * <p>If a remote device offers multiple characteristics with the same UUID,
+ * the instance ID is used to distuinguish between characteristics.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Instance ID of this characteristic
+ */
+ public int getInstanceId() {
+ return mInstance;
+ }
+
+ /**
+ * Returns the properties of this characteristic.
+ *
+ * <p>The properties contain a bit mask of property flags indicating
+ * the features of this characteristic.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Properties of this characteristic
+ */
+ public int getProperties() {
+ return mProperties;
+ }
+
+ /**
+ * Returns the permissions for this characteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Permissions of this characteristic
+ */
+ public int getPermissions() {
+ return mPermissions;
+ }
+
+ /**
+ * Gets the write type for this characteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Write type for this characteristic
+ */
+ public int getWriteType() {
+ return mWriteType;
+ }
+
+ /**
+ * Set the write type for this characteristic
+ *
+ * <p>Setting the write type of a characteristic determines how the
+ * {@link BluetoothGatt#writeCharacteristic} function write this
+ * characteristic.
+ *
+ * <p>The default write type for a characteristic is
+ * {@link #WRITE_TYPE_DEFAULT}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param writeType The write type to for this characteristic. Can be one
+ * of:
+ * {@link #WRITE_TYPE_DEFAULT},
+ * {@link #WRITE_TYPE_NO_RESPONSE} or
+ * {@link #WRITE_TYPE_SIGNED}.
+ */
+ public void setWriteType(int writeType) {
+ mWriteType = writeType;
+ }
+
+ /**
+ * Returns a list of descriptors for this characteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Descriptors for this characteristic
+ */
+ public List<BluetoothGattDescriptor> getDescriptors() {
+ return mDescriptors;
+ }
+
+ /**
+ * Returns a descriptor with a given UUID out of the list of
+ * descriptors for this characteristic.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Gatt descriptor object or null if no descriptor with the
+ * given UUID was found.
+ */
+ public BluetoothGattDescriptor getDescriptor(UUID uuid) {
+ for(BluetoothGattDescriptor descriptor : mDescriptors) {
+ if (descriptor.getUuid().equals(uuid)) {
+ return descriptor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the stored value for this characteristic.
+ *
+ * <p>This function returns the stored value for this characteristic as
+ * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached
+ * value of the characteristic is updated as a result of a read characteristic
+ * operation or if a characteristic update notification has been received.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Cached value of the characteristic
+ */
+ public byte[] getValue() {
+ return mValue;
+ }
+
+ /**
+ * Return the stored value of this characteristic.
+ *
+ * <p>The formatType parameter determines how the characteristic value
+ * is to be interpreted. For example, settting formatType to
+ * {@link #FORMAT_UINT16} specifies that the first two bytes of the
+ * characteristic value at the given offset are interpreted to generate the
+ * return value.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param formatType The format type used to interpret the characteristic
+ * value.
+ * @param offset Offset at which the integer value can be found.
+ * @return Cached value of the characteristic or null of offset exceeds
+ * value size.
+ */
+ public Integer getIntValue(int formatType, int offset) {
+ if ((offset + getTypeLen(formatType)) > mValue.length) return null;
+
+ switch (formatType) {
+ case FORMAT_UINT8:
+ return unsignedByteToInt(mValue[offset]);
+
+ case FORMAT_UINT16:
+ return unsignedBytesToInt(mValue[offset], mValue[offset+1]);
+
+ case FORMAT_UINT32:
+ return unsignedBytesToInt(mValue[offset], mValue[offset+1],
+ mValue[offset+2], mValue[offset+3]);
+ case FORMAT_SINT8:
+ return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8);
+
+ case FORMAT_SINT16:
+ return unsignedToSigned(unsignedBytesToInt(mValue[offset],
+ mValue[offset+1]), 16);
+
+ case FORMAT_SINT32:
+ return unsignedToSigned(unsignedBytesToInt(mValue[offset],
+ mValue[offset+1], mValue[offset+2], mValue[offset+3]), 32);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the stored value of this characteristic.
+ * <p>See {@link #getValue} for details.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param formatType The format type used to interpret the characteristic
+ * value.
+ * @param offset Offset at which the float value can be found.
+ * @return Cached value of the characteristic at a given offset or null
+ * if the requested offset exceeds the value size.
+ */
+ public Float getFloatValue(int formatType, int offset) {
+ if ((offset + getTypeLen(formatType)) > mValue.length) return null;
+
+ switch (formatType) {
+ case FORMAT_SFLOAT:
+ return bytesToFloat(mValue[offset], mValue[offset+1]);
+
+ case FORMAT_FLOAT:
+ return bytesToFloat(mValue[offset], mValue[offset+1],
+ mValue[offset+2], mValue[offset+3]);
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the stored value of this characteristic.
+ * <p>See {@link #getValue} for details.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ * @param offset Offset at which the string value can be found.
+ * @return Cached value of the characteristic
+ */
+ public String getStringValue(int offset) {
+ if (offset > mValue.length) return null;
+ byte[] strBytes = new byte[mValue.length - offset];
+ for (int i=0; i != (mValue.length-offset); ++i) strBytes[i] = mValue[offset+i];
+ return new String(strBytes);
+ }
+
+ /**
+ * Updates the locally stored value of this characteristic.
+ *
+ * <p>This function modifies the locally stored cached value of this
+ * characteristic. To send the value to the remote device, call
+ * {@link BluetoothGatt#writeCharacteristic} to send the value to the
+ * remote device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param value New value for this characteristic
+ * @return true if the locally stored value has been set, false if the
+ * requested value could not be stored locally.
+ */
+ public boolean setValue(byte[] value) {
+ mValue = value;
+ return true;
+ }
+
+ /**
+ * Set the locally stored value of this characteristic.
+ * <p>See {@link #setValue(byte[])} for details.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param value New value for this characteristic
+ * @param formatType Integer format type used to transform the value parameter
+ * @param offset Offset at which the value should be placed
+ * @return true if the locally stored value has been set
+ */
+ public boolean setValue(int value, int formatType, int offset) {
+ int len = offset + getTypeLen(formatType);
+ if (mValue == null) mValue = new byte[len];
+ if (len > mValue.length) return false;
+
+ switch (formatType) {
+ case FORMAT_SINT8:
+ value = intToSignedBits(value, 8);
+ // Fall-through intended
+ case FORMAT_UINT8:
+ mValue[offset] = (byte)(value & 0xFF);
+ break;
+
+ case FORMAT_SINT16:
+ value = intToSignedBits(value, 16);
+ // Fall-through intended
+ case FORMAT_UINT16:
+ mValue[offset++] = (byte)(value & 0xFF);
+ mValue[offset] = (byte)((value >> 8) & 0xFF);
+ break;
+
+ case FORMAT_SINT32:
+ value = intToSignedBits(value, 32);
+ // Fall-through intended
+ case FORMAT_UINT32:
+ mValue[offset++] = (byte)(value & 0xFF);
+ mValue[offset++] = (byte)((value >> 8) & 0xFF);
+ mValue[offset] = (byte)((value >> 16) & 0xFF);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set the locally stored value of this characteristic.
+ * <p>See {@link #setValue(byte[])} for details.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ * @param mantissa Mantissa for this characteristic
+ * @param exponent exponent value for this characteristic
+ * @param formatType Float format type used to transform the value parameter
+ * @param offset Offset at which the value should be placed
+ * @return true if the locally stored value has been set
+ */
+ public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
+ int len = offset + getTypeLen(formatType);
+ if (mValue == null) mValue = new byte[len];
+ if (len > mValue.length) return false;
+
+ switch (formatType) {
+ case FORMAT_SFLOAT:
+ mantissa = intToSignedBits(mantissa, 12);
+ exponent = intToSignedBits(exponent, 4);
+ mValue[offset++] = (byte)(mantissa & 0xFF);
+ mValue[offset] = (byte)((mantissa >> 8) & 0x0F);
+ mValue[offset] += (byte)((exponent & 0x0F) << 4);
+ break;
+
+ case FORMAT_FLOAT:
+ mantissa = intToSignedBits(mantissa, 24);
+ exponent = intToSignedBits(exponent, 8);
+ mValue[offset++] = (byte)(mantissa & 0xFF);
+ mValue[offset++] = (byte)((mantissa >> 8) & 0xFF);
+ mValue[offset++] = (byte)((mantissa >> 16) & 0xFF);
+ mValue[offset] += (byte)(exponent & 0xFF);
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Set the locally stored value of this characteristic.
+ * <p>See {@link #setValue(byte[])} for details.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ * @param value New value for this characteristic
+ * @return true if the locally stored value has been set
+ */
+ public boolean setValue(String value) {
+ mValue = value.getBytes();
+ return true;
+ }
+
+ /**
+ * Returns the size of a give value type.
+ * @hide
+ */
+ private int getTypeLen(int formatType) {
+ return formatType & 0xF;
+ }
+
+ /**
+ * Convert a signed byte to an unsigned int.
+ * @hide
+ */
+ private int unsignedByteToInt(byte b) {
+ return b & 0xFF;
+ }
+
+ /**
+ * Convert signed bytes to a 16-bit unsigned int.
+ * @hide
+ */
+ private int unsignedBytesToInt(byte b0, byte b1) {
+ return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
+ }
+
+ /**
+ * Convert signed bytes to a 32-bit unsigned int.
+ * @hide
+ */
+ private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) {
+ return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
+ + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24);
+ }
+
+ /**
+ * Convert signed bytes to a 16-bit short float value.
+ * @hide
+ */
+ private float bytesToFloat(byte b0, byte b1) {
+ int mantissa = unsignedToSigned(unsignedByteToInt(b0)
+ + ((unsignedByteToInt(b1) & 0x0F) << 8), 12);
+ int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4);
+ return (float)(mantissa * Math.pow(10, exponent));
+ }
+
+ /**
+ * Convert signed bytes to a 32-bit short float value.
+ * @hide
+ */
+ private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
+ int mantissa = unsignedToSigned(unsignedByteToInt(b0)
+ + (unsignedByteToInt(b1) << 8)
+ + (unsignedByteToInt(b2) << 16), 24);
+ return (float)(mantissa * Math.pow(10, b3));
+ }
+
+ /**
+ * Convert an unsigned integer value to a two's-complement encoded
+ * signed value.
+ * @hide
+ */
+ private int unsignedToSigned(int unsigned, int size) {
+ if ((unsigned & (1 << size-1)) != 0) {
+ unsigned = -1 * ((1 << size-1) - (unsigned & ((1 << size-1) - 1)));
+ }
+ return unsigned;
+ }
+
+ /**
+ * Convert an integer into the signed bits of a given length.
+ * @hide
+ */
+ private int intToSignedBits(int i, int size) {
+ if (i < 0) {
+ i = (1 << size-1) + (i & ((1 << size-1) - 1));
+ }
+ return i;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
new file mode 100644
index 0000000..ba1f28a
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.util.UUID;
+
+/**
+ * Represents a Bluetooth Gatt Descriptor
+ * @hide
+ */
+public class BluetoothGattDescriptor {
+
+ /**
+ * Value used to enable notification for a client configuration descriptor
+ */
+ public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00};
+
+ /**
+ * Value used to enable indication for a client configuration descriptor
+ */
+ public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00};
+
+ /**
+ * Value used to disable notifications or indicatinos
+ */
+ public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00};
+
+ /**
+ * Descriptor read permission
+ */
+ public static final int PERMISSION_READ = 0x01;
+
+ /**
+ * Descriptor permission: Allow encrypted read operations
+ */
+ public static final int PERMISSION_READ_ENCRYPTED = 0x02;
+
+ /**
+ * Descriptor permission: Allow reading with man-in-the-middle protection
+ */
+ public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
+
+ /**
+ * Descriptor write permission
+ */
+ public static final int PERMISSION_WRITE = 0x10;
+
+ /**
+ * Descriptor permission: Allow encrypted writes
+ */
+ public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
+
+ /**
+ * Descriptor permission: Allow encrypted writes with man-in-the-middle
+ * protection
+ */
+ public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
+
+ /**
+ * Descriptor permission: Allow signed write operations
+ */
+ public static final int PERMISSION_WRITE_SIGNED = 0x80;
+
+ /**
+ * Descriptor permission: Allow signed write operations with
+ * man-in-the-middle protection
+ */
+ public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
+
+ /**
+ * The UUID of this descriptor.
+ * @hide
+ */
+ protected UUID mUuid;
+
+ /**
+ * Permissions for this descriptor
+ * @hide
+ */
+ protected int mPermissions;
+
+ /**
+ * Back-reference to the characteristic this descriptor belongs to.
+ * @hide
+ */
+ protected BluetoothGattCharacteristic mCharacteristic;
+
+ /**
+ * The value for this descriptor.
+ * @hide
+ */
+ protected byte[] mValue;
+
+ /**
+ * Create a new BluetoothGattDescriptor.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic The characteristic this descriptor belongs to
+ * @param uuid The UUID for this descriptor
+ * @param permissions Permissions for this descriptor
+ */
+ /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
+ int permissions) {
+ mCharacteristic = characteristic;
+ mUuid = uuid;
+ mPermissions = permissions;
+ }
+
+ /**
+ * Returns the characteristic this descriptor belongs to.
+ * @return The characteristic.
+ */
+ public BluetoothGattCharacteristic getCharacteristic() {
+ return mCharacteristic;
+ }
+
+ /**
+ * Returns the UUID of this descriptor.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return UUID of this descriptor
+ */
+ public UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns the permissions for this descriptor.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Permissions of this descriptor
+ */
+ public int getPermissions() {
+ return mPermissions;
+ }
+
+ /**
+ * Returns the stored value for this descriptor
+ *
+ * <p>This function returns the stored value for this descriptor as
+ * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached
+ * value of the descriptor is updated as a result of a descriptor read
+ * operation.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Cached value of the descriptor
+ */
+ public byte[] getValue() {
+ return mValue;
+ }
+
+ /**
+ * Updates the locally stored value of this descriptor.
+ *
+ * <p>This function modifies the locally stored cached value of this
+ * descriptor. To send the value to the remote device, call
+ * {@link BluetoothGatt#writeDescriptor} to send the value to the
+ * remote device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param value New value for this descriptor
+ * @return true if the locally stored value has been set, false if the
+ * requested value could not be stored locally.
+ */
+ public boolean setValue(byte[] value) {
+ mValue = value;
+ return true;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
new file mode 100644
index 0000000..91a1a94
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.IBluetoothStateChangeCallback;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Public API for the Bluetooth Gatt Profile server role.
+ *
+ * <p>This class provides Bluetooth Gatt server role functionality,
+ * allowing applications to create and advertise Bluetooth Smart services
+ * and characteristics.
+ *
+ * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
+ * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothGatt proxy object.
+ * @hide
+ */
+public final class BluetoothGattServer implements BluetoothProfile {
+ private static final String TAG = "BluetoothGattServer";
+ private static final boolean DBG = true;
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private BluetoothAdapter mAdapter;
+ private IBluetoothGatt mService;
+ private BluetoothGattServerCallback mCallback;
+ private int mServerIf;
+
+ private List<BluetoothGattService> mServices;
+
+ /**
+ * Bluetooth state change handlers
+ */
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (DBG) Log.d(TAG,"Unbinding service...");
+ synchronized (mConnection) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ }
+ }
+ } else {
+ synchronized (mConnection) {
+ try {
+ if (mService == null) {
+ if (DBG) Log.d(TAG,"Binding service...");
+ if (!mContext.bindService(new
+ Intent(IBluetoothGatt.class.getName()),
+ mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth GATT Service");
+ }
+ }
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Service binder handling
+ */
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ mService = IBluetoothGatt.Stub.asInterface(service);
+ ServiceListener serviceListner = mServiceListener;
+ if (serviceListner != null) {
+ serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER,
+ BluetoothGattServer.this);
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ mService = null;
+ ServiceListener serviceListner = mServiceListener;
+ if (serviceListner != null) {
+ serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER);
+ }
+ }
+ };
+
+ /**
+ * Bluetooth GATT interface callbacks
+ */
+ private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
+ new IBluetoothGattServerCallback.Stub() {
+ /**
+ * Application interface registered - app is ready to go
+ * @hide
+ */
+ public void onServerRegistered(int status, int serverIf) {
+ if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
+ + " serverIf=" + serverIf);
+ mServerIf = serverIf;
+ try {
+ mCallback.onAppRegistered(status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ * @hide
+ */
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+ try {
+ mCallback.onScanResult(mAdapter.getRemoteDevice(address),
+ rssi, advData);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Server connection state changed
+ * @hide
+ */
+ public void onServerConnectionState(int status, int serverIf,
+ boolean connected, String address) {
+ if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
+ + " serverIf=" + serverIf + " device=" + address);
+ try {
+ mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
+ connected ? BluetoothProfile.STATE_CONNECTED :
+ BluetoothProfile.STATE_DISCONNECTED);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Service has been added
+ * @hide
+ */
+ public void onServiceAdded(int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcId) {
+ UUID srvcUuid = srvcId.getUuid();
+ if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid
+ + "status=" + status);
+
+ BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
+ if (service == null) return;
+
+ try {
+ mCallback.onServiceAdded((int)status, service);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote client characteristic read request.
+ * @hide
+ */
+ public void onCharacteristicReadRequest(String address, int transId,
+ int offset, boolean isLong, int srvcType, int srvcInstId,
+ ParcelUuid srvcId, int charInstId, ParcelUuid charId) {
+ UUID srvcUuid = srvcId.getUuid();
+ UUID charUuid = charId.getUuid();
+ if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - "
+ + "service=" + srvcUuid + ", characteristic=" + charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(
+ charUuid);
+ if (characteristic == null) return;
+
+ try {
+ mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote client descriptor read request.
+ * @hide
+ */
+ public void onDescriptorReadRequest(String address, int transId,
+ int offset, boolean isLong, int srvcType, int srvcInstId,
+ ParcelUuid srvcId, int charInstId, ParcelUuid charId,
+ ParcelUuid descrId) {
+ UUID srvcUuid = srvcId.getUuid();
+ UUID charUuid = charId.getUuid();
+ UUID descrUuid = descrId.getUuid();
+ if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - "
+ + "service=" + srvcUuid + ", characteristic=" + charUuid
+ + "descriptor=" + descrUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
+ if (characteristic == null) return;
+
+ BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
+ if (descriptor == null) return;
+
+ try {
+ mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Remote client characteristic write request.
+ * @hide
+ */
+ public void onCharacteristicWriteRequest(String address, int transId,
+ int offset, int length, boolean isPrep, boolean needRsp,
+ int srvcType, int srvcInstId, ParcelUuid srvcId,
+ int charInstId, ParcelUuid charId, byte[] value) {
+ UUID srvcUuid = srvcId.getUuid();
+ UUID charUuid = charId.getUuid();
+ if (DBG) Log.d(TAG, "onCharacteristicWriteRequest() - "
+ + "service=" + srvcUuid + ", characteristic=" + charUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
+ if (characteristic == null) return;
+
+ try {
+ mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
+ isPrep, needRsp, offset, value);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+
+ }
+
+ /**
+ * Remote client descriptor write request.
+ * @hide
+ */
+ public void onDescriptorWriteRequest(String address, int transId,
+ int offset, int length, boolean isPrep, boolean needRsp,
+ int srvcType, int srvcInstId, ParcelUuid srvcId,
+ int charInstId, ParcelUuid charId, ParcelUuid descrId,
+ byte[] value) {
+ UUID srvcUuid = srvcId.getUuid();
+ UUID charUuid = charId.getUuid();
+ UUID descrUuid = descrId.getUuid();
+ if (DBG) Log.d(TAG, "onDescriptorWriteRequest() - "
+ + "service=" + srvcUuid + ", characteristic=" + charUuid
+ + "descriptor=" + descrUuid);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+
+ BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
+ if (service == null) return;
+
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
+ if (characteristic == null) return;
+
+ BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
+ if (descriptor == null) return;
+
+ try {
+ mCallback.onDescriptorWriteRequest(device, transId, descriptor,
+ isPrep, needRsp, offset, value);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * Execute pending writes.
+ * @hide
+ */
+ public void onExecuteWrite(String address, int transId,
+ boolean execWrite) {
+ if (DBG) Log.d(TAG, "onExecuteWrite() - "
+ + "device=" + address + ", transId=" + transId
+ + "execWrite=" + execWrite);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onExecuteWrite(device, transId, execWrite);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothGattServer proxy object.
+ */
+ /*package*/ BluetoothGattServer(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mServices = new ArrayList<BluetoothGattService>();
+
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
+ if (b != null) {
+ IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
+ }
+ } else {
+ Log.e(TAG, "Unable to get BluetoothManager interface.");
+ throw new RuntimeException("BluetoothManager inactive");
+ }
+
+ //Bind to the service only if the Bluetooth is ON
+ if(mAdapter.isEnabled()){
+ if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
+ }
+ }
+ }
+
+ /**
+ * Close the connection to the gatt service.
+ */
+ /*package*/ void close() {
+ if (DBG) Log.d(TAG, "close()");
+
+ unregisterApp();
+ mServiceListener = null;
+
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
+ if (b != null) {
+ IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
+ }
+ }
+
+ synchronized (mConnection) {
+ if (mService != null) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a service by UUID, instance and type.
+ * @hide
+ */
+ /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
+ for(BluetoothGattService svc : mServices) {
+ if (svc.getType() == type &&
+ svc.getInstanceId() == instanceId &&
+ svc.getUuid().equals(uuid)) {
+ return svc;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Register an application callback to start using Gatt.
+ *
+ * <p>This is an asynchronous call. The callback is used to notify
+ * success or failure if the function returns true.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback Gatt callback handler that will receive asynchronous
+ * callbacks.
+ * @return true, if application was successfully registered.
+ */
+ public boolean registerApp(BluetoothGattServerCallback callback) {
+ if (DBG) Log.d(TAG, "registerApp()");
+ if (mService == null) return false;
+
+ mCallback = callback;
+ UUID uuid = UUID.randomUUID();
+ if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
+
+ try {
+ mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Unregister the current application and callbacks.
+ */
+ public void unregisterApp() {
+ if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf);
+ if (mService == null || mServerIf == 0) return;
+
+ try {
+ mCallback = null;
+ mService.unregisterServer(mServerIf);
+ mServerIf = 0;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices.
+ *
+ * <p>Results of the scan are reported using the
+ * {@link BluetoothGattServerCallback#onScanResult} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return true, if the scan was started successfully
+ */
+ public boolean startScan() {
+ if (DBG) Log.d(TAG, "startScan()");
+ if (mService == null || mServerIf == 0) return false;
+
+ try {
+ mService.startScan(mServerIf, true);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices, looking for devices that
+ * advertise given services.
+ *
+ * <p>Devices which advertise all specified services are reported using the
+ * {@link BluetoothGattServerCallback#onScanResult} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param serviceUuids Array of services to look for
+ * @return true, if the scan was started successfully
+ */
+ public boolean startScan(UUID[] serviceUuids) {
+ if (DBG) Log.d(TAG, "startScan() - with UUIDs");
+ if (mService == null || mServerIf == 0) return false;
+
+ try {
+ ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
+ for(int i = 0; i != uuids.length; ++i) {
+ uuids[i] = new ParcelUuid(serviceUuids[i]);
+ }
+ mService.startScanWithUuids(mServerIf, true, uuids);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Stops an ongoing Bluetooth LE device scan.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ */
+ public void stopScan() {
+ if (DBG) Log.d(TAG, "stopScan()");
+ if (mService == null || mServerIf == 0) return;
+
+ try {
+ mService.stopScan(mServerIf, true);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Initiate a connection to a Bluetooth Gatt capable device.
+ *
+ * <p>The connection may not be established right away, but will be
+ * completed when the remote device is available. A
+ * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
+ * invoked when the connection state changes as a result of this function.
+ *
+ * <p>The autoConnect paramter determines whether to actively connect to
+ * the remote device, or rather passively scan and finalize the connection
+ * when the remote device is in range/available. Generally, the first ever
+ * connection to a device should be direct (autoConnect set to false) and
+ * subsequent connections to known devices should be invoked with the
+ * autoConnect parameter set to false.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device to connect to
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @return true, if the connection attempt was initiated successfully
+ */
+ public boolean connect(BluetoothDevice device, boolean autoConnect) {
+ if (DBG) Log.d(TAG, "connect: " + device.getAddress() + ", auto: " + autoConnect);
+ if (mService == null || mServerIf == 0) return false;
+
+ try {
+ mService.serverConnect(mServerIf, device.getAddress(),
+ autoConnect ? false : true); // autoConnect is inverse of "isDirect"
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnects an established connection, or cancels a connection attempt
+ * currently in progress.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device
+ */
+ public void cancelConnection(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
+ if (mService == null || mServerIf == 0) return;
+
+ try {
+ mService.serverDisconnect(mServerIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Send a response to a read or write request to a remote device.
+ *
+ * <p>This function must be invoked in when a remote read/write request
+ * is received by one of these callback methots:
+ *
+ * <ul>
+ * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
+ * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
+ * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
+ * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device The remote device to send this response to
+ * @param requestId The ID of the request that was received with the callback
+ * @param status The status of the request to be sent to the remote devices
+ * @param offset Value offset for partial read/write response
+ * @param value The value of the attribute that was read/written (optional)
+ */
+ public boolean sendResponse(BluetoothDevice device, int requestId,
+ int status, int offset, byte[] value) {
+ if (DBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
+ if (mService == null || mServerIf == 0) return false;
+
+ try {
+ mService.sendResponse(mServerIf, device.getAddress(), requestId,
+ status, offset, value);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Send a notification or indication that a local characteristic has been
+ * updated.
+ *
+ * <p>A notification or indication is sent to the remote device to signal
+ * that the characteristic has been updated. This function should be invoked
+ * for every client that requests notifications/indications by writing
+ * to the "Client Configuration" descriptor for the given characteristic.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device The remote device to receive the notification/indication
+ * @param characteristic The local characteristic that has been updated
+ * @param confirm true to request confirmation from the client (indication),
+ * false to send a notification
+ * @return true, if the notification has been triggered successfully
+ */
+ public boolean notifyCharacteristicChanged(BluetoothDevice device,
+ BluetoothGattCharacteristic characteristic, boolean confirm) {
+ if (DBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
+ if (mService == null || mServerIf == 0) return false;
+
+ BluetoothGattService service = characteristic.getService();
+ if (service == null) return false;
+
+ try {
+ mService.sendNotification(mServerIf, device.getAddress(),
+ service.getType(), service.getInstanceId(),
+ new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
+ new ParcelUuid(characteristic.getUuid()), confirm,
+ characteristic.getValue());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a service to the list of services to be advertised.
+ *
+ * <p>Once a service has been addded to the the list, the service and it's
+ * included characteristics will be advertised by the local device.
+ *
+ * <p>If the local device is already advertising services when this function
+ * is called, a service update notification will be sent to all clients.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param service Service to be added to the list of services advertised
+ * by this device.
+ * @return true, if the service has been added successfully
+ */
+ public boolean addService(BluetoothGattService service) {
+ if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
+ if (mService == null || mServerIf == 0) return false;
+
+ mServices.add(service);
+
+ try {
+ mService.beginServiceDeclaration(mServerIf, service.getType(),
+ service.getInstanceId(), service.getHandles(),
+ new ParcelUuid(service.getUuid()));
+
+ List<BluetoothGattService> includedServices = service.getIncludedServices();
+ for (BluetoothGattService includedService : includedServices) {
+ mService.addIncludedService(mServerIf,
+ includedService.getType(),
+ includedService.getInstanceId(),
+ new ParcelUuid(includedService.getUuid()));
+ }
+
+ List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
+ for (BluetoothGattCharacteristic characteristic : characteristics) {
+ int permission = ((characteristic.getKeySize() - 7) << 12)
+ + characteristic.getPermissions();
+ mService.addCharacteristic(mServerIf,
+ new ParcelUuid(characteristic.getUuid()),
+ characteristic.getProperties(), permission);
+
+ List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
+ for (BluetoothGattDescriptor descriptor: descriptors) {
+ mService.addDescriptor(mServerIf,
+ new ParcelUuid(descriptor.getUuid()),
+ descriptor.getPermissions());
+ }
+ }
+
+ mService.endServiceDeclaration(mServerIf);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Removes a service from the list of services to be advertised.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param service Service to beremoved.
+ * @return true, if the service has been removed
+ */
+ public boolean removeService(BluetoothGattService service) {
+ if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
+ if (mService == null || mServerIf == 0) return false;
+
+ BluetoothGattService intService = getService(service.getUuid(),
+ service.getInstanceId(), service.getType());
+ if (intService == null) return false;
+
+ try {
+ mService.removeService(mServerIf, service.getType(),
+ service.getInstanceId(), new ParcelUuid(service.getUuid()));
+ mServices.remove(intService);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove all services from the list of advertised services.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ */
+ public void clearServices() {
+ if (DBG) Log.d(TAG, "clearServices()");
+ if (mService == null || mServerIf == 0) return;
+
+ try {
+ mService.clearServices(mServerIf);
+ mServices.clear();
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Returns a list of GATT services offered bu this device.
+ *
+ * <p>An application must call {@link #addService} to add a serice to the
+ * list of services offered by this device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return List of services. Returns an empty list
+ * if no services have been added yet.
+ */
+ public List<BluetoothGattService> getServices() {
+ return mServices;
+ }
+
+ /**
+ * Returns a {@link BluetoothGattService} from the list of services offered
+ * by this device.
+ *
+ * <p>If multiple instances of the same service (as identified by UUID)
+ * exist, the first instance of the service is returned.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid UUID of the requested service
+ * @return BluetoothGattService if supported, or null if the requested
+ * service is not offered by this device.
+ */
+ public BluetoothGattService getService(UUID uuid) {
+ for (BluetoothGattService service : mServices) {
+ if (service.getUuid().equals(uuid)) {
+ return service;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the current connection state of the profile.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote bluetooth device.
+ * @return State of the profile connection. One of
+ * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) Log.d(TAG,"getConnectionState()");
+ if (mService == null) return STATE_DISCONNECTED;
+
+ List<BluetoothDevice> connectedDevices = getConnectedDevices();
+ for(BluetoothDevice connectedDevice : connectedDevices) {
+ if (device.equals(connectedDevice)) {
+ return STATE_CONNECTED;
+ }
+ }
+
+ return STATE_DISCONNECTED;
+ }
+
+ /**
+ * Get connected devices for the Gatt profile.
+ *
+ * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return List of devices. The list will be empty on error.
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) Log.d(TAG,"getConnectedDevices");
+
+ List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
+ if (mService == null) return connectedDevices;
+
+ try {
+ connectedDevices = mService.getDevicesMatchingConnectionStates(
+ new int[] { BluetoothProfile.STATE_CONNECTED });
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return connectedDevices;
+ }
+
+ /**
+ * Get a list of devices that match any of the given connection
+ * states.
+ *
+ * <p> If none of the devices match any of the given states,
+ * an empty list will be returned.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param states Array of states. States can be one of
+ * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
+ * @return List of devices. The list will be empty on error.
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
+
+ List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+ if (mService == null) return devices;
+
+ try {
+ devices = mService.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return devices;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
new file mode 100644
index 0000000..4f608ff
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+import android.util.Log;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
+ * @hide
+ */
+public abstract class BluetoothGattServerCallback {
+ /**
+ * Callback to inform change in registration state of the application.
+ *
+ * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
+ * was successfully registered.
+ */
+ public void onAppRegistered(int status) {
+ }
+
+ /**
+ * Callback reporting an LE device found during a device scan initiated
+ * by the {@link BluetoothGattServer#startScan} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device as reported by the
+ * Bluetooth hardware. 0 if no RSSI value is available.
+ * @param scanRecord The content of the advertisement record offered by
+ * the remote device.
+ */
+ public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
+ }
+
+ /**
+ * Callback indicating when a remote device has been connected or disconnected.
+ *
+ * @param device Remote device that has been connected or disconnected.
+ * @param status Status of the connect or disconnect operation.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothDevice device, int status,
+ int newState) {
+ }
+
+ /**
+ * Indicates whether a local service has been added successfully.
+ *
+ * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service
+ * was added successfully.
+ * @param service The service that has been added
+ */
+ public void onServiceAdded(int status, BluetoothGattService service) {
+ }
+
+ /**
+ * A remote client has requested to read a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param characteristic Characteristic to be read
+ */
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * A remote client has requested to write to a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param characteristic Characteristic to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the characteristic
+ */
+ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * A remote client has requested to read a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param descriptor Descriptor to be read
+ */
+ public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattDescriptor descriptor) {
+ }
+
+ /**
+ * A remote client has requested to write to a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param descriptor Descriptor to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the descriptor
+ */
+ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * Execute all pending write operations for this device.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operations
+ * @param requestId The Id of the request
+ * @param execute Whether the pending writes should be executed (true) or
+ * cancelled (false)
+ */
+ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
new file mode 100644
index 0000000..6a3ce66e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Represents a Bluetooth Gatt Service
+ * @hide
+ */
+public class BluetoothGattService {
+
+ /**
+ * Primary service
+ */
+ public static final int SERVICE_TYPE_PRIMARY = 0;
+
+ /**
+ * Secondary service (included by primary services)
+ */
+ public static final int SERVICE_TYPE_SECONDARY = 1;
+
+
+ /**
+ * The remote device his service is associated with.
+ * This applies to client applications only.
+ * @hide
+ */
+ protected BluetoothDevice mDevice;
+
+ /**
+ * The UUID of this service.
+ * @hide
+ */
+ protected UUID mUuid;
+
+ /**
+ * Instance ID for this service.
+ * @hide
+ */
+ protected int mInstanceId;
+
+ /**
+ * Handle counter override (for conformance testing).
+ * @hide
+ */
+ protected int mHandles = 0;
+
+ /**
+ * Service type (Primary/Secondary).
+ * @hide
+ */
+ protected int mServiceType;
+
+ /**
+ * List of characteristics included in this service.
+ */
+ protected List<BluetoothGattCharacteristic> mCharacteristics;
+
+ /**
+ * List of included services for this service.
+ */
+ protected List<BluetoothGattService> mIncludedServices;
+
+ /**
+ * Create a new BluetoothGattService.
+ * @hide
+ */
+ /*package*/ BluetoothGattService(UUID uuid, int serviceType) {
+ mDevice = null;
+ mUuid = uuid;
+ mInstanceId = 0;
+ mServiceType = serviceType;
+ mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
+ mIncludedServices = new ArrayList<BluetoothGattService>();
+ }
+
+ /**
+ * Create a new BluetoothGattService
+ * @hide
+ */
+ /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid,
+ int instanceId, int serviceType) {
+ mDevice = device;
+ mUuid = uuid;
+ mInstanceId = instanceId;
+ mServiceType = serviceType;
+ mCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
+ mIncludedServices = new ArrayList<BluetoothGattService>();
+ }
+
+ /**
+ * Returns the device associated with this service.
+ * @hide
+ */
+ /*package*/ BluetoothDevice getDevice() {
+ return mDevice;
+ }
+
+ /**
+ * Add a characteristic to this service.
+ * @hide
+ */
+ /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) {
+ mCharacteristics.add(characteristic);
+ }
+
+ /**
+ * Get characteristic by UUID and instanceId.
+ * @hide
+ */
+ /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) {
+ for(BluetoothGattCharacteristic characteristic : mCharacteristics) {
+ if (uuid.equals(characteristic.getUuid()) &&
+ mInstanceId == instanceId)
+ return characteristic;
+ }
+ return null;
+ }
+
+ /**
+ * Get the handle count override (conformance testing.
+ * @hide
+ */
+ /*package*/ int getHandles() {
+ return mHandles;
+ }
+
+ /**
+ * Add an included service to the internal map.
+ * @hide
+ */
+ /*package*/ void addIncludedService(BluetoothGattService includedService) {
+ mIncludedServices.add(includedService);
+ }
+
+ /**
+ * Returns the UUID of this service
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return UUID of this service
+ */
+ public UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns the instance ID for this service
+ *
+ * <p>If a remote device offers multiple services with the same UUID
+ * (ex. multiple battery services for different batteries), the instance
+ * ID is used to distuinguish services.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Instance ID of this service
+ */
+ public int getInstanceId() {
+ return mInstanceId;
+ }
+
+ /**
+ * Get the type of this service (primary/secondary)
+ * @hide
+ */
+ public int getType() {
+ return mServiceType;
+ }
+
+ /**
+ * Get the list of included Gatt services for this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return List of included services or empty list if no included services
+ * were discovered.
+ */
+ public List<BluetoothGattService> getIncludedServices() {
+ return mIncludedServices;
+ }
+
+ /**
+ * Returns a list of characteristics included in this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Characteristics included in this service
+ */
+ public List<BluetoothGattCharacteristic> getCharacteristics() {
+ return mCharacteristics;
+ }
+
+ /**
+ * Returns a characteristic with a given UUID out of the list of
+ * characteristics offered by this service.
+ *
+ * <p>This is a convenience function to allow access to a given characteristic
+ * without enumerating over the list returned by {@link #getCharacteristics}
+ * manually.
+ *
+ * <p>If a remote service offers multiple characteristics with the same
+ * UUID, the first instance of a characteristic with the given UUID
+ * is returned.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return Gatt characteristic object or null if no characteristic with the
+ * given UUID was found.
+ */
+ public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+ for(BluetoothGattCharacteristic characteristic : mCharacteristics) {
+ if (uuid.equals(characteristic.getUuid()))
+ return characteristic;
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
old mode 100755
new mode 100644
index 1920efa..9ee202a
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -88,6 +88,18 @@
public static final int PBAP = 6;
/**
+ * GATT
+ * @hide
+ */
+ static public final int GATT = 7;
+
+ /**
+ * GATT_SERVER
+ * @hide
+ */
+ static public final int GATT_SERVER = 8;
+
+ /**
* Default priority for devices that we try to auto-connect to and
* and allow incoming connections for the profile
* @hide
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
new file mode 100644
index 0000000..c89d132
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.os.ParcelUuid;
+
+import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothGattServerCallback;
+
+/**
+ * API for interacting with BLE / GATT
+ * @hide
+ */
+interface IBluetoothGatt {
+ List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+
+ void startScan(in int appIf, in boolean isServer);
+ void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids);
+ void stopScan(in int appIf, in boolean isServer);
+
+ void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
+ void unregisterClient(in int clientIf);
+ void clientConnect(in int clientIf, in String address, in boolean isDirect);
+ void clientDisconnect(in int clientIf, in String address);
+ void refreshDevice(in int clientIf, in String address);
+ void discoverServices(in int clientIf, in String address);
+ void readCharacteristic(in int clientIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in int authReq);
+ void writeCharacteristic(in int clientIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in int writeType, in int authReq, in byte[] value);
+ void readDescriptor(in int clientIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in ParcelUuid descrUuid, in int authReq);
+ void writeDescriptor(in int clientIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in ParcelUuid descrId, in int writeType,
+ in int authReq, in byte[] value);
+ void registerForNotification(in int clientIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in boolean enable);
+ void beginReliableWrite(in int clientIf, in String address);
+ void endReliableWrite(in int clientIf, in String address, in boolean execute);
+ void readRemoteRssi(in int clientIf, in String address);
+
+ void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
+ void unregisterServer(in int serverIf);
+ void serverConnect(in int servertIf, in String address, in boolean isDirect);
+ void serverDisconnect(in int serverIf, in String address);
+ void beginServiceDeclaration(in int serverIf, in int srvcType,
+ in int srvcInstanceId, in int minHandles,
+ in ParcelUuid srvcId);
+ void addIncludedService(in int serverIf, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId);
+ void addCharacteristic(in int serverIf, in ParcelUuid charId,
+ in int properties, in int permissions);
+ void addDescriptor(in int serverIf, in ParcelUuid descId,
+ in int permissions);
+ void endServiceDeclaration(in int serverIf);
+ void removeService(in int serverIf, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId);
+ void clearServices(in int serverIf);
+ void sendResponse(in int serverIf, in String address, in int requestId,
+ in int status, in int offset, in byte[] value);
+ void sendNotification(in int serverIf, in String address, in int srvcType,
+ in int srvcInstanceId, in ParcelUuid srvcId,
+ in int charInstanceId, in ParcelUuid charId,
+ in boolean confirm, in byte[] value);
+}
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
new file mode 100644
index 0000000..fc52172
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.os.ParcelUuid;
+
+
+/**
+ * Callback definitions for interacting with BLE / GATT
+ * @hide
+ */
+interface IBluetoothGattCallback {
+ void onClientRegistered(in int status, in int clientIf);
+ void onClientConnectionState(in int status, in int clientIf,
+ in boolean connected, in String address);
+ void onScanResult(in String address, in int rssi, in byte[] advData);
+ void onGetService(in String address, in int srvcType, in int srvcInstId,
+ in ParcelUuid srvcUuid);
+ void onGetIncludedService(in String address, in int srvcType, in int srvcInstId,
+ in ParcelUuid srvcUuid, in int inclSrvcType,
+ in int inclSrvcInstId, in ParcelUuid inclSrvcUuid);
+ void onGetCharacteristic(in String address, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in int charProps);
+ void onGetDescriptor(in String address, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in ParcelUuid descrUuid);
+ void onSearchComplete(in String address, in int status);
+ void onCharacteristicRead(in String address, in int status, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in byte[] value);
+ void onCharacteristicWrite(in String address, in int status, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid);
+ void onExecuteWrite(in String address, in int status);
+ void onDescriptorRead(in String address, in int status, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in ParcelUuid descrUuid, in byte[] value);
+ void onDescriptorWrite(in String address, in int status, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in ParcelUuid descrUuid);
+ void onNotify(in String address, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcUuid,
+ in int charInstId, in ParcelUuid charUuid,
+ in byte[] value);
+ void onReadRemoteRssi(in String address, in int rssi, in int status);
+}
diff --git a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
new file mode 100644
index 0000000..ae9bffc
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.os.ParcelUuid;
+
+
+/**
+ * Callback definitions for interacting with BLE / GATT
+ * @hide
+ */
+interface IBluetoothGattServerCallback {
+ void onServerRegistered(in int status, in int serverIf);
+ void onScanResult(in String address, in int rssi, in byte[] advData);
+ void onServerConnectionState(in int status, in int serverIf,
+ in boolean connected, in String address);
+ void onServiceAdded(in int status, in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcId);
+ void onCharacteristicReadRequest(in String address, in int transId,
+ in int offset, in boolean isLong,
+ in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcId,
+ in int charInstId, in ParcelUuid charId);
+ void onDescriptorReadRequest(in String address, in int transId,
+ in int offset, in boolean isLong,
+ in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcId,
+ in int charInstId, in ParcelUuid charId,
+ in ParcelUuid descrId);
+ void onCharacteristicWriteRequest(in String address, in int transId,
+ in int offset, in int length,
+ in boolean isPrep,
+ in boolean needRsp,
+ in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcId,
+ in int charInstId, in ParcelUuid charId,
+ in byte[] value);
+ void onDescriptorWriteRequest(in String address, in int transId,
+ in int offset, in int length,
+ in boolean isPrep,
+ in boolean needRsp,
+ in int srvcType,
+ in int srvcInstId, in ParcelUuid srvcId,
+ in int charInstId, in ParcelUuid charId,
+ in ParcelUuid descrId,
+ in byte[] value);
+ void onExecuteWrite(in String address, in int transId, in boolean execWrite);
+}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
new file mode 100644
index 0000000..c05abb2
--- /dev/null
+++ b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Mutable variant of a Bluetooth Gatt Characteristic
+ * @hide
+ */
+public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic {
+
+ /**
+ * Create a new MutableBluetoothGattCharacteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid The UUID for this characteristic
+ * @param properties Properties of this characteristic
+ * @param permissions Permissions for this characteristic
+ */
+ public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
+ super(null, uuid, 0, properties, permissions);
+ }
+
+ /**
+ * Adds a descriptor to this characteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param descriptor Descriptor to be added to this characteristic.
+ */
+ public void addDescriptor(MutableBluetoothGattDescriptor descriptor) {
+ mDescriptors.add(descriptor);
+ descriptor.setCharacteristic(this);
+ }
+
+ /**
+ * Set the desired key size.
+ * @hide
+ */
+ public void setKeySize(int keySize) {
+ mKeySize = keySize;
+ }
+
+ /**
+ * Sets the service associated with this device.
+ * @hide
+ */
+ /*package*/ void setService(BluetoothGattService service) {
+ mService = service;
+ }
+}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
new file mode 100644
index 0000000..e455392
--- /dev/null
+++ b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.util.UUID;
+
+/**
+ * Mutable variant of a Bluetooth Gatt Descriptor
+ * @hide
+ */
+public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor {
+
+ /**
+ * Create a new BluetoothGattDescriptor.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid The UUID for this descriptor
+ * @param permissions Permissions for this descriptor
+ */
+ public MutableBluetoothGattDescriptor(UUID uuid, int permissions) {
+ super(null, uuid, permissions);
+ }
+
+ /**
+ * Set the back-reference to the associated characteristic
+ * @hide
+ */
+ /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
+ mCharacteristic = characteristic;
+ }
+}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattService.java b/core/java/android/bluetooth/MutableBluetoothGattService.java
new file mode 100644
index 0000000..927f5ab
--- /dev/null
+++ b/core/java/android/bluetooth/MutableBluetoothGattService.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Represents a Bluetooth Gatt Service
+ * @hide
+ */
+public class MutableBluetoothGattService extends BluetoothGattService {
+
+ /**
+ * Create a new MutableBluetoothGattService.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid The UUID for this service
+ * @param serviceType The type of this service (primary/secondary)
+ */
+ public MutableBluetoothGattService(UUID uuid, int serviceType) {
+ super(uuid, serviceType);
+ }
+
+ /**
+ * Add an included service to this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param service The service to be added
+ * @return true, if the included service was added to the service
+ */
+ public boolean addService(BluetoothGattService service) {
+ mIncludedServices.add(service);
+ return true;
+ }
+
+ /**
+ * Add a characteristic to this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic The characteristics to be added
+ * @return true, if the characteristic was added to the service
+ */
+ public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) {
+ mCharacteristics.add(characteristic);
+ characteristic.setService(this);
+ return true;
+ }
+
+ /**
+ * Force the instance ID.
+ * This is needed for conformance testing only.
+ * @hide
+ */
+ public void setInstanceId(int instanceId) {
+ mInstanceId = instanceId;
+ }
+
+ /**
+ * Force the number of handles to reserve for this service.
+ * This is needed for conformance testing only.
+ * @hide
+ */
+ public void setHandles(int handles) {
+ mHandles = handles;
+ }
+}