/* * 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)); } }