summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGanesh Ganapathi Batta <ganeshg@broadcom.com>2013-02-05 15:38:27 -0800
committerMatthew Xie <mattx@google.com>2013-02-27 18:22:30 -0800
commit03b8386de26ba6500af2d66687bff9b01f2cbbd7 (patch)
tree340d93172e589ec6b504927914f19ceaad75cdd8 /src
parent8eb70f8bdf4e8c970810b3400aba8d08d14ce222 (diff)
downloadandroid_packages_apps_Bluetooth-03b8386de26ba6500af2d66687bff9b01f2cbbd7.tar.gz
android_packages_apps_Bluetooth-03b8386de26ba6500af2d66687bff9b01f2cbbd7.tar.bz2
android_packages_apps_Bluetooth-03b8386de26ba6500af2d66687bff9b01f2cbbd7.zip
Initial version of BLE support for Bluedroid
Change-Id: I9579b3074bc4bc59dd45f71c0937e8879196555e
Diffstat (limited to 'src')
-rw-r--r--[-rwxr-xr-x]src/com/android/bluetooth/btservice/AbstractionLayer.java2
-rw-r--r--[-rwxr-xr-x]src/com/android/bluetooth/btservice/BondStateMachine.java4
-rw-r--r--src/com/android/bluetooth/btservice/Config.java7
-rw-r--r--src/com/android/bluetooth/gatt/ContextMap.java251
-rw-r--r--src/com/android/bluetooth/gatt/GattDebugUtils.java210
-rw-r--r--src/com/android/bluetooth/gatt/GattService.java1676
-rw-r--r--src/com/android/bluetooth/gatt/GattServiceConfig.java26
-rw-r--r--src/com/android/bluetooth/gatt/HandleMap.java213
-rw-r--r--src/com/android/bluetooth/gatt/ScanClient.java41
-rw-r--r--src/com/android/bluetooth/gatt/SearchQueue.java92
-rw-r--r--src/com/android/bluetooth/gatt/ServiceDeclaration.java108
11 files changed, 2628 insertions, 2 deletions
diff --git a/src/com/android/bluetooth/btservice/AbstractionLayer.java b/src/com/android/bluetooth/btservice/AbstractionLayer.java
index 24bcb5dbd..4e0ebd0ec 100755..100644
--- a/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -76,4 +76,6 @@ final public class AbstractionLayer {
public static final int BT_STATUS_UNHANDLED = 8;
public static final int BT_STATUS_AUTH_FAILURE = 9;
public static final int BT_STATUS_RMT_DEV_DOWN = 10;
+ public static final int BT_STATUS_AUTH_REJECTED =11;
+ public static final int BT_STATUS_AUTH_TIMEOUT = 12;
}
diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java
index d80ad7cc5..0b6478b72 100755..100644
--- a/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -347,6 +347,10 @@ final class BondStateMachine extends StateMachine {
return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE)
return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
+ else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED)
+ return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
+ else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT)
+ return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
/* default */
return BluetoothDevice.UNBOND_REASON_REMOVED;
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
index d8d11948b..a2b2ff393 100644
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -28,6 +28,7 @@ import com.android.bluetooth.hdp.HealthService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hid.HidService;
import com.android.bluetooth.pan.PanService;
+import com.android.bluetooth.gatt.GattService;
public class Config {
private static final String TAG = "AdapterServiceConfig";
@@ -42,7 +43,8 @@ public class Config {
A2dpService.class,
HidService.class,
HealthService.class,
- PanService.class
+ PanService.class,
+ GattService.class
};
/**
* Resource flag to indicate whether profile is supported or not.
@@ -52,7 +54,8 @@ public class Config {
R.bool.profile_supported_a2dp,
R.bool.profile_supported_hid,
R.bool.profile_supported_hdp,
- R.bool.profile_supported_pan
+ R.bool.profile_supported_pan,
+ R.bool.profile_supported_gatt
};
private static Class[] SUPPORTED_PROFILES = new Class[0];
diff --git a/src/com/android/bluetooth/gatt/ContextMap.java b/src/com/android/bluetooth/gatt/ContextMap.java
new file mode 100644
index 000000000..ca265b116
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/ContextMap.java
@@ -0,0 +1,251 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that keeps track of registered GATT applications.
+ * This class manages application callbacks and keeps track of GATT connections.
+ * @hide
+ */
+/*package*/ class ContextMap<T> {
+ private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap";
+
+ /**
+ * Connection class helps map connection IDs to device addresses.
+ */
+ class Connection {
+ int connId;
+ String address;
+ int appId;
+
+ Connection(int connId, String address,int appId) {
+ this.connId = connId;
+ this.address = address;
+ this.appId = appId;
+ }
+ }
+
+ /**
+ * Application entry mapping UUIDs to appIDs and callbacks.
+ */
+ class App {
+ /** The UUID of the application */
+ UUID uuid;
+
+ /** The id of the application */
+ int id;
+
+ /** Application callbacks */
+ T callback;
+
+ /**
+ * Creates a new app context.
+ */
+ App(UUID uuid, T callback) {
+ this.uuid = uuid;
+ this.callback = callback;
+ }
+ }
+
+ /** Our internal application list */
+ List<App> mApps = new ArrayList<App>();
+
+ /** Internal list of connected devices **/
+ Set<Connection> mConnections = new HashSet<Connection>();
+
+ /**
+ * Add an entry to the application context list.
+ */
+ void add(UUID uuid, T callback) {
+ mApps.add(new App(uuid, callback));
+ }
+
+ /**
+ * Remove the context for a given application ID.
+ */
+ void remove(int id) {
+ Iterator<App> i = mApps.iterator();
+ while(i.hasNext()) {
+ App entry = i.next();
+ if (entry.id == id) {
+ i.remove();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Add a new connection for a given application ID.
+ */
+ void addConnection(int id, int connId, String address) {
+ App entry = getById(id);
+ if (entry != null){
+ mConnections.add(new Connection(connId, address, id));
+ }
+ }
+
+ /**
+ * Remove a connection with the given ID.
+ */
+ void removeConnection(int id, int connId) {
+ Iterator<Connection> i = mConnections.iterator();
+ while(i.hasNext()) {
+ Connection connection = i.next();
+ if (connection.connId == connId) {
+ i.remove();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get an application context by ID.
+ */
+ App getById(int id) {
+ Iterator<App> i = mApps.iterator();
+ while(i.hasNext()) {
+ App entry = i.next();
+ if (entry.id == id) return entry;
+ }
+ Log.e(TAG, "Context not found for ID " + id);
+ return null;
+ }
+
+ /**
+ * Get an application context by UUID.
+ */
+ App getByUuid(UUID uuid) {
+ Iterator<App> i = mApps.iterator();
+ while(i.hasNext()) {
+ App entry = i.next();
+ if (entry.uuid.equals(uuid)) return entry;
+ }
+ Log.e(TAG, "Context not found for UUID " + uuid);
+ return null;
+ }
+
+ /**
+ * Get the device addresses for all connected devices
+ */
+ Set<String> getConnectedDevices() {
+ Set<String> addresses = new HashSet<String>();
+ Iterator<Connection> i = mConnections.iterator();
+ while(i.hasNext()) {
+ Connection connection = i.next();
+ addresses.add(connection.address);
+ }
+ return addresses;
+ }
+
+ /**
+ * Get an application context by a connection ID.
+ */
+ App getByConnId(int connId) {
+ Iterator<Connection> ii = mConnections.iterator();
+ while(ii.hasNext()) {
+ Connection connection = ii.next();
+ if (connection.connId == connId){
+ return getById(connection.appId);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a connection ID for a given device address.
+ */
+ Integer connIdByAddress(int id, String address) {
+ App entry = getById(id);
+ if (entry == null) return null;
+
+ Iterator<Connection> i = mConnections.iterator();
+ while(i.hasNext()) {
+ Connection connection = i.next();
+ if (connection.address.equals(address) && connection.appId == id)
+ return connection.connId;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the device address for a given connection ID.
+ */
+ String addressByConnId(int connId) {
+ Iterator<Connection> i = mConnections.iterator();
+ while(i.hasNext()) {
+ Connection connection = i.next();
+ if (connection.connId == connId) return connection.address;
+ }
+ return null;
+ }
+
+ List<Connection> getConnectionByApp(int appId) {
+ List<Connection> currentConnections = new ArrayList<Connection>();
+ Iterator<Connection> i = mConnections.iterator();
+ while(i.hasNext()) {
+ Connection connection = i.next();
+ if (connection.appId == appId)
+ currentConnections.add(connection);
+ }
+ return currentConnections;
+ }
+
+ /**
+ * Erases all application context entries.
+ */
+ void clear() {
+ mApps.clear();
+ mConnections.clear();
+ }
+
+ /**
+ * Logs debug information.
+ */
+ void dump() {
+ StringBuilder b = new StringBuilder();
+ b.append( "-------------- GATT Context Map ----------------");
+ b.append("\nEntries: " + mApps.size());
+
+ Iterator<App> i = mApps.iterator();
+ while(i.hasNext()) {
+ App entry = i.next();
+ List<Connection> connections = getConnectionByApp(entry.id);
+
+ b.append("\n\nApplication Id: " + entry.id);
+ b.append("\nUUID: " + entry.uuid);
+ b.append("\nConnections: " + connections.size());
+
+ Iterator<Connection> ii = connections.iterator();
+ while(ii.hasNext()) {
+ Connection connection = ii.next();
+ b.append("\n " + connection.connId + ": " + connection.address);
+ }
+ }
+
+ b.append("\n------------------------------------------------");
+ Log.d(TAG, b.toString());
+ }
+}
diff --git a/src/com/android/bluetooth/gatt/GattDebugUtils.java b/src/com/android/bluetooth/gatt/GattDebugUtils.java
new file mode 100644
index 000000000..3911e6cda
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/GattDebugUtils.java
@@ -0,0 +1,210 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import java.util.UUID;
+
+/**
+ * Helper class containing useful tools for GATT service debugging.
+ */
+/*package*/ class GattDebugUtils {
+ private static final String TAG = GattServiceConfig.TAG_PREFIX + "DebugUtils";
+ private static final boolean DEBUG_ADMIN = GattServiceConfig.DEBUG_ADMIN;
+
+ private static final String ACTION_DEBUG_DUMP_CLIENTMAP =
+ "android.bluetooth.action.DEBUG_DUMP_CLIENTMAP";
+ private static final String ACTION_DEBUG_DUMP_SERVERMAP =
+ "android.bluetooth.action.DEBUG_DUMP_SERVERMAP";
+ private static final String ACTION_DEBUG_DUMP_HANDLEMAP =
+ "android.bluetooth.action.DEBUG_DUMP_HANDLEMAP";
+
+ private static final String ACTION_GATT_PAIRING_CONFIG =
+ "android.bluetooth.action.GATT_PAIRING_CONFIG";
+
+ private static final String ACTION_GATT_TEST_USAGE =
+ "android.bluetooth.action.GATT_TEST_USAGE";
+ private static final String ACTION_GATT_TEST_ENABLE =
+ "android.bluetooth.action.GATT_TEST_ENABLE";
+ private static final String ACTION_GATT_TEST_CONNECT =
+ "android.bluetooth.action.GATT_TEST_CONNECT";
+ private static final String ACTION_GATT_TEST_DISCONNECT =
+ "android.bluetooth.action.GATT_TEST_DISCONNECT";
+ private static final String ACTION_GATT_TEST_DISCOVER =
+ "android.bluetooth.action.GATT_TEST_DISCOVER";
+
+ private static final String EXTRA_ENABLE = "enable";
+ private static final String EXTRA_ADDRESS = "address";
+ private static final String EXTRA_UUID = "uuid";
+ private static final String EXTRA_TYPE = "type";
+ private static final String EXTRA_SHANDLE = "start";
+ private static final String EXTRA_EHANDLE = "end";
+ private static final String EXTRA_AUTH_REQ = "auth_req";
+ private static final String EXTRA_IO_CAP = "io_cap";
+ private static final String EXTRA_INIT_KEY = "init_key";
+ private static final String EXTRA_RESP_KEY = "resp_key";
+ private static final String EXTRA_MAX_KEY = "max_key";
+
+ /**
+ * Handles intents passed in via GattService.onStartCommand().
+ * This allows sending debug actions to the running service.
+ * To trigger a debug action, invoke the following shell command:
+ *
+ * adb shell am startservice -a <action> <component>
+ *
+ * Where <action> represents one of the ACTION_* constants defines above
+ * and <component> identifies the GATT service.
+ *
+ * For example:
+ * import com.android.bluetooth.gatt.GattService;
+ */
+ static boolean handleDebugAction(GattService svc, Intent intent) {
+ if (!DEBUG_ADMIN) return false;
+
+ String action = intent.getAction();
+ Log.d(TAG, "handleDebugAction() action=" + action);
+
+ /*
+ * Debug log functinos
+ */
+
+ if (ACTION_DEBUG_DUMP_CLIENTMAP.equals(action)) {
+ svc.mClientMap.dump();
+
+ } else if (ACTION_DEBUG_DUMP_SERVERMAP.equals(action)) {
+ svc.mServerMap.dump();
+
+ } else if (ACTION_DEBUG_DUMP_HANDLEMAP.equals(action)) {
+ svc.mHandleMap.dump();
+
+ /*
+ * PTS test commands
+ */
+
+ } else if (ACTION_GATT_TEST_USAGE.equals(action)) {
+ logUsageInfo();
+
+ } else if (ACTION_GATT_TEST_ENABLE.equals(action)) {
+ boolean bEnable = intent.getBooleanExtra(EXTRA_ENABLE, true);
+ svc.gattTestCommand( 0x01, null,null, bEnable ? 1 : 0, 0,0,0,0);
+
+ } else if (ACTION_GATT_TEST_CONNECT.equals(action)) {
+ String address = intent.getStringExtra(EXTRA_ADDRESS);
+ int type = intent.getIntExtra(EXTRA_TYPE, 2 /* LE device */);
+ svc.gattTestCommand( 0x02, null, address, type, 0,0,0,0);
+
+ } else if (ACTION_GATT_TEST_DISCONNECT.equals(action)) {
+ svc.gattTestCommand( 0x03, null, null, 0,0,0,0,0);
+
+ } else if (ACTION_GATT_TEST_DISCOVER.equals(action)) {
+ UUID uuid = getUuidExtra(intent);
+ int type = intent.getIntExtra(EXTRA_TYPE, 1 /* All services */);
+ int shdl = getHandleExtra(intent, EXTRA_SHANDLE, 1);
+ int ehdl = getHandleExtra(intent, EXTRA_EHANDLE, 0xFFFF);
+ svc.gattTestCommand( 0x04, uuid, null, type, shdl, ehdl, 0,0);
+
+ } else if (ACTION_GATT_PAIRING_CONFIG.equals(action)) {
+ int auth_req = intent.getIntExtra(EXTRA_AUTH_REQ, 5);
+ int io_cap = intent.getIntExtra(EXTRA_IO_CAP, 4);
+ int init_key = intent.getIntExtra(EXTRA_INIT_KEY, 7);
+ int resp_key = intent.getIntExtra(EXTRA_RESP_KEY, 7);
+ int max_key = intent.getIntExtra(EXTRA_MAX_KEY, 16);
+ svc.gattTestCommand( 0xF0, null, null, auth_req, io_cap, init_key,
+ resp_key, max_key);
+
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempts to retrieve an extra from an intent first as hex value,
+ * then as an ineger.
+ * @hide
+ */
+ static private int getHandleExtra(Intent intent, String extra, int default_value) {
+ Bundle extras = intent.getExtras();
+ Object uuid = extras != null ? extras.get(extra) : null;
+ if (uuid != null && uuid.getClass().getName().equals("java.lang.String")) {
+ try
+ {
+ return Integer.parseInt(extras.getString(extra), 16);
+ } catch (NumberFormatException e) {
+ return default_value;
+ }
+ }
+
+ return intent.getIntExtra(extra, default_value);
+ }
+
+ /**
+ * Retrieves the EXTRA_UUID parameter.
+ * If a string of length 4 is detected, a 16-bit hex UUID is assumed and
+ * the default Bluetooth UUID is appended.
+ * @hide
+ */
+ static private UUID getUuidExtra(Intent intent) {
+ String uuidStr = intent.getStringExtra(EXTRA_UUID);
+ if (uuidStr != null && uuidStr.length() == 4) {
+ uuidStr = String.format("0000%s-0000-1000-8000-00805f9b34fb", uuidStr);
+ }
+ return (uuidStr != null) ? UUID.fromString(uuidStr) : null;
+ }
+
+ /**
+ * Log usage information.
+ * @hide
+ */
+ static private void logUsageInfo() {
+ StringBuilder b = new StringBuilder();
+ b.append( "------------ GATT TEST ACTIONS ----------------");
+ b.append("\nGATT_TEST_ENABLE");
+ b.append("\n [--ez enable <bool>] Enable or disable,");
+ b.append("\n defaults to true (enable).\n");
+ b.append("\nGATT_TEST_CONNECT");
+ b.append("\n --es address <bda>");
+ b.append("\n [--ei type <type>] Default is 2 (LE Only)\n");
+ b.append("\nGATT_TEST_DISCONNECT\n");
+ b.append("\nGATT_TEST_DISCOVER");
+ b.append("\n [--ei type <type>] Possible values:");
+ b.append("\n 1 = Discover all services (default)");
+ b.append("\n 2 = Discover services by UUID");
+ b.append("\n 3 = Discover included services");
+ b.append("\n 4 = Discover characteristics");
+ b.append("\n 5 = Discover descriptors\n");
+ b.append("\n [--es uuid <uuid>] Optional; Can be either full 128-bit");
+ b.append("\n UUID hex string, or 4 hex characters");
+ b.append("\n for 16-bit UUIDs.\n");
+ b.append("\n [--ei start <hdl>] Start of handle range (default 1)");
+ b.append("\n [--ei end <hdl>] End of handle range (default 65355)");
+ b.append("\n or");
+ b.append("\n [--es start <hdl>] Start of handle range (hex format)");
+ b.append("\n [--es end <hdl>] End of handle range (hex format)\n");
+ b.append("\nGATT_PAIRING_CONFIG");
+ b.append("\n [--ei auth_req] Authentication flag (default 5)");
+ b.append("\n [--ei io_cap] IO capabilities (default 4)");
+ b.append("\n [--ei init_key] Initial key size (default 7)");
+ b.append("\n [--ei resp_key] Response key size (default 7)");
+ b.append("\n [--ei max_key] Maximum key size (default 16)");
+ b.append("\n------------------------------------------------");
+ Log.i(TAG, b.toString());
+ }
+}
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
new file mode 100644
index 000000000..56db06dae
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -0,0 +1,1676 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Intent;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothGattServerCallback;
+
+/**
+ * Provides Bluetooth Gatt profile, as a service in
+ * the Bluetooth application.
+ * @hide
+ */
+public class GattService extends ProfileService {
+ private static final boolean DBG = GattServiceConfig.DBG;
+ private static final String TAG = GattServiceConfig.TAG_PREFIX + "GattService";
+ BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ /**
+ * Search queue to serialize remote onbject inspection.
+ */
+ SearchQueue mSearchQueue = new SearchQueue();
+
+ /**
+ * List of our registered clients.
+ */
+
+ class ClientMap extends ContextMap<IBluetoothGattCallback> {}
+ ClientMap mClientMap = new ClientMap();
+
+ /**
+ * List of our registered server apps.
+ */
+ class ServerMap extends ContextMap<IBluetoothGattServerCallback> {}
+ ServerMap mServerMap = new ServerMap();
+
+ /**
+ * Server handle map.
+ */
+ HandleMap mHandleMap = new HandleMap();
+
+ /**
+ * Pending service declaration queue
+ */
+ private List<ServiceDeclaration> mServiceDeclarations = new ArrayList<ServiceDeclaration>();
+
+ private ServiceDeclaration addDeclaration() {
+ synchronized (mServiceDeclarations) {
+ mServiceDeclarations.add(new ServiceDeclaration());
+ }
+ return getActiveDeclaration();
+ }
+
+ private ServiceDeclaration getActiveDeclaration() {
+ synchronized (mServiceDeclarations) {
+ if (mServiceDeclarations.size() > 0)
+ return mServiceDeclarations.get(mServiceDeclarations.size() - 1);
+ }
+ return null;
+ }
+
+ private ServiceDeclaration getPendingDeclaration() {
+ synchronized (mServiceDeclarations) {
+ if (mServiceDeclarations.size() > 0)
+ return mServiceDeclarations.get(0);
+ }
+ return null;
+ }
+
+ private void removePendingDeclaration() {
+ synchronized (mServiceDeclarations) {
+ if (mServiceDeclarations.size() > 0)
+ mServiceDeclarations.remove(0);
+ }
+ }
+
+ /**
+ * List of clients intereste in scan results.
+ */
+ private List<ScanClient> mScanQueue = new ArrayList<ScanClient>();
+
+ private ScanClient getScanClient(int appIf, boolean isServer) {
+ for(ScanClient client : mScanQueue) {
+ if (client.appIf == appIf && client.isServer == isServer) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+ private void removeScanClient(int appIf, boolean isServer) {
+ for(ScanClient client : mScanQueue) {
+ if (client.appIf == appIf && client.isServer == isServer) {
+ mScanQueue.remove(client);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reliable write queue
+ */
+ private Set<String> mReliableQueue = new HashSet<String>();
+
+ static {
+ classInitNative();
+ }
+
+ protected String getName() {
+ return TAG;
+ }
+
+ protected IProfileServiceBinder initBinder() {
+ return new BluetoothGattBinder(this);
+ }
+
+ protected boolean start() {
+ if (DBG) Log.d(TAG, "start()");
+ initializeNative();
+ return true;
+ }
+
+ protected boolean stop() {
+ if (DBG) Log.d(TAG, "stop()");
+ mClientMap.clear();
+ mServerMap.clear();
+ mSearchQueue.clear();
+ mScanQueue.clear();
+ mHandleMap.clear();
+ mServiceDeclarations.clear();
+ mReliableQueue.clear();
+ return true;
+ }
+
+ protected boolean cleanup() {
+ if (DBG) Log.d(TAG, "cleanup()");
+ cleanupNative();
+ return true;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (GattDebugUtils.handleDebugAction(this, intent)) {
+ return Service.START_NOT_STICKY;
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ /**
+ * Handlers for incoming service calls
+ */
+ private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder {
+ private GattService mService;
+
+ public BluetoothGattBinder(GattService svc) {
+ mService = svc;
+ }
+
+ public boolean cleanup() {
+ mService = null;
+ return true;
+ }
+
+ private GattService getService() {
+ if (mService != null && mService.isAvailable()) return mService;
+ Log.e(TAG, "getService() - Service requested, but not available!");
+ return null;
+ }
+
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ GattService service = getService();
+ if (service == null) return new ArrayList<BluetoothDevice>();
+ return service.getDevicesMatchingConnectionStates(states);
+ }
+
+ public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
+ GattService service = getService();
+ if (service == null) return;
+ service.registerClient(uuid.getUuid(), callback);
+ }
+
+ public void unregisterClient(int clientIf) {
+ GattService service = getService();
+ if (service == null) return;
+ service.unregisterClient(clientIf);
+ }
+
+ public void startScan(int appIf, boolean isServer) {
+ GattService service = getService();
+ if (service == null) return;
+ service.startScan(appIf, isServer);
+ }
+
+ public void startScanWithUuids(int appIf, boolean isServer, ParcelUuid[] ids) {
+ GattService service = getService();
+ if (service == null) return;
+ UUID[] uuids = new UUID[ids.length];
+ for(int i = 0; i != ids.length; ++i) {
+ uuids[i] = ids[i].getUuid();
+ }
+ service.startScanWithUuids(appIf, isServer, uuids);
+ }
+
+ public void stopScan(int appIf, boolean isServer) {
+ GattService service = getService();
+ if (service == null) return;
+ service.stopScan(appIf, isServer);
+ }
+
+ public void clientConnect(int clientIf, String address, boolean isDirect) {
+ GattService service = getService();
+ if (service == null) return;
+ service.clientConnect(clientIf, address, isDirect);
+ }
+
+ public void clientDisconnect(int clientIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.clientDisconnect(clientIf, address);
+ }
+
+ public void refreshDevice(int clientIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.refreshDevice(clientIf, address);
+ }
+
+ public void discoverServices(int clientIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.discoverServices(clientIf, address);
+ }
+
+ public void readCharacteristic(int clientIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ int authReq) {
+ GattService service = getService();
+ if (service == null) return;
+ service.readCharacteristic(clientIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId,
+ charId.getUuid(), authReq);
+ }
+
+ public void writeCharacteristic(int clientIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ int writeType, int authReq, byte[] value) {
+ GattService service = getService();
+ if (service == null) return;
+ service.writeCharacteristic(clientIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId,
+ charId.getUuid(), writeType, authReq,
+ value);
+ }
+
+ public void readDescriptor(int clientIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ ParcelUuid descrId, int authReq) {
+ GattService service = getService();
+ if (service == null) return;
+ service.readDescriptor(clientIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId,
+ charId.getUuid(), descrId.getUuid(),
+ authReq);
+ }
+
+ public void writeDescriptor(int clientIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ ParcelUuid descrId, int writeType,
+ int authReq, byte[] value) {
+ GattService service = getService();
+ if (service == null) return;
+ service.writeDescriptor(clientIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId,
+ charId.getUuid(), descrId.getUuid(),
+ writeType, authReq, value);
+ }
+
+ public void beginReliableWrite(int clientIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.beginReliableWrite(clientIf, address);
+ }
+
+ public void endReliableWrite(int clientIf, String address, boolean execute) {
+ GattService service = getService();
+ if (service == null) return;
+ service.endReliableWrite(clientIf, address, execute);
+ }
+
+ public void registerForNotification(int clientIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ boolean enable) {
+ GattService service = getService();
+ if (service == null) return;
+ service.registerForNotification(clientIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId,
+ charId.getUuid(), enable);
+ }
+
+ public void readRemoteRssi(int clientIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.readRemoteRssi(clientIf, address);
+ }
+
+ public void registerServer(ParcelUuid uuid, IBluetoothGattServerCallback callback) {
+ GattService service = getService();
+ if (service == null) return;
+ service.registerServer(uuid.getUuid(), callback);
+ }
+
+ public void unregisterServer(int serverIf) {
+ GattService service = getService();
+ if (service == null) return;
+ service.unregisterServer(serverIf);
+ }
+
+ public void serverConnect(int serverIf, String address, boolean isDirect) {
+ GattService service = getService();
+ if (service == null) return;
+ service.serverConnect(serverIf, address, isDirect);
+ }
+
+ public void serverDisconnect(int serverIf, String address) {
+ GattService service = getService();
+ if (service == null) return;
+ service.serverDisconnect(serverIf, address);
+ }
+
+ public void beginServiceDeclaration(int serverIf, int srvcType,
+ int srvcInstanceId, int minHandles,
+ ParcelUuid srvcId) {
+ GattService service = getService();
+ if (service == null) return;
+ service.beginServiceDeclaration(serverIf, srvcType, srvcInstanceId,
+ minHandles, srvcId.getUuid());
+ }
+
+ public void addIncludedService(int serverIf, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId) {
+ GattService service = getService();
+ if (service == null) return;
+ service.addIncludedService(serverIf, srvcType, srvcInstanceId,
+ srvcId.getUuid());
+ }
+
+ public void addCharacteristic(int serverIf, ParcelUuid charId,
+ int properties, int permissions) {
+ GattService service = getService();
+ if (service == null) return;
+ service.addCharacteristic(serverIf, charId.getUuid(), properties,
+ permissions);
+ }
+
+ public void addDescriptor(int serverIf, ParcelUuid descId,
+ int permissions) {
+ GattService service = getService();
+ if (service == null) return;
+ service.addDescriptor(serverIf, descId.getUuid(), permissions);
+ }
+
+ public void endServiceDeclaration(int serverIf) {
+ GattService service = getService();
+ if (service == null) return;
+ service.endServiceDeclaration(serverIf);
+ }
+
+ public void removeService(int serverIf, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId) {
+ GattService service = getService();
+ if (service == null) return;
+ service.removeService(serverIf, srvcType, srvcInstanceId,
+ srvcId.getUuid());
+ }
+
+ public void clearServices(int serverIf) {
+ GattService service = getService();
+ if (service == null) return;
+ service.clearServices(serverIf);
+ }
+
+ public void sendResponse(int serverIf, String address, int requestId,
+ int status, int offset, byte[] value) {
+ GattService service = getService();
+ if (service == null) return;
+ service.sendResponse(serverIf, address, requestId, status, offset, value);
+ }
+
+ public void sendNotification(int serverIf, String address, int srvcType,
+ int srvcInstanceId, ParcelUuid srvcId,
+ int charInstanceId, ParcelUuid charId,
+ boolean confirm, byte[] value) {
+ GattService service = getService();
+ if (service == null) return;
+ service.sendNotification(serverIf, address, srvcType, srvcInstanceId,
+ srvcId.getUuid(), charInstanceId, charId.getUuid(), confirm, value);
+ }
+
+ };
+
+ /**************************************************************************
+ * Callback functions - CLIENT
+ *************************************************************************/
+
+ void onScanResult(String address, int rssi, byte[] adv_data) {
+ if (DBG) Log.d(TAG, "onScanResult() - address=" + address
+ + ", rssi=" + rssi);
+
+ List<UUID> remoteUuids = parseUuids(adv_data);
+ for (ScanClient client : mScanQueue) {
+ if (client.uuids.length > 0) {
+ int matches = 0;
+ for (UUID search : client.uuids) {
+ for (UUID remote: remoteUuids) {
+ if (remote.equals(search)) {
+ ++matches;
+ break; // Only count 1st match in case of duplicates
+ }
+ }
+ }
+
+ if (matches < client.uuids.length) continue;
+ }
+
+ if (!client.isServer) {
+ ClientMap.App app = mClientMap.getById(client.appIf);
+ if (app != null) {
+ try {
+ app.callback.onScanResult(address, rssi, adv_data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception: " + e);
+ mClientMap.remove(client.appIf);
+ mScanQueue.remove(client);
+ }
+ }
+ } else {
+ ServerMap.App app = mServerMap.getById(client.appIf);
+ if (app != null) {
+ try {
+ app.callback.onScanResult(address, rssi, adv_data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception: " + e);
+ mServerMap.remove(client.appIf);
+ mScanQueue.remove(client);
+ }
+ }
+ }
+ }
+ }
+
+ void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
+ throws RemoteException {
+ UUID uuid = new UUID(uuidMsb, uuidLsb);
+ if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
+ ClientMap.App app = mClientMap.getByUuid(uuid);
+ if (app != null) {
+ app.id = clientIf;
+ app.callback.onClientRegistered(status, clientIf);
+ }
+ }
+
+ void onConnected(int clientIf, int connId, int status, String address)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onConnected() - clientIf=" + clientIf
+ + ", connId=" + connId + ", address=" + address);
+
+ if (status == 0) mClientMap.addConnection(clientIf, connId, address);
+ ClientMap.App app = mClientMap.getById(clientIf);
+ if (app != null) {
+ app.callback.onClientConnectionState(status, clientIf, true, address);
+ }
+ }
+
+ void onDisconnected(int clientIf, int connId, int status, String address)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onDisconnected() - clientIf=" + clientIf
+ + ", connId=" + connId + ", address=" + address);
+
+ mClientMap.removeConnection(clientIf, connId);
+ mSearchQueue.removeConnId(connId);
+ ClientMap.App app = mClientMap.getById(clientIf);
+ if (app != null) {
+ app.callback.onClientConnectionState(status, clientIf, false, address);
+ }
+ }
+
+ void onSearchCompleted(int connId, int status) throws RemoteException {
+ if (DBG) Log.d(TAG, "onSearchCompleted() - connId=" + connId+ ", status=" + status);
+ // We got all services, now let's explore characteristics...
+ continueSearch(connId, status);
+ }
+
+ void onSearchResult(int connId, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb)
+ throws RemoteException {
+ UUID uuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onSearchResult() - address=" + address + ", uuid=" + uuid);
+
+ mSearchQueue.add(connId, srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onGetService(address, srvcType, srvcInstId,
+ new ParcelUuid(uuid));
+ }
+ }
+
+ void onGetCharacteristic(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ int charProp) throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onGetCharacteristic() - address=" + address
+ + ", status=" + status + ", charUuid=" + charUuid + ", prop=" + charProp);
+
+ if (status == 0) {
+ mSearchQueue.add(connId, srvcType,
+ srvcInstId, srvcUuidLsb, srvcUuidMsb,
+ charInstId, charUuidLsb, charUuidMsb);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onGetCharacteristic(address, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid), charProp);
+ }
+
+ // Get next characteristic in the current service
+ gattClientGetCharacteristicNative(connId, srvcType,
+ srvcInstId, srvcUuidLsb, srvcUuidMsb,
+ charInstId, charUuidLsb, charUuidMsb);
+ } else {
+ // Check for included services next
+ gattClientGetIncludedServiceNative(connId,
+ srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb,
+ 0,0,0,0);
+ }
+ }
+
+ void onGetDescriptor(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ long descrUuidLsb, long descrUuidMsb) throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ UUID descUuid = new UUID(descrUuidMsb, descrUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onGetDescriptor() - address=" + address
+ + ", status=" + status + ", descUuid=" + descUuid);
+
+ if (status == 0) {
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onGetDescriptor(address, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid),
+ new ParcelUuid(descUuid));
+ }
+
+ // Get next descriptor for the current characteristic
+ gattClientGetDescriptorNative(connId, srvcType,
+ srvcInstId, srvcUuidLsb, srvcUuidMsb,
+ charInstId, charUuidLsb, charUuidMsb,
+ descrUuidLsb, descrUuidMsb);
+ } else {
+ // Explore the next service
+ continueSearch(connId, 0);
+ }
+ }
+
+ void onGetIncludedService(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb, int inclSrvcType,
+ int inclSrvcInstId, long inclSrvcUuidLsb, long inclSrvcUuidMsb)
+ throws RemoteException {
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID inclSrvcUuid = new UUID(inclSrvcUuidMsb, inclSrvcUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onGetIncludedService() - address=" + address
+ + ", status=" + status + ", uuid=" + srvcUuid
+ + ", inclUuid=" + inclSrvcUuid);
+
+ if (status == 0) {
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onGetIncludedService(address,
+ srvcType, srvcInstId, new ParcelUuid(srvcUuid),
+ inclSrvcType, inclSrvcInstId, new ParcelUuid(inclSrvcUuid));
+ }
+
+ // Find additional included services
+ gattClientGetIncludedServiceNative(connId,
+ srvcType, srvcInstId, srvcUuidLsb, srvcUuidMsb,
+ inclSrvcType, inclSrvcInstId, inclSrvcUuidLsb, inclSrvcUuidMsb);
+ } else {
+ // Discover descriptors now
+ continueSearch(connId, 0);
+ }
+ }
+
+ void onRegisterForNotifications(int connId, int status, int registered, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb) {
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onRegisterForNotifications() - address=" + address
+ + ", status=" + status + ", registered=" + registered
+ + ", charUuid=" + charUuid);
+ }
+
+ void onNotify(int connId, String address, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ boolean isNotify, byte[] data) throws RemoteException {
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+
+ if (DBG) Log.d(TAG, "onNotify() - address=" + address
+ + ", charUuid=" + charUuid + ", length=" + data.length);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onNotify(address, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid),
+ data);
+ }
+ }
+
+ void onReadCharacteristic(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ int charType, byte[] data) throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onReadCharacteristic() - address=" + address
+ + ", status=" + status + ", length=" + data.length);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onCharacteristicRead(address, status, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid), data);
+ }
+ }
+
+ void onWriteCharacteristic(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb)
+ throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onWriteCharacteristic() - address=" + address
+ + ", status=" + status);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onCharacteristicWrite(address, status, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid));
+ }
+ }
+
+ void onExecuteCompleted(int connId, int status) throws RemoteException {
+ String address = mClientMap.addressByConnId(connId);
+ if (DBG) Log.d(TAG, "onExecuteCompleted() - address=" + address
+ + ", status=" + status);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onExecuteWrite(address, status);
+ }
+ }
+
+ void onReadDescriptor(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ long descrUuidLsb, long descrUuidMsb,
+ int charType, byte[] data) throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ UUID descrUuid = new UUID(descrUuidMsb, descrUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onReadDescriptor() - address=" + address
+ + ", status=" + status + ", length=" + data.length);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onDescriptorRead(address, status, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid),
+ new ParcelUuid(descrUuid), data);
+ }
+ }
+
+ void onWriteDescriptor(int connId, int status, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb,
+ long descrUuidLsb, long descrUuidMsb) throws RemoteException {
+
+ UUID srvcUuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ UUID charUuid = new UUID(charUuidMsb, charUuidLsb);
+ UUID descrUuid = new UUID(descrUuidMsb, descrUuidLsb);
+ String address = mClientMap.addressByConnId(connId);
+
+ if (DBG) Log.d(TAG, "onWriteDescriptor() - address=" + address
+ + ", status=" + status);
+
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onDescriptorWrite(address, status, srvcType,
+ srvcInstId, new ParcelUuid(srvcUuid),
+ charInstId, new ParcelUuid(charUuid),
+ new ParcelUuid(descrUuid));
+ }
+ }
+
+ void onReadRemoteRssi(int clientIf, String address,
+ int rssi, int status) throws RemoteException{
+ if (DBG) Log.d(TAG, "onReadRemoteRssi() - clientIf=" + clientIf + " address=" +
+ address + ", rssi=" + rssi + ", status=" + status);
+
+ ClientMap.App app = mClientMap.getById(clientIf);
+ if (app != null) {
+ app.callback.onReadRemoteRssi(address, rssi, status);
+ }
+ }
+
+ /**************************************************************************
+ * GATT Service functions - Shared CLIENT/SERVER
+ *************************************************************************/
+
+ List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ final int DEVICE_TYPE_BREDR = 0x1;
+
+ Map<BluetoothDevice, Integer> deviceStates = new HashMap<BluetoothDevice,
+ Integer>();
+
+ // Add paired LE devices
+
+ Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
+ for (BluetoothDevice device : bondedDevices) {
+ if (getDeviceType(device) != DEVICE_TYPE_BREDR) {
+ deviceStates.put(device, BluetoothProfile.STATE_DISCONNECTED);
+ }
+ }
+
+ // Add connected deviceStates
+
+ Set<String> connectedDevices = new HashSet<String>();
+ connectedDevices.addAll(mClientMap.getConnectedDevices());
+ connectedDevices.addAll(mServerMap.getConnectedDevices());
+
+ for (String address : connectedDevices ) {
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device != null) {
+ deviceStates.put(device, BluetoothProfile.STATE_CONNECTED);
+ }
+ }
+
+ // Create matching device sub-set
+
+ List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
+
+ for (Map.Entry<BluetoothDevice, Integer> entry : deviceStates.entrySet()) {
+ for(int state : states) {
+ if (entry.getValue() == state) {
+ deviceList.add(entry.getKey());
+ }
+ }
+ }
+
+ return deviceList;
+ }
+
+ void startScan(int appIf, boolean isServer) {
+ if (DBG) Log.d(TAG, "startScan() - queue=" + mScanQueue.size());
+
+ if (getScanClient(appIf, isServer) == null) {
+ if (DBG) Log.d(TAG, "startScan() - adding client=" + appIf);
+ mScanQueue.add(new ScanClient(appIf, isServer));
+ }
+
+ gattClientScanNative(appIf, true);
+ }
+
+ void startScanWithUuids(int appIf, boolean isServer, UUID[] uuids) {
+ if (DBG) Log.d(TAG, "startScanWithUuids() - queue=" + mScanQueue.size());
+
+ if (getScanClient(appIf, isServer) == null) {
+ if (DBG) Log.d(TAG, "startScanWithUuids() - adding client=" + appIf);
+ mScanQueue.add(new ScanClient(appIf, isServer, uuids));
+ }
+
+ gattClientScanNative(appIf, true);
+ }
+
+ void stopScan(int appIf, boolean isServer) {
+ if (DBG) Log.d(TAG, "stopScan() - queue=" + mScanQueue.size());
+
+ removeScanClient(appIf, isServer);
+
+ if (mScanQueue.isEmpty()) {
+ if (DBG) Log.d(TAG, "stopScan() - queue empty; stopping scan");
+ gattClientScanNative(appIf, false);
+ }
+ }
+
+ /**************************************************************************
+ * GATT Service functions - CLIENT
+ *************************************************************************/
+
+ void registerClient(UUID uuid, IBluetoothGattCallback callback) {
+ if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
+ mClientMap.add(uuid, callback);
+ gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
+ uuid.getMostSignificantBits());
+ }
+
+ void unregisterClient(int clientIf) {
+ if (DBG) Log.d(TAG, "unregisterClient() - clientIf=" + clientIf);
+ removeScanClient(clientIf, false);
+ mClientMap.remove(clientIf);
+ gattClientUnregisterAppNative(clientIf);
+ }
+
+ void clientConnect(int clientIf, String address, boolean isDirect) {
+ if (DBG) Log.d(TAG, "clientConnect() - address=" + address);
+ gattClientConnectNative(clientIf, address, isDirect);
+ }
+
+ void clientDisconnect(int clientIf, String address) {
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (DBG) Log.d(TAG, "clientDisconnect() - address=" + address + ", connId=" + connId);
+
+ gattClientDisconnectNative(clientIf, address, connId != null ? connId : 0);
+ }
+
+ List<String> getConnectedDevices() {
+ Set<String> connectedDevAddress = new HashSet<String>();
+ connectedDevAddress.addAll(mClientMap.getConnectedDevices());
+ connectedDevAddress.addAll(mServerMap.getConnectedDevices());
+ List<String> connectedDeviceList = new ArrayList<String>(connectedDevAddress);
+ return connectedDeviceList;
+ }
+
+ void refreshDevice(int clientIf, String address) {
+ if (DBG) Log.d(TAG, "refreshDevice() - address=" + address);
+ gattClientRefreshNative(clientIf, address);
+ }
+
+ void discoverServices(int clientIf, String address) {
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (DBG) Log.d(TAG, "discoverServices() - address=" + address + ", connId=" + connId);
+
+ if (connId != null)
+ gattClientSearchServiceNative(connId, true, 0, 0);
+ else
+ Log.e(TAG, "discoverServices() - No connection for " + address + "...");
+ }
+
+ void readCharacteristic(int clientIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid, int authReq) {
+ if (DBG) Log.d(TAG, "readCharacteristic() - address=" + address);
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null)
+ gattClientReadCharacteristicNative(connId, srvcType,
+ srvcInstanceId, srvcUuid.getLeastSignificantBits(),
+ srvcUuid.getMostSignificantBits(), charInstanceId,
+ charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
+ authReq);
+ else
+ Log.e(TAG, "readCharacteristic() - No connection for " + address + "...");
+ }
+
+ void writeCharacteristic(int clientIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid, int writeType,
+ int authReq, byte[] value) {
+ if (DBG) Log.d(TAG, "writeCharacteristic() - address=" + address);
+
+ if (mReliableQueue.contains(address)) writeType = 3; // Prepared write
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null)
+ gattClientWriteCharacteristicNative(connId, srvcType,
+ srvcInstanceId, srvcUuid.getLeastSignificantBits(),
+ srvcUuid.getMostSignificantBits(), charInstanceId,
+ charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
+ writeType, authReq, value);
+ else
+ Log.e(TAG, "writeCharacteristic() - No connection for " + address + "...");
+ }
+
+ void readDescriptor(int clientIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid,
+ UUID descrUuid, int authReq) {
+ if (DBG) Log.d(TAG, "readDescriptor() - address=" + address);
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null)
+ gattClientReadDescriptorNative(connId, srvcType,
+ srvcInstanceId, srvcUuid.getLeastSignificantBits(),
+ srvcUuid.getMostSignificantBits(), charInstanceId,
+ charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
+ descrUuid.getLeastSignificantBits(), descrUuid.getMostSignificantBits(),
+ authReq);
+ else
+ Log.e(TAG, "readDescriptor() - No connection for " + address + "...");
+ };
+
+ void writeDescriptor(int clientIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid,
+ UUID descrUuid, int writeType,
+ int authReq, byte[] value) {
+ if (DBG) Log.d(TAG, "writeDescriptor() - address=" + address);
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null)
+ gattClientWriteDescriptorNative(connId, srvcType,
+ srvcInstanceId, srvcUuid.getLeastSignificantBits(),
+ srvcUuid.getMostSignificantBits(), charInstanceId,
+ charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
+ descrUuid.getLeastSignificantBits(), descrUuid.getMostSignificantBits(),
+ writeType, authReq, value);
+ else
+ Log.e(TAG, "writeDescriptor() - No connection for " + address + "...");
+ }
+
+ void beginReliableWrite(int clientIf, String address) {
+ if (DBG) Log.d(TAG, "beginReliableWrite() - address=" + address);
+ mReliableQueue.add(address);
+ }
+
+ void endReliableWrite(int clientIf, String address, boolean execute) {
+ if (DBG) Log.d(TAG, "endReliableWrite() - address=" + address
+ + " execute: " + execute);
+ mReliableQueue.remove(address);
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null) gattClientExecuteWriteNative(connId, execute);
+ }
+
+ void registerForNotification(int clientIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid,
+ boolean enable) {
+ if (DBG) Log.d(TAG, "registerForNotification() - address=" + address + " enable: " + enable);
+
+ Integer connId = mClientMap.connIdByAddress(clientIf, address);
+ if (connId != null) {
+ gattClientRegisterForNotificationsNative(clientIf, address,
+ srvcType, srvcInstanceId, srvcUuid.getLeastSignificantBits(),
+ srvcUuid.getMostSignificantBits(), charInstanceId,
+ charUuid.getLeastSignificantBits(), charUuid.getMostSignificantBits(),
+ enable);
+ } else {
+ Log.e(TAG, "registerForNotification() - No connection for " + address + "...");
+ }
+ }
+
+ void readRemoteRssi(int clientIf, String address) {
+ if (DBG) Log.d(TAG, "readRemoteRssi() - address=" + address);
+ gattClientReadRemoteRssiNative(clientIf, address);
+ }
+
+ /**************************************************************************
+ * Callback functions - SERVER
+ *************************************************************************/
+
+ void onServerRegistered(int status, int serverIf, long uuidLsb, long uuidMsb)
+ throws RemoteException {
+
+ UUID uuid = new UUID(uuidMsb, uuidLsb);
+ if (DBG) Log.d(TAG, "onServerRegistered() - UUID=" + uuid + ", serverIf=" + serverIf);
+ ServerMap.App app = mServerMap.getByUuid(uuid);
+ if (app != null) {
+ app.id = serverIf;
+ app.callback.onServerRegistered(status, serverIf);
+ }
+ }
+
+ void onServiceAdded(int status, int serverIf, int srvcType, int srvcInstId,
+ long srvcUuidLsb, long srvcUuidMsb, int srvcHandle)
+ throws RemoteException {
+ UUID uuid = new UUID(srvcUuidMsb, srvcUuidLsb);
+ if (DBG) Log.d(TAG, "onServiceAdded() UUID=" + uuid + ", status=" + status
+ + ", handle=" + srvcHandle);
+ if (status == 0)
+ mHandleMap.addService(serverIf, srvcHandle, uuid, srvcType, srvcInstId);
+ continueServiceDeclaration(serverIf, status, srvcHandle);
+ }
+
+ void onIncludedServiceAdded(int status, int serverIf, int srvcHandle,
+ int includedSrvcHandle) throws RemoteException {
+ if (DBG) Log.d(TAG, "onIncludedServiceAdded() status=" + status
+ + ", service=" + srvcHandle + ", included=" + includedSrvcHandle);
+ continueServiceDeclaration(serverIf, status, srvcHandle);
+ }
+
+ void onCharacteristicAdded(int status, int serverIf,
+ long charUuidLsb, long charUuidMsb,
+ int srvcHandle, int charHandle)
+ throws RemoteException {
+ UUID uuid = new UUID(charUuidMsb, charUuidLsb);
+ if (DBG) Log.d(TAG, "onCharacteristicAdded() UUID=" + uuid + ", status=" + status
+ + ", srvcHandle=" + srvcHandle + ", charHandle=" + charHandle);
+ if (status == 0)
+ mHandleMap.addCharacteristic(serverIf, charHandle, uuid, srvcHandle);
+ continueServiceDeclaration(serverIf, status, srvcHandle);
+ }
+
+ void onDescriptorAdded(int status, int serverIf,
+ long descrUuidLsb, long descrUuidMsb,
+ int srvcHandle, int descrHandle)
+ throws RemoteException {
+ UUID uuid = new UUID(descrUuidMsb, descrUuidLsb);
+ if (DBG) Log.d(TAG, "onDescriptorAdded() UUID=" + uuid + ", status=" + status
+ + ", srvcHandle=" + srvcHandle + ", descrHandle=" + descrHandle);
+ if (status == 0)
+ mHandleMap.addDescriptor(serverIf, descrHandle, uuid, srvcHandle);
+ continueServiceDeclaration(serverIf, status, srvcHandle);
+ }
+
+ void onServiceStarted(int status, int serverIf, int srvcHandle)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onServiceStarted() srvcHandle=" + srvcHandle
+ + ", status=" + status);
+ if (status == 0)
+ mHandleMap.setStarted(serverIf, srvcHandle, true);
+ }
+
+ void onServiceStopped(int status, int serverIf, int srvcHandle)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onServiceStopped() srvcHandle=" + srvcHandle
+ + ", status=" + status);
+ if (status == 0)
+ mHandleMap.setStarted(serverIf, srvcHandle, false);
+ stopNextService(serverIf, status);
+ }
+
+ void onServiceDeleted(int status, int serverIf, int srvcHandle) {
+ if (DBG) Log.d(TAG, "onServiceDeleted() srvcHandle=" + srvcHandle
+ + ", status=" + status);
+ mHandleMap.deleteService(serverIf, srvcHandle);
+ }
+
+ void onClientConnected(String address, boolean connected, int connId)
+ throws RemoteException {
+
+ if (DBG) Log.d(TAG, "onConnected() connId=" + connId
+ + ", address=" + address + ", connected=" + connected);
+
+ Iterator<ServerMap.App> i = mServerMap.mApps.iterator();
+ while(i.hasNext()) {
+ ServerMap.App entry = i.next();
+ if (connected) {
+ mServerMap.addConnection(entry.id, connId, address);
+ } else {
+ mServerMap.removeConnection(entry.id, connId);
+ }
+ entry.callback.onServerConnectionState((byte)0, entry.id, connected, address);
+ }
+ }
+
+ void onAttributeRead(String address, int connId, int transId,
+ int attrHandle, int offset, boolean isLong)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onAttributeRead() connId=" + connId
+ + ", address=" + address + ", handle=" + attrHandle
+ + ", requestId=" + transId + ", offset=" + offset);
+
+ HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
+ if (entry == null) return;
+
+ if (DBG) Log.d(TAG, "onAttributeRead() UUID=" + entry.uuid
+ + ", serverIf=" + entry.serverIf + ", type=" + entry.type);
+
+ mHandleMap.addRequest(transId, attrHandle);
+
+ ServerMap.App app = mServerMap.getById(entry.serverIf);
+ if (app == null) return;
+
+ switch(entry.type) {
+ case HandleMap.TYPE_CHARACTERISTIC:
+ {
+ HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
+ app.callback.onCharacteristicReadRequest(address, transId, offset, isLong,
+ serviceEntry.serviceType, serviceEntry.instance,
+ new ParcelUuid(serviceEntry.uuid), entry.instance,
+ new ParcelUuid(entry.uuid));
+ break;
+ }
+
+ case HandleMap.TYPE_DESCRIPTOR:
+ {
+ HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
+ HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle);
+ app.callback.onDescriptorReadRequest(address, transId, offset, isLong,
+ serviceEntry.serviceType, serviceEntry.instance,
+ new ParcelUuid(serviceEntry.uuid), charEntry.instance,
+ new ParcelUuid(charEntry.uuid),
+ new ParcelUuid(entry.uuid));
+ break;
+ }
+
+ default:
+ Log.e(TAG, "onAttributeRead() - Requested unknown attribute type.");
+ break;
+ }
+ }
+
+ void onAttributeWrite(String address, int connId, int transId,
+ int attrHandle, int offset, int length,
+ boolean needRsp, boolean isPrep,
+ byte[] data)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onAttributeWrite() connId=" + connId
+ + ", address=" + address + ", handle=" + attrHandle
+ + ", requestId=" + transId + ", isPrep=" + isPrep
+ + ", offset=" + offset);
+
+ HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
+ if (entry == null) return;
+
+ if (DBG) Log.d(TAG, "onAttributeWrite() UUID=" + entry.uuid
+ + ", serverIf=" + entry.serverIf + ", type=" + entry.type);
+
+ mHandleMap.addRequest(transId, attrHandle);
+
+ ServerMap.App app = mServerMap.getById(entry.serverIf);
+ if (app == null) return;
+
+ switch(entry.type) {
+ case HandleMap.TYPE_CHARACTERISTIC:
+ {
+ HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
+ app.callback.onCharacteristicWriteRequest(address, transId,
+ offset, length, isPrep, needRsp,
+ serviceEntry.serviceType, serviceEntry.instance,
+ new ParcelUuid(serviceEntry.uuid), entry.instance,
+ new ParcelUuid(entry.uuid), data);
+ break;
+ }
+
+ case HandleMap.TYPE_DESCRIPTOR:
+ {
+ HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle);
+ HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle);
+ app.callback.onDescriptorWriteRequest(address, transId,
+ offset, length, isPrep, needRsp,
+ serviceEntry.serviceType, serviceEntry.instance,
+ new ParcelUuid(serviceEntry.uuid), charEntry.instance,
+ new ParcelUuid(charEntry.uuid),
+ new ParcelUuid(entry.uuid), data);
+ break;
+ }
+
+ default:
+ Log.e(TAG, "onAttributeWrite() - Requested unknown attribute type.");
+ break;
+ }
+ }
+
+ void onExecuteWrite(String address, int connId, int transId, int execWrite)
+ throws RemoteException {
+ if (DBG) Log.d(TAG, "onExecuteWrite() connId=" + connId
+ + ", address=" + address + ", transId=" + transId);
+
+ ServerMap.App app = mServerMap.getByConnId(connId);
+ if (app == null) return;
+
+ app.callback.onExecuteWrite(address, transId, execWrite == 1);
+ }
+
+ void onResponseSendCompleted(int status, int attrHandle) {
+ if (DBG) Log.d(TAG, "onResponseSendCompleted() handle=" + attrHandle);
+ }
+
+ /**************************************************************************
+ * GATT Service functions - SERVER
+ *************************************************************************/
+
+ void registerServer(UUID uuid, IBluetoothGattServerCallback callback) {
+ if (DBG) Log.d(TAG, "registerServer() - UUID=" + uuid);
+ mServerMap.add(uuid, callback);
+ gattServerRegisterAppNative(uuid.getLeastSignificantBits(),
+ uuid.getMostSignificantBits());
+ }
+
+ void unregisterServer(int serverIf) {
+ if (DBG) Log.d(TAG, "unregisterServer() - serverIf=" + serverIf);
+
+ deleteServices(serverIf);
+
+ mServerMap.remove(serverIf);
+ gattServerUnregisterAppNative(serverIf);
+ }
+
+ void serverConnect(int serverIf, String address, boolean isDirect) {
+ if (DBG) Log.d(TAG, "serverConnect() - address=" + address);
+ gattServerConnectNative(serverIf, address, isDirect);
+ }
+
+ void serverDisconnect(int serverIf, String address) {
+ Integer connId = mServerMap.connIdByAddress(serverIf, address);
+ if (DBG) Log.d(TAG, "serverDisconnect() - address=" + address + ", connId=" + connId);
+
+ gattServerDisconnectNative(serverIf, address, connId != null ? connId : 0);
+ }
+
+ void beginServiceDeclaration(int serverIf, int srvcType, int srvcInstanceId,
+ int minHandles, UUID srvcUuid) {
+ if (DBG) Log.d(TAG, "beginServiceDeclaration() - uuid=" + srvcUuid);
+ ServiceDeclaration serviceDeclaration = addDeclaration();
+ serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles);
+ }
+
+ void addIncludedService(int serverIf, int srvcType, int srvcInstanceId,
+ UUID srvcUuid) {
+ if (DBG) Log.d(TAG, "addIncludedService() - uuid=" + srvcUuid);
+ getActiveDeclaration().addIncludedService(srvcUuid, srvcType, srvcInstanceId);
+ }
+
+ void addCharacteristic(int serverIf, UUID charUuid, int properties,
+ int permissions) {
+ if (DBG) Log.d(TAG, "addCharacteristic() - uuid=" + charUuid);
+ getActiveDeclaration().addCharacteristic(charUuid, properties, permissions);
+ }
+
+ void addDescriptor(int serverIf, UUID descUuid, int permissions) {
+ if (DBG) Log.d(TAG, "addDescriptor() - uuid=" + descUuid);
+ getActiveDeclaration().addDescriptor(descUuid, permissions);
+ }
+
+ void endServiceDeclaration(int serverIf) {
+ if (DBG) Log.d(TAG, "endServiceDeclaration()");
+
+ if (getActiveDeclaration() == getPendingDeclaration()) {
+ try {
+ continueServiceDeclaration(serverIf, (byte)0, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG,""+e);
+ }
+ }
+ }
+
+ void removeService(int serverIf, int srvcType,
+ int srvcInstanceId, UUID srvcUuid) {
+ if (DBG) Log.d(TAG, "removeService() - uuid=" + srvcUuid);
+
+ int srvcHandle = mHandleMap.getServiceHandle(srvcUuid, srvcType, srvcInstanceId);
+ if (srvcHandle == 0) return;
+ gattServerDeleteServiceNative(serverIf, srvcHandle);
+ }
+
+ void clearServices(int serverIf) {
+ if (DBG) Log.d(TAG, "clearServices()");
+ deleteServices(serverIf);
+ }
+
+ void sendResponse(int serverIf, String address, int requestId,
+ int status, int offset, byte[] value) {
+ if (DBG) Log.d(TAG, "sendResponse() - address=" + address);
+
+ int handle = 0;
+ HandleMap.Entry entry = mHandleMap.getByRequestId(requestId);
+ if (entry != null) handle = entry.handle;
+
+ int connId = mServerMap.connIdByAddress(serverIf, address);
+ gattServerSendResponseNative(serverIf, connId, requestId, (byte)status,
+ handle, offset, value, (byte)0);
+ mHandleMap.deleteRequest(requestId);
+ }
+
+ void sendNotification(int serverIf, String address, int srvcType,
+ int srvcInstanceId, UUID srvcUuid,
+ int charInstanceId, UUID charUuid,
+ boolean confirm, byte[] value) {
+ if (DBG) Log.d(TAG, "sendNotification() - address=" + address);
+
+ int srvcHandle = mHandleMap.getServiceHandle(srvcUuid, srvcType, srvcInstanceId);
+ if (srvcHandle == 0) return;
+
+ int charHandle = mHandleMap.getCharacteristicHandle(srvcHandle, charUuid, charInstanceId);
+ if (charHandle == 0) return;
+
+ int connId = mServerMap.connIdByAddress(serverIf, address);
+ if (connId == 0) return;
+
+ if (confirm) {
+ gattServerSendIndicationNative(serverIf, charHandle, connId, value);
+ } else {
+ gattServerSendNotificationNative(serverIf, charHandle, connId, value);
+ }
+ }
+
+ /**************************************************************************
+ * Private functions
+ *************************************************************************/
+
+ private int getDeviceType(BluetoothDevice device) {
+ int type = gattClientGetDeviceTypeNative(device.getAddress());
+ if (DBG) Log.d(TAG, "getDeviceType() - device=" + device
+ + ", type=" + type);
+ return type;
+ }
+
+ private void continueSearch(int connId, int status) throws RemoteException {
+ if (status == 0 && !mSearchQueue.isEmpty()) {
+ SearchQueue.Entry svc = mSearchQueue.pop();
+
+ if (svc.charUuidLsb == 0) {
+ // Characteristic is up next
+ gattClientGetCharacteristicNative(svc.connId, svc.srvcType,
+ svc.srvcInstId, svc.srvcUuidLsb, svc.srvcUuidMsb, 0, 0, 0);
+ } else {
+ // Descriptor is up next
+ gattClientGetDescriptorNative(svc.connId, svc.srvcType,
+ svc.srvcInstId, svc.srvcUuidLsb, svc.srvcUuidMsb,
+ svc.charInstId, svc.charUuidLsb, svc.charUuidMsb, 0,0);
+ }
+ } else {
+ ClientMap.App app = mClientMap.getByConnId(connId);
+ if (app != null) {
+ app.callback.onSearchComplete(mClientMap.addressByConnId(connId), status);
+ }
+ }
+ }
+
+ private void continueServiceDeclaration(int serverIf, int status, int srvcHandle) throws RemoteException {
+ if (mServiceDeclarations.size() == 0) return;
+ if (DBG) Log.d(TAG, "continueServiceDeclaration() - srvcHandle=" + srvcHandle);
+
+ boolean finished = false;
+
+ ServiceDeclaration.Entry entry = null;
+ if (status == 0)
+ entry = getPendingDeclaration().getNext();
+
+ if (entry != null) {
+ if (DBG) Log.d(TAG, "continueServiceDeclaration() - next entry type="
+ + entry.type);
+ switch(entry.type) {
+ case ServiceDeclaration.TYPE_SERVICE:
+ gattServerAddServiceNative(serverIf, entry.serviceType,
+ entry.instance,
+ entry.uuid.getLeastSignificantBits(),
+ entry.uuid.getMostSignificantBits(),
+ getPendingDeclaration().getNumHandles());
+ break;
+
+ case ServiceDeclaration.TYPE_CHARACTERISTIC:
+ gattServerAddCharacteristicNative(serverIf, srvcHandle,
+ entry.uuid.getLeastSignificantBits(),
+ entry.uuid.getMostSignificantBits(),
+ entry.properties, entry.permissions);
+ break;
+
+ case ServiceDeclaration.TYPE_DESCRIPTOR:
+ gattServerAddDescriptorNative(serverIf, srvcHandle,
+ entry.uuid.getLeastSignificantBits(),
+ entry.uuid.getMostSignificantBits(),
+ entry.permissions);
+ break;
+
+ case ServiceDeclaration.TYPE_INCLUDED_SERVICE:
+ {
+ int inclSrvc = mHandleMap.getServiceHandle(entry.uuid,
+ entry.serviceType, entry.instance);
+ if (inclSrvc != 0) {
+ gattServerAddIncludedServiceNative(serverIf, srvcHandle,
+ inclSrvc);
+ } else {
+ finished = true;
+ }
+ break;
+ }
+ }
+ } else {
+ gattServerStartServiceNative(serverIf, srvcHandle, (byte)2 /*BREDR/LE*/);
+ finished = true;
+ }
+
+ if (finished) {
+ if (DBG) Log.d(TAG, "continueServiceDeclaration() - completed.");
+ ServerMap.App app = mServerMap.getById(serverIf);
+ if (app != null) {
+ HandleMap.Entry serviceEntry = mHandleMap.getByHandle(srvcHandle);
+ if (serviceEntry != null) {
+ app.callback.onServiceAdded(status, serviceEntry.serviceType,
+ serviceEntry.instance, new ParcelUuid(serviceEntry.uuid));
+ } else {
+ app.callback.onServiceAdded(status, 0, 0, null);
+ }
+ }
+ removePendingDeclaration();
+
+ if (getPendingDeclaration() != null) {
+ continueServiceDeclaration(serverIf, (byte)0, 0);
+ }
+ }
+ }
+
+ private void stopNextService(int serverIf, int status) throws RemoteException {
+ if (DBG) Log.d(TAG, "stopNextService() - serverIf=" + serverIf
+ + ", status=" + status);
+
+ if (status == 0) {
+ List<HandleMap.Entry> entries = mHandleMap.getEntries();
+ for(HandleMap.Entry entry : entries) {
+ if (entry.type != HandleMap.TYPE_SERVICE ||
+ entry.serverIf != serverIf ||
+ entry.started == false)
+ continue;
+
+ gattServerStopServiceNative(serverIf, entry.handle);
+ return;
+ }
+ }
+ }
+
+ private void deleteServices(int serverIf) {
+ if (DBG) Log.d(TAG, "deleteServices() - serverIf=" + serverIf);
+
+ /*
+ * Figure out which handles to delete.
+ * The handles are copied into a new list to avoid race conditions.
+ */
+ List<Integer> handleList = new ArrayList<Integer>();
+ List<HandleMap.Entry> entries = mHandleMap.getEntries();
+ for(HandleMap.Entry entry : entries) {
+ if (entry.type != HandleMap.TYPE_SERVICE ||
+ entry.serverIf != serverIf)
+ continue;
+ handleList.add(entry.handle);
+ }
+
+ /* Now actually delete the services.... */
+ for(Integer handle : handleList) {
+ gattServerDeleteServiceNative(serverIf, handle);
+ }
+ }
+
+ private List<UUID> parseUuids(byte[] adv_data) {
+ List<UUID> uuids = new ArrayList<UUID>();
+
+ int offset = 0;
+ while(offset < (adv_data.length-2)) {
+ int len = adv_data[offset++];
+ if (len == 0) break;
+
+ int type = adv_data[offset++];
+ switch (type) {
+ case 0x02: // Partial list of 16-bit UUIDs
+ case 0x03: // Complete list of 16-bit UUIDs
+ while (len > 1) {
+ int uuid16 = adv_data[offset++];
+ uuid16 += (adv_data[offset++] << 8);
+ len -= 2;
+ uuids.add(UUID.fromString(String.format(
+ "%08x-0000-1000-8000-00805f9b34fb", uuid16)));
+ }
+ break;
+
+ default:
+ offset += (len - 1);
+ break;
+ }
+ }
+
+ return uuids;
+ }
+
+ /**************************************************************************
+ * GATT Test functions
+ *************************************************************************/
+
+ void gattTestCommand(int command, UUID uuid1, String bda1,
+ int p1, int p2, int p3, int p4, int p5) {
+ if (bda1 == null) bda1 = "00:00:00:00:00:00";
+ if (uuid1 != null)
+ gattTestNative(command, uuid1.getLeastSignificantBits(),
+ uuid1.getMostSignificantBits(), bda1, p1, p2, p3, p4, p5);
+ else
+ gattTestNative(command, 0,0, bda1, p1, p2, p3, p4, p5);
+ }
+
+ private native void gattTestNative(int command,
+ long uuid1_lsb, long uuid1_msb, String bda1,
+ int p1, int p2, int p3, int p4, int p5);
+
+ /**************************************************************************
+ * Native functions prototypes
+ *************************************************************************/
+
+ private native static void classInitNative();
+ private native void initializeNative();
+ private native void cleanupNative();
+
+ private native int gattClientGetDeviceTypeNative(String address);
+
+ private native void gattClientRegisterAppNative(long app_uuid_lsb,
+ long app_uuid_msb);
+
+ private native void gattClientUnregisterAppNative(int clientIf);
+
+ private native void gattClientScanNative(int clientIf, boolean start);
+
+ private native void gattClientConnectNative(int clientIf, String address,
+ boolean isDirect);
+
+ private native void gattClientDisconnectNative(int clientIf, String address,
+ int conn_id);
+
+ private native void gattClientRefreshNative(int clientIf, String address);
+
+ private native void gattClientSearchServiceNative(int conn_id,
+ boolean search_all, long service_uuid_lsb, long service_uuid_msb);
+
+ private native void gattClientGetCharacteristicNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb);
+
+ private native void gattClientGetDescriptorNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb, long descr_id_uuid_lsb, long descr_id_uuid_msb);
+
+ private native void gattClientGetIncludedServiceNative(int conn_id,
+ int service_type, int service_id_inst_id,
+ long service_id_uuid_lsb, long service_id_uuid_msb,
+ int incl_service_id_inst_id, int incl_service_type,
+ long incl_service_id_uuid_lsb, long incl_service_id_uuid_msb);
+
+ private native void gattClientReadCharacteristicNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb, int authReq);
+
+ private native void gattClientReadDescriptorNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb, long descr_id_uuid_lsb, long descr_id_uuid_msb,
+ int authReq);
+
+ private native void gattClientWriteCharacteristicNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb, int write_type, int auth_req, byte[] value);
+
+ private native void gattClientWriteDescriptorNative(int conn_id,
+ int service_type, int service_id_inst_id, long service_id_uuid_lsb,
+ long service_id_uuid_msb, int char_id_inst_id, long char_id_uuid_lsb,
+ long char_id_uuid_msb, long descr_id_uuid_lsb, long descr_id_uuid_msb,
+ int write_type, int auth_req, byte[] value);
+
+ private native void gattClientExecuteWriteNative(int conn_id, boolean execute);
+
+ private native void gattClientRegisterForNotificationsNative(int clientIf,
+ String address, int service_type, int service_id_inst_id,
+ long service_id_uuid_lsb, long service_id_uuid_msb,
+ int char_id_inst_id, long char_id_uuid_lsb, long char_id_uuid_msb,
+ boolean enable);
+
+ private native void gattClientReadRemoteRssiNative(int clientIf,
+ String address);
+
+ private native void gattServerRegisterAppNative(long app_uuid_lsb,
+ long app_uuid_msb);
+
+ private native void gattServerUnregisterAppNative(int serverIf);
+
+ private native void gattServerConnectNative(int server_if, String address,
+ boolean is_direct);
+
+ private native void gattServerDisconnectNative(int serverIf, String address,
+ int conn_id);
+
+ private native void gattServerAddServiceNative (int server_if,
+ int service_type, int service_id_inst_id,
+ long service_id_uuid_lsb, long service_id_uuid_msb,
+ int num_handles);
+
+ private native void gattServerAddIncludedServiceNative (int server_if,
+ int svc_handle, int included_svc_handle);
+
+ private native void gattServerAddCharacteristicNative (int server_if,
+ int svc_handle, long char_uuid_lsb, long char_uuid_msb,
+ int properties, int permissions);
+
+ private native void gattServerAddDescriptorNative (int server_if,
+ int svc_handle, long desc_uuid_lsb, long desc_uuid_msb,
+ int permissions);
+
+ private native void gattServerStartServiceNative (int server_if,
+ int svc_handle, int transport );
+
+ private native void gattServerStopServiceNative (int server_if,
+ int svc_handle);
+
+ private native void gattServerDeleteServiceNative (int server_if,
+ int svc_handle);
+
+ private native void gattServerSendIndicationNative (int server_if,
+ int attr_handle, int conn_id, byte[] val);
+
+ private native void gattServerSendNotificationNative (int server_if,
+ int attr_handle, int conn_id, byte[] val);
+
+ private native void gattServerSendResponseNative (int server_if,
+ int conn_id, int trans_id, int status, int handle, int offset,
+ byte[] val, int auth_req);
+}
diff --git a/src/com/android/bluetooth/gatt/GattServiceConfig.java b/src/com/android/bluetooth/gatt/GattServiceConfig.java
new file mode 100644
index 000000000..9b5a8abca
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/GattServiceConfig.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+/**
+ * GattService configuration.
+ */
+/*package*/ class GattServiceConfig {
+ public static final boolean DBG = true;
+ public static final String TAG_PREFIX = "BtGatt.";
+ public static final boolean DEBUG_ADMIN = true;
+}
diff --git a/src/com/android/bluetooth/gatt/HandleMap.java b/src/com/android/bluetooth/gatt/HandleMap.java
new file mode 100644
index 000000000..5d45654b9
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/HandleMap.java
@@ -0,0 +1,213 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+class HandleMap {
+ private static final boolean DBG = GattServiceConfig.DBG;
+ private static final String TAG = GattServiceConfig.TAG_PREFIX + "HandleMap";
+
+ public static final int TYPE_UNDEFINED = 0;
+ public static final int TYPE_SERVICE = 1;
+ public static final int TYPE_CHARACTERISTIC = 2;
+ public static final int TYPE_DESCRIPTOR = 3;
+
+ class Entry {
+ int serverIf = 0;
+ int type = TYPE_UNDEFINED;
+ int handle = 0;
+ UUID uuid = null;
+ int instance = 0;
+ int serviceType = 0;
+ int serviceHandle = 0;
+ int charHandle = 0;
+ boolean started = false;
+
+ Entry(int serverIf, int handle, UUID uuid, int serviceType, int instance) {
+ this.serverIf = serverIf;
+ this.type = TYPE_SERVICE;
+ this.handle = handle;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceType = serviceType;
+ }
+
+ Entry(int serverIf, int type, int handle, UUID uuid, int serviceHandle) {
+ this.serverIf = serverIf;
+ this.type = type;
+ this.handle = handle;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceHandle = serviceHandle;
+ }
+
+ Entry(int serverIf, int type, int handle, UUID uuid, int serviceHandle, int charHandle) {
+ this.serverIf = serverIf;
+ this.type = type;
+ this.handle = handle;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceHandle = serviceHandle;
+ this.charHandle = charHandle;
+ }
+ }
+
+ List<Entry> mEntries = null;
+ Map<Integer, Integer> mRequestMap = null;
+ int mLastCharacteristic = 0;
+
+ HandleMap() {
+ mEntries = new ArrayList<Entry>();
+ mRequestMap = new HashMap<Integer, Integer>();
+ }
+
+ void clear() {
+ mEntries.clear();
+ mRequestMap.clear();
+ }
+
+ void addService(int serverIf, int handle, UUID uuid, int serviceType, int instance) {
+ mEntries.add(new Entry(serverIf, handle, uuid, serviceType, instance));
+ }
+
+ void addCharacteristic(int serverIf, int handle, UUID uuid, int serviceHandle) {
+ mLastCharacteristic = handle;
+ mEntries.add(new Entry(serverIf, TYPE_CHARACTERISTIC, handle, uuid, serviceHandle));
+ }
+
+ void addDescriptor(int serverIf, int handle, UUID uuid, int serviceHandle) {
+ mEntries.add(new Entry(serverIf, TYPE_DESCRIPTOR, handle, uuid, serviceHandle, mLastCharacteristic));
+ }
+
+ void setStarted(int serverIf, int handle, boolean started) {
+ for(Entry entry : mEntries) {
+ if (entry.type != TYPE_SERVICE ||
+ entry.serverIf != serverIf ||
+ entry.handle != handle)
+ continue;
+
+ entry.started = started;
+ return;
+ }
+ }
+
+ Entry getByHandle(int handle) {
+ for(Entry entry : mEntries) {
+ if (entry.handle == handle)
+ return entry;
+ }
+ Log.e(TAG, "getByHandle() - Handle " + handle + " not found!");
+ return null;
+ }
+
+ int getServiceHandle(UUID uuid, int serviceType, int instance) {
+ for(Entry entry : mEntries) {
+ if (entry.type == TYPE_SERVICE &&
+ entry.serviceType == serviceType &&
+ entry.instance == instance &&
+ entry.uuid.equals(uuid)) {
+ return entry.handle;
+ }
+ }
+ Log.e(TAG, "getServiceHandle() - UUID " + uuid + " not found!");
+ return 0;
+ }
+
+ int getCharacteristicHandle(int serviceHandle, UUID uuid, int instance) {
+ for(Entry entry : mEntries) {
+ if (entry.type == TYPE_CHARACTERISTIC &&
+ entry.serviceHandle == serviceHandle &&
+ entry.instance == instance &&
+ entry.uuid.equals(uuid)) {
+ return entry.handle;
+ }
+ }
+ Log.e(TAG, "getCharacteristicHandle() - Service " + serviceHandle
+ + ", UUID " + uuid + " not found!");
+ return 0;
+ }
+
+ void deleteService(int serverIf, int serviceHandle) {
+ for(Iterator <Entry> it = mEntries.iterator(); it.hasNext();) {
+ Entry entry = it.next();
+ if (entry.serverIf != serverIf) continue;
+
+ if (entry.handle == serviceHandle ||
+ entry.serviceHandle == serviceHandle)
+ it.remove();
+ }
+ }
+
+ List<Entry> getEntries() {
+ return mEntries;
+ }
+
+ void addRequest(int requestId, int handle) {
+ mRequestMap.put(requestId, handle);
+ }
+
+ void deleteRequest(int requestId) {
+ mRequestMap.remove(requestId);
+ }
+
+ Entry getByRequestId(int requestId) {
+ Integer handle = mRequestMap.get(requestId);
+ if (handle == null) {
+ Log.e(TAG, "getByRequestId() - Request ID " + requestId + " not found!");
+ return null;
+ }
+ return getByHandle(handle);
+ }
+
+
+ /**
+ * Logs debug information.
+ */
+ void dump() {
+ StringBuilder b = new StringBuilder();
+ b.append( "-------------- GATT Handle Map -----------------");
+ b.append("\nEntries: " + mEntries.size());
+ b.append("\nRequests: " + mRequestMap.size());
+
+ for (Entry entry : mEntries) {
+ b.append("\n" + entry.serverIf + ": [" + entry.handle + "] ");
+ switch(entry.type) {
+ case TYPE_SERVICE:
+ b.append("Service " + entry.uuid);
+ b.append(", started " + entry.started);
+ break;
+
+ case TYPE_CHARACTERISTIC:
+ b.append(" Characteristic " + entry.uuid);
+ break;
+
+ case TYPE_DESCRIPTOR:
+ b.append(" Descriptor " + entry.uuid);
+ break;
+ }
+ }
+
+ b.append("\n------------------------------------------------");
+ Log.d(TAG, b.toString());
+ }
+}
diff --git a/src/com/android/bluetooth/gatt/ScanClient.java b/src/com/android/bluetooth/gatt/ScanClient.java
new file mode 100644
index 000000000..3b1e4216c
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/ScanClient.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import java.util.UUID;
+
+/**
+ * Helper class identifying a client that has requested LE scan results.
+ * @hide
+ */
+/*package*/ class ScanClient {
+ int appIf;
+ boolean isServer;
+ UUID[] uuids;
+
+ ScanClient(int appIf, boolean isServer) {
+ this.appIf = appIf;
+ this.isServer = isServer;
+ this.uuids = new UUID[0];
+ }
+
+ ScanClient(int appIf, boolean isServer, UUID[] uuids) {
+ this.appIf = appIf;
+ this.isServer = isServer;
+ this.uuids = uuids;
+ }
+}
diff --git a/src/com/android/bluetooth/gatt/SearchQueue.java b/src/com/android/bluetooth/gatt/SearchQueue.java
new file mode 100644
index 000000000..3064a51e3
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/SearchQueue.java
@@ -0,0 +1,92 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Helper class to store characteristics and descriptors that will be
+ * queued up for future exploration.
+ * @hide
+ */
+/*package*/ class SearchQueue {
+ class Entry {
+ public int connId;
+ public int srvcType;
+ public int srvcInstId;
+ public long srvcUuidLsb;
+ public long srvcUuidMsb;
+ public int charInstId;
+ public long charUuidLsb;
+ public long charUuidMsb;
+ }
+
+ private List<Entry> mEntries = new ArrayList<Entry>();
+
+ void add(int connId, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb) {
+ Entry entry = new Entry();
+ entry.connId = connId;
+ entry.srvcType = srvcType;
+ entry.srvcInstId = srvcInstId;
+ entry.srvcUuidLsb = srvcUuidLsb;
+ entry.srvcUuidMsb = srvcUuidMsb;
+ entry.charUuidLsb = 0;
+ mEntries.add(entry);
+ }
+
+ void add(int connId, int srvcType,
+ int srvcInstId, long srvcUuidLsb, long srvcUuidMsb,
+ int charInstId, long charUuidLsb, long charUuidMsb)
+ {
+ Entry entry = new Entry();
+ entry.connId = connId;
+ entry.srvcType = srvcType;
+ entry.srvcInstId = srvcInstId;
+ entry.srvcUuidLsb = srvcUuidLsb;
+ entry.srvcUuidMsb = srvcUuidMsb;
+ entry.charInstId = charInstId;
+ entry.charUuidLsb = charUuidLsb;
+ entry.charUuidMsb = charUuidMsb;
+ mEntries.add(entry);
+ }
+
+ Entry pop() {
+ Entry entry = mEntries.get(0);
+ mEntries.remove(0);
+ return entry;
+ }
+
+ void removeConnId(int connId) {
+ for (Iterator<Entry> it = mEntries.iterator(); it.hasNext();) {
+ Entry entry = it.next();
+ if (entry.connId == connId) {
+ it.remove();
+ }
+ }
+ }
+
+ boolean isEmpty() {
+ return mEntries.isEmpty();
+ }
+
+ void clear() {
+ mEntries.clear();
+ }
+}
diff --git a/src/com/android/bluetooth/gatt/ServiceDeclaration.java b/src/com/android/bluetooth/gatt/ServiceDeclaration.java
new file mode 100644
index 000000000..0c9a51b2f
--- /dev/null
+++ b/src/com/android/bluetooth/gatt/ServiceDeclaration.java
@@ -0,0 +1,108 @@
+/*
+ * 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 com.android.bluetooth.gatt;
+
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+class ServiceDeclaration {
+ private static final boolean DBG = GattServiceConfig.DBG;
+ private static final String TAG = GattServiceConfig.TAG_PREFIX + "ServiceDeclaration";
+
+ public static final byte TYPE_UNDEFINED = 0;
+ public static final byte TYPE_SERVICE = 1;
+ public static final byte TYPE_CHARACTERISTIC = 2;
+ public static final byte TYPE_DESCRIPTOR = 3;
+ public static final byte TYPE_INCLUDED_SERVICE = 4;
+
+ class Entry {
+ byte type = TYPE_UNDEFINED;
+ UUID uuid = null;
+ int instance = 0;
+ int permissions = 0;
+ int properties = 0;
+ int serviceType = 0;
+ int serviceHandle = 0;
+
+ Entry(UUID uuid, int serviceType, int instance) {
+ this.type = TYPE_SERVICE;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceType = serviceType;
+ }
+
+ Entry(UUID uuid, int properties, int permissions, int instance) {
+ this.type = TYPE_CHARACTERISTIC;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.permissions = permissions;
+ this.properties = properties;
+ }
+
+ Entry(UUID uuid, int permissions) {
+ this.type = TYPE_DESCRIPTOR;
+ this.uuid = uuid;
+ this.permissions = permissions;
+ }
+ }
+
+ List<Entry> mEntries = null;
+ int mNumHandles = 0;
+
+ ServiceDeclaration() {
+ mEntries = new ArrayList<Entry>();
+ }
+
+ void addService(UUID uuid, int serviceType, int instance, int minHandles) {
+ mEntries.add(new Entry(uuid, serviceType, instance));
+ if (minHandles == 0) {
+ ++mNumHandles;
+ } else {
+ mNumHandles = minHandles;
+ }
+ }
+
+ void addIncludedService(UUID uuid, int serviceType, int instance) {
+ Entry entry = new Entry(uuid, serviceType, instance);
+ entry.type = TYPE_INCLUDED_SERVICE;
+ mEntries.add(entry);
+ ++mNumHandles;
+ }
+
+ void addCharacteristic(UUID uuid, int properties, int permissions) {
+ mEntries.add(new Entry(uuid, properties, permissions, 0 /*instance*/));
+ mNumHandles += 2;
+ }
+
+ void addDescriptor(UUID uuid, int permissions) {
+ mEntries.add(new Entry(uuid, permissions));
+ ++mNumHandles;
+ }
+
+ Entry getNext() {
+ if (mEntries.isEmpty()) return null;
+ Entry entry = mEntries.get(0);
+ mEntries.remove(0);
+ return entry;
+ }
+
+ int getNumHandles() {
+ return mNumHandles;
+ }
+}