/* * Copyright (C) 2016 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 "BluetoothAvrcpServiceJni" #define LOG_NDEBUG 0 #include "android_runtime/AndroidRuntime.h" #include "com_android_bluetooth.h" #include "hardware/bt_rc.h" #include "utils/Log.h" #include <inttypes.h> #include <string.h> namespace android { static jmethodID method_getRcFeatures; static jmethodID method_getPlayStatus; static jmethodID method_getElementAttr; static jmethodID method_registerNotification; static jmethodID method_volumeChangeCallback; static jmethodID method_handlePassthroughCmd; static jmethodID method_getFolderItemsCallback; static jmethodID method_setAddressedPlayerCallback; static jmethodID method_setBrowsedPlayerCallback; static jmethodID method_changePathCallback; static jmethodID method_searchCallback; static jmethodID method_playItemCallback; static jmethodID method_getItemAttrCallback; static jmethodID method_addToPlayListCallback; static jmethodID method_getTotalNumOfItemsCallback; static const btrc_interface_t* sBluetoothAvrcpInterface = NULL; static jobject mCallbacksObj = NULL; /* Function declarations */ static bool copy_item_attributes(JNIEnv* env, jobject object, btrc_folder_items_t* pitem, jint* p_attributesIds, jobjectArray attributesArray, int item_idx, int attribCopiedIndex); static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr, JNIEnv* env); static void cleanup_items(btrc_folder_items_t* p_items, int numItems); static void btavrcp_remote_features_callback(const RawAddress& bd_addr, btrc_remote_features_t features) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Unable to allocate byte array for bd_addr"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr.get(), (jint)features); } /** Callback for play status request */ static void btavrcp_get_play_status_callback(const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get_play_status command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr.get()); } static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t* p_attrs, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get_element_attr command"); return; } ScopedLocalRef<jintArray> attrs( sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr)); if (!attrs.get()) { ALOGE("Fail to new jintArray for attrs"); return; } sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs); sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, addr.get(), (jbyte)num_attr, attrs.get()); } static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for register_notification command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification, addr.get(), (jint)event_id, (jint)param); } static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for volume_change command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, addr.get(), (jint)volume, (jint)ctype); } static void btavrcp_passthrough_command_callback(int id, int pressed, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for passthrough_command command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, addr.get(), (jint)id, (jint)pressed); } static void btavrcp_set_addressed_player_callback(uint16_t player_id, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for set_addressed_player command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayerCallback, addr.get(), (jint)player_id); } static void btavrcp_set_browsed_player_callback(uint16_t player_id, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for set_browsed_player command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setBrowsedPlayerCallback, addr.get(), (jint)player_id); } static void btavrcp_get_folder_items_callback( uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t num_attr, uint32_t* p_attr_ids, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get_folder_items command"); return; } uint32_t* puiAttr = (uint32_t*)p_attr_ids; ScopedLocalRef<jintArray> attr_ids(sCallbackEnv.get(), NULL); sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); /* check number of attributes requested by remote device */ if ((num_attr != BTRC_NUM_ATTR_ALL) && (num_attr != BTRC_NUM_ATTR_NONE)) { /* allocate memory for attr_ids only if some attributes passed from below * layer */ attr_ids.reset((jintArray)sCallbackEnv->NewIntArray(num_attr)); if (!attr_ids.get()) { ALOGE("Fail to allocate new jintArray for attrs"); return; } sCallbackEnv->SetIntArrayRegion(attr_ids.get(), 0, num_attr, (jint*)puiAttr); } sCallbackEnv->CallVoidMethod( mCallbacksObj, method_getFolderItemsCallback, addr.get(), (jbyte)scope, (jlong)start_item, (jlong)end_item, (jbyte)num_attr, attr_ids.get()); } static void btavrcp_change_path_callback(uint8_t direction, uint8_t* folder_uid, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE)); if (!attrs.get()) { ALOGE("Fail to new jintArray for attrs"); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for change_path command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->SetByteArrayRegion( attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)folder_uid); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePathCallback, addr.get(), (jbyte)direction, attrs.get()); } static void btavrcp_get_item_attr_callback(uint8_t scope, uint8_t* uid, uint16_t uid_counter, uint8_t num_attr, btrc_media_attr_t* p_attrs, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> attr_uid( sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE)); if (!attr_uid.get()) { ALOGE("Fail to new jintArray for attr_uid"); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get_item_attr command"); return; } ScopedLocalRef<jintArray> attrs( sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr)); if (!attrs.get()) { ALOGE("Fail to new jintArray for attrs"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs); sCallbackEnv->SetByteArrayRegion( attr_uid.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getItemAttrCallback, addr.get(), (jbyte)scope, attr_uid.get(), (jint)uid_counter, (jbyte)num_attr, attrs.get()); } static void btavrcp_play_item_callback(uint8_t scope, uint16_t uid_counter, uint8_t* uid, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE)); if (!attrs.get()) { ALOGE("%s: Fail to new jByteArray attrs for play_item command", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for play_item command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->SetByteArrayRegion( attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItemCallback, addr.get(), (jbyte)scope, (jint)uid_counter, attrs.get()); } static void btavrcp_get_total_num_items_callback(uint8_t scope, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for get_total_num_items command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getTotalNumOfItemsCallback, addr.get(), (jbyte)scope); } static void btavrcp_search_callback(uint16_t charset_id, uint16_t str_len, uint8_t* p_str, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(), sCallbackEnv->NewByteArray(str_len)); if (!attrs.get()) { ALOGE("Fail to new jintArray for attrs"); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for search command"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->SetByteArrayRegion(attrs.get(), 0, str_len * sizeof(uint8_t), (jbyte*)p_str); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_searchCallback, addr.get(), (jint)charset_id, attrs.get()); } static void btavrcp_add_to_play_list_callback(uint8_t scope, uint8_t* uid, uint16_t uid_counter, const RawAddress& bd_addr) { CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!mCallbacksObj) { ALOGE("%s: mCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("Fail to new jbyteArray bd addr for add_to_play_list command"); return; } ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE)); if (!attrs.get()) { ALOGE("Fail to new jByteArray for attrs"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->SetByteArrayRegion( attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_addToPlayListCallback, addr.get(), (jbyte)scope, attrs.get(), (jint)uid_counter); } static btrc_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), btavrcp_remote_features_callback, btavrcp_get_play_status_callback, NULL, NULL, NULL, NULL, NULL, NULL, btavrcp_get_element_attr_callback, btavrcp_register_notification_callback, btavrcp_volume_change_callback, btavrcp_passthrough_command_callback, btavrcp_set_addressed_player_callback, btavrcp_set_browsed_player_callback, btavrcp_get_folder_items_callback, btavrcp_change_path_callback, btavrcp_get_item_attr_callback, btavrcp_play_item_callback, btavrcp_get_total_num_items_callback, btavrcp_search_callback, btavrcp_add_to_play_list_callback, }; static void classInitNative(JNIEnv* env, jclass clazz) { method_getRcFeatures = env->GetMethodID(clazz, "getRcFeaturesRequestFromNative", "([BI)V"); method_getPlayStatus = env->GetMethodID(clazz, "getPlayStatusRequestFromNative", "([B)V"); method_getElementAttr = env->GetMethodID(clazz, "getElementAttrRequestFromNative", "([BB[I)V"); method_registerNotification = env->GetMethodID( clazz, "registerNotificationRequestFromNative", "([BII)V"); method_volumeChangeCallback = env->GetMethodID(clazz, "volumeChangeRequestFromNative", "([BII)V"); method_handlePassthroughCmd = env->GetMethodID( clazz, "handlePassthroughCmdRequestFromNative", "([BII)V"); method_setAddressedPlayerCallback = env->GetMethodID(clazz, "setAddressedPlayerRequestFromNative", "([BI)V"); method_setBrowsedPlayerCallback = env->GetMethodID(clazz, "setBrowsedPlayerRequestFromNative", "([BI)V"); method_getFolderItemsCallback = env->GetMethodID(clazz, "getFolderItemsRequestFromNative", "([BBJJB[I)V"); method_changePathCallback = env->GetMethodID(clazz, "changePathRequestFromNative", "([BB[B)V"); method_getItemAttrCallback = env->GetMethodID(clazz, "getItemAttrRequestFromNative", "([BB[BIB[I)V"); method_playItemCallback = env->GetMethodID(clazz, "playItemRequestFromNative", "([BBI[B)V"); method_getTotalNumOfItemsCallback = env->GetMethodID(clazz, "getTotalNumOfItemsRequestFromNative", "([BB)V"); method_searchCallback = env->GetMethodID(clazz, "searchRequestFromNative", "([BI[B)V"); method_addToPlayListCallback = env->GetMethodID(clazz, "addToPlayListRequestFromNative", "([BB[BI)V"); ALOGI("%s: succeeds", __func__); } static void initNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == 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; } sBluetoothAvrcpInterface = (btrc_interface_t*)btInf->get_profile_interface(BT_PROFILE_AV_RC_ID); if (sBluetoothAvrcpInterface == NULL) { ALOGE("Failed to get Bluetooth Avrcp Interface"); return; } bt_status_t status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status); sBluetoothAvrcpInterface = NULL; return; } mCallbacksObj = env->NewGlobalRef(object); } static void cleanupNative(JNIEnv* env, jobject object) { const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == 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 getPlayStatusRspNative(JNIEnv* env, jobject object, jbyteArray address, jint playStatus, jint songLen, jint songPos) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_play_status_rsp( rawAddress, (btrc_play_status_t)playStatus, songLen, songPos); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed get_play_status_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean getElementAttrRspNative(JNIEnv* env, jobject object, jbyteArray address, jbyte numAttr, jintArray attrIds, jobjectArray textArray) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) { ALOGE("get_element_attr_rsp: number of attributes exceed maximum"); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr]; if (!pAttrs) { ALOGE("get_element_attr_rsp: not have enough memeory"); env->ReleaseByteArrayElements(address, addr, 0); return JNI_FALSE; } jint* attr = env->GetIntArrayElements(attrIds, NULL); if (!attr) { delete[] pAttrs; jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(address, addr, 0); return JNI_FALSE; } int attr_cnt; for (attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) { pAttrs[attr_cnt].attr_id = attr[attr_cnt]; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt)); if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { break; } } if (attr_cnt < numAttr) { delete[] pAttrs; env->ReleaseIntArrayElements(attrIds, attr, 0); ALOGE("%s: Failed to copy attributes", __func__); return JNI_FALSE; } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_element_attr_rsp( rawAddress, numAttr, pAttrs); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed get_element_attr_rsp, status: %d", status); } delete[] pAttrs; env->ReleaseIntArrayElements(attrIds, attr, 0); env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean getItemAttrRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jbyte numAttr, jintArray attrIds, jobjectArray textArray) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) { ALOGE("get_element_attr_rsp: number of attributes exceed maximum"); return JNI_FALSE; } btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr]; if (!pAttrs) { ALOGE("%s: not have enough memory", __func__); env->ReleaseByteArrayElements(address, addr, 0); return JNI_FALSE; } jint* attr = NULL; if (attrIds != NULL) { attr = env->GetIntArrayElements(attrIds, NULL); if (!attr) { delete[] pAttrs; jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(address, addr, 0); return JNI_FALSE; } } for (int attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) { pAttrs[attr_cnt].attr_id = attr[attr_cnt]; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt)); if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { rspStatus = BTRC_STS_INTERNAL_ERR; ALOGE("%s: Failed to copy attributes", __func__); break; } } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_item_attr_rsp( rawAddress, (btrc_status_t)rspStatus, numAttr, pAttrs); if (status != BT_STATUS_SUCCESS) ALOGE("Failed get_item_attr_rsp, status: %d", status); if (pAttrs) delete[] pAttrs; if (attr) env->ReleaseIntArrayElements(attrIds, attr, 0); env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspPlayStatusNative(JNIEnv* env, jobject object, jint type, jint playStatus) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; param.play_status = (btrc_play_status_t)playStatus; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_PLAY_STATUS_CHANGED, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp play status, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspTrackChangeNative(JNIEnv* env, jobject object, jint type, jbyteArray track) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* trk = env->GetByteArrayElements(track, NULL); if (!trk) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } btrc_register_notification_t param; uint64_t uid = 0; for (int uid_idx = 0; uid_idx < BTRC_UID_SIZE; ++uid_idx) { param.track[uid_idx] = trk[uid_idx]; uid = uid + (trk[uid_idx] << (BTRC_UID_SIZE - 1 - uid_idx)); } ALOGV("%s: Sending track change notification: %d -> %" PRIu64, __func__, type, uid); bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_TRACK_CHANGE, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp track change, status: %d", status); } env->ReleaseByteArrayElements(track, trk, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspPlayPosNative(JNIEnv* env, jobject object, jint type, jint playPos) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; param.song_pos = (uint32_t)playPos; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_PLAY_POS_CHANGED, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp play position, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspNowPlayingChangedNative(JNIEnv* env, jobject object, jint type) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp, nowPlaying Content status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspUIDsChangedNative(JNIEnv* env, jobject object, jint type, jint uidCounter) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; param.uids_changed.uid_counter = (uint16_t)uidCounter; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_UIDS_CHANGED, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp, uids changed status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspAddrPlayerChangedNative( JNIEnv* env, jobject object, jint type, jint playerId, jint uidCounter) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; param.addr_player_changed.player_id = (uint16_t)playerId; param.addr_player_changed.uid_counter = (uint16_t)uidCounter; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_ADDR_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed register_notification_rsp address player changed status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean registerNotificationRspAvalPlayerChangedNative(JNIEnv* env, jobject object, jint type) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } btrc_register_notification_t param; bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp( BTRC_EVT_AVAL_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m); if (status != BT_STATUS_SUCCESS) { ALOGE( "Failed register_notification_rsp available player changed status, " "status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } bt_status_t status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed set_volume, status: %d", status); } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } /* native response for scope as Media player */ static jboolean mediaPlayerListRspNative( JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jint uidCounter, jbyte itemType, jint numItems, jintArray playerIds, jbyteArray playerTypes, jintArray playerSubtypes, jbyteArray playStatusValues, jshortArray featureBitmask, jobjectArray textArray) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } jbyte *p_playerTypes = NULL, *p_PlayStatusValues = NULL; jshort* p_FeatBitMaskValues = NULL; jint *p_playerIds = NULL, *p_playerSubTypes = NULL; btrc_folder_items_t* p_items = NULL; if (rspStatus == BTRC_STS_NO_ERROR) { /* allocate memory */ p_playerIds = env->GetIntArrayElements(playerIds, NULL); p_playerTypes = env->GetByteArrayElements(playerTypes, NULL); p_playerSubTypes = env->GetIntArrayElements(playerSubtypes, NULL); p_PlayStatusValues = env->GetByteArrayElements(playStatusValues, NULL); p_FeatBitMaskValues = env->GetShortArrayElements(featureBitmask, NULL); p_items = new btrc_folder_items_t[numItems]; /* deallocate memory and return if allocation failed */ if (!p_playerIds || !p_playerTypes || !p_playerSubTypes || !p_PlayStatusValues || !p_FeatBitMaskValues || !p_items) { if (p_playerIds) env->ReleaseIntArrayElements(playerIds, p_playerIds, 0); if (p_playerTypes) env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0); if (p_playerSubTypes) env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0); if (p_PlayStatusValues) env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0); if (p_FeatBitMaskValues) env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0); if (p_items) delete[] p_items; jniThrowIOException(env, EINVAL); ALOGE("%s: not have enough memory", __func__); return JNI_FALSE; } p_items->item_type = (uint8_t)itemType; /* copy list of media players along with other parameters */ int itemIdx; for (itemIdx = 0; itemIdx < numItems; ++itemIdx) { p_items[itemIdx].player.player_id = p_playerIds[itemIdx]; p_items[itemIdx].player.major_type = p_playerTypes[itemIdx]; p_items[itemIdx].player.sub_type = p_playerSubTypes[itemIdx]; p_items[itemIdx].player.play_status = p_PlayStatusValues[itemIdx]; p_items[itemIdx].player.charset_id = BTRC_CHARSET_ID_UTF8; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(textArray, itemIdx)); /* copy player name */ if (!copy_jstring(p_items[itemIdx].player.name, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) break; /* Feature bit mask is 128-bit value each */ for (int InnCnt = 0; InnCnt < 16; InnCnt++) { p_items[itemIdx].player.features[InnCnt] = (uint8_t)p_FeatBitMaskValues[(itemIdx * 16) + InnCnt]; } } /* failed to copy list of media players */ if (itemIdx < numItems) { rspStatus = BTRC_STS_INTERNAL_ERR; ALOGE("%s: Failed to copy Media player attributes", __func__); } } RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp( *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed get_folder_items_list_rsp, status: %d", status); } /* release allocated memory */ if (p_items) delete[] p_items; if (p_playerTypes) env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0); if (p_playerSubTypes) env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0); if (p_PlayStatusValues) env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0); if (p_FeatBitMaskValues) { env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean getFolderItemsRspNative( JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jshort uidCounter, jbyte scope, jint numItems, jbyteArray folderType, jbyteArray playable, jbyteArray itemType, jbyteArray itemUidArray, jobjectArray displayNameArray, jintArray numAttrs, jintArray attributesIds, jobjectArray attributesArray) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } jbyte *p_playable = NULL, *p_item_uid = NULL; jbyte* p_item_types = NULL; /* Folder or Media Item */ jint* p_attributesIds = NULL; jbyte* p_folder_types = NULL; /* Folder properties like Album/Genre/Artists etc */ jint* p_num_attrs = NULL; btrc_folder_items_t* p_items = NULL; /* none of the parameters should be null when no error */ if (rspStatus == BTRC_STS_NO_ERROR) { /* allocate memory to each rsp item */ if (folderType != NULL) p_folder_types = env->GetByteArrayElements(folderType, NULL); if (playable != NULL) p_playable = env->GetByteArrayElements(playable, NULL); if (itemType != NULL) p_item_types = env->GetByteArrayElements(itemType, NULL); if (NULL != numAttrs) p_num_attrs = env->GetIntArrayElements(numAttrs, NULL); if (NULL != attributesIds) p_attributesIds = env->GetIntArrayElements(attributesIds, NULL); if (itemUidArray != NULL) p_item_uid = (jbyte*)env->GetByteArrayElements(itemUidArray, NULL); p_items = new btrc_folder_items_t[numItems]; /* if memory alloc failed, release memory */ if (p_items && p_folder_types && p_playable && p_item_types && p_item_uid && /* attributes can be null if remote requests 0 attributes */ ((numAttrs != NULL && p_num_attrs) || (!numAttrs && !p_num_attrs)) && ((attributesIds != NULL && p_attributesIds) || (!attributesIds && !p_attributesIds))) { memset(p_items, 0, sizeof(btrc_folder_items_t) * numItems); if (scope == BTRC_SCOPE_FILE_SYSTEM || scope == BTRC_SCOPE_SEARCH || scope == BTRC_SCOPE_NOW_PLAYING) { int attribCopiedIndex = 0; for (int item_idx = 0; item_idx < numItems; item_idx++) { if (BTRC_ITEM_FOLDER == p_item_types[item_idx]) { btrc_folder_items_t* pitem = &p_items[item_idx]; memcpy(pitem->folder.uid, p_item_uid + item_idx * BTRC_UID_SIZE, BTRC_UID_SIZE); pitem->item_type = (uint8_t)BTRC_ITEM_FOLDER; pitem->folder.charset_id = BTRC_CHARSET_ID_UTF8; pitem->folder.type = p_folder_types[item_idx]; pitem->folder.playable = p_playable[item_idx]; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(displayNameArray, item_idx)); if (!copy_jstring(pitem->folder.name, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { rspStatus = BTRC_STS_INTERNAL_ERR; ALOGE("%s: failed to copy display name of folder item", __func__); break; } } else if (BTRC_ITEM_MEDIA == p_item_types[item_idx]) { btrc_folder_items_t* pitem = &p_items[item_idx]; memcpy(pitem->media.uid, p_item_uid + item_idx * BTRC_UID_SIZE, BTRC_UID_SIZE); pitem->item_type = (uint8_t)BTRC_ITEM_MEDIA; pitem->media.charset_id = BTRC_CHARSET_ID_UTF8; pitem->media.type = BTRC_MEDIA_TYPE_AUDIO; pitem->media.num_attrs = (p_num_attrs != NULL) ? p_num_attrs[item_idx] : 0; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(displayNameArray, item_idx)); if (!copy_jstring(pitem->media.name, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { rspStatus = BTRC_STS_INTERNAL_ERR; ALOGE("%s: failed to copy display name of media item", __func__); break; } /* copy item attributes */ if (!copy_item_attributes(env, object, pitem, p_attributesIds, attributesArray, item_idx, attribCopiedIndex)) { ALOGE("%s: error in copying attributes of item = %s", __func__, pitem->media.name); rspStatus = BTRC_STS_INTERNAL_ERR; break; } attribCopiedIndex += pitem->media.num_attrs; } } } } else { rspStatus = BTRC_STS_INTERNAL_ERR; ALOGE("%s: unable to allocate memory", __func__); } } RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp( *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items); if (status != BT_STATUS_SUCCESS) ALOGE("Failed get_folder_items_list_rsp, status: %d", status); /* Release allocated memory for all attributes in each media item */ if (p_items) cleanup_items(p_items, numItems); /* Release allocated memory */ if (p_folder_types) env->ReleaseByteArrayElements(folderType, p_folder_types, 0); if (p_playable) env->ReleaseByteArrayElements(playable, p_playable, 0); if (p_item_types) env->ReleaseByteArrayElements(itemType, p_item_types, 0); if (p_num_attrs) env->ReleaseIntArrayElements(numAttrs, p_num_attrs, 0); if (p_attributesIds) env->ReleaseIntArrayElements(attributesIds, p_attributesIds, 0); if (p_item_uid) env->ReleaseByteArrayElements(itemUidArray, p_item_uid, 0); if (p_items) delete[] p_items; env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setAddressedPlayerRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_rsp( *btAddr, (btrc_status_t)rspStatus); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed set_addressed_player_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean setBrowsedPlayerRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jbyte depth, jint numItems, jobjectArray textArray) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } btrc_br_folder_name_t* p_folders = NULL; if (rspStatus == BTRC_STS_NO_ERROR) { if (depth > 0) { p_folders = new btrc_br_folder_name_t[depth]; } for (int folder_idx = 0; folder_idx < depth; folder_idx++) { /* copy folder names */ ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement(textArray, folder_idx)); if (!copy_jstring(p_folders[folder_idx].p_str, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { rspStatus = BTRC_STS_INTERNAL_ERR; delete[] p_folders; env->ReleaseByteArrayElements(address, addr, 0); ALOGE("%s: Failed to copy folder name", __func__); return JNI_FALSE; } p_folders[folder_idx].str_len = strlen((char*)p_folders[folder_idx].p_str); } } uint8_t folder_depth = depth; /* folder_depth is 0 if current folder is root */ uint16_t charset_id = BTRC_CHARSET_ID_UTF8; RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_rsp( *btAddr, (btrc_status_t)rspStatus, numItems, charset_id, folder_depth, p_folders); if (status != BT_STATUS_SUCCESS) { ALOGE("%s: Failed set_browsed_player_rsp, status: %d", __func__, status); } if (depth > 0) { delete[] p_folders; } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean changePathRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jint numItems) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } uint32_t nItems = (uint32_t)numItems; RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->change_path_rsp( *btAddr, (btrc_status_t)rspStatus, (uint32_t)nItems); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed change_path_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean searchRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jint uidCounter, jint numItems) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } uint32_t nItems = (uint32_t)numItems; RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->search_rsp( *btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter, (uint32_t)nItems); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed search_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean playItemRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->play_item_rsp( *btAddr, (btrc_status_t)rspStatus); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed play_item_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean getTotalNumOfItemsRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus, jint uidCounter, jint numItems) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } uint32_t nItems = (uint32_t)numItems; RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->get_total_num_of_items_rsp( *btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter, (uint32_t)nItems); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed get_total_num_of_items_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean addToNowPlayingRspNative(JNIEnv* env, jobject object, jbyteArray address, jint rspStatus) { if (!sBluetoothAvrcpInterface) { ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); return JNI_FALSE; } jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } RawAddress* btAddr = (RawAddress*)addr; bt_status_t status = sBluetoothAvrcpInterface->add_to_now_playing_rsp( *btAddr, (btrc_status_t)rspStatus); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed add_to_now_playing_rsp, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static JNINativeMethod sMethods[] = { {"classInitNative", "()V", (void*)classInitNative}, {"initNative", "()V", (void*)initNative}, {"cleanupNative", "()V", (void*)cleanupNative}, {"getPlayStatusRspNative", "([BIII)Z", (void*)getPlayStatusRspNative}, {"getElementAttrRspNative", "([BB[I[Ljava/lang/String;)Z", (void*)getElementAttrRspNative}, {"registerNotificationRspPlayStatusNative", "(II)Z", (void*)registerNotificationRspPlayStatusNative}, {"registerNotificationRspTrackChangeNative", "(I[B)Z", (void*)registerNotificationRspTrackChangeNative}, {"registerNotificationRspPlayPosNative", "(II)Z", (void*)registerNotificationRspPlayPosNative}, {"setVolumeNative", "(I)Z", (void*)setVolumeNative}, {"setAddressedPlayerRspNative", "([BI)Z", (void*)setAddressedPlayerRspNative}, {"setBrowsedPlayerRspNative", "([BIBI[Ljava/lang/String;)Z", (void*)setBrowsedPlayerRspNative}, {"mediaPlayerListRspNative", "([BIIBI[I[B[I[B[S[Ljava/lang/String;)Z", (void*)mediaPlayerListRspNative}, {"getFolderItemsRspNative", "([BISBI[B[B[B[B[Ljava/lang/String;[I[I[Ljava/lang/String;)Z", (void*)getFolderItemsRspNative}, {"changePathRspNative", "([BII)Z", (void*)changePathRspNative}, {"getItemAttrRspNative", "([BIB[I[Ljava/lang/String;)Z", (void*)getItemAttrRspNative}, {"playItemRspNative", "([BI)Z", (void*)playItemRspNative}, {"getTotalNumOfItemsRspNative", "([BIII)Z", (void*)getTotalNumOfItemsRspNative}, {"searchRspNative", "([BIII)Z", (void*)searchRspNative}, {"addToNowPlayingRspNative", "([BI)Z", (void*)addToNowPlayingRspNative}, {"registerNotificationRspAddrPlayerChangedNative", "(III)Z", (void*)registerNotificationRspAddrPlayerChangedNative}, {"registerNotificationRspAvalPlayerChangedNative", "(I)Z", (void*)registerNotificationRspAvalPlayerChangedNative}, {"registerNotificationRspUIDsChangedNative", "(II)Z", (void*)registerNotificationRspUIDsChangedNative}, {"registerNotificationRspNowPlayingChangedNative", "(I)Z", (void*)registerNotificationRspNowPlayingChangedNative}}; int register_com_android_bluetooth_avrcp(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/Avrcp", sMethods, NELEM(sMethods)); } /* Helper function to copy attributes of item. * Assumes that all items in response have same number of attributes * * returns true on succes, false otherwise. */ static bool copy_item_attributes(JNIEnv* env, jobject object, btrc_folder_items_t* pitem, jint* p_attributesIds, jobjectArray attributesArray, int item_idx, int attribCopiedIndex) { bool success = true; /* copy attributes of the item */ if (0 < pitem->media.num_attrs) { int num_attrs = pitem->media.num_attrs; ALOGI("%s num_attr = %d", __func__, num_attrs); pitem->media.p_attrs = new btrc_element_attr_val_t[num_attrs]; if (!pitem->media.p_attrs) { return false; } for (int tempAtrCount = 0; tempAtrCount < pitem->media.num_attrs; ++tempAtrCount) { pitem->media.p_attrs[tempAtrCount].attr_id = p_attributesIds[attribCopiedIndex + tempAtrCount]; ScopedLocalRef<jstring> text( env, (jstring)env->GetObjectArrayElement( attributesArray, attribCopiedIndex + tempAtrCount)); if (!copy_jstring(pitem->media.p_attrs[tempAtrCount].text, BTRC_MAX_ATTR_STR_LEN, text.get(), env)) { success = false; ALOGE("%s: failed to copy attributes", __func__); break; } } } return success; } /* Helper function to copy String data from java to native * * returns true on succes, false otherwise */ static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr, JNIEnv* env) { if (str == NULL || jstr == NULL || env == NULL) return false; memset(str, 0, maxBytes); const char* p_str = env->GetStringUTFChars(jstr, NULL); size_t len = strnlen(p_str, maxBytes - 1); memcpy(str, p_str, len); env->ReleaseStringUTFChars(jstr, p_str); return true; } /* Helper function to cleanup items */ static void cleanup_items(btrc_folder_items_t* p_items, int numItems) { for (int item_idx = 0; item_idx < numItems; item_idx++) { /* release memory for attributes in case item is media item */ if ((BTRC_ITEM_MEDIA == p_items[item_idx].item_type) && p_items[item_idx].media.p_attrs != NULL) delete[] p_items[item_idx].media.p_attrs; } } }