summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfredc <fredc@broadcom.com>2012-04-12 00:18:52 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-07-16 21:59:14 -0700
commit6654f5c903de510a70f9e72cd5ad7837b615d93f (patch)
tree293e5e07f1b092f37acd4ecada866cd91016f0ff
parent990b2cd65392bc6db58eda732ad41de22b713202 (diff)
downloadandroid_packages_apps_Bluetooth-6654f5c903de510a70f9e72cd5ad7837b615d93f.tar.gz
android_packages_apps_Bluetooth-6654f5c903de510a70f9e72cd5ad7837b615d93f.tar.bz2
android_packages_apps_Bluetooth-6654f5c903de510a70f9e72cd5ad7837b615d93f.zip
Non persistent adapter service
Change-Id: I65e1c18e2899cea0a1e5c0102c4d24d39dce0249 Conflicts: jni/com_android_bluetooth_hdp.cpp jni/com_android_bluetooth_hid.cpp Conflicts: jni/com_android_bluetooth_hid.cpp
-rw-r--r--AndroidManifest.xml21
-rw-r--r--jni/Android.mk3
-rw-r--r--jni/com_android_bluetooth.h2
-rw-r--r--jni/com_android_bluetooth_a2dp.cpp65
-rwxr-xr-xjni/com_android_bluetooth_btservice_AdapterService.cpp9
-rw-r--r--jni/com_android_bluetooth_hdp.cpp67
-rw-r--r--jni/com_android_bluetooth_hfp.cpp67
-rwxr-xr-xjni/com_android_bluetooth_hid.cpp73
-rw-r--r--jni/com_android_bluetooth_pan.cpp239
-rw-r--r--src/com/android/bluetooth/Utils.java15
-rwxr-xr-xsrc/com/android/bluetooth/a2dp/A2dpService.java63
-rwxr-xr-xsrc/com/android/bluetooth/a2dp/A2dpStateMachine.java9
-rw-r--r--src/com/android/bluetooth/btservice/AdapterApp.java36
-rwxr-xr-xsrc/com/android/bluetooth/btservice/AdapterProperties.java12
-rwxr-xr-xsrc/com/android/bluetooth/btservice/AdapterService.java290
-rw-r--r--src/com/android/bluetooth/btservice/AdapterState.java25
-rwxr-xr-xsrc/com/android/bluetooth/btservice/BondStateMachine.java21
-rw-r--r--src/com/android/bluetooth/btservice/JniCallbacks.java12
-rwxr-xr-xsrc/com/android/bluetooth/btservice/RemoteDevices.java14
-rw-r--r--src/com/android/bluetooth/hdp/HealthService.java82
-rwxr-xr-xsrc/com/android/bluetooth/hfp/HeadsetService.java69
-rwxr-xr-xsrc/com/android/bluetooth/hfp/HeadsetStateMachine.java20
-rwxr-xr-xsrc/com/android/bluetooth/hid/HidService.java59
-rw-r--r--src/com/android/bluetooth/pan/PanService.java544
-rw-r--r--src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java6
-rw-r--r--src/com/android/bluetooth/pbap/BluetoothPbapService.java33
26 files changed, 1711 insertions, 145 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index df07838df..a104eebbd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -39,10 +39,13 @@
<uses-permission android:name="android.permission.NET_ADMIN" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.NET_TUNNELING" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<application
- android:name=".btservice.AdapterService"
+ android:name=".btservice.AdapterApp"
android:icon="@drawable/bt_share"
- android:persistent="true"
+ android:persistent="false"
android:label="@string/app_name">
<uses-library android:name="javax.obex" />
<provider android:name=".opp.BluetoothOppProvider"
@@ -54,6 +57,13 @@
</provider>
<service
android:process="@string/process"
+ android:name = ".btservice.AdapterService">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetooth" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
android:name=".opp.BluetoothOppService"
android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
<receiver
@@ -210,5 +220,12 @@
<action android:name="android.bluetooth.IBluetoothHealth" />
</intent-filter>
</service>
+ <service
+ android:process="@string/process"
+ android:name = ".pan.PanService">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothPan" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/jni/Android.mk b/jni/Android.mk
index 5f07ab525..4098907f7 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -7,7 +7,8 @@ LOCAL_SRC_FILES:= \
com_android_bluetooth_hfp.cpp \
com_android_bluetooth_a2dp.cpp \
com_android_bluetooth_hid.cpp \
- com_android_bluetooth_hdp.cpp
+ com_android_bluetooth_hdp.cpp \
+ com_android_bluetooth_pan.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/jni/com_android_bluetooth.h b/jni/com_android_bluetooth.h
index 7f8426771..366fbe1c4 100644
--- a/jni/com_android_bluetooth.h
+++ b/jni/com_android_bluetooth.h
@@ -27,6 +27,8 @@ int register_com_android_bluetooth_hid(JNIEnv* env);
int register_com_android_bluetooth_hdp(JNIEnv* env);
+int register_com_android_bluetooth_pan(JNIEnv* env);
+
}
#endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/jni/com_android_bluetooth_a2dp.cpp b/jni/com_android_bluetooth_a2dp.cpp
index ebc23a5a8..710530e29 100644
--- a/jni/com_android_bluetooth_a2dp.cpp
+++ b/jni/com_android_bluetooth_a2dp.cpp
@@ -69,6 +69,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
+ /*
if ( (btInf = getBluetoothInterface()) == NULL) {
LOGE("Bluetooth module is not loaded");
return;
@@ -79,24 +80,76 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
LOGE("Failed to get Bluetooth A2DP Interface");
return;
}
+ */
// TODO(BT) do this only once or
// Do we need to do this every time the BT reenables?
+ /*
if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
LOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
sBluetoothA2dpInterface = NULL;
return;
- }
+ }*/
LOGI("%s: succeeds", __FUNCTION__);
}
-static void initializeNativeDataNative(JNIEnv *env, jobject object) {
- // TODO(BT) clean it up when a2dp service is stopped
- // Is there a need to do cleanup since A2DP is always present for phone and tablet?
+static void initNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothA2dpInterface !=NULL) {
+ LOGW("Cleaning up A2DP Interface before initializing...");
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up A2DP callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sBluetoothA2dpInterface = (btav_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
+ LOGE("Failed to get Bluetooth A2DP Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
+ LOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
+ sBluetoothA2dpInterface = NULL;
+ return;
+ }
+
mCallbacksObj = env->NewGlobalRef(object);
}
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothA2dpInterface !=NULL) {
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
jbyte *addr;
bt_bdaddr_t * btAddr;
@@ -140,10 +193,10 @@ static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray add
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
+ {"initNative", "()V", (void *) initNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
{"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
{"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
- // TODO(BT) clean up
};
int register_com_android_bluetooth_a2dp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 2af399b85..a9c276f93 100755
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -496,6 +496,8 @@ static bool cleanupNative(JNIEnv *env, jobject obj) {
if (!sBluetoothInterface) return result;
sBluetoothInterface->cleanup();
+ LOGV("%s: return from cleanup",__FUNCTION__);
+
env->DeleteGlobalRef(sJniCallbacksObj);
return JNI_TRUE;
}
@@ -837,7 +839,7 @@ static int createSocketChannelNative(JNIEnv *env, jobject object, jint type,
LOGE("failed to get uuid");
goto Fail;
}
-
+ LOGE("SOCK FLAG = %x ***********************",flag);
if ( (status = sBluetoothSocketInterface->listen((btsock_type_t) type, service_name,
(const uint8_t*) uuid, channel, &socket_fd, flag)) != BT_STATUS_SUCCESS) {
LOGE("Socket listen failed: %d", status);
@@ -934,5 +936,10 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved)
return JNI_ERR;
}
+ if ((status = android::register_com_android_bluetooth_pan(e)) < 0) {
+ LOGE("jni pan registration failure: %d", status);
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_6;
}
diff --git a/jni/com_android_bluetooth_hdp.cpp b/jni/com_android_bluetooth_hdp.cpp
index 07c0e4f21..2389307e8 100644
--- a/jni/com_android_bluetooth_hdp.cpp
+++ b/jni/com_android_bluetooth_hdp.cpp
@@ -87,13 +87,14 @@ static bthl_callbacks_t sBluetoothHdpCallbacks = {
static void classInitNative(JNIEnv* env, jclass clazz) {
int err;
- const bt_interface_t* btInf;
- bt_status_t status;
+// const bt_interface_t* btInf;
+// bt_status_t status;
method_onAppRegistrationState = env->GetMethodID(clazz, "onAppRegistrationState", "(II)V");
method_onChannelStateChanged = env->GetMethodID(clazz, "onChannelStateChanged",
"(I[BIIILjava/io/FileDescriptor;)V");
+/*
if ( (btInf = getBluetoothInterface()) == NULL) {
LOGE("Bluetooth module is not loaded");
return;
@@ -112,15 +113,69 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
sBluetoothHdpInterface = NULL;
return;
}
+*/
LOGI("%s: succeeds", __FUNCTION__);
}
-static void initializeNativeDataNative(JNIEnv *env, jobject object) {
- // TODO(BT) clean it up when hdp service is stopped
+static void initializeNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHdpInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth Health Interface before initializing...");
+ sBluetoothHdpInterface->cleanup();
+ sBluetoothHdpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth Health callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sBluetoothHdpInterface = (bthl_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_HEALTH_ID)) == NULL) {
+ LOGE("Failed to get Bluetooth Health Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothHdpInterface->init(&sBluetoothHdpCallbacks)) != BT_STATUS_SUCCESS) {
+ LOGE("Failed to initialize Bluetooth HDP, status: %d", status);
+ sBluetoothHdpInterface = NULL;
+ return;
+ }
+
mCallbacksObj = env->NewGlobalRef(object);
}
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHdpInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth Health Interface...");
+ sBluetoothHdpInterface->cleanup();
+ sBluetoothHdpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth Health object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
static jint registerHealthAppNative(JNIEnv *env, jobject object, jint data_type,
jint role, jstring name, jint channel_type) {
bt_status_t status;
@@ -201,12 +256,12 @@ static jboolean disconnectChannelNative(JNIEnv *env, jobject object, jint channe
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
{"registerHealthAppNative", "(IILjava/lang/String;I)I", (void *) registerHealthAppNative},
{"unregisterHealthAppNative", "(I)Z", (void *) unregisterHealthAppNative},
{"connectChannelNative", "([BI)I", (void *) connectChannelNative},
{"disconnectChannelNative", "(I)Z", (void *) disconnectChannelNative},
- // TBD
};
int register_com_android_bluetooth_hdp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_hfp.cpp b/jni/com_android_bluetooth_hfp.cpp
index ef84252bb..c3d4c9225 100644
--- a/jni/com_android_bluetooth_hfp.cpp
+++ b/jni/com_android_bluetooth_hfp.cpp
@@ -202,8 +202,10 @@ static bthf_callbacks_t sBluetoothHfpCallbacks = {
static void classInitNative(JNIEnv* env, jclass clazz) {
int err;
+ /*
const bt_interface_t* btInf;
bt_status_t status;
+ */
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
@@ -223,6 +225,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
method_onUnknownAt = env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;)V");
method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "()V");
+ /*
if ( (btInf = getBluetoothInterface()) == NULL) {
LOGE("Bluetooth module is not loaded");
return;
@@ -241,17 +244,69 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
sBluetoothHfpInterface = NULL;
return;
}
+ */
LOGI("%s: succeeds", __FUNCTION__);
}
-static void initializeNativeDataNative(JNIEnv *env, jobject object) {
- // TODO(BT) clean it up when hfp service is stopped
- // Is there a need to do cleanup since HFP is always present for phone?
- // We need handle it for tablets. But should that be handle at compile time?
+static void initializeNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHfpInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth Handsfree Interface before initializing...");
+ sBluetoothHfpInterface->cleanup();
+ sBluetoothHfpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth Handsfree callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sBluetoothHfpInterface = (bthf_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID)) == NULL) {
+ LOGE("Failed to get Bluetooth Handsfree Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothHfpInterface->init(&sBluetoothHfpCallbacks)) != BT_STATUS_SUCCESS) {
+ LOGE("Failed to initialize Bluetooth HFP, status: %d", status);
+ sBluetoothHfpInterface = NULL;
+ return;
+ }
+
mCallbacksObj = env->NewGlobalRef(object);
}
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHfpInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth Handsfree Interface...");
+ sBluetoothHfpInterface->cleanup();
+ sBluetoothHfpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth Handsfree callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {
jbyte *addr;
bt_status_t status;
@@ -468,7 +523,8 @@ static jboolean phoneStateChangeNative(JNIEnv *env, jobject object, jint num_act
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
{"connectHfpNative", "([B)Z", (void *) connectHfpNative},
{"disconnectHfpNative", "([B)Z", (void *) disconnectHfpNative},
{"connectAudioNative", "([B)Z", (void *) connectAudioNative},
@@ -483,7 +539,6 @@ static JNINativeMethod sMethods[] = {
{"atResponseCodeNative", "(I)Z", (void *)atResponseCodeNative},
{"clccResponseNative", "(IIIIZLjava/lang/String;I)Z", (void *) clccResponseNative},
{"phoneStateChangeNative", "(IIILjava/lang/String;I)Z", (void *) phoneStateChangeNative},
- // TODO(BT) clean up
};
int register_com_android_bluetooth_hfp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_hid.cpp b/jni/com_android_bluetooth_hid.cpp
index d0c178daa..b517ad05a 100755
--- a/jni/com_android_bluetooth_hid.cpp
+++ b/jni/com_android_bluetooth_hid.cpp
@@ -132,13 +132,14 @@ static bthh_callbacks_t sBluetoothHidCallbacks = {
static void classInitNative(JNIEnv* env, jclass clazz) {
int err;
- const bt_interface_t* btInf;
- bt_status_t status;
+// const bt_interface_t* btInf;
+// bt_status_t status;
method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
+/*
if ( (btInf = getBluetoothInterface()) == NULL) {
LOGE("Bluetooth module is not loaded");
return;
@@ -158,14 +159,73 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
return;
}
- LOGI("%s: succeeds", __FUNCTION__);
+ ALOGI("%s: succeeds", __FUNCTION__);
+*/
}
-static void initializeNativeDataNative(JNIEnv *env, jobject object) {
- // TODO(BT) clean it up when hid service is stopped
+static void initializeNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHidInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth HID Interface before initializing...");
+ sBluetoothHidInterface->cleanup();
+ sBluetoothHidInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth GID callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+
+ if ( (sBluetoothHidInterface = (bthh_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_HIDHOST_ID)) == NULL) {
+ LOGE("Failed to get Bluetooth HID Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothHidInterface->init(&sBluetoothHidCallbacks)) != BT_STATUS_SUCCESS) {
+ LOGE("Failed to initialize Bluetooth HID, status: %d", status);
+ sBluetoothHidInterface = NULL;
+ return;
+ }
+
+
+
mCallbacksObj = env->NewGlobalRef(object);
}
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothHidInterface !=NULL) {
+ LOGW("Cleaning up Bluetooth HID Interface...");
+ sBluetoothHidInterface->cleanup();
+ sBluetoothHidInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth GID callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ env->DeleteGlobalRef(mCallbacksObj);
+}
+
static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
bt_status_t status;
jbyte *addr;
@@ -372,7 +432,8 @@ static jboolean sendDataNative(JNIEnv *env, jobject object, jbyteArray address,
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initializeNativeDataNative", "()V", (void *) initializeNativeDataNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
{"connectHidNative", "([B)Z", (void *) connectHidNative},
{"disconnectHidNative", "([B)Z", (void *) disconnectHidNative},
{"getProtocolModeNative", "([B)Z", (void *) getProtocolModeNative},
diff --git a/jni/com_android_bluetooth_pan.cpp b/jni/com_android_bluetooth_pan.cpp
new file mode 100644
index 000000000..ccdf33b6f
--- /dev/null
+++ b/jni/com_android_bluetooth_pan.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ */
+
+#define LOG_TAG "BluetoothPanServiceJni"
+
+#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_pan.h"
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <string.h>
+
+#include <cutils/log.h>
+#define info(fmt, ...) LOGI ("%s(L%d): " fmt,__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define debug(fmt, ...) LOGD ("%s(L%d): " fmt,__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define warn(fmt, ...) LOGW ("## WARNING : %s(L%d): " fmt "##",__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define error(fmt, ...) LOGE ("## ERROR : %s(L%d): " fmt "##",__FUNCTION__, __LINE__, ## __VA_ARGS__)
+#define asrt(s) if(!(s)) LOGE ("## %s(L%d): ASSERT %s failed! ##",__FUNCTION__, __LINE__, #s)
+
+
+namespace android {
+
+static jmethodID method_onConnectStateChanged;
+static jmethodID method_onControlStateChanged;
+
+static const btpan_interface_t *sPanIf = 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;
+}
+
+static void control_state_callback(btpan_control_state_t state, bt_status_t error, int local_role,
+ const char* ifname) {
+ debug("state:%d, local_role:%d, ifname:%s", state, local_role, ifname);
+ CHECK_CALLBACK_ENV
+ jstring js_ifname = sCallbackEnv->NewStringUTF(ifname);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onControlStateChanged, (jint)state, (jint)error,
+ (jint)local_role, js_ifname);
+}
+
+static void connection_state_callback(btpan_connection_state_t state, bt_status_t error, const bt_bdaddr_t *bd_addr,
+ int local_role, int remote_role) {
+ jbyteArray addr;
+ debug("state:%d, local_role:%d, remote_role:%d", state, local_role, remote_role);
+ CHECK_CALLBACK_ENV
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ error("Fail to new jbyteArray bd addr for PAN channel state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectStateChanged, addr, (jint) state,
+ (jint)error, (jint)local_role, (jint)remote_role);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static btpan_callbacks_t sBluetoothPanCallbacks = {
+ sizeof(sBluetoothPanCallbacks),
+ control_state_callback,
+ connection_state_callback
+};
+
+// Define native functions
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ int err;
+ bt_status_t status;
+
+ method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged",
+ "([BIIII)V");
+ method_onControlStateChanged = env->GetMethodID(clazz, "onControlStateChanged",
+ "(IIILjava/lang/String;)V");
+
+ info("succeeds");
+}
+static void initializeNative(JNIEnv *env, jobject object) {
+ static const bt_interface_t* btIf;
+ debug("pan");
+ if(btIf)
+ return;
+
+ if ( (btIf = getBluetoothInterface()) == NULL) {
+ error("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sPanIf !=NULL) {
+ LOGW("Cleaning up Bluetooth PAN Interface before initializing...");
+ sPanIf->cleanup();
+ sPanIf = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth PAN callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sPanIf = (btpan_interface_t *)
+ btIf->get_profile_interface(BT_PROFILE_PAN_ID)) == NULL) {
+ error("Failed to get Bluetooth PAN Interface");
+ return;
+ }
+
+ bt_status_t status;
+ if ( (status = sPanIf->init(&sBluetoothPanCallbacks)) != BT_STATUS_SUCCESS) {
+ error("Failed to initialize Bluetooth PAN, status: %d", status);
+ sPanIf = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ LOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sPanIf !=NULL) {
+ LOGW("Cleaning up Bluetooth PAN Interface...");
+ sPanIf->cleanup();
+ sPanIf = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ LOGW("Cleaning up Bluetooth PAN callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
+static jboolean enablePanNative(JNIEnv *env, jobject object, jint local_role) {
+ bt_status_t status = BT_STATUS_FAIL;
+ debug("in");
+ jbyte *addr;
+ if (sPanIf)
+ status = sPanIf->enable(local_role);
+ debug("out");
+ return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE;
+}
+static jint getPanLocalRoleNative(JNIEnv *env, jobject object) {
+ debug("in");
+ int local_role = 0;
+ jbyte *addr;
+ if (sPanIf)
+ local_role = sPanIf->get_local_role();
+ debug("out");
+ return (jint)local_role;
+}
+
+
+
+static jboolean connectPanNative(JNIEnv *env, jobject object, jbyteArray address,
+ jint src_role, jint dest_role) {
+ debug("in");
+ bt_status_t status;
+ jbyte *addr;
+ jboolean ret = JNI_TRUE;
+ if (!sPanIf) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ error("Bluetooth device address null");
+ return JNI_FALSE;
+ }
+
+ if ((status = sPanIf->connect((bt_bdaddr_t *) addr, src_role, dest_role)) !=
+ BT_STATUS_SUCCESS) {
+ error("Failed PAN channel connection, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return ret;
+}
+
+static jboolean disconnectPanNative(JNIEnv *env, jobject object, jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ jboolean ret = JNI_TRUE;
+ if (!sPanIf) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ error("Bluetooth device address null");
+ return JNI_FALSE;
+ }
+
+ if ( (status = sPanIf->disconnect((bt_bdaddr_t *) addr)) !=
+ BT_STATUS_SUCCESS) {
+ error("Failed disconnect pan channel, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return ret;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+ {"connectPanNative", "([BII)Z", (void *) connectPanNative},
+ {"enablePanNative", "(I)Z", (void *) enablePanNative},
+ {"getPanLocalRoleNative", "()I", (void *) getPanLocalRoleNative},
+ {"disconnectPanNative", "([B)Z", (void *) disconnectPanNative},
+ // TBD cleanup
+};
+
+int register_com_android_bluetooth_pan(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/pan/PanService",
+ sMethods, NELEM(sMethods));
+}
+
+}
diff --git a/src/com/android/bluetooth/Utils.java b/src/com/android/bluetooth/Utils.java
index 22ab469b0..477f8c5e4 100644
--- a/src/com/android/bluetooth/Utils.java
+++ b/src/com/android/bluetooth/Utils.java
@@ -4,6 +4,7 @@
package com.android.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.os.ParcelUuid;
import java.nio.ByteBuffer;
@@ -19,6 +20,10 @@ final public class Utils {
static final int BD_UUID_LEN = 16; // bytes
public static String getAddressStringFromByte(byte[] address) {
+ if (address == null || address.length !=6) {
+ return null;
+ }
+
return String.format("%02X:%02X:%02X:%02X:%02X:%02X",
address[0], address[1], address[2], address[3], address[4],
address[5]);
@@ -107,4 +112,14 @@ final public class Utils {
}
return puuids;
}
+
+ public static String debugGetAdapterStateString(int state) {
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF : return "STATE_OFF";
+ case BluetoothAdapter.STATE_ON : return "STATE_ON";
+ case BluetoothAdapter.STATE_TURNING_ON : return "STATE_TURNING_ON";
+ case BluetoothAdapter.STATE_TURNING_OFF : return "STATE_TURNING_OFF";
+ default : return "UNKNOWN";
+ }
+ }
}
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 728bcc29c..c33d5ed88 100755
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -17,6 +17,10 @@ import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import android.content.pm.PackageManager;
+import com.android.bluetooth.btservice.AdapterService;
/**
* Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
@@ -38,12 +42,6 @@ public class A2dpService extends Service {
log("onCreate");
super.onCreate();
mAdapter = BluetoothAdapter.getDefaultAdapter();
- if (mAdapter == null) {
- // no bluetooth on this device
- return;
- }
- mStateMachine = new A2dpStateMachine(this);
- mStateMachine.start();
}
@Override
@@ -54,16 +52,63 @@ public class A2dpService extends Service {
public void onStart(Intent intent, int startId) {
log("onStart");
+
if (mAdapter == null) {
- Log.w(TAG, "Stopping Bluetooth A2dpService: device does not have BT");
- stopSelf();
+ Log.w(TAG, "Stopping profile service: device does not have BT");
+ stop();
+ }
+
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return;
+ }
+
+ String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+ if (!AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ Log.e(TAG, "Invalid action " + action);
+ return;
+ }
+
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if(state==BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state== BluetoothAdapter.STATE_ON){
+ start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
- if (DBG) log("Stopping Bluetooth A2dpService");
+ if (DBG) log("Destroying service.");
+ }
+
+ private void start() {
+ if (DBG) log("startService()");
+ mStateMachine = new A2dpStateMachine(this);
+ mStateMachine.start();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+ }
+ }
+
+ private void stop() {
+ if (DBG) log("stopService()");
+ if (mStateMachine!= null) {
+ mStateMachine.quit();
+ mStateMachine.cleanup();
+ mStateMachine=null;
+ }
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
+ }
+ stopSelf();
}
/**
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 38e71709b..c4dd1e045 100755
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -93,7 +93,7 @@ final class A2dpStateMachine extends StateMachine {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
- initializeNativeDataNative();
+ initNative();
mDisconnected = new Disconnected();
mPending = new Pending();
@@ -106,6 +106,10 @@ final class A2dpStateMachine extends StateMachine {
setInitialState(mDisconnected);
}
+ public void cleanup() {
+ cleanupNative();
+ }
+
private class Disconnected extends State {
@Override
public void enter() {
@@ -611,7 +615,8 @@ final class A2dpStateMachine extends StateMachine {
final static int CONNECTION_STATE_DISCONNECTING = 3;
private native static void classInitNative();
- private native void initializeNativeDataNative();
+ private native void initNative();
+ private native void cleanupNative();
private native boolean connectA2dpNative(byte[] address);
private native boolean disconnectA2dpNative(byte[] address);
}
diff --git a/src/com/android/bluetooth/btservice/AdapterApp.java b/src/com/android/bluetooth/btservice/AdapterApp.java
new file mode 100644
index 000000000..9116072dd
--- /dev/null
+++ b/src/com/android/bluetooth/btservice/AdapterApp.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ */
+
+/**
+ * @hide
+ */
+
+package com.android.bluetooth.btservice;
+
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.content.IntentFilter;
+import android.util.Log;
+
+public class AdapterApp extends Application {
+ private static final String TAG = "BluetoothAdapterApp";
+ private static final boolean DBG = true;
+
+ static final String BLUETOOTH_ADMIN_PERM =
+ android.Manifest.permission.BLUETOOTH_ADMIN;
+ static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (DBG) Log.d(TAG, "onCreate");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (DBG) Log.d(TAG, "finalize");
+ }
+}
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index 47f8c63be..4d40a72c1 100755
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -59,10 +59,20 @@ class AdapterProperties {
}
static synchronized AdapterProperties getInstance(AdapterService service, Context context) {
- if (sInstance == null) sInstance = new AdapterProperties(service, context);
+ if (sInstance == null) {
+ sInstance = new AdapterProperties(service, context);
+ } else {
+ sInstance.mService = service;
+ sInstance.mContext = context;
+ //Cleanup needed?
+ }
return sInstance;
}
+ public void init() {
+ mProfileConnectionState.clear();
+ }
+
public Object Clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 741e44e6a..c5338c5b3 100755
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -20,12 +20,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.ParcelUuid;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
@@ -34,6 +34,7 @@ import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hid.HidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hdp.HealthService;
+import com.android.bluetooth.pan.PanService;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
@@ -41,11 +42,33 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.util.HashMap;
import java.util.Set;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import android.content.pm.PackageManager;
-public class AdapterService extends Application {
+
+public class AdapterService extends Service {
private static final String TAG = "BluetoothAdapterService";
private static final boolean DBG = true;
+ /**
+ * List of profile services to support.Comment out to disable a profile
+ * Profiles started in order of appearance
+ */
+ @SuppressWarnings("rawtypes")
+ private static final Class[] SUPPORTED_PROFILE_SERVICES = {
+ HeadsetService.class,
+ A2dpService.class,
+ HidService.class,
+ HealthService.class,
+ PanService.class
+ };
+
+ public static final String ACTION_LOAD_ADAPTER_PROPERTIES="com.android.bluetooth.btservice.action.LOAD_ADAPTER_PROPERTIES";
+ public static final String ACTION_SERVICE_STATE_CHANGED="com.android.bluetooth.btservice.action.STATE_CHANGED";
+ public static final String EXTRA_ACTION="action";
+
static final String BLUETOOTH_ADMIN_PERM =
android.Manifest.permission.BLUETOOTH_ADMIN;
static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -57,61 +80,254 @@ public class AdapterService extends Application {
private boolean mIsAirplaneToggleable;
private static AdapterService sAdapterService;
- private BluetoothAdapter mAdapter;
private AdapterState mAdapterStateMachine;
private BondStateMachine mBondStateMachine;
private JniCallbacks mJniCallbacks;
+ private RemoteDevices mRemoteDevices;
+ private boolean mStopPending;
+ private boolean mStartPending;
+ private boolean mProfilesStarted;
+ private boolean mNativeAvailable;
+ private HashMap<String,Integer> mProfileServicesState = new HashMap<String,Integer>();
+
+ //In case Bluetooth app crashes, we need to reinit JNI
+ private static boolean mIsJniInited;
+ private static synchronized void initJni() {
+ if (!mIsJniInited) {
+ if (DBG) Log.d(TAG,"Initializing JNI Library");
+ System.loadLibrary("bluetooth_jni");
+ classInitNative();
+ mIsJniInited=true;
+ }
+ }
+ public static AdapterService getAdapterService(){
+ return sAdapterService;
+ }
- private RemoteDevices mRemoteDevices;
- static {
- System.loadLibrary("bluetooth_jni");
- classInitNative();
+ public void onProfileServiceStateChanged(String serviceName, int state) {
+ if (DBG) Log.d(TAG,"onProfileServiceStateChange: serviceName=" + serviceName + ", state = " + state);
+ boolean doUpdate=false;
+ synchronized (mProfileServicesState) {
+ Integer prevState = mProfileServicesState.get(serviceName);
+ if (prevState != null && prevState != state) {
+ mProfileServicesState.put(serviceName,state);
+ doUpdate=true;
+ }
+ }
+ if (!doUpdate) {
+ return;
+ }
+ if (mStopPending) {
+ //Process stop or disable pending
+ //Check if all services are stopped if so, do cleanup
+ if (DBG) Log.d(TAG,"Checking if all profiles are stopped...");
+ synchronized (mProfileServicesState) {
+ Iterator<Map.Entry<String,Integer>> i = mProfileServicesState.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String,Integer> entry = i.next();
+ if (BluetoothAdapter.STATE_OFF != entry.getValue()) {
+ //Log.d(TAG, "Profile still running: " entry.getKey());
+ return;
+ }
+ }
+ }
+ if (DBG) Log.d(TAG, "All profile services stopped...");
+ Message m = mHandler.obtainMessage(MESSAGE_ONSTOPPED_STOPPENDING);
+ mHandler.sendMessage(m);
+ } else if (mStartPending) {
+ //Process start pending
+ //Check if all services are started if so, update state
+ if (DBG) Log.d(TAG,"Checking if all profiles are running...");
+ synchronized (mProfileServicesState) {
+ Iterator<Map.Entry<String,Integer>> i = mProfileServicesState.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String,Integer> entry = i.next();
+ if (BluetoothAdapter.STATE_ON != entry.getValue()) {
+ //Log.d(TAG, "Profile still not running:" + entry.getKey());
+ return;
+ }
+ }
+ }
+ if (DBG) Log.d(TAG, "All profile services started.");
+ Message m = mHandler.obtainMessage(MESSAGE_ONSTARTED);
+ mHandler.sendMessage(m);
+ }
}
@Override
public void onCreate() {
super.onCreate();
- ServiceManager.addService(Context.BLUETOOTH_SERVICE, mBinder);
-
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (DBG) Log.d(TAG, "onCreate");
mContext = this;
sAdapterService = this;
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
- registerForAirplaneMode(filter);
- registerReceiver(mReceiver, filter);
-
+ initJni(); //We always check and init JNI in case we crashed and restarted
mRemoteDevices = RemoteDevices.getInstance(this, mContext);
+ mRemoteDevices.init();
mAdapterProperties = AdapterProperties.getInstance(this, mContext);
- mAdapterStateMachine = new AdapterState(this, mContext, mAdapterProperties);
+ mAdapterProperties.init();
+ mAdapterStateMachine = new AdapterState(this, mContext, mAdapterProperties);
mBondStateMachine = new BondStateMachine(this, mContext, mAdapterProperties);
mJniCallbacks = JniCallbacks.getInstance(mRemoteDevices, mAdapterProperties,
mAdapterStateMachine, mBondStateMachine);
+ initNative();
+ mNativeAvailable=true;
+ //Load the name and address
+ getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR);
+ getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME);
+ }
- initNative();
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (DBG) Log.d(TAG, "onBind");
+ return mBinder;
+ }
+ public boolean onUnbind(Intent intent) {
+ if (DBG) Log.d(TAG,"onUnbind");
+ return super.onUnbind(intent);
+ }
+
+ public void onDestroy() {
+ if (DBG) Log.d(TAG, "onDestroy()");
+ super.onDestroy();
+ if (mBondStateMachine != null) {
+ mBondStateMachine.cleanup();
+ mBondStateMachine = null;
+ }
+ if (mAdapterStateMachine != null) {
+ mAdapterStateMachine.cleanup();
+ mAdapterStateMachine = null;
+ }
+ if (mNativeAvailable) {
+ Log.d(TAG, "Cleaning up adapter native....");
+ cleanupNative();
+ Log.d(TAG, "Done cleaning up adapter native....");
+ mNativeAvailable=false;
+ }
+ }
+
+ public int onStartCommand(Intent intent ,int flags, int startId) {
+ if (DBG) Log.d(TAG, "onStartCommand");
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return START_STICKY;
+ }
+
+ String action = intent.getStringExtra(EXTRA_ACTION);
+ if (DBG) Log.d(TAG,"onStartCommand(): action = " + action);
+ if (ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
+ if (DBG) Log.d(TAG,"onStartCommand(): state = " + Utils.debugGetAdapterStateString(state));
+ if (state == BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state == BluetoothAdapter.STATE_ON) {
+ start();
+ }
+ }
+ return START_STICKY;
+ }
+
+ private void start() {
+ if (DBG) Log.d(TAG,"start() called");
+ if (mProfilesStarted || mStartPending) return;
+ if (DBG) Log.d(TAG,"starting bluetooth state machine and profiles..");
+ mStartPending=true;
mAdapterStateMachine.start();
mBondStateMachine.start();
- //TODO(BT): Remove this when BT is no longer a persitent process.
- int bluetoothOn = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.BLUETOOTH_ON, 0);
- if (!isAirplaneModeOn() && bluetoothOn != 0) mAdapter.enable();
- startService(new Intent(this, HeadsetService.class));
- startService(new Intent(this, A2dpService.class));
- startService(new Intent(this, HidService.class));
- startService(new Intent(this, HealthService.class));
+ //Start profile services
+ for (int i=0; i <SUPPORTED_PROFILE_SERVICES.length;i++) {
+ startProfileService(SUPPORTED_PROFILE_SERVICES[i]);
+ }
}
- @Override
- protected void finalize() throws Throwable {
- mContext.unregisterReceiver(mReceiver);
- try {
- cleanupNative();
- } finally {
- super.finalize();
+ //Called as part of disable or independently (when just getting address)
+ private void stop() {
+ Log.d(TAG,"stop() called");
+ if (mStopPending) return;
+ mStopPending=true;
+ if (mProfilesStarted) {
+ //TODO: Start timeout
+ mProfilesStarted = false;
+
+ for (int i=SUPPORTED_PROFILE_SERVICES.length-1; i >=0;i--) {
+ stopProfileService(SUPPORTED_PROFILE_SERVICES[i]);
+ }
+ } else {
+ mStopPending=false;
+ finish();
+ }
+ }
+
+ private static final int MESSAGE_ONSTARTED=1;
+ private static final int MESSAGE_ONSTOPPED_STOPPENDING =2;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) Log.d (TAG, "Message: " + msg.what);
+
+ switch (msg.what) {
+ case MESSAGE_ONSTARTED: {
+ Log.d(TAG, "onStarted()");
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+ registerForAirplaneMode(filter);
+ registerReceiver(mReceiver, filter);
+ mProfilesStarted = true;
+ mStartPending = false;
+ }
+ break;
+
+ case MESSAGE_ONSTOPPED_STOPPENDING: {
+ Log.d(TAG, "onStopped_StopPending() called");
+ mStopPending=false;
+ mBondStateMachine.quit();
+ mAdapterStateMachine.quit();
+ finish();
+ }
+ break;
+ }
}
+ };
+
+ /**
+ * Last step in the disable->stop->cleanup sequence
+ */
+ private void finish() {
+ stopSelf();
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void startProfileService(Class service) {
+ String serviceName = service.getName();
+ Integer serviceState = mProfileServicesState.get(serviceName);
+ if(serviceState != null && serviceState != BluetoothAdapter.STATE_OFF) {
+ Log.w(TAG, "Unable to start service "+serviceName+". Invalid state: " + serviceState);
+ return;
+ }
+
+ mProfileServicesState.put(serviceName,BluetoothAdapter.STATE_TURNING_ON);
+ Intent i = new Intent(this,service);
+ i.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
+ i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_ON);
+ if (DBG) Log.d(TAG, "Starting profile service "+serviceName);
+ startService(i);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void stopProfileService(Class service) {
+ String serviceName = service.getName();
+ Integer serviceState = mProfileServicesState.get(service.getName());
+ if(serviceState == null || serviceState != BluetoothAdapter.STATE_ON) {
+ Log.w(TAG, "Unable to stop service " + serviceName + ". Invalid state: " + serviceState);
+ return;
+ }
+ mProfileServicesState.put(serviceName, BluetoothAdapter.STATE_TURNING_OFF);
+ Intent i = new Intent(this, service);
+ i.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);
+ i.putExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.STATE_OFF);
+ if (DBG) Log.d(TAG, "Stopping profile service "+serviceName);
+ startService(i);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -133,8 +349,6 @@ public class AdapterService extends Application {
}
};
-
-
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
final String airplaneModeRadios = Settings.System.getString(resolver,
@@ -157,6 +371,7 @@ public class AdapterService extends Application {
return Settings.System.getInt(mContext.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) == 1;
}
+ private boolean mPersistDisable;
/**
* Handlers for incoming service calls
@@ -175,10 +390,10 @@ public class AdapterService extends Application {
public boolean enable() {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
- // Persist the setting
+ Log.d(TAG,"enable() called...");
Message m =
mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);
- m.arg1 = 1;
+ m.arg1 = 1; //persist state
mAdapterStateMachine.sendMessage(m);
return true;
}
@@ -186,6 +401,7 @@ public class AdapterService extends Application {
public boolean disable(boolean persist) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
+ unregisterReceiver(mReceiver);
int val = (persist ? 1 : 0);
Message m =
mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF);
diff --git a/src/com/android/bluetooth/btservice/AdapterState.java b/src/com/android/bluetooth/btservice/AdapterState.java
index 1086c2205..fd475bf6f 100644
--- a/src/com/android/bluetooth/btservice/AdapterState.java
+++ b/src/com/android/bluetooth/btservice/AdapterState.java
@@ -39,10 +39,10 @@ final class AdapterState extends StateMachine {
private static final int DISCONNECT_TIMEOUT = 3000;
private static final int ENABLE_TIMEOUT_DELAY = 6000; // 6 secs
- private final AdapterService mAdapterService;
- private final Context mContext;
- private final AdapterProperties mAdapterProperties;
-
+ private AdapterService mAdapterService;
+ private Context mContext;
+ private AdapterProperties mAdapterProperties;
+ private boolean mPendingPersistEnable;
private PendingCommandState mPendingCommandState = new PendingCommandState();
private OnState mOnState = new OnState();
private OffState mOffState = new OffState();
@@ -53,10 +53,13 @@ final class AdapterState extends StateMachine {
addState(mOnState);
addState(mOffState);
addState(mPendingCommandState);
- setInitialState(mOffState);
mAdapterService = service;
mContext = context;
mAdapterProperties = adapterProperties;
+ setInitialState(mOffState);
+ }
+
+ public void cleanup() {
}
private class OffState extends State {
@@ -70,7 +73,9 @@ final class AdapterState extends StateMachine {
switch(msg.what) {
case USER_TURN_ON:
int persist = msg.arg1;
- if (persist == 1) mAdapterService.persistBluetoothSetting(true);
+ //if (persist == 1) mAdapterService.persistBluetoothSetting(true);
+ //Persist enable state only once enable completes
+ mPendingPersistEnable = (persist ==1);
sendIntent(BluetoothAdapter.STATE_TURNING_ON);
boolean ret = mAdapterService.enableNative();
if (!ret) {
@@ -123,7 +128,8 @@ final class AdapterState extends StateMachine {
case USER_TURN_OFF:
int persist = msg.arg1;
if (persist == 1) {
- mAdapterService.persistBluetoothSetting(false);
+ //Persist disable immediately even before disable completes
+ mAdapterService.persistBluetoothSetting(false);
}
//Fall Through
case AIRPLANE_MODE_ON:
@@ -176,6 +182,11 @@ final class AdapterState extends StateMachine {
break;
case ENABLED_READY:
removeMessages(ENABLE_TIMEOUT);
+ //Persist enable state only once enable completes
+ if (mPendingPersistEnable) {
+ mAdapterService.persistBluetoothSetting(true);
+ mPendingPersistEnable=false;
+ }
mAdapterProperties.onBluetoothReady();
sendIntent(BluetoothAdapter.STATE_ON);
transitionTo(mOnState);
diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java
index 56b2a0550..a9f0f1159 100755
--- a/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -39,10 +39,10 @@ final class BondStateMachine extends StateMachine {
static final int BOND_STATE_BONDING = 1;
static final int BOND_STATE_BONDED = 2;
- private final AdapterService mAdapterService;
- private final Context mContext;
- private final AdapterProperties mAdapterProperties;
- private final RemoteDevices mRemoteDevices;
+ private AdapterService mAdapterService;
+ private Context mContext;
+ private AdapterProperties mAdapterProperties;
+ private RemoteDevices mRemoteDevices;
private PendingCommandState mPendingCommandState = new PendingCommandState();
private StableState mStableState = new StableState();
@@ -52,11 +52,14 @@ final class BondStateMachine extends StateMachine {
super("BondStateMachine:");
addState(mStableState);
addState(mPendingCommandState);
- setInitialState(mStableState);
+ mRemoteDevices = RemoteDevices.getInstance(service, context);
mAdapterService = service;
mAdapterProperties = prop;
mContext = context;
- mRemoteDevices = RemoteDevices.getInstance(service, context);
+ setInitialState(mStableState);
+ }
+
+ public void cleanup() {
}
private class StableState extends State {
@@ -67,8 +70,14 @@ final class BondStateMachine extends StateMachine {
@Override
public boolean processMessage(Message msg) {
+ if (msg.what == SM_QUIT_CMD) {
+ return false;
+ }
+
BluetoothDevice dev = (BluetoothDevice)msg.obj;
+
switch(msg.what) {
+
case CREATE_BOND:
createBond(dev, true);
break;
diff --git a/src/com/android/bluetooth/btservice/JniCallbacks.java b/src/com/android/bluetooth/btservice/JniCallbacks.java
index d9feb6fd4..109db55fd 100644
--- a/src/com/android/bluetooth/btservice/JniCallbacks.java
+++ b/src/com/android/bluetooth/btservice/JniCallbacks.java
@@ -12,7 +12,7 @@ final class JniCallbacks {
private AdapterState mAdapterStateMachine;
private BondStateMachine mBondStateMachine;
- JniCallbacks(RemoteDevices remoteDevices, AdapterProperties adapterProperties,
+ private JniCallbacks(RemoteDevices remoteDevices, AdapterProperties adapterProperties,
AdapterState adapterStateMachine, BondStateMachine bondStateMachine) {
mRemoteDevices = remoteDevices;
mAdapterProperties = adapterProperties;
@@ -23,12 +23,18 @@ final class JniCallbacks {
static synchronized JniCallbacks getInstance(RemoteDevices remoteDevices,
AdapterProperties adapterProperties, AdapterState adapterStateMachine,
BondStateMachine bondStateMachine) {
- if (sInstance == null) sInstance =
+ if (sInstance == null) {
+ sInstance =
new JniCallbacks(remoteDevices, adapterProperties, adapterStateMachine,
bondStateMachine);
+ } else {
+ sInstance.mRemoteDevices = remoteDevices;
+ sInstance.mAdapterProperties = adapterProperties;
+ sInstance.mAdapterStateMachine = adapterStateMachine;
+ sInstance.mBondStateMachine = bondStateMachine;
+ }
return sInstance;
}
-
public Object Clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java
index 8233ad5de..a5d7bcf89 100755
--- a/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -41,17 +41,27 @@ final class RemoteDevices {
private RemoteDevices(AdapterService service, Context context) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mContext = context;
mAdapterService = service;
mSdpTracker = new ArrayList<BluetoothDevice>();
mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
- mContext = context;
}
static synchronized RemoteDevices getInstance(AdapterService service, Context context) {
- if (sInstance == null) sInstance = new RemoteDevices(service, context);
+ if (sInstance == null) {
+ sInstance = new RemoteDevices(service, context);
+ } else {
+ mContext = context;
+ mAdapterService = service;
+ }
return sInstance;
}
+ public void init() {
+ mSdpTracker.clear();
+ mDevices.clear();
+ }
+
public Object Clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
diff --git a/src/com/android/bluetooth/hdp/HealthService.java b/src/com/android/bluetooth/hdp/HealthService.java
index 2ae3f65ed..ee6f39051 100644
--- a/src/com/android/bluetooth/hdp/HealthService.java
+++ b/src/com/android/bluetooth/hdp/HealthService.java
@@ -32,6 +32,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.android.bluetooth.Utils;
+import android.content.pm.PackageManager;
+import com.android.bluetooth.btservice.AdapterService;
/**
* Provides Bluetooth Health Device profile, as a service in
@@ -67,18 +69,6 @@ public class HealthService extends Service {
@Override
public void onCreate() {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- mHealthChannels = Collections.synchronizedList(new ArrayList<HealthChannel>());
- mApps = Collections.synchronizedMap(new HashMap<BluetoothHealthAppConfiguration,
- AppInfo>());
- mHealthDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
-
- HandlerThread thread = new HandlerThread("BluetoothHdpHandler");
- thread.start();
- Looper looper = thread.getLooper();
- mHandler = new HealthServiceMessageHandler(looper);
- mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
-
- initializeNativeDataNative();
}
@Override
@@ -92,15 +82,74 @@ public class HealthService extends Service {
log("onStart");
if (mAdapter == null) {
Log.w(TAG, "Stopping Bluetooth HealthService: device does not have BT");
- stopSelf();
+ stop();
+ }
+
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return;
+ }
+
+ String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+ if (!AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ Log.e(TAG, "Invalid action " + action);
+ return;
+ }
+
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if(state==BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state== BluetoothAdapter.STATE_ON){
+ start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
- if (DBG) log("Stopping Bluetooth HealthService");
- // TBD
+ if (DBG) log("Destroying service.");
+ }
+
+ private void start() {
+ if (DBG) log("startService");
+ mHealthChannels = Collections.synchronizedList(new ArrayList<HealthChannel>());
+ mApps = Collections.synchronizedMap(new HashMap<BluetoothHealthAppConfiguration,
+ AppInfo>());
+ mHealthDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
+
+ HandlerThread thread = new HandlerThread("BluetoothHdpHandler");
+ thread.start();
+ Looper looper = thread.getLooper();
+ mHandler = new HealthServiceMessageHandler(looper);
+ mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
+ initializeNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+ }
+ }
+
+ private void stop() {
+ if (DBG) log("stop()");
+
+ //Cleanup looper
+ Looper looper = mHandler.getLooper();
+ if (looper != null) {
+ looper.quit();
+ }
+
+ //Cleanup native
+ cleanupNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
+ }
+ if (DBG) log("stop() done.");
+ stopSelf();
}
private final class HealthServiceMessageHandler extends Handler {
@@ -716,7 +765,8 @@ public class HealthService extends Service {
private static final int CHANNEL_TYPE_ANY =2;
private native static void classInitNative();
- private native void initializeNativeDataNative();
+ private native void initializeNative();
+ private native void cleanupNative();
private native int registerHealthAppNative(int dataType, int role, String name, int channelType);
private native boolean unregisterHealthAppNative(int appId);
private native int connectChannelNative(byte[] btAddress, int appId);
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 602af8962..c07016ca3 100755
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -21,6 +21,10 @@ import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import android.content.pm.PackageManager;
+import com.android.bluetooth.btservice.AdapterService;
/**
* Provides Bluetooth Headset and Handsfree profile, as a service in
@@ -43,15 +47,6 @@ public class HeadsetService extends Service {
log("onCreate");
super.onCreate();
mAdapter = BluetoothAdapter.getDefaultAdapter();
- if (mAdapter == null) {
- // no bluetooth on this device
- return;
- }
- mStateMachine = new HeadsetStateMachine(this);
- mStateMachine.start();
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
- registerReceiver(mHeadsetReceiver, filter);
}
@Override
@@ -62,17 +57,67 @@ public class HeadsetService extends Service {
public void onStart(Intent intent, int startId) {
log("onStart");
+
if (mAdapter == null) {
- Log.w(TAG, "Stopping Bluetooth HeadsetService: device does not have BT");
- stopSelf();
+ Log.w(TAG, "Stopping profile service: device does not have BT");
+ stop();
+ }
+
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return;
+ }
+
+ String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+ if (!AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ Log.e(TAG, "Invalid action " + action);
+ return;
+ }
+
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if(state==BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state== BluetoothAdapter.STATE_ON){
+ start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
- if (DBG) log("Stopping Bluetooth HeadsetService");
+ if (DBG) log("Destroying service.");
+ }
+
+ private void start() {
+ mStateMachine = new HeadsetStateMachine(this);
+ mStateMachine.start();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+ registerReceiver(mHeadsetReceiver, filter);
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+ }
+ }
+
+ private void stop() {
+ if (DBG) log("stopService()");
unregisterReceiver(mHeadsetReceiver);
+
+ if (mStateMachine!= null) {
+ mStateMachine.quit();
+ mStateMachine.cleanup();
+ mStateMachine=null;
+ }
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
+ }
+ stopSelf();
}
private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() {
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 1b5dabeb9..f496ae2f0 100755
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -165,7 +165,7 @@ final class HeadsetStateMachine extends StateMachine {
Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
}
- initializeNativeDataNative();
+ initializeNative();
mDisconnected = new Disconnected();
mPending = new Pending();
@@ -185,6 +185,21 @@ final class HeadsetStateMachine extends StateMachine {
setInitialState(mDisconnected);
}
+ public void cleanup() {
+ cleanupNative();
+ if (mPhoneProxy != null) {
+ if (DBG) Log.d(TAG,"Unbinding service...");
+ synchronized (mConnection) {
+ try {
+ mPhoneProxy = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
+ }
+ }
+ }
+ }
+
private class Disconnected extends State {
@Override
public void enter() {
@@ -1535,7 +1550,8 @@ final class HeadsetStateMachine extends StateMachine {
}
private native static void classInitNative();
- private native void initializeNativeDataNative();
+ private native void initializeNative();
+ private native void cleanupNative();
private native boolean connectHfpNative(byte[] address);
private native boolean disconnectHfpNative(byte[] address);
private native boolean connectAudioNative(byte[] address);
diff --git a/src/com/android/bluetooth/hid/HidService.java b/src/com/android/bluetooth/hid/HidService.java
index 2bf580f2a..624362764 100755
--- a/src/com/android/bluetooth/hid/HidService.java
+++ b/src/com/android/bluetooth/hid/HidService.java
@@ -26,6 +26,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.android.bluetooth.Utils;
+import android.content.pm.PackageManager;
+import com.android.bluetooth.btservice.AdapterService;
/**
* Provides Bluetooth Hid Device profile, as a service in
@@ -64,9 +66,6 @@ public class HidService extends Service {
@Override
public void onCreate() {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
- mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
- initializeNativeDataNative();
}
@Override
@@ -75,22 +74,65 @@ public class HidService extends Service {
return mBinder;
}
- @Override
public void onStart(Intent intent, int startId) {
log("onStart");
+
if (mAdapter == null) {
- Log.w(TAG, "Stopping Bluetooth HidService: device does not have BT");
- stopSelf();
+ Log.w(TAG, "Stopping profile service: device does not have BT");
+ stop();
+ }
+
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return;
+ }
+
+ String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+ if (!AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ Log.e(TAG, "Invalid action " + action);
+ return;
+ }
+
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if(state==BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state== BluetoothAdapter.STATE_ON){
+ start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
+ if (DBG) log("Destroying service.");
+ }
+
+ private void start() {
+ mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
+ mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
+ initializeNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+ }
+ }
+
+ private void stop() {
if (DBG) log("Stopping Bluetooth HidService");
- // TBD native cleanup
+ cleanupNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
+ }
+ stopSelf();
}
+
+
private final Handler mHandler = new Handler() {
@Override
@@ -477,7 +519,8 @@ public class HidService extends Service {
private final static int CONN_STATE_DISCONNECTING = 3;
private native static void classInitNative();
- private native void initializeNativeDataNative();
+ private native void initializeNative();
+ private native void cleanupNative();
private native boolean connectHidNative(byte[] btAddress);
private native boolean disconnectHidNative(byte[] btAddress);
private native boolean getProtocolModeNative(byte[] btAddress);
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
new file mode 100644
index 000000000..3f79df452
--- /dev/null
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ */
+
+package com.android.bluetooth.pan;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothTetheringDataTracker;
+import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothPan;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources.NotFoundException;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+
+/**
+ * Provides Bluetooth Pan Device profile, as a service in
+ * the Bluetooth application.
+ * @hide
+ */
+public class PanService extends Service {
+ private static final String TAG = "BluetoothPanService";
+ private static final boolean DBG = true;
+
+ static final String BLUETOOTH_ADMIN_PERM =
+ android.Manifest.permission.BLUETOOTH_ADMIN;
+ static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+
+ private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
+ private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
+ private static final int BLUETOOTH_PREFIX_LENGTH = 24;
+
+ private BluetoothAdapter mAdapter;
+ private IBluetooth mAdapterService;
+ private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
+ private ArrayList<String> mBluetoothIfaceAddresses;
+ private int mMaxPanDevices;
+ private String mPanIfName;
+
+ private static final int MESSAGE_CONNECT = 1;
+ private static final int MESSAGE_DISCONNECT = 2;
+ private static final int MESSAGE_SET_TETHERING = 3;
+ private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
+
+
+ static {
+ classInitNative();
+ }
+
+ @Override
+ public void onCreate() {
+ Log.d(TAG, "onCreate");
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ log("onStart");
+ if (mAdapter == null) {
+ Log.w(TAG, "Stopping Bluetooth Pan Service: device does not have BT");
+ stop();
+ }
+
+ if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Permission denied!");
+ return;
+ }
+
+ String action = intent.getStringExtra(AdapterService.EXTRA_ACTION);
+ if (!AdapterService.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+ Log.e(TAG, "Invalid action " + action);
+ return;
+ }
+
+ int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if(state==BluetoothAdapter.STATE_OFF) {
+ stop();
+ } else if (state== BluetoothAdapter.STATE_ON){
+ start();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ log("onBind");
+ return mBinder;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DBG) log("Stopping Bluetooth PanService");
+ }
+
+ private void start() {
+ if (DBG) log("start");
+
+ mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
+ mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
+ mBluetoothIfaceAddresses = new ArrayList<String>();
+ try {
+ mMaxPanDevices = getResources().getInteger(
+ com.android.internal.R.integer.config_max_pan_devices);
+ } catch (NotFoundException e) {
+ mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
+ }
+
+ initializeNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_ON);
+ }
+
+ }
+
+ private void stop() {
+ if (DBG) log("stop");
+
+ //Cleanup native
+ cleanupNative();
+
+ //Notify adapter service
+ AdapterService sAdapter = AdapterService.getAdapterService();
+ if (sAdapter!= null) {
+ sAdapter.onProfileServiceStateChanged(getClass().getName(), BluetoothAdapter.STATE_OFF);
+ }
+ if (DBG) log("stop() done.");
+ stopSelf();
+ }
+
+ private final Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_CONNECT:
+ {
+ BluetoothDevice device = (BluetoothDevice) msg.obj;
+ if (!connectPanNative(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);
+ break;
+ }
+ }
+ break;
+ case MESSAGE_DISCONNECT:
+ {
+ BluetoothDevice device = (BluetoothDevice) msg.obj;
+ if (!disconnectPanNative(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);
+ break;
+ }
+ }
+ break;
+ case MESSAGE_SET_TETHERING:
+ boolean tetherOn = (Boolean) msg.obj;
+ if(tetherOn)
+ enablePanNative(BluetoothPan.LOCAL_NAP_ROLE | BluetoothPan.LOCAL_PANU_ROLE);
+ else enablePanNative(BluetoothPan.LOCAL_PANU_ROLE);
+ break;
+ case MESSAGE_CONNECT_STATE_CHANGED:
+ {
+ 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);
+ }
+ break;
+ }
+ }
+ };
+
+ /**
+ * Handlers for incoming service calls
+ */
+ private final IBluetoothPan.Stub mBinder = new IBluetoothPan.Stub() {
+ public boolean connect(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
+ Log.e(TAG, "Pan Device not disconnected: " + device);
+ return false;
+ }
+ Message msg = mHandler.obtainMessage(MESSAGE_CONNECT);
+ msg.obj = device;
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT);
+ msg.obj = device;
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ public int getConnectionState(BluetoothDevice device) {
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ if (panDevice == null) {
+ return BluetoothPan.STATE_DISCONNECTED;
+ }
+ return panDevice.mState;
+ }
+ private boolean isPanNapOn() {
+ if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
+ return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
+ }
+ private boolean isPanUOn() {
+ if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
+ return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
+ }
+ public boolean isTetheringOn() {
+ // TODO(BT) have a variable marking the on/off state
+ return isPanNapOn();
+ }
+
+ public void setBluetoothTethering(boolean value) {
+ if(DBG) Log.d(TAG, "setBluetoothTethering: " + value);
+ enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+ Message msg = mHandler.obtainMessage(MESSAGE_SET_TETHERING);
+ msg.obj = new Boolean(value);
+ mHandler.sendMessage(msg);
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED});
+ return devices;
+ }
+
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
+
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ int panDeviceState = getConnectionState(device);
+ for (int state : states) {
+ if (state == panDeviceState) {
+ panDevices.add(device);
+ break;
+ }
+ }
+ }
+ return panDevices;
+ }
+ };
+ static protected class ConnectState {
+ public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) {
+ this.addr = address;
+ this.state = state;
+ this.error = error;
+ this.local_role = local_role;
+ this.remote_role = remote_role;
+ }
+ byte[] addr;
+ int state;
+ int error;
+ 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);
+ Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
+ msg.obj = new ConnectState(address, state, error, local_role, remote_role);
+ mHandler.sendMessage(msg);
+ }
+ private void onControlStateChanged(int local_role, int state, int error, String ifname) {
+ if (DBG)
+ log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname);
+ if(error == 0)
+ mPanIfName = ifname;
+ }
+ private BluetoothDevice getDevice(byte[] address) {
+ return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
+ }
+
+ private byte[] getByteAddress(BluetoothDevice device) {
+ return Utils.getBytesFromAddress(device.getAddress());
+ }
+
+ private int convertHalState(int halState) {
+ switch (halState) {
+ case CONN_STATE_CONNECTED:
+ return BluetoothProfile.STATE_CONNECTED;
+ case CONN_STATE_CONNECTING:
+ return BluetoothProfile.STATE_CONNECTING;
+ case CONN_STATE_DISCONNECTED:
+ return BluetoothProfile.STATE_DISCONNECTED;
+ case CONN_STATE_DISCONNECTING:
+ return BluetoothProfile.STATE_DISCONNECTING;
+ default:
+ Log.e(TAG, "bad pan connection state: " + halState);
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+
+ 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);
+ int prevState;
+ String ifaceAddr = null;
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ if (panDevice == null) {
+ prevState = BluetoothProfile.STATE_DISCONNECTED;
+ } else {
+ prevState = panDevice.mState;
+ ifaceAddr = panDevice.mIfaceAddr;
+ }
+ Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
+ if (prevState == state) return;
+ if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
+ Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ if(DBG) Log.d(TAG, "handlePanDeviceStateChange: nap STATE_CONNECTED, enableTethering iface: " + iface);
+ ifaceAddr = enableTethering(iface);
+ if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
+ } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ if (ifaceAddr != null) {
+ mBluetoothIfaceAddresses.remove(ifaceAddr);
+ ifaceAddr = null;
+ }
+ }
+ } else {
+ // PANU Role = reverse Tether
+ Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE");
+ 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;
+ }
+ }
+ }
+
+ if (panDevice == null) {
+ panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role);
+ mPanDevices.put(device, panDevice);
+ } else {
+ panDevice.mState = state;
+ panDevice.mIfaceAddr = ifaceAddr;
+ panDevice.mLocalRole = local_role;
+ panDevice.mIface = iface;
+ }
+
+ Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothPan.EXTRA_STATE, state);
+ intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role);
+ sendBroadcast(intent, BLUETOOTH_PERM);
+
+ if (DBG) Log.d(TAG, "Pan Device state : device: " + device + " State:" +
+ prevState + "->" + state);
+ try {
+ mAdapterService.sendConnectionStateChange(device, BluetoothProfile.PAN, state,
+ prevState);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ // configured when we start tethering
+ private String enableTethering(String iface) {
+ if (DBG) Log.d(TAG, "updateTetherState:" + iface);
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+ ConnectivityManager cm =
+ (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+ // bring toggle the interfaces
+ String[] currentIfaces = new String[0];
+ try {
+ currentIfaces = service.listInterfaces();
+ } catch (Exception e) {
+ Log.e(TAG, "Error listing Interfaces :" + e);
+ return null;
+ }
+
+ boolean found = false;
+ for (String currIface: currentIfaces) {
+ if (currIface.equals(iface)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) return null;
+
+ String address = createNewTetheringAddressLocked();
+ if (address == null) return null;
+
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = service.getInterfaceConfig(iface);
+ if (ifcg != null) {
+ InetAddress addr = null;
+ if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
+ addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
+ addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
+ addr = NetworkUtils.numericToInetAddress(address);
+ }
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+ ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
+ service.setInterfaceConfig(iface, ifcg);
+ if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Error tethering "+iface);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+ return null;
+ }
+ return address;
+ }
+
+ private String createNewTetheringAddressLocked() {
+ if (getConnectedPanDevices().size() == mMaxPanDevices) {
+ if (DBG) Log.d(TAG, "Max PAN device connections reached");
+ return null;
+ }
+ String address = BLUETOOTH_IFACE_ADDR_START;
+ while (true) {
+ if (mBluetoothIfaceAddresses.contains(address)) {
+ String[] addr = address.split("\\.");
+ Integer newIp = Integer.parseInt(addr[2]) + 1;
+ address = address.replace(addr[2], newIp.toString());
+ } else {
+ break;
+ }
+ }
+ mBluetoothIfaceAddresses.add(address);
+ return address;
+ }
+
+ private List<BluetoothDevice> getConnectedPanDevices() {
+ List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
+ devices.add(device);
+ }
+ }
+ return devices;
+ }
+
+ private int getPanDeviceConnectionState(BluetoothDevice device) {
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ if (panDevice == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return panDevice.mState;
+ }
+
+ private class BluetoothPanDevice {
+ private int mState;
+ private String mIfaceAddr;
+ private String mIface;
+ private int mLocalRole; // Which local role is this PAN device bound to
+
+ BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
+ mState = state;
+ mIfaceAddr = ifaceAddr;
+ mIface = iface;
+ mLocalRole = localRole;
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ // Constants matching Hal header file bt_hh.h
+ // bthh_connection_state_t
+ private final static int CONN_STATE_CONNECTED = 0;
+ private final static int CONN_STATE_CONNECTING = 1;
+ private final static int CONN_STATE_DISCONNECTED = 2;
+ private final static int CONN_STATE_DISCONNECTING = 3;
+
+ private native static void classInitNative();
+ private native void initializeNative();
+ private native void cleanupNative();
+ private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
+ private native boolean disconnectPanNative(byte[] btAddress);
+ private native boolean enablePanNative(int local_role);
+ private native int getPanLocalRoleNative();
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
index 55cb66c8e..6aa09dce8 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
@@ -46,17 +46,20 @@ public class BluetoothPbapReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (V) Log.v(TAG, "PbapReceiver onReceive: " + intent.getAction());
+ if (V) Log.v(TAG, "PbapReceiver onReceive ");
Intent in = new Intent();
in.putExtras(intent);
in.setClass(context, BluetoothPbapService.class);
String action = intent.getAction();
in.putExtra("action", action);
+ if (V) Log.v(TAG,"***********action = " + action);
+
boolean startService = true;
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
in.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+ if (V) Log.v(TAG,"***********state = " + state);
if ((state == BluetoothAdapter.STATE_TURNING_ON)
|| (state == BluetoothAdapter.STATE_TURNING_OFF)) {
startService = false;
@@ -69,6 +72,7 @@ public class BluetoothPbapReceiver extends BroadcastReceiver {
}
}
if (startService) {
+ if (V) Log.v(TAG,"***********Calling start service!!!! with action = " + in.getAction());
context.startService(in);
}
}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index a5c5f20c4..b2b1d4842 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -75,9 +75,9 @@ public class BluetoothPbapService extends Service {
* DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
*/
- public static final boolean DEBUG = false;
+ public static final boolean DEBUG = true;
- public static final boolean VERBOSE = false;
+ public static final boolean VERBOSE = true;
/**
* Intent indicating incoming obex authentication request which is from
@@ -182,9 +182,12 @@ public class BluetoothPbapService extends Service {
public BluetoothPbapService() {
mState = BluetoothPbap.STATE_DISCONNECTED;
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+ // IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+ IBinder b = null;
if (b == null) {
- throw new RuntimeException("Bluetooth service not available");
+ Log.e(TAG, "fail to get bluetooth service");
+ // throw new RuntimeException("Bluetooth service not available");
+ return;
}
mBluetoothService = IBluetooth.Stub.asInterface(b);
}
@@ -211,7 +214,6 @@ public class BluetoothPbapService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (VERBOSE) Log.v(TAG, "Pbap Service onStartCommand");
int retCode = super.onStartCommand(intent, flags, startId);
if (retCode == START_STICKY) {
mStartId = startId;
@@ -237,6 +239,8 @@ public class BluetoothPbapService extends Service {
if (VERBOSE) Log.v(TAG, "action: " + action);
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (VERBOSE) Log.v(TAG, "state: " + state);
+
boolean removeTimeoutMsg = true;
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
if (state == BluetoothAdapter.STATE_OFF) {
@@ -532,7 +536,7 @@ public class BluetoothPbapService extends Service {
if (trust) {
try {
- if (VERBOSE) Log.v(TAG, "incomming connection accepted from: "
+ if (VERBOSE) Log.v(TAG, "incoming connection accepted from: "
+ sRemoteDeviceName + " automatically as trusted device");
startObexServerSession();
} catch (IOException ex) {
@@ -564,9 +568,12 @@ public class BluetoothPbapService extends Service {
}
stopped = true; // job done ,close this thread;
} catch (IOException ex) {
+ stopped=true;
+ /*
if (stopped) {
break;
}
+ */
if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
}
}
@@ -638,11 +645,15 @@ public class BluetoothPbapService extends Service {
intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
sendBroadcast(intent, BLUETOOTH_PERM);
- try {
- mBluetoothService.sendConnectionStateChange(mRemoteDevice, BluetoothProfile.PBAP,
- mState, prevState);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in sendConnectionStateChange");
+ if (mBluetoothService != null) {
+ try {
+ mBluetoothService.sendConnectionStateChange(mRemoteDevice, BluetoothProfile.PBAP,
+ mState, prevState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in sendConnectionStateChange");
+ }
+ } else {
+ Log.e(TAG, "null mBluetoothService");
}
}
}