| /* |
| * 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 "BluetoothAvrcpControllerJni" |
| |
| #define LOG_NDEBUG 0 |
| |
| #include "com_android_bluetooth.h" |
| #include "hardware/bt_rc.h" |
| #include "utils/Log.h" |
| #include "android_runtime/AndroidRuntime.h" |
| |
| #include <string.h> |
| |
| namespace android { |
| static jmethodID method_handlePassthroughRsp; |
| static jmethodID method_onConnectionStateChanged; |
| static jmethodID method_getRcFeatures; |
| static jmethodID method_setplayerappsettingrsp; |
| static jmethodID method_handleplayerappsetting; |
| static jmethodID method_handleplayerappsettingchanged; |
| static jmethodID method_handleSetAbsVolume; |
| static jmethodID method_handleRegisterNotificationAbsVol; |
| static jmethodID method_handletrackchanged; |
| static jmethodID method_handleplaypositionchanged; |
| static jmethodID method_handleplaystatuschanged; |
| static jmethodID method_handleGroupNavigationRsp; |
| |
| |
| static const btrc_ctrl_interface_t *sBluetoothAvrcpInterface = NULL; |
| static jobject mCallbacksObj = NULL; |
| static JNIEnv *sCallbackEnv = NULL; |
| |
| static bool checkCallbackThread() { |
| // Always fetch the latest callbackEnv from AdapterService. |
| // Caching this could cause this sCallbackEnv to go out-of-sync |
| // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event |
| // is received |
| sCallbackEnv = getCallbackEnv(); |
| |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| if (sCallbackEnv != env || sCallbackEnv == NULL) return false; |
| return true; |
| } |
| |
| static void btavrcp_passthrough_response_callback(int id, int pressed) { |
| ALOGI("%s", __FUNCTION__); |
| ALOGI("id: %d, pressed: %d", id, pressed); |
| |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughRsp, (jint)id, |
| (jint)pressed); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| } |
| |
| static void btavrcp_groupnavigation_response_callback(int id, int pressed) { |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleGroupNavigationRsp, (jint)id, |
| (jint)pressed); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| } |
| |
| static void btavrcp_connection_state_callback(bool state, bt_bdaddr_t* bd_addr) { |
| jbyteArray addr; |
| |
| ALOGI("%s", __FUNCTION__); |
| ALOGI("conn state: %d", state); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for connection state"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jboolean) state, |
| addr); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_get_rcfeatures_callback(bt_bdaddr_t *bd_addr, int features) { |
| jbyteArray addr; |
| |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_setplayerapplicationsetting_rsp_callback(bt_bdaddr_t *bd_addr, |
| uint8_t accepted) { |
| jbyteArray addr; |
| |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setplayerappsettingrsp, addr, (jint)accepted); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_playerapplicationsetting_callback(bt_bdaddr_t *bd_addr, uint8_t num_attr, |
| btrc_player_app_attr_t *app_attrs, uint8_t num_ext_attr, |
| btrc_player_app_ext_attr_t *ext_attrs) { |
| ALOGI("%s", __FUNCTION__); |
| jbyteArray addr; |
| jbyteArray playerattribs; |
| jint arraylen; |
| int i,k; |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| /* TODO ext attrs |
| * Flattening defined attributes: <id,num_values,values[]> |
| */ |
| arraylen = 0; |
| for (i = 0; i < num_attr; i++) |
| { |
| /*2 bytes for id and num */ |
| arraylen += 2 + app_attrs[i].num_val; |
| } |
| ALOGI(" arraylen %d", arraylen); |
| playerattribs = sCallbackEnv->NewByteArray(arraylen); |
| if(!playerattribs) |
| { |
| ALOGE("Fail to new jbyteArray playerattribs "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| k= 0; |
| for (i = 0; (i < num_attr)&&(k < arraylen); i++) |
| { |
| sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(app_attrs[i].attr_id)); |
| k++; |
| sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(app_attrs[i].num_val)); |
| k++; |
| sCallbackEnv->SetByteArrayRegion(playerattribs, k, app_attrs[i].num_val, |
| (jbyte*)(app_attrs[i].attr_val)); |
| k = k + app_attrs[i].num_val; |
| } |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplayerappsetting, addr, |
| playerattribs, (jint)arraylen); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| sCallbackEnv->DeleteLocalRef(playerattribs); |
| } |
| |
| static void btavrcp_playerapplicationsetting_changed_callback(bt_bdaddr_t *bd_addr, |
| btrc_player_settings_t *p_vals) { |
| |
| jbyteArray addr; |
| jbyteArray playerattribs; |
| int i, k, arraylen; |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| arraylen = p_vals->num_attr*2; |
| playerattribs = sCallbackEnv->NewByteArray(arraylen); |
| if(!playerattribs) |
| { |
| ALOGE("Fail to new jbyteArray playerattribs "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| /* |
| * Flatening format: <id,val> |
| */ |
| k = 0; |
| for (i = 0; (i < p_vals->num_attr)&&( k < arraylen);i++) |
| { |
| sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(p_vals->attr_ids[i])); |
| k++; |
| sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(p_vals->attr_values[i])); |
| k++; |
| } |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplayerappsettingchanged, addr, |
| playerattribs, (jint)arraylen); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| sCallbackEnv->DeleteLocalRef(playerattribs); |
| } |
| |
| static void btavrcp_set_abs_vol_cmd_callback(bt_bdaddr_t *bd_addr, uint8_t abs_vol, |
| uint8_t label) { |
| |
| jbyteArray addr; |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleSetAbsVolume, addr, (jbyte)abs_vol, |
| (jbyte)label); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_register_notification_absvol_callback(bt_bdaddr_t *bd_addr, uint8_t label) { |
| jbyteArray addr; |
| |
| ALOGI("%s", __FUNCTION__); |
| |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleRegisterNotificationAbsVol, addr, |
| (jbyte)label); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_track_changed_callback(bt_bdaddr_t *bd_addr, uint8_t num_attr, |
| btrc_element_attr_val_t *p_attrs) { |
| /* |
| * byteArray will be formatted like this: id,len,string |
| * Assuming text feild to be null terminated. |
| */ |
| jbyteArray addr; |
| jintArray attribIds; |
| jobjectArray stringArray; |
| jstring str; |
| jclass strclazz; |
| jint i; |
| ALOGI("%s", __FUNCTION__); |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \ |
| return; \ |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| attribIds = sCallbackEnv->NewIntArray(num_attr); |
| if(!attribIds) { |
| ALOGE(" failed to set new array for attribIds"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr); |
| |
| strclazz = sCallbackEnv->FindClass("java/lang/String"); |
| stringArray = sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0); |
| if(!stringArray) { |
| ALOGE(" failed to get String array"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| sCallbackEnv->DeleteLocalRef(attribIds); |
| return; |
| } |
| for(i = 0; i < num_attr; i++) |
| { |
| str = sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text)); |
| if(!str) { |
| ALOGE(" Unable to get str "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| sCallbackEnv->DeleteLocalRef(attribIds); |
| sCallbackEnv->DeleteLocalRef(stringArray); |
| return; |
| } |
| sCallbackEnv->SetIntArrayRegion(attribIds, i, 1, (jint*)&(p_attrs[i].attr_id)); |
| sCallbackEnv->SetObjectArrayElement(stringArray, i,str); |
| sCallbackEnv->DeleteLocalRef(str); |
| } |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handletrackchanged, addr, |
| (jbyte)(num_attr), attribIds, stringArray); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| sCallbackEnv->DeleteLocalRef(attribIds); |
| /* TODO check do we need to delete str seperately or not */ |
| sCallbackEnv->DeleteLocalRef(stringArray); |
| sCallbackEnv->DeleteLocalRef(strclazz); |
| } |
| |
| static void btavrcp_play_position_changed_callback(bt_bdaddr_t *bd_addr, uint32_t song_len, |
| uint32_t song_pos) { |
| |
| jbyteArray addr; |
| ALOGI("%s", __FUNCTION__); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplaypositionchanged, addr, |
| (jint)(song_len), (jint)song_pos); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_play_status_changed_callback(bt_bdaddr_t *bd_addr, |
| btrc_play_status_t play_status) { |
| jbyteArray addr; |
| ALOGI("%s", __FUNCTION__); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if ((!addr)) { |
| ALOGE("Fail to get new array "); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplaystatuschanged, addr, |
| (jbyte)play_status); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { |
| sizeof(sBluetoothAvrcpCallbacks), |
| btavrcp_passthrough_response_callback, |
| btavrcp_groupnavigation_response_callback, |
| btavrcp_connection_state_callback, |
| btavrcp_get_rcfeatures_callback, |
| btavrcp_setplayerapplicationsetting_rsp_callback, |
| btavrcp_playerapplicationsetting_callback, |
| btavrcp_playerapplicationsetting_changed_callback, |
| btavrcp_set_abs_vol_cmd_callback, |
| btavrcp_register_notification_absvol_callback, |
| btavrcp_track_changed_callback, |
| btavrcp_play_position_changed_callback, |
| btavrcp_play_status_changed_callback |
| }; |
| |
| static void classInitNative(JNIEnv* env, jclass clazz) { |
| method_handlePassthroughRsp = |
| env->GetMethodID(clazz, "handlePassthroughRsp", "(II)V"); |
| |
| method_handleGroupNavigationRsp = |
| env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V"); |
| |
| method_onConnectionStateChanged = |
| env->GetMethodID(clazz, "onConnectionStateChanged", "(Z[B)V"); |
| |
| method_getRcFeatures = |
| env->GetMethodID(clazz, "getRcFeatures", "([BI)V"); |
| |
| method_setplayerappsettingrsp = |
| env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V"); |
| |
| method_handleplayerappsetting = |
| env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V"); |
| |
| method_handleplayerappsettingchanged = |
| env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V"); |
| |
| method_handleSetAbsVolume = |
| env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V"); |
| |
| method_handleRegisterNotificationAbsVol = |
| env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V"); |
| |
| method_handletrackchanged = |
| env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V"); |
| |
| method_handleplaypositionchanged = |
| env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V"); |
| |
| method_handleplaystatuschanged = |
| env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V"); |
| ALOGI("%s: succeeds", __FUNCTION__); |
| } |
| |
| static void initNative(JNIEnv *env, jobject object) { |
| const bt_interface_t* btInf; |
| bt_status_t status; |
| |
| if ( (btInf = getBluetoothInterface()) == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothAvrcpInterface !=NULL) { |
| ALOGW("Cleaning up Avrcp Interface before initializing..."); |
| sBluetoothAvrcpInterface->cleanup(); |
| sBluetoothAvrcpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| ALOGW("Cleaning up Avrcp callback object"); |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| |
| if ( (sBluetoothAvrcpInterface = (btrc_ctrl_interface_t *) |
| btInf->get_profile_interface(BT_PROFILE_AV_RC_CTRL_ID)) == NULL) { |
| ALOGE("Failed to get Bluetooth Avrcp Controller Interface"); |
| return; |
| } |
| |
| if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) != |
| BT_STATUS_SUCCESS) { |
| ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d", status); |
| sBluetoothAvrcpInterface = NULL; |
| return; |
| } |
| |
| mCallbacksObj = env->NewGlobalRef(object); |
| } |
| |
| static void cleanupNative(JNIEnv *env, jobject object) { |
| const bt_interface_t* btInf; |
| |
| if ( (btInf = getBluetoothInterface()) == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothAvrcpInterface !=NULL) { |
| sBluetoothAvrcpInterface->cleanup(); |
| sBluetoothAvrcpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| } |
| |
| static jboolean sendPassThroughCommandNative(JNIEnv *env, jobject object, jbyteArray address, |
| jint key_code, jint key_state) { |
| jbyte *addr; |
| bt_status_t status; |
| |
| if (!sBluetoothAvrcpInterface) return JNI_FALSE; |
| |
| ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); |
| |
| ALOGI("key_code: %d, key_state: %d", key_code, key_state); |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->send_pass_through_cmd((bt_bdaddr_t *)addr, |
| (uint8_t)key_code, (uint8_t)key_state))!= BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending passthru command, status: %d", status); |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| |
| return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| static jboolean sendGroupNavigationCommandNative(JNIEnv *env, jobject object, jbyteArray address, |
| jint key_code, jint key_state) { |
| jbyte *addr; |
| bt_status_t status; |
| |
| if (!sBluetoothAvrcpInterface) return JNI_FALSE; |
| |
| ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); |
| |
| ALOGI("key_code: %d, key_state: %d", key_code, key_state); |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->send_group_navigation_cmd((bt_bdaddr_t *)addr, |
| (uint8_t)key_code, (uint8_t)key_state))!= BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending Grp Navigation command, status: %d", status); |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| |
| return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| static void setPlayerApplicationSettingValuesNative(JNIEnv *env, jobject object, jbyteArray address, |
| jbyte num_attrib, jbyteArray attrib_ids, |
| jbyteArray attrib_val) { |
| bt_status_t status; |
| jbyte *addr; |
| uint8_t *pAttrs = NULL; |
| uint8_t *pAttrsVal = NULL; |
| int i; |
| jbyte *attr; |
| jbyte *attr_val; |
| |
| if (!sBluetoothAvrcpInterface) return; |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return; |
| } |
| |
| pAttrs = new uint8_t[num_attrib]; |
| pAttrsVal = new uint8_t[num_attrib]; |
| if ((!pAttrs) ||(!pAttrsVal)) { |
| delete[] pAttrs; |
| ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory"); |
| return; |
| } |
| attr = env->GetByteArrayElements(attrib_ids, NULL); |
| attr_val = env->GetByteArrayElements(attrib_val, NULL); |
| if ((!attr)||(!attr_val)) { |
| delete[] pAttrs; |
| delete[] pAttrsVal; |
| jniThrowIOException(env, EINVAL); |
| return; |
| } |
| for (i = 0; i < num_attrib; ++i) { |
| pAttrs[i] = (uint8_t)attr[i]; |
| pAttrsVal[i] = (uint8_t)attr_val[i]; |
| } |
| if (i < num_attrib) { |
| delete[] pAttrs; |
| delete[] pAttrsVal; |
| env->ReleaseByteArrayElements(attrib_ids, attr, 0); |
| env->ReleaseByteArrayElements(attrib_val, attr_val, 0); |
| return; |
| } |
| |
| ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); |
| if ((status = sBluetoothAvrcpInterface->set_player_app_setting_cmd((bt_bdaddr_t *)addr, |
| (uint8_t)num_attrib, pAttrs, pAttrsVal))!= BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending setPlAppSettValNative command, status: %d", status); |
| } |
| delete[] pAttrs; |
| delete[] pAttrsVal; |
| env->ReleaseByteArrayElements(attrib_ids, attr, 0); |
| env->ReleaseByteArrayElements(attrib_val, attr_val, 0); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| } |
| |
| static void sendAbsVolRspNative(JNIEnv *env, jobject object, jbyteArray address, |
| jint abs_vol, jint label) { |
| bt_status_t status; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) return; |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return; |
| } |
| |
| ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); |
| if ((status = sBluetoothAvrcpInterface->set_volume_rsp((bt_bdaddr_t *)addr, |
| (uint8_t)abs_vol, (uint8_t)label))!= BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status); |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| } |
| |
| static void sendRegisterAbsVolRspNative(JNIEnv *env, jobject object, jbyteArray address, |
| jbyte rsp_type, jint abs_vol, jint label) { |
| bt_status_t status; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) return; |
| addr = env->GetByteArrayElements(address, NULL); |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return; |
| } |
| ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface); |
| if ((status = sBluetoothAvrcpInterface->register_abs_vol_rsp((bt_bdaddr_t *)addr, |
| (btrc_notification_type_t)rsp_type,(uint8_t)abs_vol, (uint8_t)label)) |
| != BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d", status); |
| } |
| env->ReleaseByteArrayElements(address, addr, 0); |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| {"classInitNative", "()V", (void *) classInitNative}, |
| {"initNative", "()V", (void *) initNative}, |
| {"cleanupNative", "()V", (void *) cleanupNative}, |
| {"sendPassThroughCommandNative", "([BII)Z",(void *) sendPassThroughCommandNative}, |
| {"sendGroupNavigationCommandNative", "([BII)Z",(void *) sendGroupNavigationCommandNative}, |
| {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V", |
| (void *) setPlayerApplicationSettingValuesNative}, |
| {"sendAbsVolRspNative", "([BII)V",(void *) sendAbsVolRspNative}, |
| {"sendRegisterAbsVolRspNative", "([BBII)V",(void *) sendRegisterAbsVolRspNative}, |
| }; |
| |
| int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) |
| { |
| return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/AvrcpControllerService", |
| sMethods, NELEM(sMethods)); |
| } |
| |
| } |