Initial version of BLE support for Bluedroid
Change-Id: I9579b3074bc4bc59dd45f71c0937e8879196555e
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f4c0d3b..389a21c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -219,6 +219,14 @@
</receiver>
<service
android:process="@string/process"
+ android:name = ".gatt.GattService"
+ android:enabled="@bool/profile_supported_gatt">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothGatt" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
android:name = ".hfp.HeadsetService"
android:enabled="@bool/profile_supported_hs_hfp">
<intent-filter>
diff --git a/jni/Android.mk b/jni/Android.mk
index 4098907..ee822d9 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -8,7 +8,8 @@
com_android_bluetooth_a2dp.cpp \
com_android_bluetooth_hid.cpp \
com_android_bluetooth_hdp.cpp \
- com_android_bluetooth_pan.cpp
+ com_android_bluetooth_pan.cpp \
+ com_android_bluetooth_gatt.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/jni/com_android_bluetooth.h b/jni/com_android_bluetooth.h
index 2ffe7de..8689247 100644
--- a/jni/com_android_bluetooth.h
+++ b/jni/com_android_bluetooth.h
@@ -41,6 +41,8 @@
int register_com_android_bluetooth_pan(JNIEnv* env);
+int register_com_android_bluetooth_gatt (JNIEnv* env);
+
}
#endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
old mode 100755
new mode 100644
index 5989c79..9722b5e
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -443,6 +443,13 @@
}
}
+static void dut_mode_recv_callback (uint16_t opcode, uint8_t *buf, uint8_t len) {
+
+}
+static void le_test_mode_recv_callback (bt_status_t status, uint16_t packet_count) {
+
+ ALOGV("%s: status:%d packet_count:%d ", __FUNCTION__, status, packet_count);
+}
bt_callbacks_t sBluetoothCallbacks = {
sizeof(sBluetoothCallbacks),
adapter_state_change_callback,
@@ -455,6 +462,9 @@
bond_state_changed_callback,
acl_state_changed_callback,
callback_thread_event,
+ dut_mode_recv_callback,
+
+ le_test_mode_recv_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -924,7 +934,7 @@
{"getRemoteServicesNative", "([B)Z", (void*) getRemoteServicesNative},
{"connectSocketNative", "([BI[BII)I", (void*) connectSocketNative},
{"createSocketChannelNative", "(ILjava/lang/String;[BII)I",
- (void*) createSocketChannelNative},
+ (void*) createSocketChannelNative}
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
@@ -982,5 +992,9 @@
return JNI_ERR;
}
+ if ((status = android::register_com_android_bluetooth_gatt(e)) < 0) {
+ ALOGE("jni gatt registration failure: %d", status);
+ return JNI_ERR;
+ }
return JNI_VERSION_1_6;
}
diff --git a/jni/com_android_bluetooth_gatt.cpp b/jni/com_android_bluetooth_gatt.cpp
new file mode 100644
index 0000000..a77be9c
--- /dev/null
+++ b/jni/com_android_bluetooth_gatt.cpp
@@ -0,0 +1,1276 @@
+/*
+ * 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.
+ */
+
+
+#define ALOG_TAG "BtGatt.JNI"
+
+#define ALOG_NDEBUG 0
+
+#define CHECK_CALLBACK_ENV \
+ if (!checkCallbackThread()) { \
+ error("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
+ return; \
+ }
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_gatt.h"
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <string.h>
+
+#include <cutils/log.h>
+#define info(fmt, ...) ALOGI ("%s(L%d): " fmt,__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define debug(fmt, ...) ALOGD ("%s(L%d): " fmt,__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define warn(fmt, ...) ALOGW ("WARNING: %s(L%d): " fmt "##",__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define error(fmt, ...) ALOGE ("ERROR: %s(L%d): " fmt "##",__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define asrt(s) if(!(s)) ALOGE ("%s(L%d): ASSERT %s failed! ##",__FUNCTION__, __LINE__, #s)
+
+#define BD_ADDR_LEN 6
+
+#define UUID_PARAMS(uuid_ptr) \
+ uuid_lsb(uuid_ptr), uuid_msb(uuid_ptr)
+
+#define CHAR_ID_PARAMS(char_ptr) \
+ char_ptr->inst_id, \
+ UUID_PARAMS((&char_ptr->uuid))
+
+#define SRVC_ID_PARAMS(srvc_ptr) \
+ (srvc_ptr->is_primary ? \
+ BTGATT_SERVICE_TYPE_PRIMARY : BTGATT_SERVICE_TYPE_SECONDARY), \
+ CHAR_ID_PARAMS((&srvc_ptr->id))
+
+
+static void set_uuid(uint8_t* uuid, jlong uuid_msb, jlong uuid_lsb)
+{
+ for (int i = 0; i != 8; ++i)
+ {
+ uuid[i] = (uuid_lsb >> (8 * i)) & 0xFF;
+ uuid[i + 8] = (uuid_msb >> (8 * i)) & 0xFF;
+ }
+}
+
+static uint64_t uuid_lsb(bt_uuid_t* uuid)
+{
+ uint64_t lsb = 0;
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ {
+ lsb <<= 8;
+ lsb |= uuid->uu[i];
+ }
+
+ return lsb;
+}
+
+static uint64_t uuid_msb(bt_uuid_t* uuid)
+{
+ uint64_t msb = 0;
+ int i;
+
+ for (i = 15; i >= 8; i--)
+ {
+ msb <<= 8;
+ msb |= uuid->uu[i];
+ }
+
+ return msb;
+}
+
+static void bd_addr_str_to_addr(const char* str, uint8_t *bd_addr)
+{
+ int i;
+ char c;
+
+ c = *str++;
+ for (i = 0; i < BD_ADDR_LEN; i++)
+ {
+ if (c >= '0' && c <= '9')
+ bd_addr[i] = c - '0';
+ else if (c >= 'a' && c <= 'z')
+ bd_addr[i] = c - 'a' + 10;
+ else // (c >= 'A' && c <= 'Z')
+ bd_addr[i] = c - 'A' + 10;
+
+ c = *str++;
+ if (c != ':')
+ {
+ bd_addr[i] <<= 4;
+ if (c >= '0' && c <= '9')
+ bd_addr[i] |= c - '0';
+ else if (c >= 'a' && c <= 'z')
+ bd_addr[i] |= c - 'a' + 10;
+ else // (c >= 'A' && c <= 'Z')
+ bd_addr[i] |= c - 'A' + 10;
+
+ c = *str++;
+ }
+
+ c = *str++;
+ }
+}
+
+static void jstr2bdaddr(JNIEnv* env, bt_bdaddr_t *bda, jstring address)
+{
+ const char* c_bda = env->GetStringUTFChars(address, NULL);
+ if (c_bda != NULL && bda != NULL && strlen(c_bda) == 17)
+ {
+ bd_addr_str_to_addr(c_bda, bda->address);
+ env->ReleaseStringUTFChars(address, c_bda);
+ }
+}
+
+namespace android {
+
+/**
+ * Client callback methods
+ */
+
+static jmethodID method_onClientRegistered;
+static jmethodID method_onScanResult;
+static jmethodID method_onConnected;
+static jmethodID method_onDisconnected;
+static jmethodID method_onReadCharacteristic;
+static jmethodID method_onWriteCharacteristic;
+static jmethodID method_onExecuteCompleted;
+static jmethodID method_onSearchCompleted;
+static jmethodID method_onSearchResult;
+static jmethodID method_onReadDescrExtProp;
+static jmethodID method_onReadDescriptor;
+static jmethodID method_onWriteDescriptor;
+static jmethodID method_onNotify;
+static jmethodID method_onGetCharacteristic;
+static jmethodID method_onGetDescriptor;
+static jmethodID method_onGetIncludedService;
+static jmethodID method_onRegisterForNotifications;
+static jmethodID method_onReadRemoteRssi;
+
+/**
+ * Server callback methods
+ */
+static jmethodID method_onServerRegistered;
+static jmethodID method_onClientConnected;
+static jmethodID method_onServiceAdded;
+static jmethodID method_onIncludedServiceAdded;
+static jmethodID method_onCharacteristicAdded;
+static jmethodID method_onDescriptorAdded;
+static jmethodID method_onServiceStarted;
+static jmethodID method_onServiceStopped;
+static jmethodID method_onServiceDeleted;
+static jmethodID method_onResponseSendCompleted;
+static jmethodID method_onAttributeRead;
+static jmethodID method_onAttributeWrite;
+static jmethodID method_onExecuteWrite;
+
+/**
+ * Static variables
+ */
+
+static const btgatt_interface_t *sGattIf = NULL;
+static jobject mCallbacksObj = NULL;
+static JNIEnv *sCallbackEnv = NULL;
+
+static bool checkCallbackThread() {
+ sCallbackEnv = getCallbackEnv();
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
+ return true;
+}
+
+/**
+ * BTA client callbacks
+ */
+
+void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
+ clientIf, UUID_PARAMS(app_uuid));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_scan_result_cb(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ snprintf(c_address, sizeof(c_address),"%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ jbyteArray jb = sCallbackEnv->NewByteArray(62);
+ sCallbackEnv->SetByteArrayRegion(jb, 0, 62, (jbyte *) adv_data);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult
+ , address, rssi, jb);
+
+ sCallbackEnv->DeleteLocalRef(address);
+ sCallbackEnv->DeleteLocalRef(jb);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_open_cb(int conn_id, int status, int clientIf, bt_bdaddr_t* bda)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ snprintf(c_address, sizeof(c_address),"%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnected,
+ clientIf, conn_id, status, address);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_close_cb(int conn_id, int status, int clientIf, bt_bdaddr_t* bda)
+{
+ CHECK_CALLBACK_ENV
+ char c_address[32];
+ snprintf(c_address, sizeof(c_address),"%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDisconnected,
+ clientIf, conn_id, status, address);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_search_complete_cb(int conn_id, int status)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSearchCompleted,
+ conn_id, status);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSearchResult, conn_id,
+ SRVC_ID_PARAMS(srvc_id));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_get_characteristic_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id,
+ int char_prop)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetCharacteristic
+ , conn_id, status, SRVC_ID_PARAMS(srvc_id), CHAR_ID_PARAMS(char_id)
+ , char_prop);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_get_descriptor_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id,
+ bt_uuid_t *descr_id)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetDescriptor
+ , conn_id, status, SRVC_ID_PARAMS(srvc_id), CHAR_ID_PARAMS(char_id)
+ , UUID_PARAMS(descr_id));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_get_included_service_cb(int conn_id, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetIncludedService
+ , conn_id, status, SRVC_ID_PARAMS(srvc_id), SRVC_ID_PARAMS(incl_srvc_id));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_register_for_notification_cb(int conn_id, int registered, int status,
+ btgatt_srvc_id_t *srvc_id, btgatt_char_id_t *char_id)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRegisterForNotifications
+ , conn_id, status, registered, SRVC_ID_PARAMS(srvc_id), CHAR_ID_PARAMS(char_id));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ snprintf(c_address, sizeof(c_address), "%02X:%02X:%02X:%02X:%02X:%02X",
+ p_data->bda.address[0], p_data->bda.address[1], p_data->bda.address[2],
+ p_data->bda.address[3], p_data->bda.address[4], p_data->bda.address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ jbyteArray jb = sCallbackEnv->NewByteArray(p_data->len);
+ sCallbackEnv->SetByteArrayRegion(jb, 0, p_data->len, (jbyte *) p_data->value);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onNotify
+ , conn_id, address, SRVC_ID_PARAMS((&p_data->srvc_id))
+ , CHAR_ID_PARAMS((&p_data->char_id)), p_data->is_notify, jb);
+
+ sCallbackEnv->DeleteLocalRef(address);
+ sCallbackEnv->DeleteLocalRef(jb);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_read_characteristic_cb(int conn_id, int status, btgatt_read_params_t *p_data)
+{
+ CHECK_CALLBACK_ENV
+
+ jbyteArray jb;
+ if ( status == 0 ) //successful
+ {
+ jb = sCallbackEnv->NewByteArray(p_data->value.len);
+ sCallbackEnv->SetByteArrayRegion(jb, 0, p_data->value.len,
+ (jbyte *) p_data->value.value);
+ } else {
+ uint8_t value = 0;
+ jb = sCallbackEnv->NewByteArray(1);
+ sCallbackEnv->SetByteArrayRegion(jb, 0, 1, (jbyte *) &value);
+ }
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadCharacteristic
+ , conn_id, status, SRVC_ID_PARAMS((&p_data->srvc_id))
+ , CHAR_ID_PARAMS((&p_data->char_id)), p_data->value_type, jb);
+ sCallbackEnv->DeleteLocalRef(jb);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_write_characteristic_cb(int conn_id, int status, btgatt_write_params_t *p_data)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWriteCharacteristic
+ , conn_id, status, SRVC_ID_PARAMS((&p_data->srvc_id))
+ , CHAR_ID_PARAMS((&p_data->char_id)));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_execute_write_cb(int conn_id, int status)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExecuteCompleted
+ , conn_id, status);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_read_descriptor_cb(int conn_id, int status, btgatt_read_params_t *p_data)
+{
+ CHECK_CALLBACK_ENV
+
+ jbyteArray jb;
+ if ( p_data->value.len != 0 )
+ {
+ jb = sCallbackEnv->NewByteArray(p_data->value.len);
+ sCallbackEnv->SetByteArrayRegion(jb, 0, p_data->value.len,
+ (jbyte *) p_data->value.value);
+ } else {
+ jb = sCallbackEnv->NewByteArray(1);
+ }
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadDescriptor
+ , conn_id, status, SRVC_ID_PARAMS((&p_data->srvc_id))
+ , CHAR_ID_PARAMS((&p_data->char_id)), UUID_PARAMS((&p_data->descr_id))
+ , p_data->value_type, jb);
+
+ sCallbackEnv->DeleteLocalRef(jb);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_write_descriptor_cb(int conn_id, int status, btgatt_write_params_t *p_data)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWriteDescriptor
+ , conn_id, status, SRVC_ID_PARAMS((&p_data->srvc_id))
+ , CHAR_ID_PARAMS((&p_data->char_id)), UUID_PARAMS((&p_data->descr_id)));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgattc_remote_rssi_cb(int client_if,bt_bdaddr_t* bda, int rssi, int status)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ snprintf(c_address, sizeof(c_address),"%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReadRemoteRssi,
+ client_if, address, rssi, status);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static const btgatt_client_callbacks_t sGattClientCallbacks = {
+ btgattc_register_app_cb,
+ btgattc_scan_result_cb,
+ btgattc_open_cb,
+ btgattc_close_cb,
+ btgattc_search_complete_cb,
+ btgattc_search_result_cb,
+ btgattc_get_characteristic_cb,
+ btgattc_get_descriptor_cb,
+ btgattc_get_included_service_cb,
+ btgattc_register_for_notification_cb,
+ btgattc_notify_cb,
+ btgattc_read_characteristic_cb,
+ btgattc_write_characteristic_cb,
+ btgattc_read_descriptor_cb,
+ btgattc_write_descriptor_cb,
+ btgattc_execute_write_cb,
+ btgattc_remote_rssi_cb
+};
+
+
+/**
+ * BTA server callbacks
+ */
+
+void btgatts_register_app_cb(int status, int server_if, bt_uuid_t *uuid)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServerRegistered
+ , status, server_if, UUID_PARAMS(uuid));
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_connection_cb(int conn_id, int connected, bt_bdaddr_t *bda)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ sprintf(c_address, "%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientConnected,
+ address, connected, conn_id);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_service_added_cb(int status, int server_if,
+ btgatt_srvc_id_t *srvc_id, int srvc_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceAdded, status,
+ server_if, SRVC_ID_PARAMS(srvc_id),
+ srvc_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_included_service_added_cb(int status, int server_if,
+ int srvc_handle,
+ int incl_srvc_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onIncludedServiceAdded,
+ status, server_if, srvc_handle, incl_srvc_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_characteristic_added_cb(int status, int server_if, bt_uuid_t *char_id,
+ int srvc_handle, int char_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCharacteristicAdded,
+ status, server_if, UUID_PARAMS(char_id),
+ srvc_handle, char_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_descriptor_added_cb(int status, int server_if,
+ bt_uuid_t *descr_id, int srvc_handle,
+ int descr_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onDescriptorAdded,
+ status, server_if, UUID_PARAMS(descr_id),
+ srvc_handle, descr_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_service_started_cb(int status, int server_if, int srvc_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceStarted, status,
+ server_if, srvc_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_service_stopped_cb(int status, int server_if, int srvc_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceStopped, status,
+ server_if, srvc_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_service_deleted_cb(int status, int server_if, int srvc_handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onServiceDeleted, status,
+ server_if, srvc_handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda,
+ int attr_handle, int offset, bool is_long)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ sprintf(c_address, "%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAttributeRead,
+ address, conn_id, trans_id, attr_handle,
+ offset, is_long);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_request_write_cb(int conn_id, int trans_id,
+ bt_bdaddr_t *bda, int attr_handle,
+ int offset, int length,
+ bool need_rsp, bool is_prep, uint8_t* value)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ sprintf(c_address, "%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+
+ jbyteArray val = sCallbackEnv->NewByteArray(length);
+ if (val) sCallbackEnv->SetByteArrayRegion(val, 0, length, (jbyte*)value);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAttributeWrite,
+ address, conn_id, trans_id, attr_handle,
+ offset, length, need_rsp, is_prep, val);
+ sCallbackEnv->DeleteLocalRef(address);
+ sCallbackEnv->DeleteLocalRef(val);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_request_exec_write_cb(int conn_id, int trans_id,
+ bt_bdaddr_t *bda, int exec_write)
+{
+ CHECK_CALLBACK_ENV
+
+ char c_address[32];
+ sprintf(c_address, "%02X:%02X:%02X:%02X:%02X:%02X",
+ bda->address[0], bda->address[1], bda->address[2],
+ bda->address[3], bda->address[4], bda->address[5]);
+
+ jstring address = sCallbackEnv->NewStringUTF(c_address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExecuteWrite,
+ address, conn_id, trans_id, exec_write);
+ sCallbackEnv->DeleteLocalRef(address);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+void btgatts_response_confirmation_cb(int status, int handle)
+{
+ CHECK_CALLBACK_ENV
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onResponseSendCompleted,
+ status, handle);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static const btgatt_server_callbacks_t sGattServerCallbacks = {
+ btgatts_register_app_cb,
+ btgatts_connection_cb,
+ btgatts_service_added_cb,
+ btgatts_included_service_added_cb,
+ btgatts_characteristic_added_cb,
+ btgatts_descriptor_added_cb,
+ btgatts_service_started_cb,
+ btgatts_service_stopped_cb,
+ btgatts_service_deleted_cb,
+ btgatts_request_read_cb,
+ btgatts_request_write_cb,
+ btgatts_request_exec_write_cb,
+ btgatts_response_confirmation_cb
+};
+
+/**
+ * GATT callbacks
+ */
+
+static const btgatt_callbacks_t sGattCallbacks = {
+ sizeof(btgatt_callbacks_t),
+ &sGattClientCallbacks,
+ &sGattServerCallbacks
+};
+
+/**
+ * Native function definitions
+ */
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+ // Client callbacks
+
+ method_onClientRegistered = env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
+ method_onScanResult = env->GetMethodID(clazz, "onScanResult", "(Ljava/lang/String;I[B)V");
+ method_onConnected = env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V");
+ method_onDisconnected = env->GetMethodID(clazz, "onDisconnected", "(IIILjava/lang/String;)V");
+ method_onReadCharacteristic = env->GetMethodID(clazz, "onReadCharacteristic", "(IIIIJJIJJI[B)V");
+ method_onWriteCharacteristic = env->GetMethodID(clazz, "onWriteCharacteristic", "(IIIIJJIJJ)V");
+ method_onExecuteCompleted = env->GetMethodID(clazz, "onExecuteCompleted", "(II)V");
+ method_onSearchCompleted = env->GetMethodID(clazz, "onSearchCompleted", "(II)V");
+ method_onSearchResult = env->GetMethodID(clazz, "onSearchResult", "(IIIJJ)V");
+ method_onReadDescriptor = env->GetMethodID(clazz, "onReadDescriptor", "(IIIIJJIJJJJI[B)V");
+ method_onWriteDescriptor = env->GetMethodID(clazz, "onWriteDescriptor", "(IIIIJJIJJJJ)V");
+ method_onNotify = env->GetMethodID(clazz, "onNotify", "(ILjava/lang/String;IIJJIJJZ[B)V");
+ method_onGetCharacteristic = env->GetMethodID(clazz, "onGetCharacteristic", "(IIIIJJIJJI)V");
+ method_onGetDescriptor = env->GetMethodID(clazz, "onGetDescriptor", "(IIIIJJIJJJJ)V");
+ method_onGetIncludedService = env->GetMethodID(clazz, "onGetIncludedService", "(IIIIJJIIJJ)V");
+ method_onRegisterForNotifications = env->GetMethodID(clazz, "onRegisterForNotifications", "(IIIIIJJIJJ)V");
+ method_onReadRemoteRssi = env->GetMethodID(clazz, "onReadRemoteRssi", "(ILjava/lang/String;II)V");
+
+ // Server callbacks
+
+ method_onServerRegistered = env->GetMethodID(clazz, "onServerRegistered", "(IIJJ)V");
+ method_onClientConnected = env->GetMethodID(clazz, "onClientConnected", "(Ljava/lang/String;ZI)V");
+ method_onServiceAdded = env->GetMethodID(clazz, "onServiceAdded", "(IIIIJJI)V");
+ method_onIncludedServiceAdded = env->GetMethodID(clazz, "onIncludedServiceAdded", "(IIII)V");
+ method_onCharacteristicAdded = env->GetMethodID(clazz, "onCharacteristicAdded", "(IIJJII)V");
+ method_onDescriptorAdded = env->GetMethodID(clazz, "onDescriptorAdded", "(IIJJII)V");
+ method_onServiceStarted = env->GetMethodID(clazz, "onServiceStarted", "(III)V");
+ method_onServiceStopped = env->GetMethodID(clazz, "onServiceStopped", "(III)V");
+ method_onServiceDeleted = env->GetMethodID(clazz, "onServiceDeleted", "(III)V");
+ method_onResponseSendCompleted = env->GetMethodID(clazz, "onResponseSendCompleted", "(II)V");
+ method_onAttributeRead= env->GetMethodID(clazz, "onAttributeRead", "(Ljava/lang/String;IIIIZ)V");
+ method_onAttributeWrite= env->GetMethodID(clazz, "onAttributeWrite", "(Ljava/lang/String;IIIIIZZ[B)V");
+ method_onExecuteWrite= env->GetMethodID(clazz, "onExecuteWrite", "(Ljava/lang/String;III)V");
+
+ info("classInitNative: Success!");
+}
+
+static const bt_interface_t* btIf;
+
+static void initializeNative(JNIEnv *env, jobject object) {
+ if(btIf)
+ return;
+
+ if ( (btIf = getBluetoothInterface()) == NULL) {
+ error("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sGattIf != NULL) {
+ ALOGW("Cleaning up Bluetooth GATT Interface before initializing...");
+ sGattIf->cleanup();
+ sGattIf = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Bluetooth GATT callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sGattIf = (btgatt_interface_t *)
+ btIf->get_profile_interface(BT_PROFILE_GATT_ID)) == NULL) {
+ error("Failed to get Bluetooth GATT Interface");
+ return;
+ }
+
+ bt_status_t status;
+ if ( (status = sGattIf->init(&sGattCallbacks)) != BT_STATUS_SUCCESS) {
+ error("Failed to initialize Bluetooth GATT, status: %d", status);
+ sGattIf = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+ bt_status_t status;
+ if (!btIf) return;
+
+ if (sGattIf != NULL) {
+ sGattIf->cleanup();
+ sGattIf = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+ btIf = NULL;
+}
+
+/**
+ * Native Client functions
+ */
+
+static int gattClientGetDeviceTypeNative(JNIEnv* env, jobject object, jstring address)
+{
+ if (!sGattIf) return 0;
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+ return sGattIf->client->get_device_type(&bda);
+}
+
+static void gattClientRegisterAppNative(JNIEnv* env, jobject object,
+ jlong app_uuid_lsb, jlong app_uuid_msb )
+{
+ bt_uuid_t uuid;
+
+ if (!sGattIf) return;
+ set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
+ sGattIf->client->register_client(&uuid);
+}
+
+static void gattClientUnregisterAppNative(JNIEnv* env, jobject object, jint clientIf)
+{
+ if (!sGattIf) return;
+ sGattIf->client->unregister_client(clientIf);
+}
+
+static void gattClientScanNative(JNIEnv* env, jobject object, jint clientIf, jboolean start)
+{
+ if (!sGattIf) return;
+ sGattIf->client->scan(clientIf, start);
+}
+
+static void gattClientConnectNative(JNIEnv* env, jobject object, jint clientif,
+ jstring address, jboolean isDirect)
+{
+ if (!sGattIf) return;
+
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+ sGattIf->client->connect(clientif, &bda, isDirect);
+}
+
+static void gattClientDisconnectNative(JNIEnv* env, jobject object, jint clientIf,
+ jstring address, jint conn_id)
+{
+ if (!sGattIf) return;
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+ sGattIf->client->disconnect(clientIf, &bda, conn_id);
+}
+
+static void gattClientRefreshNative(JNIEnv* env, jobject object, jint clientIf,
+ jstring address)
+{
+ if (!sGattIf) return;
+
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+ sGattIf->client->refresh(clientIf, &bda);
+}
+
+static void gattClientSearchServiceNative(JNIEnv* env, jobject object, jint conn_id,
+ jboolean search_all, jlong service_uuid_lsb, jlong service_uuid_msb)
+{
+ if (!sGattIf) return;
+
+ bt_uuid_t uuid;
+ set_uuid(uuid.uu, service_uuid_msb, service_uuid_lsb);
+ sGattIf->client->search_service(conn_id, search_all ? 0 : &uuid);
+}
+
+static void gattClientGetCharacteristicNative(JNIEnv* env, jobject object,
+ jint conn_id,
+ jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ if (char_id_uuid_lsb == 0)
+ {
+ sGattIf->client->get_characteristic(conn_id, &srvc_id, 0);
+ } else {
+ sGattIf->client->get_characteristic(conn_id, &srvc_id, &char_id);
+ }
+}
+
+static void gattClientGetDescriptorNative(JNIEnv* env, jobject object,
+ jint conn_id,
+ jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jlong descr_id_uuid_lsb, jlong descr_id_uuid_msb)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ bt_uuid_t descr_id;
+ set_uuid(descr_id.uu, descr_id_uuid_msb, descr_id_uuid_lsb);
+
+ if (descr_id_uuid_lsb == 0)
+ {
+ sGattIf->client->get_descriptor(conn_id, &srvc_id, &char_id, 0);
+ } else {
+ sGattIf->client->get_descriptor(conn_id, &srvc_id, &char_id, &descr_id);
+ }
+}
+
+static void gattClientGetIncludedServiceNative(JNIEnv* env, jobject object,
+ jint conn_id, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint incl_service_id_inst_id, jint incl_service_type,
+ jlong incl_service_id_uuid_lsb, jlong incl_service_id_uuid_msb)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_srvc_id_t incl_srvc_id;
+ incl_srvc_id.id.inst_id = (uint8_t) incl_service_id_inst_id;
+ incl_srvc_id.is_primary = (incl_service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(incl_srvc_id.id.uuid.uu, incl_service_id_uuid_msb, incl_service_id_uuid_lsb);
+
+ if (incl_service_id_uuid_lsb == 0)
+ {
+ sGattIf->client->get_included_service(conn_id, &srvc_id, 0);
+ } else {
+ sGattIf->client->get_included_service(conn_id, &srvc_id, &incl_srvc_id);
+ }
+}
+
+static void gattClientReadCharacteristicNative(JNIEnv* env, jobject object,
+ jint conn_id, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jint authReq)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ sGattIf->client->read_characteristic(conn_id, &srvc_id, &char_id, authReq);
+}
+
+static void gattClientReadDescriptorNative(JNIEnv* env, jobject object,
+ jint conn_id, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jlong descr_id_uuid_lsb, jlong descr_id_uuid_msb,
+ jint authReq)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ bt_uuid_t descr_id;
+ set_uuid(descr_id.uu, descr_id_uuid_msb, descr_id_uuid_lsb);
+
+ sGattIf->client->read_descriptor(conn_id, &srvc_id, &char_id, &descr_id, authReq);
+}
+
+static void gattClientWriteCharacteristicNative(JNIEnv* env, jobject object,
+ jint conn_id, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jint write_type, jint auth_req, jbyteArray value)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ uint16_t len = (uint16_t) env->GetArrayLength(value);
+ jbyte *p_value = env->GetByteArrayElements(value, NULL);
+ if (p_value == NULL) return;
+
+ sGattIf->client->write_characteristic(conn_id, &srvc_id, &char_id,
+ write_type, len, auth_req, (char*)p_value);
+ env->ReleaseByteArrayElements(value, p_value, 0);
+}
+
+static void gattClientExecuteWriteNative(JNIEnv* env, jobject object,
+ jint conn_id, jboolean execute)
+{
+ if (!sGattIf) return;
+ sGattIf->client->execute_write(conn_id, execute ? 1 : 0);
+}
+
+static void gattClientWriteDescriptorNative(JNIEnv* env, jobject object,
+ jint conn_id, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jlong descr_id_uuid_lsb, jlong descr_id_uuid_msb,
+ jint write_type, jint auth_req, jbyteArray value)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ bt_uuid_t descr_id;
+ set_uuid(descr_id.uu, descr_id_uuid_msb, descr_id_uuid_lsb);
+
+ uint16_t len = (uint16_t) env->GetArrayLength(value);
+ jbyte *p_value = env->GetByteArrayElements(value, NULL);
+ if (p_value == NULL) return;
+
+ sGattIf->client->write_descriptor(conn_id, &srvc_id, &char_id, &descr_id,
+ write_type, len, auth_req, (char*)p_value);
+ env->ReleaseByteArrayElements(value, p_value, 0);
+}
+
+static void gattClientRegisterForNotificationsNative(JNIEnv* env, jobject object,
+ jint clientIf, jstring address,
+ jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint char_id_inst_id,
+ jlong char_id_uuid_lsb, jlong char_id_uuid_msb,
+ jboolean enable)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ btgatt_char_id_t char_id;
+ char_id.inst_id = (uint8_t) char_id_inst_id;
+ set_uuid(char_id.uuid.uu, char_id_uuid_msb, char_id_uuid_lsb);
+
+ bt_bdaddr_t bd_addr;
+ const char *c_address = env->GetStringUTFChars(address, NULL);
+ bd_addr_str_to_addr(c_address, bd_addr.address);
+
+ if (enable)
+ sGattIf->client->register_for_notification(clientIf, &bd_addr, &srvc_id, &char_id);
+ else
+ sGattIf->client->deregister_for_notification(clientIf, &bd_addr, &srvc_id, &char_id);
+}
+
+static void gattClientReadRemoteRssiNative(JNIEnv* env, jobject object, jint clientif,
+ jstring address)
+{
+ if (!sGattIf) return;
+
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+
+ sGattIf->client->read_remote_rssi(clientif, &bda);
+}
+
+
+/**
+ * Native server functions
+ */
+static void gattServerRegisterAppNative(JNIEnv* env, jobject object,
+ jlong app_uuid_lsb, jlong app_uuid_msb )
+{
+ bt_uuid_t uuid;
+ if (!sGattIf) return;
+ set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
+ sGattIf->server->register_server(&uuid);
+}
+
+static void gattServerUnregisterAppNative(JNIEnv* env, jobject object, jint serverIf)
+{
+ if (!sGattIf) return;
+ sGattIf->server->unregister_server(serverIf);
+}
+
+static void gattServerConnectNative(JNIEnv *env, jobject object,
+ jint server_if, jstring address, jboolean is_direct)
+{
+ if (!sGattIf) return;
+
+ bt_bdaddr_t bd_addr;
+ const char *c_address = env->GetStringUTFChars(address, NULL);
+ bd_addr_str_to_addr(c_address, bd_addr.address);
+
+ sGattIf->server->connect(server_if, &bd_addr, is_direct);
+}
+
+static void gattServerDisconnectNative(JNIEnv* env, jobject object, jint serverIf,
+ jstring address, jint conn_id)
+{
+ if (!sGattIf) return;
+ bt_bdaddr_t bda;
+ jstr2bdaddr(env, &bda, address);
+ sGattIf->server->disconnect(serverIf, &bda, conn_id);
+}
+
+static void gattServerAddServiceNative (JNIEnv *env, jobject object,
+ jint server_if, jint service_type, jint service_id_inst_id,
+ jlong service_id_uuid_lsb, jlong service_id_uuid_msb,
+ jint num_handles)
+{
+ if (!sGattIf) return;
+
+ btgatt_srvc_id_t srvc_id;
+ srvc_id.id.inst_id = (uint8_t) service_id_inst_id;
+ srvc_id.is_primary = (service_type == BTGATT_SERVICE_TYPE_PRIMARY ? 1 : 0);
+ set_uuid(srvc_id.id.uuid.uu, service_id_uuid_msb, service_id_uuid_lsb);
+
+ sGattIf->server->add_service(server_if, &srvc_id, num_handles);
+}
+
+static void gattServerAddIncludedServiceNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle, jint included_svc_handle)
+{
+ if (!sGattIf) return;
+ sGattIf->server->add_included_service(server_if, svc_handle,
+ included_svc_handle);
+}
+
+static void gattServerAddCharacteristicNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle,
+ jlong char_uuid_lsb, jlong char_uuid_msb,
+ jint properties, jint permissions)
+{
+ if (!sGattIf) return;
+
+ bt_uuid_t uuid;
+ set_uuid(uuid.uu, char_uuid_msb, char_uuid_lsb);
+
+ sGattIf->server->add_characteristic(server_if, svc_handle,
+ &uuid, properties, permissions);
+}
+
+static void gattServerAddDescriptorNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle,
+ jlong desc_uuid_lsb, jlong desc_uuid_msb,
+ jint permissions)
+{
+ if (!sGattIf) return;
+
+ bt_uuid_t uuid;
+ set_uuid(uuid.uu, desc_uuid_msb, desc_uuid_lsb);
+
+ sGattIf->server->add_descriptor(server_if, svc_handle, &uuid, permissions);
+}
+
+static void gattServerStartServiceNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle, jint transport )
+{
+ if (!sGattIf) return;
+ sGattIf->server->start_service(server_if, svc_handle, transport);
+}
+
+static void gattServerStopServiceNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle)
+{
+ if (!sGattIf) return;
+ sGattIf->server->stop_service(server_if, svc_handle);
+}
+
+static void gattServerDeleteServiceNative (JNIEnv *env, jobject object,
+ jint server_if, jint svc_handle)
+{
+ if (!sGattIf) return;
+ sGattIf->server->delete_service(server_if, svc_handle);
+}
+
+static void gattServerSendIndicationNative (JNIEnv *env, jobject object,
+ jint server_if, jint attr_handle, jint conn_id, jbyteArray val)
+{
+ if (!sGattIf) return;
+
+ jbyte* array = env->GetByteArrayElements(val, 0);
+ int val_len = env->GetArrayLength(val);
+
+ sGattIf->server->send_indication(server_if, attr_handle, conn_id, val_len,
+ /*confirm*/ 1, (char*)array);
+ env->ReleaseByteArrayElements(val, array, JNI_ABORT);
+}
+
+static void gattServerSendNotificationNative (JNIEnv *env, jobject object,
+ jint server_if, jint attr_handle, jint conn_id, jbyteArray val)
+{
+ if (!sGattIf) return;
+
+ jbyte* array = env->GetByteArrayElements(val, 0);
+ int val_len = env->GetArrayLength(val);
+
+ sGattIf->server->send_indication(server_if, attr_handle, conn_id, val_len,
+ /*confirm*/ 0, (char*)array);
+ env->ReleaseByteArrayElements(val, array, JNI_ABORT);
+}
+
+static void gattServerSendResponseNative (JNIEnv *env, jobject object,
+ jint server_if, jint conn_id, jint trans_id, jint status,
+ jint handle, jint offset, jbyteArray val, jint auth_req)
+{
+ if (!sGattIf) return;
+
+ btgatt_response_t response;
+
+ response.attr_value.handle = handle;
+ response.attr_value.auth_req = auth_req;
+ response.attr_value.offset = offset;
+ response.attr_value.len = 0;
+
+ if (val != NULL)
+ {
+ response.attr_value.len = (uint16_t) env->GetArrayLength(val);
+ jbyte* array = env->GetByteArrayElements(val, 0);
+
+ for (int i = 0; i != response.attr_value.len; ++i)
+ response.attr_value.value[i] = (uint8_t) array[i];
+ env->ReleaseByteArrayElements(val, array, JNI_ABORT);
+ }
+
+ sGattIf->server->send_response(conn_id, trans_id, status, &response);
+}
+
+static void gattTestNative(JNIEnv *env, jobject object, jint command,
+ jlong uuid1_lsb, jlong uuid1_msb, jstring bda1,
+ jint p1, jint p2, jint p3, jint p4, jint p5 )
+{
+ if (!sGattIf) return;
+
+ bt_bdaddr_t bt_bda1;
+ jstr2bdaddr(env, &bt_bda1, bda1);
+
+ bt_uuid_t uuid1;
+ set_uuid(uuid1.uu, uuid1_msb, uuid1_lsb);
+
+ btgatt_test_params_t params;
+ params.bda1 = &bt_bda1;
+ params.uuid1 = &uuid1;
+ params.u1 = p1;
+ params.u2 = p2;
+ params.u3 = p3;
+ params.u4 = p4;
+ params.u5 = p5;
+ sGattIf->client->test_command(command, ¶ms);
+}
+
+/**
+ * JNI function definitinos
+ */
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+
+ {"gattClientGetDeviceTypeNative", "(Ljava/lang/String;)I", (void *) gattClientGetDeviceTypeNative},
+ {"gattClientRegisterAppNative", "(JJ)V", (void *) gattClientRegisterAppNative},
+ {"gattClientUnregisterAppNative", "(I)V", (void *) gattClientUnregisterAppNative},
+ {"gattClientScanNative", "(IZ)V", (void *) gattClientScanNative},
+ {"gattClientConnectNative", "(ILjava/lang/String;Z)V", (void *) gattClientConnectNative},
+ {"gattClientDisconnectNative", "(ILjava/lang/String;I)V", (void *) gattClientDisconnectNative},
+ {"gattClientRefreshNative", "(ILjava/lang/String;)V", (void *) gattClientRefreshNative},
+ {"gattClientSearchServiceNative", "(IZJJ)V", (void *) gattClientSearchServiceNative},
+ {"gattClientGetCharacteristicNative", "(IIIJJIJJ)V", (void *) gattClientGetCharacteristicNative},
+ {"gattClientGetDescriptorNative", "(IIIJJIJJJJ)V", (void *) gattClientGetDescriptorNative},
+ {"gattClientGetIncludedServiceNative", "(IIIJJIIJJ)V", (void *) gattClientGetIncludedServiceNative},
+ {"gattClientReadCharacteristicNative", "(IIIJJIJJI)V", (void *) gattClientReadCharacteristicNative},
+ {"gattClientReadDescriptorNative", "(IIIJJIJJJJI)V", (void *) gattClientReadDescriptorNative},
+ {"gattClientWriteCharacteristicNative", "(IIIJJIJJII[B)V", (void *) gattClientWriteCharacteristicNative},
+ {"gattClientWriteDescriptorNative", "(IIIJJIJJJJII[B)V", (void *) gattClientWriteDescriptorNative},
+ {"gattClientExecuteWriteNative", "(IZ)V", (void *) gattClientExecuteWriteNative},
+ {"gattClientRegisterForNotificationsNative", "(ILjava/lang/String;IIJJIJJZ)V", (void *) gattClientRegisterForNotificationsNative},
+ {"gattClientReadRemoteRssiNative", "(ILjava/lang/String;)V", (void *) gattClientReadRemoteRssiNative},
+
+ {"gattServerRegisterAppNative", "(JJ)V", (void *) gattServerRegisterAppNative},
+ {"gattServerUnregisterAppNative", "(I)V", (void *) gattServerUnregisterAppNative},
+ {"gattServerConnectNative", "(ILjava/lang/String;Z)V", (void *) gattServerConnectNative},
+ {"gattServerDisconnectNative", "(ILjava/lang/String;I)V", (void *) gattServerDisconnectNative},
+ {"gattServerAddServiceNative", "(IIIJJI)V", (void *) gattServerAddServiceNative},
+ {"gattServerAddIncludedServiceNative", "(III)V", (void *) gattServerAddIncludedServiceNative},
+ {"gattServerAddCharacteristicNative", "(IIJJII)V", (void *) gattServerAddCharacteristicNative},
+ {"gattServerAddDescriptorNative", "(IIJJI)V", (void *) gattServerAddDescriptorNative},
+ {"gattServerStartServiceNative", "(III)V", (void *) gattServerStartServiceNative},
+ {"gattServerStopServiceNative", "(II)V", (void *) gattServerStopServiceNative},
+ {"gattServerDeleteServiceNative", "(II)V", (void *) gattServerDeleteServiceNative},
+ {"gattServerSendIndicationNative", "(III[B)V", (void *) gattServerSendIndicationNative},
+ {"gattServerSendNotificationNative", "(III[B)V", (void *) gattServerSendNotificationNative},
+ {"gattServerSendResponseNative", "(IIIIII[BI)V", (void *) gattServerSendResponseNative},
+
+ {"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void *) gattTestNative},
+};
+
+int register_com_android_bluetooth_gatt(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService",
+ sMethods, NELEM(sMethods));
+}
+
+}
diff --git a/res/values/config.xml b/res/values/config.xml
index 7616fb6..af90926 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -20,6 +20,7 @@
<bool name="profile_supported_opp">true</bool>
<bool name="profile_supported_pan">true</bool>
<bool name="profile_supported_pbap">true</bool>
+ <bool name="profile_supported_gatt">true</bool>
<bool name="pbap_include_photos_in_vcard">false</bool>
<bool name="pbap_use_profile_for_owner_vcard">true</bool>
</resources>
diff --git a/src/com/android/bluetooth/btservice/AbstractionLayer.java b/src/com/android/bluetooth/btservice/AbstractionLayer.java
old mode 100755
new mode 100644
index 24bcb5d..4e0ebd0
--- a/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -76,4 +76,6 @@
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
old mode 100755
new mode 100644
index d80ad7c..0b6478b
--- a/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -347,6 +347,10 @@
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 d8d1194..a2b2ff3 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.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 @@
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 @@
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 0000000..ca265b1
--- /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 0000000..3911e6c
--- /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 0000000..56db06d
--- /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 0000000..9b5a8ab
--- /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 0000000..5d45654
--- /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 0000000..3b1e421
--- /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 0000000..3064a51
--- /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 0000000..0c9a51b
--- /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;
+ }
+}