0. Load a JNI lib
JNI codes are built into a shared library lib like libxxx_jni.so. Once get loaded, a dedicated function named JNI_OnLoad() of the JNI lib will be called to bind JNI functions with Java functions.
/*
* JNI Initialization
*/
jint JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *e;
int status;
ALOGV("Bluetooth Adapter Service : loading JNI\n");
// Check JNI version
if (jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) {
ALOGE("JNI version mismatch error");
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_btservice_AdapterService(e)) < 0) {
ALOGE("jni adapter service registration failure, status: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_hfp(e)) < 0) {
ALOGE("jni hfp registration failure, status: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_a2dp(e)) < 0) {
ALOGE("jni a2dp registration failure: %d", status);
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;
}
if ((status = android::register_com_android_bluetooth_hdp(e)) < 0) {
ALOGE("jni hdp registration failure: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_pan(e)) < 0) {
ALOGE("jni pan registration failure: %d", status);
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;
}
1. Bind JNI functions to Java declared native function
After binding, the native functions declared in Java are able to be used by Java objects.
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void *) classInitNative},
{"initNative", "()Z", (void *) initNative},
{"cleanupNative", "()V", (void*) cleanupNative},
{"enableNative", "()Z", (void*) enableNative},
{"disableNative", "()Z", (void*) disableNative},
{"setAdapterPropertyNative", "(I[B)Z", (void*) setAdapterPropertyNative},
{"getAdapterPropertiesNative", "()Z", (void*) getAdapterPropertiesNative},
{"getAdapterPropertyNative", "(I)Z", (void*) getAdapterPropertyNative},
{"getDevicePropertyNative", "([BI)Z", (void*) getDevicePropertyNative},
{"setDevicePropertyNative", "([BI[B)Z", (void*) setDevicePropertyNative},
{"startDiscoveryNative", "()Z", (void*) startDiscoveryNative},
{"cancelDiscoveryNative", "()Z", (void*) cancelDiscoveryNative},
{"createBondNative", "([B)Z", (void*) createBondNative},
{"removeBondNative", "([B)Z", (void*) removeBondNative},
{"cancelBondNative", "([B)Z", (void*) cancelBondNative},
{"pinReplyNative", "([BZI[B)Z", (void*) pinReplyNative},
{"sspReplyNative", "([BIZI)Z", (void*) sspReplyNative},
{"getRemoteServicesNative", "([B)Z", (void*) getRemoteServicesNative},
{"connectSocketNative", "([BI[BII)I", (void*) connectSocketNative},
{"createSocketChannelNative", "(ILjava/lang/String;[BII)I",
(void*) createSocketChannelNative},
{"configHciSnoopLogNative", "(Z)Z", (void*) configHciSnoopLogNative}
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
{
// Class name: com/android/bluetooth/btservice/AdapterService
// Declared native functions: sMethods
return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/AdapterService",
sMethods, NELEM(sMethods));
}
static bool initNative(JNIEnv* env, jobject obj) {
ALOGV("%s:",__FUNCTION__);
sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
if (sBluetoothInterface) {
int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
if (ret != BT_STATUS_SUCCESS) {
ALOGE("Error while setting the callbacks \n");
sBluetoothInterface = NULL;
return JNI_FALSE;
}
if ( (sBluetoothSocketInterface = (btsock_interface_t *)
sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
ALOGE("Error getting socket interface");
}
return JNI_TRUE;
}
return JNI_FALSE;
}
2. Bind a Java object to JNI functions
At first, we need save the Java object which is achieved by initNative(). The reference to the Java object is stored by sJniCallbacksObj.
Then we bind identifiers to Java methods which is done by classInitNative(). At last we can call Java object's method at JNI layer.
static void classInitNative(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
jclass jniCallbackClass =
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
sJniCallbacksField = env->GetFieldID(clazz, "mJniCallbacks",
"Lcom/android/bluetooth/btservice/JniCallbacks;");
method_stateChangeCallback = env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");
method_adapterPropertyChangedCallback = env->GetMethodID(jniCallbackClass,
"adapterPropertyChangedCallback",
"([I[[B)V");
method_discoveryStateChangeCallback = env->GetMethodID(jniCallbackClass,
"discoveryStateChangeCallback", "(I)V");
method_devicePropertyChangedCallback = env->GetMethodID(jniCallbackClass,
"devicePropertyChangedCallback",
"([B[I[[B)V");
method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
method_pinRequestCallback = env->GetMethodID(jniCallbackClass, "pinRequestCallback",
"([B[BI)V");
method_sspRequestCallback = env->GetMethodID(jniCallbackClass, "sspRequestCallback",
"([B[BIII)V");
method_bondStateChangeCallback = env->GetMethodID(jniCallbackClass,
"bondStateChangeCallback", "(I[BI)V");
method_aclStateChangeCallback = env->GetMethodID(jniCallbackClass,
"aclStateChangeCallback", "(I[BI)V");
char value[PROPERTY_VALUE_MAX];
property_get("bluetooth.mock_stack", value, "");
const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);
err = hw_get_module(id, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t* abstraction;
err = module->methods->open(module, id, &abstraction);
if (err == 0) {
bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
sBluetoothInterface = btStack->get_bluetooth_interface();
} else {
ALOGE("Error while opening Bluetooth library");
}
} else {
ALOGE("No Bluetooth Library found");
}
}
static void adapter_state_change_callback(bt_state_t status) {
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
ALOGV("%s: Status is: %d", __FUNCTION__, status);
callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status);
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
}
JNI codes are built into a shared library lib like libxxx_jni.so. Once get loaded, a dedicated function named JNI_OnLoad() of the JNI lib will be called to bind JNI functions with Java functions.
/*
* JNI Initialization
*/
jint JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *e;
int status;
ALOGV("Bluetooth Adapter Service : loading JNI\n");
// Check JNI version
if (jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) {
ALOGE("JNI version mismatch error");
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_btservice_AdapterService(e)) < 0) {
ALOGE("jni adapter service registration failure, status: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_hfp(e)) < 0) {
ALOGE("jni hfp registration failure, status: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_a2dp(e)) < 0) {
ALOGE("jni a2dp registration failure: %d", status);
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;
}
if ((status = android::register_com_android_bluetooth_hdp(e)) < 0) {
ALOGE("jni hdp registration failure: %d", status);
return JNI_ERR;
}
if ((status = android::register_com_android_bluetooth_pan(e)) < 0) {
ALOGE("jni pan registration failure: %d", status);
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;
}
1. Bind JNI functions to Java declared native function
After binding, the native functions declared in Java are able to be used by Java objects.
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void *) classInitNative},
{"initNative", "()Z", (void *) initNative},
{"cleanupNative", "()V", (void*) cleanupNative},
{"enableNative", "()Z", (void*) enableNative},
{"disableNative", "()Z", (void*) disableNative},
{"setAdapterPropertyNative", "(I[B)Z", (void*) setAdapterPropertyNative},
{"getAdapterPropertiesNative", "()Z", (void*) getAdapterPropertiesNative},
{"getAdapterPropertyNative", "(I)Z", (void*) getAdapterPropertyNative},
{"getDevicePropertyNative", "([BI)Z", (void*) getDevicePropertyNative},
{"setDevicePropertyNative", "([BI[B)Z", (void*) setDevicePropertyNative},
{"startDiscoveryNative", "()Z", (void*) startDiscoveryNative},
{"cancelDiscoveryNative", "()Z", (void*) cancelDiscoveryNative},
{"createBondNative", "([B)Z", (void*) createBondNative},
{"removeBondNative", "([B)Z", (void*) removeBondNative},
{"cancelBondNative", "([B)Z", (void*) cancelBondNative},
{"pinReplyNative", "([BZI[B)Z", (void*) pinReplyNative},
{"sspReplyNative", "([BIZI)Z", (void*) sspReplyNative},
{"getRemoteServicesNative", "([B)Z", (void*) getRemoteServicesNative},
{"connectSocketNative", "([BI[BII)I", (void*) connectSocketNative},
{"createSocketChannelNative", "(ILjava/lang/String;[BII)I",
(void*) createSocketChannelNative},
{"configHciSnoopLogNative", "(Z)Z", (void*) configHciSnoopLogNative}
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
{
// Class name: com/android/bluetooth/btservice/AdapterService
// Declared native functions: sMethods
return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/AdapterService",
sMethods, NELEM(sMethods));
}
static bool initNative(JNIEnv* env, jobject obj) {
ALOGV("%s:",__FUNCTION__);
sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
if (sBluetoothInterface) {
int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
if (ret != BT_STATUS_SUCCESS) {
ALOGE("Error while setting the callbacks \n");
sBluetoothInterface = NULL;
return JNI_FALSE;
}
if ( (sBluetoothSocketInterface = (btsock_interface_t *)
sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
ALOGE("Error getting socket interface");
}
return JNI_TRUE;
}
return JNI_FALSE;
}
2. Bind a Java object to JNI functions
At first, we need save the Java object which is achieved by initNative(). The reference to the Java object is stored by sJniCallbacksObj.
Then we bind identifiers to Java methods which is done by classInitNative(). At last we can call Java object's method at JNI layer.
static void classInitNative(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
jclass jniCallbackClass =
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
sJniCallbacksField = env->GetFieldID(clazz, "mJniCallbacks",
"Lcom/android/bluetooth/btservice/JniCallbacks;");
method_stateChangeCallback = env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");
method_adapterPropertyChangedCallback = env->GetMethodID(jniCallbackClass,
"adapterPropertyChangedCallback",
"([I[[B)V");
method_discoveryStateChangeCallback = env->GetMethodID(jniCallbackClass,
"discoveryStateChangeCallback", "(I)V");
method_devicePropertyChangedCallback = env->GetMethodID(jniCallbackClass,
"devicePropertyChangedCallback",
"([B[I[[B)V");
method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
method_pinRequestCallback = env->GetMethodID(jniCallbackClass, "pinRequestCallback",
"([B[BI)V");
method_sspRequestCallback = env->GetMethodID(jniCallbackClass, "sspRequestCallback",
"([B[BIII)V");
method_bondStateChangeCallback = env->GetMethodID(jniCallbackClass,
"bondStateChangeCallback", "(I[BI)V");
method_aclStateChangeCallback = env->GetMethodID(jniCallbackClass,
"aclStateChangeCallback", "(I[BI)V");
char value[PROPERTY_VALUE_MAX];
property_get("bluetooth.mock_stack", value, "");
const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);
err = hw_get_module(id, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t* abstraction;
err = module->methods->open(module, id, &abstraction);
if (err == 0) {
bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
sBluetoothInterface = btStack->get_bluetooth_interface();
} else {
ALOGE("Error while opening Bluetooth library");
}
} else {
ALOGE("No Bluetooth Library found");
}
}
static void adapter_state_change_callback(bt_state_t status) {
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
ALOGV("%s: Status is: %d", __FUNCTION__, status);
callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status);
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
}