/* * 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 "BluetoothAvrcpControllerJni" #define LOG_NDEBUG 0 #include "android_runtime/AndroidRuntime.h" #include "com_android_bluetooth.h" #include "hardware/bt_rc.h" #include "utils/Log.h" #include <string.h> #include <shared_mutex> 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_handleGetFolderItemsRsp; static jmethodID method_handleGetPlayerItemsRsp; static jmethodID method_handleGroupNavigationRsp; static jmethodID method_createFromNativeMediaItem; static jmethodID method_createFromNativeFolderItem; static jmethodID method_createFromNativePlayerItem; static jmethodID method_handleChangeFolderRsp; static jmethodID method_handleSetBrowsedPlayerRsp; static jmethodID method_handleSetAddressedPlayerRsp; static jmethodID method_handleAddressedPlayerChanged; static jmethodID method_handleNowPlayingContentChanged; static jclass class_MediaBrowser_MediaItem; static jclass class_AvrcpPlayer; static const btrc_ctrl_interface_t* sBluetoothAvrcpInterface = NULL; static jobject sCallbacksObj = NULL; static std::shared_timed_mutex sCallbacks_mutex; static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr, int id, int pressed) { ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp, (jint)id, (jint)pressed, addr.get()); } static void btavrcp_groupnavigation_response_callback(int id, int pressed) { ALOGV("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGroupNavigationRsp, (jint)id, (jint)pressed); } static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect, const RawAddress& bd_addr) { ALOGI("%s: conn state: rc: %d br: %d", __func__, rc_connect, br_connect); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_onConnectionStateChanged, (jboolean)rc_connect, (jboolean)br_connect, addr.get()); } static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr, int features) { ALOGV("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr.get(), (jint)features); } static void btavrcp_setplayerapplicationsetting_rsp_callback( const RawAddress& bd_addr, uint8_t accepted) { ALOGV("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp, addr.get(), (jint)accepted); } static void btavrcp_playerapplicationsetting_callback( const RawAddress& 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", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); /* TODO ext attrs * Flattening defined attributes: <id,num_values,values[]> */ jint arraylen = 0; for (int i = 0; i < num_attr; i++) { /*2 bytes for id and num */ arraylen += 2 + app_attrs[i].num_val; } ALOGV(" arraylen %d", arraylen); ScopedLocalRef<jbyteArray> playerattribs( sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen)); if (!playerattribs.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } for (int i = 0, k = 0; (i < num_attr) && (k < arraylen); i++) { sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, (jbyte*)&(app_attrs[i].attr_id)); k++; sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, (jbyte*)&(app_attrs[i].num_val)); k++; sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, app_attrs[i].num_val, (jbyte*)(app_attrs[i].attr_val)); k = k + app_attrs[i].num_val; } sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplayerappsetting, addr.get(), playerattribs.get(), (jint)arraylen); } static void btavrcp_playerapplicationsetting_changed_callback( const RawAddress& bd_addr, const btrc_player_settings_t& vals) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); int arraylen = vals.num_attr * 2; ScopedLocalRef<jbyteArray> playerattribs( sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen)); if (!playerattribs.get()) { ALOGE("Fail to new jbyteArray playerattribs "); return; } /* * Flatening format: <id,val> */ for (int i = 0, k = 0; (i < vals.num_attr) && (k < arraylen); i++) { sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, (jbyte*)&(vals.attr_ids[i])); k++; sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1, (jbyte*)&(vals.attr_values[i])); k++; } sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplayerappsettingchanged, addr.get(), playerattribs.get(), (jint)arraylen); } static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr, uint8_t abs_vol, uint8_t label) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume, addr.get(), (jbyte)abs_vol, (jbyte)label); } static void btavrcp_register_notification_absvol_callback( const RawAddress& bd_addr, uint8_t label) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleRegisterNotificationAbsVol, addr.get(), (jbyte)label); } static void btavrcp_track_changed_callback(const RawAddress& 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. */ ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } ScopedLocalRef<jintArray> attribIds(sCallbackEnv.get(), sCallbackEnv->NewIntArray(num_attr)); if (!attribIds.get()) { ALOGE(" failed to set new array for attribIds"); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); jclass strclazz = sCallbackEnv->FindClass("java/lang/String"); ScopedLocalRef<jobjectArray> stringArray( sCallbackEnv.get(), sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0)); if (!stringArray.get()) { ALOGE(" failed to get String array"); return; } for (jint i = 0; i < num_attr; i++) { ScopedLocalRef<jstring> str( sCallbackEnv.get(), sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text))); if (!str.get()) { ALOGE("Unable to get str"); return; } sCallbackEnv->SetIntArrayRegion(attribIds.get(), i, 1, (jint*)&(p_attrs[i].attr_id)); sCallbackEnv->SetObjectArrayElement(stringArray.get(), i, str.get()); } sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handletrackchanged, addr.get(), (jbyte)(num_attr), attribIds.get(), stringArray.get()); } static void btavrcp_play_position_changed_callback(const RawAddress& bd_addr, uint32_t song_len, uint32_t song_pos) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaypositionchanged, addr.get(), (jint)(song_len), (jint)song_pos); } static void btavrcp_play_status_changed_callback( const RawAddress& bd_addr, btrc_play_status_t play_status) { ALOGI("%s", __func__); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaystatuschanged, addr.get(), (jbyte)play_status); } static void btavrcp_get_folder_items_callback( const RawAddress& bd_addr, btrc_status_t status, const btrc_folder_items_t* folder_items, uint8_t count) { /* Folder items are list of items that can be either BTRC_ITEM_PLAYER * BTRC_ITEM_MEDIA, BTRC_ITEM_FOLDER. Here we translate them to their java * counterparts by calling the java constructor for each of the items. */ ALOGV("%s count %d", __func__, count); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); // Inspect if the first element is a folder/item or player listing. They are // always exclusive. bool isPlayerListing = count > 0 && (folder_items[0].item_type == BTRC_ITEM_PLAYER); // Initialize arrays for Folder OR Player listing. ScopedLocalRef<jobjectArray> itemArray(sCallbackEnv.get(), NULL); if (isPlayerListing) { itemArray.reset( sCallbackEnv->NewObjectArray((jint)count, class_AvrcpPlayer, 0)); } else { itemArray.reset(sCallbackEnv->NewObjectArray( (jint)count, class_MediaBrowser_MediaItem, 0)); } if (!itemArray.get()) { ALOGE("%s itemArray allocation failed.", __func__); return; } for (int i = 0; i < count; i++) { const btrc_folder_items_t* item = &(folder_items[i]); ALOGV("%s item type %d", __func__, item->item_type); switch (item->item_type) { case BTRC_ITEM_MEDIA: { // Parse name ScopedLocalRef<jstring> mediaName( sCallbackEnv.get(), sCallbackEnv->NewStringUTF((const char*)item->media.name)); if (!mediaName.get()) { ALOGE("%s can't allocate media name string!", __func__); return; } // Parse UID long long uid = *(long long*)item->media.uid; // Parse Attrs ScopedLocalRef<jintArray> attrIdArray( sCallbackEnv.get(), sCallbackEnv->NewIntArray(item->media.num_attrs)); if (!attrIdArray.get()) { ALOGE("%s can't allocate attr id array!", __func__); return; } ScopedLocalRef<jobjectArray> attrValArray( sCallbackEnv.get(), sCallbackEnv->NewObjectArray( item->media.num_attrs, sCallbackEnv->FindClass("java/lang/String"), 0)); if (!attrValArray.get()) { ALOGE("%s can't allocate attr val array!", __func__); return; } for (int j = 0; j < item->media.num_attrs; j++) { sCallbackEnv->SetIntArrayRegion( attrIdArray.get(), j, 1, (jint*)&(item->media.p_attrs[j].attr_id)); ScopedLocalRef<jstring> attrValStr( sCallbackEnv.get(), sCallbackEnv->NewStringUTF((char*)(item->media.p_attrs[j].text))); sCallbackEnv->SetObjectArrayElement(attrValArray.get(), j, attrValStr.get()); } ScopedLocalRef<jobject> mediaObj( sCallbackEnv.get(), (jobject)sCallbackEnv->CallObjectMethod( sCallbacksObj, method_createFromNativeMediaItem, uid, (jint)item->media.type, mediaName.get(), attrIdArray.get(), attrValArray.get())); if (!mediaObj.get()) { ALOGE("%s failed to creae MediaItem for type ITEM_MEDIA", __func__); return; } sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, mediaObj.get()); break; } case BTRC_ITEM_FOLDER: { // Parse name ScopedLocalRef<jstring> folderName( sCallbackEnv.get(), sCallbackEnv->NewStringUTF((const char*)item->folder.name)); if (!folderName.get()) { ALOGE("%s can't allocate folder name string!", __func__); return; } // Parse UID long long uid = *(long long*)item->folder.uid; ScopedLocalRef<jobject> folderObj( sCallbackEnv.get(), (jobject)sCallbackEnv->CallObjectMethod( sCallbacksObj, method_createFromNativeFolderItem, uid, (jint)item->folder.type, folderName.get(), (jint)item->folder.playable)); if (!folderObj.get()) { ALOGE("%s failed to create MediaItem for type ITEM_FOLDER", __func__); return; } sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, folderObj.get()); break; } case BTRC_ITEM_PLAYER: { // Parse name isPlayerListing = true; jint id = (jint)item->player.player_id; jint playerType = (jint)item->player.major_type; jint playStatus = (jint)item->player.play_status; ScopedLocalRef<jbyteArray> featureBitArray( sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_FEATURE_BIT_MASK_SIZE * sizeof(uint8_t))); if (!featureBitArray.get()) { ALOGE("%s failed to allocate featureBitArray", __func__); return; } sCallbackEnv->SetByteArrayRegion( featureBitArray.get(), 0, sizeof(uint8_t) * BTRC_FEATURE_BIT_MASK_SIZE, (jbyte*)item->player.features); ScopedLocalRef<jstring> playerName( sCallbackEnv.get(), sCallbackEnv->NewStringUTF((const char*)item->player.name)); if (!playerName.get()) { ALOGE("%s can't allocate player name string!", __func__); return; } ScopedLocalRef<jobject> playerObj( sCallbackEnv.get(), (jobject)sCallbackEnv->CallObjectMethod( sCallbacksObj, method_createFromNativePlayerItem, id, playerName.get(), featureBitArray.get(), playStatus, playerType)); if (!playerObj.get()) { ALOGE("%s failed to create AvrcpPlayer from ITEM_PLAYER", __func__); return; } sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, playerObj.get()); break; } default: ALOGE("%s cannot understand type %d", __func__, item->item_type); } } if (isPlayerListing) { sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetPlayerItemsRsp, addr.get(), itemArray.get()); } else { sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetFolderItemsRsp, addr.get(), status, itemArray.get()); } } static void btavrcp_change_path_callback(const RawAddress& bd_addr, uint32_t count) { ALOGI("%s count %d", __func__, count); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleChangeFolderRsp, addr.get(), (jint)count); } static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr, uint8_t num_items, uint8_t depth) { ALOGI("%s items %d depth %d", __func__, num_items, depth); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetBrowsedPlayerRsp, addr.get(), (jint)num_items, (jint)depth); } static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr, uint8_t status) { ALOGI("%s status %d", __func__, status); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAddressedPlayerRsp, addr.get(), (jint)status); } static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr, uint16_t id) { ALOGI("%s status %d", __func__, id); std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; if (!sCallbacksObj) { ALOGE("%s: sCallbacksObj is null", __func__); return; } ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod( sCallbacksObj, method_handleAddressedPlayerChanged, addr.get(), (jint)id); } static void btavrcp_now_playing_content_changed_callback( const RawAddress& bd_addr) { ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { ALOGE("%s: Failed to allocate a new byte array", __func__); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); sCallbackEnv->CallVoidMethod( sCallbacksObj, method_handleNowPlayingContentChanged, addr.get()); } 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, btavrcp_get_folder_items_callback, btavrcp_change_path_callback, btavrcp_set_browsed_player_callback, btavrcp_set_addressed_player_callback, btavrcp_addressed_player_changed_callback, btavrcp_now_playing_content_changed_callback}; static void classInitNative(JNIEnv* env, jclass clazz) { method_handlePassthroughRsp = env->GetMethodID(clazz, "handlePassthroughRsp", "(II[B)V"); method_handleGroupNavigationRsp = env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V"); method_onConnectionStateChanged = env->GetMethodID(clazz, "onConnectionStateChanged", "(ZZ[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"); method_handleGetFolderItemsRsp = env->GetMethodID(clazz, "handleGetFolderItemsRsp", "([BI[Landroid/media/browse/MediaBrowser$MediaItem;)V"); method_handleGetPlayerItemsRsp = env->GetMethodID( clazz, "handleGetPlayerItemsRsp", "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V"); method_createFromNativeMediaItem = env->GetMethodID(clazz, "createFromNativeMediaItem", "(JILjava/lang/String;[I[Ljava/lang/String;)Landroid/" "media/browse/MediaBrowser$MediaItem;"); method_createFromNativeFolderItem = env->GetMethodID( clazz, "createFromNativeFolderItem", "(JILjava/lang/String;I)Landroid/media/browse/MediaBrowser$MediaItem;"); method_createFromNativePlayerItem = env->GetMethodID(clazz, "createFromNativePlayerItem", "(ILjava/lang/String;[BII)Lcom/android/bluetooth/" "avrcpcontroller/AvrcpPlayer;"); method_handleChangeFolderRsp = env->GetMethodID(clazz, "handleChangeFolderRsp", "([BI)V"); method_handleSetBrowsedPlayerRsp = env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "([BII)V"); method_handleSetAddressedPlayerRsp = env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "([BI)V"); method_handleAddressedPlayerChanged = env->GetMethodID(clazz, "handleAddressedPlayerChanged", "([BI)V"); method_handleNowPlayingContentChanged = env->GetMethodID(clazz, "handleNowPlayingContentChanged", "([B)V"); ALOGI("%s: succeeds", __func__); } static void initNative(JNIEnv* env, jobject object) { std::unique_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); jclass tmpMediaItem = env->FindClass("android/media/browse/MediaBrowser$MediaItem"); class_MediaBrowser_MediaItem = (jclass)env->NewGlobalRef(tmpMediaItem); jclass tmpBtPlayer = env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpPlayer"); class_AvrcpPlayer = (jclass)env->NewGlobalRef(tmpBtPlayer); 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 (sCallbacksObj != NULL) { ALOGW("Cleaning up Avrcp callback object"); env->DeleteGlobalRef(sCallbacksObj); sCallbacksObj = NULL; } sBluetoothAvrcpInterface = (btrc_ctrl_interface_t*)btInf->get_profile_interface( BT_PROFILE_AV_RC_CTRL_ID); if (sBluetoothAvrcpInterface == NULL) { ALOGE("Failed to get Bluetooth Avrcp Controller Interface"); return; } bt_status_t status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d", status); sBluetoothAvrcpInterface = NULL; return; } sCallbacksObj = env->NewGlobalRef(object); } static void cleanupNative(JNIEnv* env, jobject object) { std::unique_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); const bt_interface_t* btInf = getBluetoothInterface(); if (btInf == NULL) { ALOGE("Bluetooth module is not loaded"); return; } if (sBluetoothAvrcpInterface != NULL) { sBluetoothAvrcpInterface->cleanup(); sBluetoothAvrcpInterface = NULL; } if (sCallbacksObj != NULL) { env->DeleteGlobalRef(sCallbacksObj); sCallbacksObj = NULL; } } static jboolean sendPassThroughCommandNative(JNIEnv* env, jobject object, jbyteArray address, jint key_code, jint key_state) { if (!sBluetoothAvrcpInterface) return JNI_FALSE; ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); ALOGI("key_code: %d, key_state: %d", key_code, key_state); 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->send_pass_through_cmd( rawAddress, (uint8_t)key_code, (uint8_t)key_state); if (status != 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) { if (!sBluetoothAvrcpInterface) return JNI_FALSE; ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); ALOGI("key_code: %d, key_state: %d", key_code, key_state); 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->send_group_navigation_cmd( rawAddress, (uint8_t)key_code, (uint8_t)key_state); if (status != 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) { ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } uint8_t* pAttrs = new uint8_t[num_attrib]; uint8_t* pAttrsVal = new uint8_t[num_attrib]; if ((!pAttrs) || (!pAttrsVal)) { delete[] pAttrs; ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory"); return; } jbyte* attr = env->GetByteArrayElements(attrib_ids, NULL); jbyte* attr_val = env->GetByteArrayElements(attrib_val, NULL); if ((!attr) || (!attr_val)) { delete[] pAttrs; delete[] pAttrsVal; jniThrowIOException(env, EINVAL); return; } int i; for (i = 0; i < num_attrib; ++i) { pAttrs[i] = (uint8_t)attr[i]; pAttrsVal[i] = (uint8_t)attr_val[i]; } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->set_player_app_setting_cmd( rawAddress, (uint8_t)num_attrib, pAttrs, pAttrsVal); if (status != 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) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->set_volume_rsp( rawAddress, (uint8_t)abs_vol, (uint8_t)label); if (status != 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) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp( rawAddress, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol, (uint8_t)label); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void getPlaybackStateNative(JNIEnv* env, jobject object, jbyteArray address) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_playback_state_cmd(rawAddress); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending getPlaybackStateNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void getNowPlayingListNative(JNIEnv* env, jobject object, jbyteArray address, jint start, jint end) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_now_playing_list_cmd( rawAddress, start, end); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending getNowPlayingListNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void getFolderListNative(JNIEnv* env, jobject object, jbyteArray address, jint start, jint end) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_folder_list_cmd(rawAddress, start, end); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending getFolderListNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void getPlayerListNative(JNIEnv* env, jobject object, jbyteArray address, jint start, jint end) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->get_player_list_cmd(rawAddress, start, end); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending getPlayerListNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void changeFolderPathNative(JNIEnv* env, jobject object, jbyteArray address, jbyte direction, jlong uid) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } // jbyte* uid = env->GetByteArrayElements(uidarr, NULL); // if (!uid) { // jniThrowIOException(env, EINVAL); // return; //} ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd( rawAddress, (uint8_t)direction, (uint8_t*)&uid); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending changeFolderPathNative command, status: %d", status); } // env->ReleaseByteArrayElements(address, addr, 0); } static void setBrowsedPlayerNative(JNIEnv* env, jobject object, jbyteArray address, jint id) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd( rawAddress, (uint16_t)id); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void setAddressedPlayerNative(JNIEnv* env, jobject object, jbyteArray address, jint id) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd( rawAddress, (uint16_t)id); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending setAddressedPlayerNative command, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); } static void playItemNative(JNIEnv* env, jobject object, jbyteArray address, jbyte scope, jlong uid, jint uidCounter) { if (!sBluetoothAvrcpInterface) return; jbyte* addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return; } // jbyte* uid = env->GetByteArrayElements(uidArr, NULL); // if (!uid) { // jniThrowIOException(env, EINVAL); // return; // } RawAddress rawAddress; rawAddress.FromOctets((uint8_t*)addr); ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface); bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd( rawAddress, (uint8_t)scope, (uint8_t*)&uid, (uint16_t)uidCounter); if (status != BT_STATUS_SUCCESS) { ALOGE("Failed sending playItemNative 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}, {"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative}, {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative}, {"getFolderListNative", "([BII)V", (void*)getFolderListNative}, {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative}, {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative}, {"playItemNative", "([BBJI)V", (void*)playItemNative}, {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative}, {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative}, }; int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) { return jniRegisterNativeMethods( env, "com/android/bluetooth/avrcpcontroller/AvrcpControllerService", sMethods, NELEM(sMethods)); } }