OSDN Git Service

Initial version of BLE support for Bluedroid
authorGanesh Ganapathi Batta <ganeshg@broadcom.com>
Tue, 5 Feb 2013 23:28:33 +0000 (15:28 -0800)
committerMatthew Xie <mattx@google.com>
Thu, 28 Feb 2013 02:08:14 +0000 (18:08 -0800)
The API classes are hidden for now. Will unhide after API console
approval.
Change-Id: I8283dd562fd6189fdd15c866ef2efb8bbdbc4109

16 files changed:
Android.mk
core/java/android/bluetooth/BluetoothAdapter.java [changed mode: 0755->0644]
core/java/android/bluetooth/BluetoothGatt.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattCallback.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattCharacteristic.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattDescriptor.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattServer.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattServerCallback.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothGattService.java [new file with mode: 0644]
core/java/android/bluetooth/BluetoothProfile.java [changed mode: 0755->0644]
core/java/android/bluetooth/IBluetoothGatt.aidl [new file with mode: 0644]
core/java/android/bluetooth/IBluetoothGattCallback.aidl [new file with mode: 0644]
core/java/android/bluetooth/IBluetoothGattServerCallback.aidl [new file with mode: 0644]
core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java [new file with mode: 0644]
core/java/android/bluetooth/MutableBluetoothGattDescriptor.java [new file with mode: 0644]
core/java/android/bluetooth/MutableBluetoothGattService.java [new file with mode: 0644]

index b9cd7bf..5dc3523 100644 (file)
@@ -102,6 +102,9 @@ LOCAL_SRC_FILES += \
        core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
        core/java/android/bluetooth/IBluetoothPbap.aidl \
        core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
+       core/java/android/bluetooth/IBluetoothGatt.aidl \
+       core/java/android/bluetooth/IBluetoothGattCallback.aidl \
+       core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
        core/java/android/content/IClipboard.aidl \
        core/java/android/content/IContentService.aidl \
        core/java/android/content/IIntentReceiver.aidl \
old mode 100755 (executable)
new mode 100644 (file)
index 6367e16..1bbfb5d
@@ -1136,8 +1136,9 @@ public final class BluetoothAdapter {
     /**
      * 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 @@ public final class BluetoothAdapter {
         } 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 @@ public final class BluetoothAdapter {
                 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 (file)
index 0000000..1e12025
--- /dev/null
@@ -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 (file)
index 0000000..afa4539
--- /dev/null
@@ -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 (file)
index 0000000..18492ab
--- /dev/null
@@ -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 (file)
index 0000000..ba1f28a
--- /dev/null
@@ -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 (file)
index 0000000..91a1a94
--- /dev/null
@@ -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 (file)
index 0000000..4f608ff
--- /dev/null
@@ -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 (file)
index 0000000..6a3ce66
--- /dev/null
@@ -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;
+    }
+}
old mode 100755 (executable)
new mode 100644 (file)
index 1920efa..9ee202a
@@ -88,6 +88,18 @@ public interface BluetoothProfile {
     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 (file)
index 0000000..c89d132
--- /dev/null
@@ -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 (file)
index 0000000..fc52172
--- /dev/null
@@ -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 (file)
index 0000000..ae9bffc
--- /dev/null
@@ -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 (file)
index 0000000..c05abb2
--- /dev/null
@@ -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 (file)
index 0000000..e455392
--- /dev/null
@@ -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 (file)
index 0000000..927f5ab
--- /dev/null
@@ -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;
+    }
+}