| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "BluetoothServiceJni" |
| #include "com_android_bluetooth.h" |
| #include "hardware/bt_sock.h" |
| #include "utils/Log.h" |
| #include "utils/misc.h" |
| #include "cutils/properties.h" |
| #include "android_runtime/AndroidRuntime.h" |
| #include "android_runtime/Log.h" |
| |
| #include <hardware/vendor.h> |
| |
| #include <string.h> |
| #include <pthread.h> |
| |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| namespace android { |
| |
| #define OOB_TK_SIZE 16 |
| |
| #define ADDITIONAL_NREFS 50 |
| static jmethodID method_stateChangeCallback; |
| static jmethodID method_adapterPropertyChangedCallback; |
| static jmethodID method_devicePropertyChangedCallback; |
| static jmethodID method_deviceFoundCallback; |
| static jmethodID method_pinRequestCallback; |
| static jmethodID method_sspRequestCallback; |
| static jmethodID method_bondStateChangeCallback; |
| static jmethodID method_aclStateChangeCallback; |
| static jmethodID method_discoveryStateChangeCallback; |
| static jmethodID method_setWakeAlarm; |
| static jmethodID method_acquireWakeLock; |
| static jmethodID method_releaseWakeLock; |
| static jmethodID method_energyInfo; |
| |
| static struct { |
| jclass clazz; |
| jmethodID constructor; |
| } android_bluetooth_UidTraffic; |
| |
| static const bt_interface_t *sBluetoothInterface = NULL; |
| static const btsock_interface_t *sBluetoothSocketInterface = NULL; |
| static const btvendor_interface_t *sBluetoothVendorInterface = NULL; |
| static JNIEnv *callbackEnv = NULL; |
| |
| static jobject sJniAdapterServiceObj = NULL; |
| static jobject sJniCallbacksObj = NULL; |
| static jfieldID sJniCallbacksField; |
| |
| |
| const bt_interface_t* getBluetoothInterface() { |
| return sBluetoothInterface; |
| } |
| |
| JNIEnv* getCallbackEnv() { |
| return callbackEnv; |
| } |
| |
| void checkAndClearExceptionFromCallback(JNIEnv* env, |
| const char* methodName) { |
| if (env->ExceptionCheck()) { |
| ALOGE("An exception was thrown by callback '%s'.", methodName); |
| LOGE_EX(env); |
| env->ExceptionClear(); |
| } |
| } |
| |
| static bool checkCallbackThread() { |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| if (callbackEnv != env || callbackEnv == NULL) { |
| ALOGE("Callback env check fail: env: %p, callback: %p", env, callbackEnv); |
| return false; |
| } |
| return true; |
| } |
| |
| 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); |
| if(sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status); |
| } else { |
| ALOGE("JNI ERROR : JNI reference already cleaned : adapter_state_change_callback", __FUNCTION__); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| } |
| |
| static int get_properties(int num_properties, bt_property_t *properties, jintArray *types, |
| jobjectArray *props) { |
| jbyteArray propVal; |
| for (int i = 0; i < num_properties; i++) { |
| propVal = callbackEnv->NewByteArray(properties[i].len); |
| if (propVal == NULL) goto Fail; |
| |
| callbackEnv->SetByteArrayRegion(propVal, 0, properties[i].len, |
| (jbyte*)properties[i].val); |
| callbackEnv->SetObjectArrayElement(*props, i, propVal); |
| // Delete reference to propVal |
| callbackEnv->DeleteLocalRef(propVal); |
| callbackEnv->SetIntArrayRegion(*types, i, 1, (jint *)&properties[i].type); |
| } |
| return 0; |
| Fail: |
| if (propVal) callbackEnv->DeleteLocalRef(propVal); |
| ALOGE("Error while allocation of array in %s", __FUNCTION__); |
| return -1; |
| } |
| |
| static void adapter_properties_callback(bt_status_t status, int num_properties, |
| bt_property_t *properties) { |
| jobjectArray props; |
| jintArray types; |
| jbyteArray val; |
| jclass mclass; |
| |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| ALOGV("%s: Status is: %d, Properties: %d", __FUNCTION__, status, num_properties); |
| |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("%s: Status %d is incorrect", __FUNCTION__, status); |
| return; |
| } |
| |
| val = (jbyteArray) callbackEnv->NewByteArray(num_properties); |
| if (val == NULL) { |
| ALOGE("%s: Error allocating byteArray", __FUNCTION__); |
| return; |
| } |
| |
| mclass = callbackEnv->GetObjectClass(val); |
| |
| /* (BT) Initialize the jobjectArray and jintArray here itself and send the |
| initialized array pointers alone to get_properties */ |
| |
| props = callbackEnv->NewObjectArray(num_properties, mclass, |
| NULL); |
| if (props == NULL) { |
| ALOGE("%s: Error allocating object Array for properties", __FUNCTION__); |
| return; |
| } |
| |
| types = (jintArray)callbackEnv->NewIntArray(num_properties); |
| |
| if (types == NULL) { |
| ALOGE("%s: Error allocating int Array for values", __FUNCTION__); |
| return; |
| } |
| // Delete the reference to val and mclass |
| callbackEnv->DeleteLocalRef(mclass); |
| callbackEnv->DeleteLocalRef(val); |
| |
| if (get_properties(num_properties, properties, &types, &props) < 0) { |
| if (props) callbackEnv->DeleteLocalRef(props); |
| if (types) callbackEnv->DeleteLocalRef(types); |
| return; |
| } |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_adapterPropertyChangedCallback, types, |
| props); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(props); |
| callbackEnv->DeleteLocalRef(types); |
| return; |
| |
| } |
| |
| static void remote_device_properties_callback(bt_status_t status, bt_bdaddr_t *bd_addr, |
| int num_properties, bt_property_t *properties) { |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| ALOGV("%s: Status is: %d, Properties: %d", __FUNCTION__, status, num_properties); |
| |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("%s: Status %d is incorrect", __FUNCTION__, status); |
| return; |
| } |
| |
| callbackEnv->PushLocalFrame(ADDITIONAL_NREFS); |
| |
| jobjectArray props; |
| jbyteArray addr; |
| jintArray types; |
| jbyteArray val; |
| jclass mclass; |
| |
| val = (jbyteArray) callbackEnv->NewByteArray(num_properties); |
| if (val == NULL) { |
| ALOGE("%s: Error allocating byteArray", __FUNCTION__); |
| return; |
| } |
| |
| mclass = callbackEnv->GetObjectClass(val); |
| |
| /* Initialize the jobjectArray and jintArray here itself and send the |
| initialized array pointers alone to get_properties */ |
| |
| props = callbackEnv->NewObjectArray(num_properties, mclass, |
| NULL); |
| if (props == NULL) { |
| ALOGE("%s: Error allocating object Array for properties", __FUNCTION__); |
| return; |
| } |
| |
| types = (jintArray)callbackEnv->NewIntArray(num_properties); |
| |
| if (types == NULL) { |
| ALOGE("%s: Error allocating int Array for values", __FUNCTION__); |
| return; |
| } |
| // Delete the reference to val and mclass |
| callbackEnv->DeleteLocalRef(mclass); |
| callbackEnv->DeleteLocalRef(val); |
| |
| addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (addr == NULL) goto Fail; |
| if (addr) callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| |
| if (get_properties(num_properties, properties, &types, &props) < 0) { |
| if (props) callbackEnv->DeleteLocalRef(props); |
| if (types) callbackEnv->DeleteLocalRef(types); |
| callbackEnv->PopLocalFrame(NULL); |
| return; |
| } |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_devicePropertyChangedCallback, addr, |
| types, props); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(props); |
| callbackEnv->DeleteLocalRef(types); |
| callbackEnv->DeleteLocalRef(addr); |
| callbackEnv->PopLocalFrame(NULL); |
| return; |
| |
| Fail: |
| ALOGE("Error while allocation byte array in %s", __FUNCTION__); |
| } |
| |
| |
| static void device_found_callback(int num_properties, bt_property_t *properties) { |
| jbyteArray addr = NULL; |
| int addr_index; |
| |
| for (int i = 0; i < num_properties; i++) { |
| if (properties[i].type == BT_PROPERTY_BDADDR) { |
| addr = callbackEnv->NewByteArray(properties[i].len); |
| if (addr) { |
| callbackEnv->SetByteArrayRegion(addr, 0, properties[i].len, |
| (jbyte*)properties[i].val); |
| addr_index = i; |
| } else { |
| ALOGE("Address is NULL (unable to allocate) in %s", __FUNCTION__); |
| return; |
| } |
| } |
| } |
| if (addr == NULL) { |
| ALOGE("Address is NULL in %s", __FUNCTION__); |
| return; |
| } |
| |
| ALOGV("%s: Properties: %d, Address: %s", __FUNCTION__, num_properties, |
| (const char *)properties[addr_index].val); |
| |
| remote_device_properties_callback(BT_STATUS_SUCCESS, (bt_bdaddr_t *)properties[addr_index].val, |
| num_properties, properties); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void bond_state_changed_callback(bt_status_t status, bt_bdaddr_t *bd_addr, |
| bt_bond_state_t state) { |
| jbyteArray addr; |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| if (!bd_addr) { |
| ALOGE("Address is null in %s", __FUNCTION__); |
| return; |
| } |
| addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (addr == NULL) { |
| ALOGE("Address allocation failed in %s", __FUNCTION__); |
| return; |
| } |
| callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *)bd_addr); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback, (jint) status, |
| addr, (jint)state); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void acl_state_changed_callback(bt_status_t status, bt_bdaddr_t *bd_addr, |
| bt_acl_state_t state) |
| { |
| jbyteArray addr; |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| if (!bd_addr) { |
| ALOGE("Address is null in %s", __FUNCTION__); |
| return; |
| } |
| addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (addr == NULL) { |
| ALOGE("Address allocation failed in %s", __FUNCTION__); |
| return; |
| } |
| callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *)bd_addr); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_aclStateChangeCallback, (jint) status, |
| addr, (jint)state); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void discovery_state_changed_callback(bt_discovery_state_t state) { |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| ALOGV("%s: DiscoveryState:%d ", __FUNCTION__, state); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_discoveryStateChangeCallback, |
| (jint)state); |
| } |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| } |
| |
| static void pin_request_callback(bt_bdaddr_t *bd_addr, bt_bdname_t *bdname, uint32_t cod, |
| bool min_16_digits) { |
| jbyteArray addr = NULL; |
| jbyteArray devname = NULL; |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| if (!bd_addr) { |
| ALOGE("Address is null in %s", __FUNCTION__); |
| return; |
| } |
| |
| addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (addr == NULL) goto Fail; |
| callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| |
| devname = callbackEnv->NewByteArray(sizeof(bt_bdname_t)); |
| if (devname == NULL) goto Fail; |
| |
| callbackEnv->SetByteArrayRegion(devname, 0, sizeof(bt_bdname_t), (jbyte*)bdname); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_pinRequestCallback, addr, devname, cod, |
| min_16_digits); |
| } |
| |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(addr); |
| callbackEnv->DeleteLocalRef(devname); |
| return; |
| |
| Fail: |
| if (addr) callbackEnv->DeleteLocalRef(addr); |
| if (devname) callbackEnv->DeleteLocalRef(devname); |
| ALOGE("Error while allocating in: %s", __FUNCTION__); |
| } |
| |
| static void ssp_request_callback(bt_bdaddr_t *bd_addr, bt_bdname_t *bdname, uint32_t cod, |
| bt_ssp_variant_t pairing_variant, uint32_t pass_key) { |
| jbyteArray addr = NULL; |
| jbyteArray devname = NULL; |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| if (!bd_addr) { |
| ALOGE("Address is null in %s", __FUNCTION__); |
| return; |
| } |
| |
| addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (addr == NULL) goto Fail; |
| callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *)bd_addr); |
| |
| devname = callbackEnv->NewByteArray(sizeof(bt_bdname_t)); |
| if (devname == NULL) goto Fail; |
| callbackEnv->SetByteArrayRegion(devname, 0, sizeof(bt_bdname_t), (jbyte*)bdname); |
| |
| if (sJniCallbacksObj) { |
| callbackEnv->CallVoidMethod(sJniCallbacksObj, method_sspRequestCallback, addr, devname, cod, |
| (jint) pairing_variant, pass_key); |
| } |
| |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(addr); |
| callbackEnv->DeleteLocalRef(devname); |
| return; |
| |
| Fail: |
| if (addr) callbackEnv->DeleteLocalRef(addr); |
| if (devname) callbackEnv->DeleteLocalRef(devname); |
| |
| ALOGE("Error while allocating in: %s", __FUNCTION__); |
| } |
| |
| static void callback_thread_event(bt_cb_thread_evt event) { |
| JavaVM* vm = AndroidRuntime::getJavaVM(); |
| if (event == ASSOCIATE_JVM) { |
| JavaVMAttachArgs args; |
| char name[] = "BT Service Callback Thread"; |
| args.version = JNI_VERSION_1_6; |
| args.name = name; |
| args.group = NULL; |
| vm->AttachCurrentThread(&callbackEnv, &args); |
| ALOGV("Callback thread attached: %p", callbackEnv); |
| } else if (event == DISASSOCIATE_JVM) { |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| vm->DetachCurrentThread(); |
| } |
| } |
| |
| static void dut_mode_recv_callback (uint16_t opcode, uint8_t *buf, uint8_t len) { |
| |
| } |
| static void le_test_mode_recv_callback (bt_status_t status, uint16_t packet_count) { |
| |
| ALOGV("%s: status:%d packet_count:%d ", __FUNCTION__, status, packet_count); |
| } |
| |
| static void energy_info_recv_callback(bt_activity_energy_info *p_energy_info, |
| bt_uid_traffic_t* uid_data) |
| { |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| jsize len = 0; |
| for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) { |
| len++; |
| } |
| |
| jobjectArray array = callbackEnv->NewObjectArray(len, android_bluetooth_UidTraffic.clazz, NULL); |
| jsize i = 0; |
| for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) { |
| jobject uidObj = callbackEnv->NewObject(android_bluetooth_UidTraffic.clazz, |
| android_bluetooth_UidTraffic.constructor, |
| (jint) data->app_uid, (jlong) data->rx_bytes, |
| (jlong) data->tx_bytes); |
| callbackEnv->SetObjectArrayElement(array, i++, uidObj); |
| callbackEnv->DeleteLocalRef(uidObj); |
| } |
| |
| if (sJniAdapterServiceObj) { |
| callbackEnv->CallVoidMethod(sJniAdapterServiceObj, method_energyInfo, p_energy_info->status, |
| p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time, |
| p_energy_info->idle_time, p_energy_info->energy_used, array); |
| } else { |
| ALOGE("JNI ERROR : JNI reference already cleaned : energy_info_recv_callback", __FUNCTION__); |
| } |
| |
| checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__); |
| callbackEnv->DeleteLocalRef(array); |
| } |
| |
| static bt_callbacks_t sBluetoothCallbacks = { |
| sizeof(sBluetoothCallbacks), |
| adapter_state_change_callback, |
| adapter_properties_callback, |
| remote_device_properties_callback, |
| device_found_callback, |
| discovery_state_changed_callback, |
| pin_request_callback, |
| ssp_request_callback, |
| bond_state_changed_callback, |
| acl_state_changed_callback, |
| callback_thread_event, |
| dut_mode_recv_callback, |
| le_test_mode_recv_callback, |
| energy_info_recv_callback, |
| NULL |
| }; |
| |
| // The callback to call when the wake alarm fires. |
| static alarm_cb sAlarmCallback; |
| |
| // The data to pass to the wake alarm callback. |
| static void *sAlarmCallbackData; |
| |
| static JavaVMAttachArgs sAttachArgs = { |
| .version = JNI_VERSION_1_6, |
| .name = "bluetooth wake", |
| .group = NULL |
| }; |
| |
| static bool set_wake_alarm_callout(uint64_t delay_millis, bool should_wake, |
| alarm_cb cb, void *data) { |
| JNIEnv *env; |
| JavaVM *vm = AndroidRuntime::getJavaVM(); |
| jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6); |
| jboolean ret = JNI_FALSE; |
| |
| if (status != JNI_OK && status != JNI_EDETACHED) { |
| ALOGE("%s unable to get environment for JNI call", __func__); |
| return false; |
| } |
| |
| if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, &sAttachArgs) != 0) { |
| ALOGE("%s unable to attach thread to VM", __func__); |
| return false; |
| } |
| |
| sAlarmCallback = cb; |
| sAlarmCallbackData = data; |
| |
| jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE; |
| if (sJniAdapterServiceObj) { |
| ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm, |
| (jlong)delay_millis, jshould_wake); |
| } else { |
| ALOGE("JNI ERROR : JNI reference already cleaned : set_wake_alarm_callout", __FUNCTION__); |
| } |
| |
| if (!ret) { |
| ALOGE("%s setWakeAlarm failed:ret= %d ", __func__, ret); |
| sAlarmCallback = NULL; |
| sAlarmCallbackData = NULL; |
| } |
| |
| if (status == JNI_EDETACHED) { |
| vm->DetachCurrentThread(); |
| } |
| |
| return !!ret; |
| } |
| |
| static int acquire_wake_lock_callout(const char *lock_name) { |
| JNIEnv *env; |
| JavaVM *vm = AndroidRuntime::getJavaVM(); |
| jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6); |
| if (status != JNI_OK && status != JNI_EDETACHED) { |
| ALOGE("%s unable to get environment for JNI call", __func__); |
| return BT_STATUS_JNI_ENVIRONMENT_ERROR; |
| } |
| if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, &sAttachArgs) != 0) { |
| ALOGE("%s unable to attach thread to VM", __func__); |
| return BT_STATUS_JNI_THREAD_ATTACH_ERROR; |
| } |
| |
| jint ret = BT_STATUS_SUCCESS; |
| jstring lock_name_jni = env->NewStringUTF(lock_name); |
| if (lock_name_jni) { |
| if (sJniAdapterServiceObj) { |
| bool acquired = env->CallBooleanMethod(sJniAdapterServiceObj, |
| method_acquireWakeLock, lock_name_jni); |
| if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR; |
| env->DeleteLocalRef(lock_name_jni); |
| } else { |
| ALOGE("JNI ERROR : JNI reference already cleaned : acquire_wake_lock_callout", __FUNCTION__); |
| } |
| } else { |
| ALOGE("%s unable to allocate string: %s", __func__, lock_name); |
| ret = BT_STATUS_NOMEM; |
| } |
| |
| if (status == JNI_EDETACHED) { |
| vm->DetachCurrentThread(); |
| } |
| |
| return ret; |
| } |
| |
| static int release_wake_lock_callout(const char *lock_name) { |
| JNIEnv *env; |
| JavaVM *vm = AndroidRuntime::getJavaVM(); |
| jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6); |
| if (status != JNI_OK && status != JNI_EDETACHED) { |
| ALOGE("%s unable to get environment for JNI call", __func__); |
| return BT_STATUS_JNI_ENVIRONMENT_ERROR; |
| } |
| if (status == JNI_EDETACHED && vm->AttachCurrentThread(&env, &sAttachArgs) != 0) { |
| ALOGE("%s unable to attach thread to VM", __func__); |
| return BT_STATUS_JNI_THREAD_ATTACH_ERROR; |
| } |
| |
| jint ret = BT_STATUS_SUCCESS; |
| jstring lock_name_jni = env->NewStringUTF(lock_name); |
| if (lock_name_jni) { |
| if (sJniAdapterServiceObj) { |
| bool released = env->CallBooleanMethod(sJniAdapterServiceObj, |
| method_releaseWakeLock, lock_name_jni); |
| if (!released) ret = BT_STATUS_WAKELOCK_ERROR; |
| env->DeleteLocalRef(lock_name_jni); |
| } else { |
| ALOGE("JNI ERROR : JNI reference already cleaned : release_wake_lock_callout", __FUNCTION__); |
| } |
| } else { |
| ALOGE("%s unable to allocate string: %s", __func__, lock_name); |
| ret = BT_STATUS_NOMEM; |
| } |
| |
| if (status == JNI_EDETACHED) { |
| vm->DetachCurrentThread(); |
| } |
| |
| return ret; |
| } |
| |
| // Called by Java code when alarm is fired. A wake lock is held by the caller |
| // over the duration of this callback. |
| static void alarmFiredNative(JNIEnv *env, jobject obj) { |
| if (sAlarmCallback) { |
| sAlarmCallback(sAlarmCallbackData); |
| } else { |
| ALOGE("%s() - Alarm fired with callback not set!", __FUNCTION__); |
| } |
| } |
| |
| static bt_os_callouts_t sBluetoothOsCallouts = { |
| sizeof(sBluetoothOsCallouts), |
| set_wake_alarm_callout, |
| acquire_wake_lock_callout, |
| release_wake_lock_callout, |
| }; |
| |
| |
| |
| static void classInitNative(JNIEnv* env, jclass clazz) { |
| int err; |
| hw_module_t* module; |
| |
| |
| jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic"); |
| android_bluetooth_UidTraffic.constructor = env->GetMethodID(jniUidTrafficClass, |
| "<init>", "(IJJ)V"); |
| |
| 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[BIZ)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"); |
| |
| method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z"); |
| method_acquireWakeLock = env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z"); |
| method_releaseWakeLock = env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z"); |
| method_energyInfo = env->GetMethodID(clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)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 bool initNative(JNIEnv* env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| android_bluetooth_UidTraffic.clazz = (jclass) env->NewGlobalRef( |
| env->FindClass("android/bluetooth/UidTraffic")); |
| |
| sJniAdapterServiceObj = env->NewGlobalRef(obj); |
| if (sJniCallbacksField) { |
| sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField)); |
| } else { |
| ALOGE("Error: sJniCallbacksField is null\n"); |
| } |
| |
| if (sBluetoothInterface) { |
| int ret = sBluetoothInterface->init(&sBluetoothCallbacks); |
| if (ret != BT_STATUS_SUCCESS && ret != BT_STATUS_DONE) { |
| ALOGE("Error while setting the callbacks: %d\n", ret); |
| sBluetoothInterface = NULL; |
| return JNI_FALSE; |
| } |
| ret = sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts); |
| if (ret != BT_STATUS_SUCCESS) { |
| ALOGE("Error while setting Bluetooth callouts: %d\n", ret); |
| sBluetoothInterface->cleanup(); |
| 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; |
| } |
| |
| static bool cleanupNative(JNIEnv *env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| sBluetoothInterface->cleanup(); |
| ALOGI("%s: return from cleanup",__FUNCTION__); |
| |
| if (sJniCallbacksObj) { |
| env->DeleteGlobalRef(sJniCallbacksObj); |
| sJniCallbacksObj = NULL; |
| } |
| if (sJniAdapterServiceObj) { |
| env->DeleteGlobalRef(sJniAdapterServiceObj); |
| sJniAdapterServiceObj = NULL; |
| } |
| if (android_bluetooth_UidTraffic.clazz) { |
| env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz); |
| android_bluetooth_UidTraffic.clazz = NULL; |
| } |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean enableNative(JNIEnv* env, jobject obj, jboolean isGuest) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| int ret = sBluetoothInterface->enable(isGuest == JNI_TRUE ? 1 : 0); |
| result = (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE : JNI_FALSE; |
| return result; |
| } |
| |
| static jboolean disableNative(JNIEnv* env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->disable(); |
| /* Retrun JNI_FALSE only when BTIF explicitly reports |
| BT_STATUS_FAIL. It is fine for the BT_STATUS_NOT_READY |
| case which indicates that stack had not been enabled. |
| */ |
| result = (ret == BT_STATUS_FAIL) ? JNI_FALSE : JNI_TRUE; |
| return result; |
| } |
| |
| static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->start_discovery(); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| return result; |
| } |
| |
| static jboolean cancelDiscoveryNative(JNIEnv* env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->cancel_discovery(); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| return result; |
| } |
| |
| static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr; |
| jboolean result = JNI_FALSE; |
| |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| int ret = sBluetoothInterface->create_bond((bt_bdaddr_t *)addr, transport); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jbyteArray callByteArrayGetter(JNIEnv* env, jobject object, |
| const char* className, |
| const char* methodName) { |
| jclass myClass = env->FindClass(className); |
| jmethodID myMethod = env->GetMethodID(myClass, methodName, "()[B"); |
| return (jbyteArray) env->CallObjectMethod(object, myMethod); |
| } |
| |
| static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, jbyteArray address, |
| jint transport, jobject oobData) { |
| jbyte *addr; |
| jboolean result = JNI_FALSE; |
| bt_out_of_band_data_t oob_data; |
| |
| memset(&oob_data, 0, sizeof(oob_data)); |
| |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| jbyte* smTKBytes = NULL; |
| jbyteArray smTK = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getSecurityManagerTk"); |
| if (smTK != NULL) { |
| smTKBytes = env->GetByteArrayElements(smTK, NULL); |
| int len = env->GetArrayLength(smTK); |
| if (len != OOB_TK_SIZE) { |
| ALOGI("%s: wrong length of smTK, should be empty or %d bytes.", __FUNCTION__, OOB_TK_SIZE); |
| jniThrowIOException(env, EINVAL); |
| goto done; |
| } |
| memcpy(oob_data.sm_tk, smTKBytes, len); |
| } |
| |
| if (sBluetoothInterface->create_bond_out_of_band((bt_bdaddr_t *)addr, transport, &oob_data) |
| == BT_STATUS_SUCCESS) |
| result = JNI_TRUE; |
| |
| done: |
| env->ReleaseByteArrayElements(address, addr, 0); |
| |
| if (smTK != NULL) |
| env->ReleaseByteArrayElements(smTK, smTKBytes, 0); |
| |
| return result; |
| } |
| |
| static jboolean removeBondNative(JNIEnv* env, jobject obj, jbyteArray address) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr; |
| jboolean result; |
| if (!sBluetoothInterface) return JNI_FALSE; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| int ret = sBluetoothInterface->remove_bond((bt_bdaddr_t *)addr); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean cancelBondNative(JNIEnv* env, jobject obj, jbyteArray address) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr; |
| jboolean result; |
| if (!sBluetoothInterface) return JNI_FALSE; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| int ret = sBluetoothInterface->cancel_bond((bt_bdaddr_t *)addr); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static int getConnectionStateNative(JNIEnv* env, jobject obj, jbyteArray address) { |
| ALOGV("%s:",__FUNCTION__); |
| if (!sBluetoothInterface) return JNI_FALSE; |
| |
| jbyte *addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| int ret = sBluetoothInterface->get_connection_state((bt_bdaddr_t *)addr); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| |
| return ret; |
| } |
| |
| static jboolean pinReplyNative(JNIEnv *env, jobject obj, jbyteArray address, jboolean accept, |
| jint len, jbyteArray pinArray) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr, *pinPtr = NULL; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| if (accept) { |
| pinPtr = env->GetByteArrayElements(pinArray, NULL); |
| if (pinPtr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| return result; |
| } |
| } |
| |
| int ret = sBluetoothInterface->pin_reply((bt_bdaddr_t*)addr, accept, len, |
| (bt_pin_code_t *) pinPtr); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| env->ReleaseByteArrayElements(pinArray, pinPtr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean sspReplyNative(JNIEnv *env, jobject obj, jbyteArray address, |
| jint type, jboolean accept, jint passkey) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| int ret = sBluetoothInterface->ssp_reply((bt_bdaddr_t *)addr, |
| (bt_ssp_variant_t) type, accept, passkey); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean setAdapterPropertyNative(JNIEnv *env, jobject obj, jint type, jbyteArray value) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *val; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| val = env->GetByteArrayElements(value, NULL); |
| bt_property_t prop; |
| prop.type = (bt_property_type_t) type; |
| prop.len = env->GetArrayLength(value); |
| prop.val = val; |
| |
| int ret = sBluetoothInterface->set_adapter_property(&prop); |
| env->ReleaseByteArrayElements(value, val, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean getAdapterPropertiesNative(JNIEnv *env, jobject obj) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->get_adapter_properties(); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean getAdapterPropertyNative(JNIEnv *env, jobject obj, jint type) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->get_adapter_property((bt_property_type_t) type); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean getDevicePropertyNative(JNIEnv *env, jobject obj, jbyteArray address, jint type) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr = NULL; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| int ret = sBluetoothInterface->get_remote_device_property((bt_bdaddr_t *)addr, |
| (bt_property_type_t) type); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static jboolean setDevicePropertyNative(JNIEnv *env, jobject obj, jbyteArray address, |
| jint type, jbyteArray value) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *val, *addr; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| val = env->GetByteArrayElements(value, NULL); |
| if (val == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| env->ReleaseByteArrayElements(value, val, 0); |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| |
| bt_property_t prop; |
| prop.type = (bt_property_type_t) type; |
| prop.len = env->GetArrayLength(value); |
| prop.val = val; |
| |
| int ret = sBluetoothInterface->set_remote_device_property((bt_bdaddr_t *)addr, &prop); |
| env->ReleaseByteArrayElements(value, val, 0); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static int getSocketOptNative(JNIEnv *env, jobject obj, jint type, jint channel, jint optionName, |
| jbyteArray optionVal) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *option_val = NULL; |
| int option_len; |
| bt_status_t status; |
| |
| if (!sBluetoothSocketInterface) return -1; |
| |
| option_val = env->GetByteArrayElements(optionVal, NULL); |
| if (option_val == NULL) { |
| ALOGE("getSocketOptNative :jniThrowIOException "); |
| jniThrowIOException(env, EINVAL); |
| return -1; |
| } |
| |
| if ( (status = sBluetoothSocketInterface->get_sock_opt((btsock_type_t)type, channel, |
| (btsock_option_type_t) optionName, (void *) option_val, &option_len)) != |
| BT_STATUS_SUCCESS) { |
| ALOGE("get_sock_opt failed: %d", status); |
| goto Fail; |
| } |
| env->ReleaseByteArrayElements(optionVal, option_val, 0); |
| |
| return option_len; |
| Fail: |
| env->ReleaseByteArrayElements(optionVal, option_val, 0); |
| return -1; |
| } |
| |
| static int setSocketOptNative(JNIEnv *env, jobject obj, jint type, jint channel, jint optionName, |
| jbyteArray optionVal, jint optionLen) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *option_val = NULL; |
| bt_status_t status; |
| |
| if (!sBluetoothSocketInterface) return -1; |
| |
| option_val = env->GetByteArrayElements(optionVal, NULL); |
| if (option_val == NULL) { |
| ALOGE("setSocketOptNative:jniThrowIOException "); |
| jniThrowIOException(env, EINVAL); |
| return -1; |
| } |
| |
| if ( (status = sBluetoothSocketInterface->set_sock_opt((btsock_type_t)type, channel, |
| (btsock_option_type_t) optionName, (void *) option_val, optionLen)) != |
| BT_STATUS_SUCCESS) { |
| ALOGE("set_sock_opt failed: %d", status); |
| goto Fail; |
| } |
| env->ReleaseByteArrayElements(optionVal, option_val, 0); |
| |
| return 0; |
| Fail: |
| env->ReleaseByteArrayElements(optionVal, option_val, 0); |
| return -1; |
| } |
| |
| static jboolean getRemoteServicesNative(JNIEnv *env, jobject obj, jbyteArray address) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jbyte *addr = NULL; |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return result; |
| } |
| |
| int ret = sBluetoothInterface->get_remote_services((bt_bdaddr_t *)addr); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| return result; |
| } |
| |
| static int connectSocketNative(JNIEnv *env, jobject object, jbyteArray address, jint type, |
| jbyteArray uuidObj, jint channel, jint flag, jint callingUid) { |
| jbyte *addr = NULL, *uuid = NULL; |
| int socket_fd; |
| bt_status_t status; |
| |
| if (!sBluetoothSocketInterface) return -1; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| ALOGE("failed to get Bluetooth device address"); |
| goto Fail; |
| } |
| |
| if(uuidObj != NULL) { |
| uuid = env->GetByteArrayElements(uuidObj, NULL); |
| if (!uuid) { |
| ALOGE("failed to get uuid"); |
| goto Fail; |
| } |
| } |
| |
| if ( (status = sBluetoothSocketInterface->connect((bt_bdaddr_t *) addr, (btsock_type_t) type, |
| (const uint8_t*) uuid, channel, &socket_fd, flag, callingUid)) |
| != BT_STATUS_SUCCESS) { |
| ALOGE("Socket connection failed: %d", status); |
| goto Fail; |
| } |
| |
| |
| if (socket_fd < 0) { |
| ALOGE("Fail to create file descriptor on socket fd"); |
| goto Fail; |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| env->ReleaseByteArrayElements(uuidObj, uuid, 0); |
| return socket_fd; |
| |
| Fail: |
| if (addr) env->ReleaseByteArrayElements(address, addr, 0); |
| if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); |
| |
| return -1; |
| } |
| |
| static int createSocketChannelNative(JNIEnv *env, jobject object, jint type, |
| jstring name_str, jbyteArray uuidObj, |
| jint channel, jint flag, jint callingUid) { |
| const char *service_name = NULL; |
| jbyte *uuid = NULL; |
| int socket_fd; |
| bt_status_t status; |
| |
| if (!sBluetoothSocketInterface) return -1; |
| |
| ALOGV("%s: SOCK FLAG = %x", __FUNCTION__, flag); |
| |
| if(name_str != NULL) { |
| service_name = env->GetStringUTFChars(name_str, NULL); |
| } |
| |
| if(uuidObj != NULL) { |
| uuid = env->GetByteArrayElements(uuidObj, NULL); |
| if (!uuid) { |
| ALOGE("failed to get uuid"); |
| goto Fail; |
| } |
| } |
| if ( (status = sBluetoothSocketInterface->listen((btsock_type_t) type, service_name, |
| (const uint8_t*) uuid, channel, &socket_fd, flag, callingUid)) |
| != BT_STATUS_SUCCESS) { |
| ALOGE("Socket listen failed: %d", status); |
| goto Fail; |
| } |
| |
| if (socket_fd < 0) { |
| ALOGE("Fail to creat file descriptor on socket fd"); |
| goto Fail; |
| } |
| if (service_name) env->ReleaseStringUTFChars(name_str, service_name); |
| if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); |
| return socket_fd; |
| |
| Fail: |
| if (service_name) env->ReleaseStringUTFChars(name_str, service_name); |
| if (uuid) env->ReleaseByteArrayElements(uuidObj, uuid, 0); |
| return -1; |
| } |
| |
| static jboolean configHciSnoopLogNative(JNIEnv* env, jobject obj, jboolean enable) { |
| ALOGV("%s:",__FUNCTION__); |
| |
| jboolean result = JNI_FALSE; |
| |
| if (!sBluetoothInterface) return result; |
| |
| int ret = sBluetoothInterface->config_hci_snoop_log(enable); |
| |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| |
| return result; |
| } |
| |
| static int readEnergyInfo() |
| { |
| ALOGV("%s:",__FUNCTION__); |
| jboolean result = JNI_FALSE; |
| if (!sBluetoothInterface) return result; |
| int ret = sBluetoothInterface->read_energy_info(); |
| result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| return result; |
| } |
| |
| static void dumpNative(JNIEnv *env, jobject obj, jobject fdObj, |
| jobjectArray argArray) |
| { |
| ALOGV("%s()", __FUNCTION__); |
| if (!sBluetoothInterface) return; |
| |
| int fd = jniGetFDFromFileDescriptor(env, fdObj); |
| if (fd < 0) return; |
| |
| int numArgs = env->GetArrayLength(argArray); |
| |
| jstring *argObjs = new jstring[numArgs]; |
| const char **args = nullptr; |
| if (numArgs > 0) |
| args = new const char*[numArgs]; |
| |
| for (int i = 0; i < numArgs; i++) { |
| argObjs[i] = (jstring) env->GetObjectArrayElement(argArray, i); |
| args[i] = env->GetStringUTFChars(argObjs[i], NULL); |
| } |
| |
| sBluetoothInterface->dump(fd, args); |
| |
| for (int i = 0; i < numArgs; i++) { |
| env->ReleaseStringUTFChars(argObjs[i], args[i]); |
| } |
| |
| delete[] args; |
| delete[] argObjs; |
| } |
| |
| static jboolean factoryResetNative(JNIEnv *env, jobject obj) { |
| ALOGV("%s:", __FUNCTION__); |
| if (!sBluetoothInterface) return JNI_FALSE; |
| int ret = sBluetoothInterface->config_clear(); |
| return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| static void interopDatabaseClearNative(JNIEnv *env, jobject obj) { |
| ALOGV("%s()", __FUNCTION__); |
| if (!sBluetoothInterface) return; |
| sBluetoothInterface->interop_database_clear(); |
| } |
| |
| static void interopDatabaseAddNative(JNIEnv *env, jobject obj, int feature, |
| jbyteArray address, int length) { |
| ALOGV("%s()", __FUNCTION__); |
| if (!sBluetoothInterface) return; |
| |
| jbyte *addr = env->GetByteArrayElements(address, NULL); |
| if (addr == NULL) { |
| jniThrowIOException(env, EINVAL); |
| return; |
| } |
| |
| sBluetoothInterface->interop_database_add(feature, (bt_bdaddr_t *)addr, length); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| {"classInitNative", "()V", (void *) classInitNative}, |
| {"initNative", "()Z", (void *) initNative}, |
| {"cleanupNative", "()V", (void*) cleanupNative}, |
| {"enableNative", "(Z)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", "([BI)Z", (void*) createBondNative}, |
| {"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;)Z", (void*) createBondOutOfBandNative}, |
| {"removeBondNative", "([B)Z", (void*) removeBondNative}, |
| {"cancelBondNative", "([B)Z", (void*) cancelBondNative}, |
| {"getConnectionStateNative", "([B)I", (void*) getConnectionStateNative}, |
| {"pinReplyNative", "([BZI[B)Z", (void*) pinReplyNative}, |
| {"sspReplyNative", "([BIZI)Z", (void*) sspReplyNative}, |
| {"getRemoteServicesNative", "([B)Z", (void*) getRemoteServicesNative}, |
| {"connectSocketNative", "([BI[BIII)I", (void*) connectSocketNative}, |
| {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I", |
| (void*) createSocketChannelNative}, |
| {"configHciSnoopLogNative", "(Z)Z", (void*) configHciSnoopLogNative}, |
| {"alarmFiredNative", "()V", (void *) alarmFiredNative}, |
| {"readEnergyInfo", "()I", (void*) readEnergyInfo}, |
| {"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V", (void*) dumpNative}, |
| {"factoryResetNative", "()Z", (void*)factoryResetNative}, |
| {"interopDatabaseClearNative", "()V", (void*) interopDatabaseClearNative}, |
| {"interopDatabaseAddNative", "(I[BI)V", (void*) interopDatabaseAddNative}, |
| {"getSocketOptNative", "(III[B)I", (void*) getSocketOptNative}, |
| {"setSocketOptNative", "(III[BI)I", (void*) setSocketOptNative} |
| |
| }; |
| |
| int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) |
| { |
| return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/AdapterService", |
| sMethods, NELEM(sMethods)); |
| } |
| |
| } /* namespace android */ |
| |
| |
| /* |
| * 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_hfpclient(e)) < 0) { |
| ALOGE("jni hfp client registration failure, status: %d", status); |
| return JNI_ERR; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_a2dp(e)) < 0) { |
| ALOGE("jni a2dp source registration failure: %d", status); |
| return JNI_ERR; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_a2dp_sink(e)) < 0) { |
| ALOGE("jni a2dp sink registration failure: %d", status); |
| return JNI_ERR; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_avrcp(e)) < 0) { |
| ALOGE("jni avrcp target registration failure: %d", status); |
| return JNI_ERR; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_avrcp_controller(e)) < 0) { |
| ALOGE("jni avrcp controller 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; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_sdp(e)) < 0) { |
| ALOGE("jni sdp registration failure: %d", status); |
| return JNI_ERR; |
| } |
| |
| if ((status = android::register_com_android_bluetooth_btservice_vendor(e)) < 0) { |
| ALOGE("jni vendor registration failure: %d", status); |
| return JNI_ERR; |
| } |
| |
| return JNI_VERSION_1_6; |
| } |