resolved conflicts for merge of c508483d to jb-mr2-dev
Change-Id: I15a1f2b2391a973645ef14c36bb3ddf71c815bc9
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f4c0d3b..4a363dd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -128,6 +128,9 @@
<data android:mimeType="application/msword" />
<data android:mimeType="application/vnd.ms-powerpoint" />
<data android:mimeType="application/pdf" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
@@ -219,6 +222,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..056c712 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -6,9 +6,11 @@
com_android_bluetooth_btservice_AdapterService.cpp \
com_android_bluetooth_hfp.cpp \
com_android_bluetooth_a2dp.cpp \
+ com_android_bluetooth_avrcp.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..506cddd 100644
--- a/jni/com_android_bluetooth.h
+++ b/jni/com_android_bluetooth.h
@@ -35,12 +35,16 @@
int register_com_android_bluetooth_a2dp(JNIEnv* env);
+int register_com_android_bluetooth_avrcp(JNIEnv* env);
+
int register_com_android_bluetooth_hid(JNIEnv* env);
int register_com_android_bluetooth_hdp(JNIEnv* env);
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_avrcp.cpp b/jni/com_android_bluetooth_avrcp.cpp
new file mode 100644
index 0000000..125ac4b
--- /dev/null
+++ b/jni/com_android_bluetooth_avrcp.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2012 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 LOG_TAG "BluetoothAvrcpServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_rc.h"
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <string.h>
+
+namespace android {
+static jmethodID method_getPlayStatus;
+static jmethodID method_getElementAttr;
+static jmethodID method_registerNotification;
+
+static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static JNIEnv *sCallbackEnv = NULL;
+
+static bool checkCallbackThread() {
+ // Always fetch the latest callbackEnv from AdapterService.
+ // Caching this could cause this sCallbackEnv to go out-of-sync
+ // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
+ // is received
+ sCallbackEnv = getCallbackEnv();
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
+ return true;
+}
+
+static void btavrcp_get_play_status_callback() {
+ ALOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
+ jintArray attrs;
+
+ ALOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
+ if (!attrs) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(attrs);
+}
+
+static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
+ ALOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
+ (jint)event_id, (jint)param);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
+ sizeof(sBluetoothAvrcpCallbacks),
+ btavrcp_get_play_status_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ btavrcp_get_element_attr_callback,
+ btavrcp_register_notification_callback
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ method_getPlayStatus =
+ env->GetMethodID(clazz, "getPlayStatus", "()V");
+
+ method_getElementAttr =
+ env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
+
+ method_registerNotification =
+ env->GetMethodID(clazz, "registerNotification", "(II)V");
+
+ ALOGI("%s: succeeds", __FUNCTION__);
+}
+
+static void initNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothAvrcpInterface !=NULL) {
+ ALOGW("Cleaning up Avrcp Interface before initializing...");
+ sBluetoothAvrcpInterface->cleanup();
+ sBluetoothAvrcpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Avrcp callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
+ ALOGE("Failed to get Bluetooth Avrcp Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
+ sBluetoothAvrcpInterface = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothAvrcpInterface !=NULL) {
+ sBluetoothAvrcpInterface->cleanup();
+ sBluetoothAvrcpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
+static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
+ jint songLen, jint songPos) {
+ bt_status_t status;
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
+ songLen, songPos)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_play_status_rsp, status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+ static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
+ jintArray attrIds, jobjectArray textArray) {
+ jint *attr;
+ bt_status_t status;
+ jstring text;
+ int i;
+ btrc_element_attr_val_t *pAttrs = NULL;
+ const char* textStr;
+
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+
+ pAttrs = new btrc_element_attr_val_t[numAttr];
+ if (!pAttrs) {
+ ALOGE("get_element_attr_rsp: not have enough memeory");
+ return JNI_FALSE;
+ }
+
+ attr = env->GetIntArrayElements(attrIds, NULL);
+ if (!attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ for (i = 0; i < numAttr; ++i) {
+ text = (jstring) env->GetObjectArrayElement(textArray, i);
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ pAttrs[i].attr_id = attr[i];
+ if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
+ ALOGE("get_element_attr_rsp: string length exceed maximum");
+ strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
+ pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
+ } else {
+ strcpy((char *)pAttrs[i].text, textStr);
+ }
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ }
+
+ if (i < numAttr) {
+ delete[] pAttrs;
+ env->ReleaseIntArrayElements(attrIds, attr, 0);
+ return JNI_FALSE;
+ }
+
+ if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_element_attr_rsp, status: %d", status);
+ }
+
+ delete[] pAttrs;
+ env->ReleaseIntArrayElements(attrIds, attr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object,
+ jint type, jint playStatus) {
+ bt_status_t status;
+ btrc_register_notification_t param;
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ param.play_status = (btrc_play_status_t)playStatus;
+ if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
+ (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp play status, status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
+ jint type, jbyteArray track) {
+ bt_status_t status;
+ btrc_register_notification_t param;
+ jbyte *trk;
+ int i;
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ trk = env->GetByteArrayElements(track, NULL);
+ if (!trk) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ for (i = 0; i < BTRC_UID_SIZE; ++i) {
+ param.track[i] = trk[i];
+ }
+
+ if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
+ (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp track change, status: %d", status);
+ }
+
+ env->ReleaseByteArrayElements(track, trk, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initNative", "()V", (void *) initNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+ {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
+ {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
+ {"registerNotificationRspPlayStatusNative", "(II)Z",
+ (void *) registerNotificationRspPlayStatusNative},
+ {"registerNotificationRspTrackChangeNative", "(I[B)Z",
+ (void *) registerNotificationRspTrackChangeNative},
+};
+
+int register_com_android_bluetooth_avrcp(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp",
+ sMethods, NELEM(sMethods));
+}
+
+}
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 a861217..310dcea
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -442,6 +442,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,
@@ -454,6 +461,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) {
@@ -923,7 +933,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)
@@ -966,6 +976,11 @@
return JNI_ERR;
}
+ if ((status = android::register_com_android_bluetooth_avrcp(e)) < 0) {
+ ALOGE("jni avrcp registration failure: %d", status);
+ return JNI_ERR;
+ }
+
if ((status = android::register_com_android_bluetooth_hid(e)) < 0) {
ALOGE("jni hid registration failure: %d", status);
return JNI_ERR;
@@ -981,5 +996,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..0c3dbb3
--- /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 LOG_TAG "BtGatt.JNI"
+
+#define LOG_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-am/strings.xml b/res/values-am/strings.xml
index 53c566a..914854b 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="permlab_bluetoothShareManager" msgid="311492132450338925">"አውርድ አደራጅን ድረስ።"</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"የብሉቱዝ አጋራ አስተዳዳሪውን ለመድረስ እና ፋይሎች እንዲያስተላልፉ ለመጠቀም ለመተግበሪያው ይፈቅዳል።"</string>
- <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"የብሉቱዝ መሳሪያ መዳረሻን በተወዳጆች ዝርዝር አስገባ።"</string>
+ <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"የብሉቱዝ መሳሪያ መዳረሻን በተወዳጆች ዝርዝር ያስገቡ።"</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"መተግበሪያው አንድን የብሉቱዝ መሳሪያ በጊዜያዊነት በተወዳጆች ዝርዝር እንዲያስገባ ይፈቅድለታል፣ ይህም መሳሪያው ወደዚህኛው መሳሪያ ፋይሎችን ያለተጠቃሚው ማረጋገጫ ለመላክ ያስችለዋል።"</string>
<string name="permlab_handoverStatus" msgid="7316032998801933554">"የBT ርክክብ ስርጭቶችን ተቀበል።"</string>
<string name="permdesc_handoverStatus" msgid="4752738070064786310">"ከብሉቱዝ የርክክብ ሁኔታ መረጃ ለመቀበል ያስችላል።"</string>
@@ -39,8 +39,8 @@
<string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"እሺ"</string>
<string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"ከ \"<xliff:g id="SENDER">%1$s</xliff:g>\" ገቢ መልዕክት ፋይል እየተቀበለ ሳለ ጊዜ አልቆ ነበር።"</string>
<string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"የብሉቱዝ መጋሪያ፡ ገቢ ፋይል"</string>
- <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ይህን ፋይል መቀበል ትፈልጋለህ?"</string>
- <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ከሌላ መሣሪያ የሚገባ ፋይል አለ፣ ይህን ፋይል ለመቀበል መፈለግህን አረጋግጥ።"</string>
+ <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"ይህን ፋይል መቀበል ይፈልጋሉ?"</string>
+ <string name="incoming_file_toast_msg" msgid="1733710749992901811">"ከሌላ መሣሪያ የሚገባ ፋይል አለ፣ ይህን ፋይል ለመቀበል መፈለግዎን ያረጋግጡ።"</string>
<string name="notification_receiving" msgid="4674648179652543984">"ብሉቱዝ ማጋሪያ፡ <xliff:g id="FILE">%1$s</xliff:g> እየተቀበለ"</string>
<string name="notification_received" msgid="3324588019186687985">"ብሉቱዝ ማጋሪያ፡ <xliff:g id="FILE">%1$s</xliff:g> ደርሷል"</string>
<string name="notification_received_fail" msgid="3619350997285714746">"ብሉቱዝ ማጋሪያ፡ ፋይል<xliff:g id="FILE">%1$s</xliff:g> አልደረሰም"</string>
@@ -78,7 +78,7 @@
<string name="not_exist_file_desc" msgid="4059531573790529229">"ፋይል የለም:: "\n</string>
<string name="enabling_progress_title" msgid="436157952334723406">"እባክዎ ይጠብቁ…"</string>
<string name="enabling_progress_content" msgid="4601542238119927904">"ብሉቱዝ በማብራት ላይ..."</string>
- <string name="bt_toast_1" msgid="972182708034353383">"ፋይሉ ይደርሳል።በማሳወቂያ ውስን ቦታ ውስጥ ሂደቱን ተመልከት።"</string>
+ <string name="bt_toast_1" msgid="972182708034353383">"ፋይሉ ይደርሳል።በማሳወቂያ ውስን ቦታ ውስጥ ሂደቱን ይመልከቱ።"</string>
<string name="bt_toast_2" msgid="8602553334099066582">"ፋይሉን መቀበል አይቻልም::"</string>
<string name="bt_toast_3" msgid="6707884165086862518">"ከ\"<xliff:g id="SENDER">%1$s</xliff:g>\" ፋይል መቀበል አቁሟል"</string>
<string name="bt_toast_4" msgid="4678812947604395649">"ፋይል ወደ \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" በመላክ ላይ"</string>
@@ -91,7 +91,7 @@
<string name="status_pending" msgid="2503691772030877944">"የፋይል ዝውውር ገና አልተጀመረም::"</string>
<string name="status_running" msgid="6562808920311008696">"የፋይል ዝውውር በመካሄድ ላይ ነው::"</string>
<string name="status_success" msgid="239573225847565868">"የፋይል ዝውውሩ በተሳካ ሁኔታ ተጠናቋል::"</string>
- <string name="status_not_accept" msgid="1695082417193780738">"ይዘት አይደገፍም::"</string>
+ <string name="status_not_accept" msgid="1695082417193780738">"ይዘቱ አይደገፍም።"</string>
<string name="status_forbidden" msgid="613956401054050725">"ይህ ዝውውር በታለመው መሣሪያ የተከለከለ ነው።"</string>
<string name="status_canceled" msgid="6664490318773098285">"ዝውውር በተጠቃሚ ተትቷል::"</string>
<string name="status_file_error" msgid="3671917770630165299">"የማከማቻ ጉዳይ"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index bda160d..c44c55e 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -25,7 +25,7 @@
<string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
<string name="unknown_device" msgid="9221903979877041009">"Dispositivo desconocido"</string>
<string name="unknownNumber" msgid="4994750948072751566">"Desconocido"</string>
- <string name="airplane_error_title" msgid="2683839635115739939">"Modo de avión"</string>
+ <string name="airplane_error_title" msgid="2683839635115739939">"Modo avión"</string>
<string name="airplane_error_msg" msgid="8698965595254137230">"No puedes usar Bluetooth en Modo avión."</string>
<string name="bt_enable_title" msgid="8657832550503456572"></string>
<string name="bt_enable_line1" msgid="7203551583048149">"Para acceder a nuestros servicios de Bluetooth, primero debes activar el Bluetooth."</string>
@@ -100,12 +100,12 @@
<string name="status_connection_error" msgid="947681831523219891">"Conexión incorrecta"</string>
<string name="status_protocol_error" msgid="3245444473429269539">"No se puede procesar la solicitud correctamente."</string>
<string name="status_unknown_error" msgid="8156660554237824912">"Error desconocido"</string>
- <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth recibido"</string>
+ <string name="btopp_live_folder" msgid="7967791481444474554">"Recibidos por Bluetooth"</string>
<string name="download_success" msgid="7036160438766730871">"Se recibieron <xliff:g id="FILE_SIZE">%1$s</xliff:g> completos."</string>
<string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> enviados por completo."</string>
<string name="inbound_history_title" msgid="6940914942271327563">"Transferencias de entrada"</string>
<string name="outbound_history_title" msgid="4279418703178140526">"Transferencias de salida"</string>
- <string name="no_transfers" msgid="3482965619151865672">"El historial de transferencia está vacío."</string>
+ <string name="no_transfers" msgid="3482965619151865672">"El historial de transferencias está vacío."</string>
<string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"Se borrarán todos los elementos de la lista."</string>
<string name="outbound_noti_title" msgid="8051906709452260849">"Compartir con Bluetooth: Archivos enviados"</string>
<string name="inbound_noti_title" msgid="4143352641953027595">"Compartir con Bluetooth: Archivos recibidos"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index d695f89..f67d9ba 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -62,7 +62,7 @@
<string name="download_fail_ok" msgid="1521733664438320300">"Oke"</string>
<string name="download_succ_line5" msgid="4509944688281573595">"File diterima"</string>
<string name="download_succ_ok" msgid="7053688246357050216">"Buka"</string>
- <string name="upload_line1" msgid="2055952074059709052">"Ke: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+ <string name="upload_line1" msgid="2055952074059709052">"Kepada: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
<string name="upload_line3" msgid="4920689672457037437">"Jenis file: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
<string name="upload_line5" msgid="7759322537674229752">"Mengirim file…"</string>
<string name="upload_succ_line5" msgid="5687317197463383601">"File terkirim"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 7a06b9e..a1593f6 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -31,7 +31,7 @@
<string name="bt_enable_line1" msgid="7203551583048149">"Para utilizar os serviços Bluetooth, tem de activar primeiro o Bluetooth."</string>
<string name="bt_enable_line2" msgid="4341936569415937994">"Activar o Bluetooth agora?"</string>
<string name="bt_enable_cancel" msgid="1988832367505151727">"Cancelar"</string>
- <string name="bt_enable_ok" msgid="3432462749994538265">"Activar"</string>
+ <string name="bt_enable_ok" msgid="3432462749994538265">"Ativar"</string>
<string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferência do ficheiro"</string>
<string name="incoming_file_confirm_content" msgid="6673812334377911289">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" quer enviar-lhe <xliff:g id="FILE">%2$s</xliff:g> (<xliff:g id="SIZE">%3$s</xliff:g>). "\n\n" Aceita o ficheiro?"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Recusar"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index b779100..384dcd9 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Fikia kidhibiti cha vipakuzi"</string>
+ <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Fikia kidhibiti cha vipakuliwa."</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Huruhusu programu kufikia kidhibiti cha BluetoothShare na kukitumia kuhamisha faili."</string>
<string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Ruhusu ufikiaji kifaa cha bluetooth."</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Huruhusu programu kuorodhesha kwa muda kifaa cha Bluetooth kwenye orodha ya vifaa maalum, ikiruhusu kifaa hicho kutuma faili kwa kifaa hiki bila uthibitishaji wa mtumiaji."</string>
@@ -73,7 +73,7 @@
<string name="upload_fail_cancel" msgid="9118496285835687125">"Funga"</string>
<string name="bt_error_btn_ok" msgid="5965151173011534240">"Sawa"</string>
<string name="unknown_file" msgid="6092727753965095366">"Faili isiyojulikana"</string>
- <string name="unknown_file_desc" msgid="480434281415453287">"Hakuna prog ya kushughulikia aina hii ya faili. "\n</string>
+ <string name="unknown_file_desc" msgid="480434281415453287">"Hakuna programu ya kushughulikia aina hii ya faili. "\n</string>
<string name="not_exist_file" msgid="3489434189599716133">"Hakuna faili."</string>
<string name="not_exist_file_desc" msgid="4059531573790529229">"Faili haipo. "\n</string>
<string name="enabling_progress_title" msgid="436157952334723406">"Tafadhali subiri…"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 13f9776..400d5a5 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -77,7 +77,7 @@
<string name="not_exist_file" msgid="3489434189599716133">"沒有檔案"</string>
<string name="not_exist_file_desc" msgid="4059531573790529229">"檔案不存在。"\n</string>
<string name="enabling_progress_title" msgid="436157952334723406">"請稍候…"</string>
- <string name="enabling_progress_content" msgid="4601542238119927904">"正在開啟藍牙..."</string>
+ <string name="enabling_progress_content" msgid="4601542238119927904">"正在開啟藍牙…"</string>
<string name="bt_toast_1" msgid="972182708034353383">"即將接收檔案,請在通知面板中查看進度。"</string>
<string name="bt_toast_2" msgid="8602553334099066582">"無法接收檔案。"</string>
<string name="bt_toast_3" msgid="6707884165086862518">"已停止接收來自「<xliff:g id="SENDER">%1$s</xliff:g>」的檔案"</string>
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/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 0e7515a..2ff6487 100755
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -39,6 +39,7 @@
private static final String TAG="A2dpService";
private A2dpStateMachine mStateMachine;
+ private Avrcp mAvrcp;
private static A2dpService sAd2dpService;
protected String getName() {
@@ -51,12 +52,14 @@
protected boolean start() {
mStateMachine = A2dpStateMachine.make(this, this);
+ mAvrcp = Avrcp.make(this);
setA2dpService(this);
return true;
}
protected boolean stop() {
mStateMachine.doQuit();
+ mAvrcp.doQuit();
return true;
}
@@ -64,6 +67,10 @@
if (mStateMachine!= null) {
mStateMachine.cleanup();
}
+ if (mAvrcp != null) {
+ mAvrcp.cleanup();
+ mAvrcp = null;
+ }
clearA2dpService();
return true;
}
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 4e1a9c8..f7cf37f 100755
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -58,7 +58,6 @@
import java.util.Set;
final class A2dpStateMachine extends StateMachine {
- private static final String TAG = "A2dpStateMachine";
private static final boolean DBG = false;
static final int CONNECT = 1;
@@ -116,7 +115,7 @@
}
private A2dpStateMachine(A2dpService svc, Context context) {
- super(TAG);
+ super("A2dpStateMachine");
mService = svc;
mContext = context;
mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -143,7 +142,7 @@
}
static A2dpStateMachine make(A2dpService svc, Context context) {
- Log.d(TAG, "make");
+ Log.d("A2dpStateMachine", "make");
A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context);
a2dpSm.start();
return a2dpSm;
@@ -167,7 +166,7 @@
public boolean processMessage(Message message) {
log("Disconnected process message: " + message.what);
if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
- Log.e(TAG, "ERROR: current, target, or mIncomingDevice not null in Disconnected");
+ loge("ERROR: current, target, or mIncomingDevice not null in Disconnected");
return NOT_HANDLED;
}
@@ -202,7 +201,7 @@
processConnectionEvent(event.valueInt, event.device);
break;
default:
- Log.e(TAG, "Unexpected stack event: " + event.type);
+ loge("Unexpected stack event: " + event.type);
break;
}
break;
@@ -221,11 +220,11 @@
private void processConnectionEvent(int state, BluetoothDevice device) {
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
- Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
+ logw("Ignore HF DISCONNECTED event, device: " + device);
break;
case CONNECTION_STATE_CONNECTING:
if (okToConnect(device)){
- Log.i(TAG,"Incoming A2DP accepted");
+ logi("Incoming A2DP accepted");
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
synchronized (A2dpStateMachine.this) {
@@ -234,7 +233,7 @@
}
} else {
//reject the connection and stay in Disconnected state itself
- Log.i(TAG,"Incoming A2DP rejected");
+ logi("Incoming A2DP rejected");
disconnectA2dpNative(getByteAddress(device));
// the other profile connection should be initiated
AdapterService adapterService = AdapterService.getAdapterService();
@@ -245,9 +244,9 @@
}
break;
case CONNECTION_STATE_CONNECTED:
- Log.w(TAG, "A2DP Connected from Disconnected state");
+ logw("A2DP Connected from Disconnected state");
if (okToConnect(device)){
- Log.i(TAG,"Incoming A2DP accepted");
+ logi("Incoming A2DP accepted");
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
synchronized (A2dpStateMachine.this) {
@@ -256,7 +255,7 @@
}
} else {
//reject the connection and stay in Disconnected state itself
- Log.i(TAG,"Incoming A2DP rejected");
+ logi("Incoming A2DP rejected");
disconnectA2dpNative(getByteAddress(device));
// the other profile connection should be initiated
AdapterService adapterService = AdapterService.getAdapterService();
@@ -267,10 +266,10 @@
}
break;
case CONNECTION_STATE_DISCONNECTING:
- Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
+ logw("Ignore HF DISCONNECTING event, device: " + device);
break;
default:
- Log.e(TAG, "Incorrect state: " + state);
+ loge("Incorrect state: " + state);
break;
}
}
@@ -317,7 +316,7 @@
processConnectionEvent(event.valueInt, event.device);
break;
default:
- Log.e(TAG, "Unexpected stack event: " + event.type);
+ loge("Unexpected stack event: " + event.type);
break;
}
break;
@@ -372,7 +371,7 @@
transitionTo(mDisconnected);
}
} else {
- Log.e(TAG, "Unknown device Disconnected: " + device);
+ loge("Unknown device Disconnected: " + device);
}
break;
case CONNECTION_STATE_CONNECTED:
@@ -405,7 +404,7 @@
transitionTo(mConnected);
}
} else {
- Log.e(TAG, "Unknown device Connected: " + device);
+ loge("Unknown device Connected: " + device);
// something is wrong here, but sync our state with stack
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
@@ -428,7 +427,7 @@
log("Stack and target device are connecting");
}
else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
- Log.e(TAG, "Another connecting event on the incoming device");
+ loge("Another connecting event on the incoming device");
} else {
// We get an incoming connecting request while Pending
// TODO(BT) is stack handing this case? let's ignore it for now
@@ -442,15 +441,15 @@
log("stack is disconnecting mCurrentDevice");
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
- Log.e(TAG, "TargetDevice is getting disconnected");
+ loge("TargetDevice is getting disconnected");
} else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
- Log.e(TAG, "IncomingDevice is getting disconnected");
+ loge("IncomingDevice is getting disconnected");
} else {
- Log.e(TAG, "Disconnecting unknow device: " + device);
+ loge("Disconnecting unknow device: " + device);
}
break;
default:
- Log.e(TAG, "Incorrect state: " + state);
+ loge("Incorrect state: " + state);
break;
}
}
@@ -470,7 +469,7 @@
public boolean processMessage(Message message) {
log("Connected process message: " + message.what);
if (mCurrentDevice == null) {
- Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
+ loge("ERROR: mCurrentDevice is null in Connected");
return NOT_HANDLED;
}
@@ -523,7 +522,7 @@
processAudioStateEvent(event.valueInt, event.device);
break;
default:
- Log.e(TAG, "Unexpected stack event: " + event.type);
+ loge("Unexpected stack event: " + event.type);
break;
}
break;
@@ -545,17 +544,17 @@
transitionTo(mDisconnected);
}
} else {
- Log.e(TAG, "Disconnected from unknown device: " + device);
+ loge("Disconnected from unknown device: " + device);
}
break;
default:
- Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
+ loge("Connection State Device: " + device + " bad state: " + state);
break;
}
}
private void processAudioStateEvent(int state, BluetoothDevice device) {
if (!mCurrentDevice.equals(device)) {
- Log.e(TAG, "Audio State Device:" + device + "is different from ConnectedDevice:" +
+ loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
mCurrentDevice);
return;
}
@@ -575,7 +574,7 @@
}
break;
default:
- Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
+ loge("Audio State Device: " + device + " bad state: " + state);
break;
}
}
@@ -607,7 +606,7 @@
}
return BluetoothProfile.STATE_DISCONNECTED;
} else {
- Log.e(TAG, "Bad currentState: " + currentState);
+ loge("Bad currentState: " + currentState);
return BluetoothProfile.STATE_DISCONNECTED;
}
}
@@ -720,10 +719,6 @@
return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
}
- private void log(String msg) {
- Log.d(TAG, msg);
- }
-
private class StackEvent {
int type = EVENT_TYPE_NONE;
int valueInt = 0;
diff --git a/src/com/android/bluetooth/a2dp/Avrcp.java b/src/com/android/bluetooth/a2dp/Avrcp.java
new file mode 100755
index 0000000..0f1e950
--- /dev/null
+++ b/src/com/android/bluetooth/a2dp/Avrcp.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2012 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.a2dp;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.AudioManager;
+import android.media.IRemoteControlDisplay;
+import android.media.MediaMetadataRetriever;
+import android.media.RemoteControlClient;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.content.Intent;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ParcelUuid;
+import android.util.Log;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * support Bluetooth AVRCP profile.
+ * support metadata, play status and event notification
+ */
+final class Avrcp {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "Avrcp";
+
+ private Context mContext;
+ private final AudioManager mAudioManager;
+ private AvrcpMessageHandler mHandler;
+ private IRemoteControlDisplayWeak mRemoteControlDisplay;
+ private int mClientGeneration;
+ private Metadata mMetadata;
+ private int mTransportControlFlags;
+ private int mCurrentPlayState;
+ private int mPlayStatusChangedNT;
+ private int mTrackChangedNT;
+ private long mTrackNumber;
+ private static final int MESSAGE_GET_PLAY_STATUS = 1;
+ private static final int MESSAGE_GET_ELEM_ATTRS = 2;
+ private static final int MESSAGE_REGISTER_NOTIFICATION = 3;
+ private static final int MSG_UPDATE_STATE = 100;
+ private static final int MSG_SET_METADATA = 101;
+ private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
+ private static final int MSG_SET_ARTWORK = 103;
+ private static final int MSG_SET_GENERATION_ID = 104;
+
+ static {
+ classInitNative();
+ }
+
+ private Avrcp(Context context) {
+ mMetadata = new Metadata();
+ mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
+ mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mTrackNumber = 0L;
+
+ mContext = context;
+
+ initNative();
+
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ private void start() {
+ HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
+ thread.start();
+ Looper looper = thread.getLooper();
+ mHandler = new AvrcpMessageHandler(looper);
+ mRemoteControlDisplay = new IRemoteControlDisplayWeak(mHandler);
+ mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
+ }
+
+ static Avrcp make(Context context) {
+ if (DEBUG) Log.v(TAG, "make");
+ Avrcp ar = new Avrcp(context);
+ ar.start();
+ return ar;
+ }
+
+ public void doQuit() {
+ mHandler.removeCallbacksAndMessages(null);
+ Looper looper = mHandler.getLooper();
+ if (looper != null) {
+ looper.quit();
+ }
+ mAudioManager.unregisterRemoteControlDisplay(mRemoteControlDisplay);
+ }
+
+ public void cleanup() {
+ cleanupNative();
+ }
+
+ private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
+ private WeakReference<Handler> mLocalHandler;
+ IRemoteControlDisplayWeak(Handler handler) {
+ mLocalHandler = new WeakReference<Handler>(handler);
+ }
+
+ @Override
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ // TODO handle current playback position and playback speed
+ handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
+ }
+ }
+
+ @Override
+ public void setMetadata(int generationId, Bundle metadata) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ }
+ }
+
+ @Override
+ public void setTransportControlFlags(int generationId, int flags) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void setArtwork(int generationId, Bitmap bitmap) {
+ }
+
+ @Override
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
+ handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
+ }
+ }
+
+ @Override
+ public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
+ boolean clearing) throws RemoteException {
+ Handler handler = mLocalHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(MSG_SET_GENERATION_ID,
+ clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
+ }
+ }
+ }
+
+ /** Handles Avrcp messages. */
+ private final class AvrcpMessageHandler extends Handler {
+ private AvrcpMessageHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2);
+ break;
+
+ case MSG_SET_METADATA:
+ if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
+ break;
+
+ case MSG_SET_TRANSPORT_CONTROLS:
+ if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
+ break;
+
+ case MSG_SET_ARTWORK:
+ if (mClientGeneration == msg.arg1) {
+ }
+ break;
+
+ case MSG_SET_GENERATION_ID:
+ if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
+ mClientGeneration = msg.arg1;
+ break;
+
+ case MESSAGE_GET_PLAY_STATUS:
+ if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
+ getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState), -1, -1);
+ break;
+
+ case MESSAGE_GET_ELEM_ATTRS:
+ {
+ String[] textArray;
+ int[] attrIds;
+ byte numAttr = (byte) msg.arg1;
+ ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
+ if (DEBUG) Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
+ attrIds = new int[numAttr];
+ textArray = new String[numAttr];
+ for (int i = 0; i < numAttr; ++i) {
+ attrIds[i] = attrList.get(i).intValue();
+ textArray[i] = getAttributeString(attrIds[i]);
+ }
+ getElementAttrRspNative(numAttr, attrIds, textArray);
+ break;
+ }
+ case MESSAGE_REGISTER_NOTIFICATION:
+ if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
+ " param=" + msg.arg2);
+ processRegisterNotification(msg.arg1, msg.arg2);
+ break;
+
+ }
+ }
+ }
+
+ private void updatePlayPauseState(int state) {
+ if (DEBUG) Log.v(TAG,
+ "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state);
+ if (state == mCurrentPlayState) {
+ return;
+ }
+
+ int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
+ int newPlayStatus = convertPlayStateToPlayStatus(state);
+ mCurrentPlayState = state;
+ if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
+ mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
+ registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
+ }
+ }
+
+ private void updateTransportControls(int transportControlFlags) {
+ mTransportControlFlags = transportControlFlags;
+ }
+
+ class Metadata {
+ private String artist;
+ private String trackTitle;
+ private String albumTitle;
+
+ public Metadata() {
+ artist = null;
+ trackTitle = null;
+ albumTitle = null;
+ }
+
+ public String toString() {
+ return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
+ albumTitle + "]";
+ }
+ }
+
+ private String getMdString(Bundle data, int id) {
+ return data.getString(Integer.toString(id));
+ }
+
+ private void updateMetadata(Bundle data) {
+ String oldMetadata = mMetadata.toString();
+ mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+ mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
+ mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
+ if (!oldMetadata.equals(mMetadata.toString())) {
+ mTrackNumber++;
+ if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
+ mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
+ sendTrackChangedRsp();
+ }
+ }
+ if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString());
+ }
+
+ private void getPlayStatus() {
+ Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
+ mHandler.sendMessage(msg);
+ }
+
+ private void getElementAttr(byte numAttr, int[] attrs) {
+ int i;
+ ArrayList<Integer> attrList = new ArrayList<Integer>();
+ for (i = 0; i < numAttr; ++i) {
+ attrList.add(attrs[i]);
+ }
+ Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, (int)numAttr, 0, attrList);
+ mHandler.sendMessage(msg);
+ }
+
+ private void registerNotification(int eventId, int param) {
+ Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
+ mHandler.sendMessage(msg);
+ }
+
+ private void processRegisterNotification(int eventId, int param) {
+ switch (eventId) {
+ case EVT_PLAY_STATUS_CHANGED:
+ mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
+ registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
+ convertPlayStateToPlayStatus(mCurrentPlayState));
+ break;
+
+ case EVT_TRACK_CHANGED:
+ mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
+ sendTrackChangedRsp();
+ break;
+
+ }
+ }
+
+ private void sendTrackChangedRsp() {
+ byte[] track = new byte[TRACK_ID_SIZE];
+ for (int i = 0; i < TRACK_ID_SIZE; ++i) {
+ track[i] = (byte) (mTrackNumber >> (8 * i));
+ }
+ registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
+ }
+
+ private String getAttributeString(int attrId) {
+ String attrStr = null;
+ switch (attrId) {
+ case MEDIA_ATTR_TITLE:
+ attrStr = mMetadata.trackTitle;
+ break;
+
+ case MEDIA_ATTR_ARTIST:
+ attrStr = mMetadata.artist;
+ break;
+
+ case MEDIA_ATTR_ALBUM:
+ attrStr = mMetadata.albumTitle;
+ break;
+
+ }
+ if (attrStr == null) {
+ attrStr = new String();
+ }
+ if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
+ return attrStr;
+ }
+
+ private int convertPlayStateToPlayStatus(int playState) {
+ int playStatus = PLAYSTATUS_ERROR;
+ switch (playState) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ playStatus = PLAYSTATUS_PLAYING;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ playStatus = PLAYSTATUS_STOPPED;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ playStatus = PLAYSTATUS_PAUSED;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ playStatus = PLAYSTATUS_FWD_SEEK;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ playStatus = PLAYSTATUS_REV_SEEK;
+ break;
+
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ case RemoteControlClient.PLAYSTATE_NONE:
+ playStatus = PLAYSTATUS_ERROR;
+ break;
+
+ }
+ return playStatus;
+ }
+
+ // Do not modify without updating the HAL bt_rc.h files.
+
+ // match up with btrc_play_status_t enum of bt_rc.h
+ final static int PLAYSTATUS_STOPPED = 0;
+ final static int PLAYSTATUS_PLAYING = 1;
+ final static int PLAYSTATUS_PAUSED = 2;
+ final static int PLAYSTATUS_FWD_SEEK = 3;
+ final static int PLAYSTATUS_REV_SEEK = 4;
+ final static int PLAYSTATUS_ERROR = 255;
+
+ // match up with btrc_media_attr_t enum of bt_rc.h
+ final static int MEDIA_ATTR_TITLE = 1;
+ final static int MEDIA_ATTR_ARTIST = 2;
+ final static int MEDIA_ATTR_ALBUM = 3;
+ final static int MEDIA_ATTR_TRACK_NUM = 4;
+ final static int MEDIA_ATTR_NUM_TRACKS = 5;
+ final static int MEDIA_ATTR_GENRE = 6;
+ final static int MEDIA_ATTR_PLAYING_TIME = 7;
+
+ // match up with btrc_event_id_t enum of bt_rc.h
+ final static int EVT_PLAY_STATUS_CHANGED = 1;
+ final static int EVT_TRACK_CHANGED = 2;
+ final static int EVT_TRACK_REACHED_END = 3;
+ final static int EVT_TRACK_REACHED_START = 4;
+ final static int EVT_PLAY_POS_CHANGED = 5;
+ final static int EVT_BATT_STATUS_CHANGED = 6;
+ final static int EVT_SYSTEM_STATUS_CHANGED = 7;
+ final static int EVT_APP_SETTINGS_CHANGED = 8;
+
+ // match up with btrc_notification_type_t enum of bt_rc.h
+ final static int NOTIFICATION_TYPE_INTERIM = 0;
+ final static int NOTIFICATION_TYPE_CHANGED = 1;
+
+ // match up with BTRC_UID_SIZE of bt_rc.h
+ final static int TRACK_ID_SIZE = 8;
+
+ private native static void classInitNative();
+ private native void initNative();
+ private native void cleanupNative();
+ private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
+ private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
+ private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
+ private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
+}
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/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index 9b3c20a..b05becd 100755
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -33,7 +33,8 @@
import java.util.ArrayList;
class AdapterProperties {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
private static final String TAG = "BluetoothAdapterProperties";
private static final int BD_ADDR_LEN = 6; // 6 bytes
@@ -203,7 +204,7 @@
*/
int getState() {
synchronized (mObject) {
- debugLog("State = " + mState);
+ if (VDBG) debugLog("State = " + mState);
return mState;
}
}
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 2fd6532..bc5851a 100755
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -889,7 +889,7 @@
return BluetoothAdapter.STATE_OFF;
}
else {
- debugLog("getState(): mAdapterProperties: " + mAdapterProperties);
+ if (DBG) debugLog("getState(): mAdapterProperties: " + mAdapterProperties);
return mAdapterProperties.getState();
}
}
diff --git a/src/com/android/bluetooth/btservice/AdapterState.java b/src/com/android/bluetooth/btservice/AdapterState.java
index a50a463..fbc32a5 100755
--- a/src/com/android/bluetooth/btservice/AdapterState.java
+++ b/src/com/android/bluetooth/btservice/AdapterState.java
@@ -36,7 +36,8 @@
*/
final class AdapterState extends StateMachine {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
private static final String TAG = "BluetoothAdapterState";
static final int USER_TURN_ON = 1;
@@ -72,13 +73,13 @@
public boolean isTurningOn() {
boolean isTurningOn= mPendingCommandState.isTurningOn();
- if (DBG) Log.d(TAG,"isTurningOn()=" + isTurningOn);
+ if (VDBG) Log.d(TAG,"isTurningOn()=" + isTurningOn);
return isTurningOn;
}
public boolean isTurningOff() {
boolean isTurningOff= mPendingCommandState.isTurningOff();
- if (DBG) Log.d(TAG,"isTurningOff()=" + isTurningOff);
+ if (VDBG) Log.d(TAG,"isTurningOff()=" + isTurningOff);
return isTurningOff;
}
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;
+ }
+}
diff --git a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
index f5fbacc..f6d282c 100755
--- a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
+++ b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
@@ -238,9 +238,12 @@
cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ // STOPSHIP: Change back to getRilVoiceRadioTechnology
if (mServiceState != null &&
- (mServiceState.getRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
- mServiceState.getRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
+ (mServiceState.getRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 ||
+ mServiceState.getRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) {
int evdoEcio = signalStrength.getEvdoEcio();
int evdoSnr = signalStrength.getEvdoSnr();
int levelEvdoEcio = 0;
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index d9b2b7d..059f326 100755
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -1957,10 +1957,10 @@
return ret;
}
-
- private void log(String msg) {
+ @Override
+ protected void log(String msg) {
if (DBG) {
- Log.d(TAG, msg);
+ super.log(msg);
}
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppBatch.java b/src/com/android/bluetooth/opp/BluetoothOppBatch.java
index 7f51fe2..08701c4 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppBatch.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppBatch.java
@@ -187,6 +187,10 @@
return (mShares.size() == 0);
}
+ public int getNumShares() {
+ return mShares.size();
+ }
+
/**
* Get the running status of the batch
* @return
diff --git a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
index bbe7899..e63d67c 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
@@ -169,7 +169,7 @@
Intent intent1 = new Intent();
intent1.setAction(action);
intent1.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent1.setData(uri);
+ intent1.setDataAndNormalize(uri);
this.sendBroadcast(intent1);
finish();
} else {
diff --git a/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java b/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java
index b1266a6..0cc0a21 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java
@@ -68,7 +68,7 @@
private static Intent createLiveFolder(Context context, Uri uri, String name, int icon) {
final Intent intent = new Intent();
- intent.setData(uri);
+ intent.setDataAndNormalize(uri);
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, new Intent(
Constants.ACTION_OPEN, BluetoothShare.CONTENT_URI));
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
diff --git a/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
index 7589850..5bc687b 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppNotification.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
@@ -330,7 +330,7 @@
Intent intent = new Intent(Constants.ACTION_LIST);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
+ intent.setDataAndNormalize(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
b.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
mNotificationMgr.notify(item.id, b.getNotification());
@@ -491,7 +491,7 @@
Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setData(contentUri);
+ intent.setDataAndNormalize(contentUri);
n.when = timeStamp;
n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
@@ -499,7 +499,7 @@
intent = new Intent(Constants.ACTION_HIDE);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setData(contentUri);
+ intent.setDataAndNormalize(contentUri);
n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
mNotificationMgr.notify(id, n);
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
index dce7fa3..d3eb881 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -83,10 +83,10 @@
mTransport = transport;
}
- public void start(Handler handler) {
+ public void start(Handler handler, int numShares) {
if (D) Log.d(TAG, "Start!");
mCallback = handler;
- mThread = new ClientThread(mContext, mTransport);
+ mThread = new ClientThread(mContext, mTransport, numShares);
mThread.start();
}
@@ -140,13 +140,15 @@
private boolean mConnected = false;
- public ClientThread(Context context, ObexTransport transport) {
+ private int mNumShares;
+
+ public ClientThread(Context context, ObexTransport transport, int initialNumShares) {
super("BtOpp ClientThread");
mContext1 = context;
mTransport1 = transport;
waitingForShare = true;
mWaitingForRemote = false;
-
+ mNumShares = initialNumShares;
PowerManager pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
}
@@ -171,7 +173,7 @@
mInterrupted = true;
}
if (!mInterrupted) {
- connect();
+ connect(mNumShares);
}
while (!mInterrupted) {
@@ -229,7 +231,7 @@
}
}
- private void connect() {
+ private void connect(int numShares) {
if (D) Log.d(TAG, "Create ClientSession with transport " + mTransport1.toString());
try {
mCs = new ClientSession(mTransport1);
@@ -240,6 +242,7 @@
if (mConnected) {
mConnected = false;
HeaderSet hs = new HeaderSet();
+ hs.setHeader(HeaderSet.COUNT, (long) numShares);
synchronized (this) {
mWaitingForRemote = true;
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
index 3512fad..5bd54af 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
@@ -128,7 +128,7 @@
/**
* Called from BluetoothOppTransfer to start the "Transfer"
*/
- public void start(Handler handler) {
+ public void start(Handler handler, int numShares) {
if (D) Log.d(TAG, "Start!");
mCallback = handler;
@@ -286,6 +286,7 @@
values.put(BluetoothShare.USER_CONFIRMATION,
BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED);
needConfirm = false;
+
}
Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
@@ -539,16 +540,39 @@
if (D) Log.d(TAG, "onConnect");
if (V) Constants.logHeader(request);
+ Long objectCount = null;
try {
byte[] uuid = (byte[])request.getHeader(HeaderSet.TARGET);
if (V) Log.v(TAG, "onConnect(): uuid =" + Arrays.toString(uuid));
if(uuid != null) {
return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
}
+
+ objectCount = (Long) request.getHeader(HeaderSet.COUNT);
} catch (IOException e) {
Log.e(TAG, e.toString());
return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
}
+ String destination;
+ if (mTransport instanceof BluetoothOppRfcommTransport) {
+ destination = ((BluetoothOppRfcommTransport)mTransport).getRemoteAddress();
+ } else {
+ destination = "FF:FF:FF:00:00:00";
+ }
+ boolean isHandover = BluetoothOppManager.getInstance(mContext).
+ isWhitelisted(destination);
+ if (isHandover) {
+ // Notify the handover requester file transfer has started
+ Intent intent = new Intent(Constants.ACTION_HANDOVER_STARTED);
+ if (objectCount != null) {
+ intent.putExtra(Constants.EXTRA_BT_OPP_OBJECT_COUNT, objectCount.intValue());
+ } else {
+ intent.putExtra(Constants.EXTRA_BT_OPP_OBJECT_COUNT,
+ Constants.COUNT_HEADER_UNAVAILABLE);
+ }
+ intent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, destination);
+ mContext.sendBroadcast(intent, Constants.HANDOVER_STATUS_PERMISSION);
+ }
mTimestamp = System.currentTimeMillis();
return ResponseCodes.OBEX_HTTP_OK;
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexSession.java
index ce16875..19c98d2 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexSession.java
@@ -66,7 +66,7 @@
int SESSION_TIMEOUT = 50000;
- void start(Handler sessionHandler);
+ void start(Handler sessionHandler, int numShares);
void stop();
diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
index a061fa8..f4c8d9c 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
@@ -116,7 +116,7 @@
Uri uri = intent.getData();
Intent in = new Intent(context, BluetoothOppIncomingFileConfirmActivity.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- in.setData(uri);
+ in.setDataAndNormalize(uri);
context.startActivity(in);
NotificationManager notMgr = (NotificationManager)context
@@ -157,7 +157,7 @@
} else {
Intent in = new Intent(context, BluetoothOppTransferActivity.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- in.setData(uri);
+ in.setDataAndNormalize(uri);
context.startActivity(in);
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
index 2be679c..2dbed49 100755
--- a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
@@ -453,7 +453,7 @@
if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());
}
- mSession.start(mSessionHandler);
+ mSession.start(mSessionHandler, mBatch.getNumShares());
processCurrentShare();
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java b/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java
index 697e3cb..abe343f 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java
@@ -289,7 +289,7 @@
} else {
Intent in = new Intent(this, BluetoothOppTransferActivity.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- in.setData(contentUri);
+ in.setDataAndNormalize(contentUri);
this.startActivity(in);
}
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
index 7e03281..a3befb9 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppUtility.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -194,7 +194,7 @@
if (isRecognizedFileType(context, path, mimetype)) {
Intent activityIntent = new Intent(Intent.ACTION_VIEW);
- activityIntent.setDataAndType(path, mimetype);
+ activityIntent.setDataAndTypeAndNormalize(path, mimetype);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
@@ -222,7 +222,7 @@
if (D) Log.d(TAG, "RecognizedFileType() fileUri: " + fileUri + " mimetype: " + mimetype);
Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW);
- mimetypeIntent.setDataAndType(fileUri, mimetype);
+ mimetypeIntent.setDataAndTypeAndNormalize(fileUri, mimetype);
List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(mimetypeIntent,
PackageManager.MATCH_DEFAULT_ONLY);
diff --git a/src/com/android/bluetooth/opp/Constants.java b/src/com/android/bluetooth/opp/Constants.java
index b17c0e4..29df875 100644
--- a/src/com/android/bluetooth/opp/Constants.java
+++ b/src/com/android/bluetooth/opp/Constants.java
@@ -88,6 +88,10 @@
public static final String ACTION_HANDOVER_SEND_MULTIPLE =
"android.btopp.intent.action.HANDOVER_SEND_MULTIPLE";
+ /** the intent that is used for indicating an incoming transfer*/
+ public static final String ACTION_HANDOVER_STARTED =
+ "android.btopp.intent.action.BT_OPP_HANDOVER_STARTED";
+
/** intent action used to indicate the progress of a handover transfer */
public static final String ACTION_BT_OPP_TRANSFER_PROGRESS =
"android.btopp.intent.action.BT_OPP_TRANSFER_PROGRESS";
@@ -104,6 +108,10 @@
public static final String EXTRA_BT_OPP_ADDRESS =
"android.btopp.intent.extra.BT_OPP_ADDRESS";
+ public static final String EXTRA_BT_OPP_OBJECT_COUNT =
+ "android.btopp.intent.extra.BT_OPP_OBJECT_COUNT";
+
+ public static final int COUNT_HEADER_UNAVAILABLE = -1;
public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
@@ -210,6 +218,9 @@
"application/msword",
"application/vnd.ms-powerpoint",
"application/pdf",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
};
/**
@@ -277,7 +288,7 @@
if (BluetoothShare.isStatusCompleted(status)) {
Intent intent = new Intent(BluetoothShare.TRANSFER_COMPLETED_ACTION);
intent.setClassName(THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
- intent.setData(contentUri);
+ intent.setDataAndNormalize(contentUri);
context.sendBroadcast(intent);
}
}
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
index c0ada7c..22f4d83 100755
--- a/src/com/android/bluetooth/pan/PanService.java
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -30,17 +30,21 @@
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
+import com.android.internal.util.AsyncChannel;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
@@ -73,6 +77,8 @@
private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
private boolean mTetherOn = false;
+ AsyncChannel mTetherAc;
+
static {
classInitNative();
@@ -97,11 +103,20 @@
}
initializeNative();
mNativeAvailable=true;
+
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ cm.supplyMessenger(ConnectivityManager.TYPE_BLUETOOTH, new Messenger(mHandler));
+
return true;
}
protected boolean stop() {
mHandler.removeCallbacksAndMessages(null);
+ if (mTetherAc != null) {
+ mTetherAc.disconnect();
+ mTetherAc = null;
+ }
return true;
}
@@ -113,8 +128,8 @@
if(mPanDevices != null) {
List<BluetoothDevice> DevList = getConnectedDevices();
for(BluetoothDevice dev : DevList) {
- handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
+ handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
}
mPanDevices.clear();
}
@@ -131,11 +146,13 @@
case MESSAGE_CONNECT:
{
BluetoothDevice device = (BluetoothDevice) msg.obj;
- if (!connectPanNative(Utils.getByteAddress(device), BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
+ if (!connectPanNative(Utils.getByteAddress(device),
+ BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
- BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
- handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
+ BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
+ handlePanDeviceStateChange(device, null,
+ BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
+ BluetoothPan.REMOTE_NAP_ROLE);
break;
}
}
@@ -144,10 +161,12 @@
{
BluetoothDevice device = (BluetoothDevice) msg.obj;
if (!disconnectPanNative(Utils.getByteAddress(device)) ) {
- handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTING,
- BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
- handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
+ handlePanDeviceStateChange(device, mPanIfName,
+ BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE,
+ BluetoothPan.REMOTE_NAP_ROLE);
+ handlePanDeviceStateChange(device, mPanIfName,
+ BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
+ BluetoothPan.REMOTE_NAP_ROLE);
break;
}
}
@@ -157,9 +176,33 @@
ConnectState cs = (ConnectState)msg.obj;
BluetoothDevice device = getDevice(cs.addr);
// TBD get iface from the msg
- if (DBG) log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
- handlePanDeviceStateChange(device, mPanIfName /* iface */, convertHalState(cs.state),
- cs.local_role, cs.remote_role);
+ if (DBG) {
+ log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
+ }
+ handlePanDeviceStateChange(device, mPanIfName /* iface */,
+ convertHalState(cs.state), cs.local_role, cs.remote_role);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ {
+ if (mTetherAc != null) {
+ mTetherAc.replyToMessage(msg,
+ AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
+ } else {
+ mTetherAc = new AsyncChannel();
+ mTetherAc.connected(null, this, msg.replyTo);
+ mTetherAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ }
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT:
+ {
+ if (mTetherAc != null) {
+ mTetherAc.disconnect();
+ mTetherAc = null;
+ }
}
break;
}
@@ -169,7 +212,8 @@
/**
* Handlers for incoming service calls
*/
- private static class BluetoothPanBinder extends IBluetoothPan.Stub implements IProfileServiceBinder {
+ private static class BluetoothPanBinder extends IBluetoothPan.Stub
+ implements IProfileServiceBinder {
private PanService mService;
public BluetoothPanBinder(PanService svc) {
mService = svc;
@@ -328,8 +372,12 @@
int local_role;
int remote_role;
};
- private void onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role) {
- if (DBG) log("onConnectStateChanged: " + state + ", local role:" + local_role + ", remote_role: " + remote_role);
+ private void onConnectStateChanged(byte[] address, int state, int error, int local_role,
+ int remote_role) {
+ if (DBG) {
+ log("onConnectStateChanged: " + state + ", local role:" + local_role +
+ ", remote_role: " + remote_role);
+ }
Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
msg.obj = new ConnectState(address, state, error, local_role, remote_role);
mHandler.sendMessage(msg);
@@ -359,8 +407,11 @@
void handlePanDeviceStateChange(BluetoothDevice device,
String iface, int state, int local_role, int remote_role) {
- if(DBG) Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
- ", state: " + state + ", local_role:" + local_role + ", remote_role:" + remote_role);
+ if(DBG) {
+ Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
+ ", state: " + state + ", local_role:" + local_role + ", remote_role:" +
+ remote_role);
+ }
int prevState;
String ifaceAddr = null;
BluetoothPanDevice panDevice = mPanDevices.get(device);
@@ -392,31 +443,18 @@
}
} else {
// PANU Role = reverse Tether
- Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE");
+ Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " +
+ state + ", prevState = " + prevState);
if (state == BluetoothProfile.STATE_CONNECTED) {
- if(DBG) Log.d(TAG, "handlePanDeviceStateChange: panu STATE_CONNECTED, startReverseTether");
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- Log.d(TAG, "call INetworkManagementService.startReverseTethering()");
- try {
- service.startReverseTethering(iface);
- } catch (Exception e) {
- Log.e(TAG, "Cannot start reverse tethering: " + e);
- return;
- }
- } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
- (prevState == BluetoothProfile.STATE_CONNECTED ||
- prevState == BluetoothProfile.STATE_DISCONNECTING)) {
- if(DBG) Log.d(TAG, "handlePanDeviceStateChange: stopReverseTether, panDevice.mIface: "
- + panDevice.mIface);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- try {
- service.stopReverseTethering();
- } catch(Exception e) {
- Log.e(TAG, "Cannot stop reverse tethering: " + e);
- return;
- }
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ mTetherAc.sendMessage(NetworkStateTracker.EVENT_NETWORK_CONNECTED, lp);
+ } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
+ (prevState == BluetoothProfile.STATE_CONNECTED ||
+ prevState == BluetoothProfile.STATE_DISCONNECTING)) {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ mTetherAc.sendMessage(NetworkStateTracker.EVENT_NETWORK_DISCONNECTED, lp);
}
}