| /* |
| * 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 "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 jmethodID method_onAvailablePlayerChanged; |
| static jmethodID method_getRcPsm; |
| |
| static jclass class_AvrcpItem; |
| 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_AvrcpItem, 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, addr.get(), |
| uid, (jint)item->media.type, mediaName.get(), |
| attrIdArray.get(), attrValArray.get())); |
| if (!mediaObj.get()) { |
| ALOGE("%s failed to create AvrcpItem 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, addr.get(), |
| uid, (jint)item->folder.type, folderName.get(), |
| (jint)item->folder.playable)); |
| if (!folderObj.get()) { |
| ALOGE("%s failed to create AvrcpItem 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, addr.get(), |
| 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 void btavrcp_available_player_changed_callback ( |
| const RawAddress& bd_addr) { |
| ALOGI("%s", __func__); |
| std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbacksObj) { |
| ALOGE("%s: sCallbacksObj is null", __func__); |
| return; |
| } |
| 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); |
| sCallbackEnv->CallVoidMethod( |
| sCallbacksObj, method_onAvailablePlayerChanged, addr.get()); |
| } |
| |
| static void btavrcp_get_rcpsm_callback(const RawAddress& bd_addr, |
| uint16_t psm) { |
| ALOGE("%s -> psm received of %d", __func__, psm); |
| std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); |
| CallbackEnv sCallbackEnv(__func__); |
| if (!sCallbacksObj) { |
| ALOGE("%s: sCallbacksObj is null", __func__); |
| return; |
| } |
| 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_getRcPsm, addr.get(), |
| (jint)psm); |
| } |
| |
| 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, |
| btavrcp_available_player_changed_callback, |
| btavrcp_get_rcpsm_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_getRcPsm = env->GetMethodID(clazz, "getRcPsm", "([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[Lcom/android/bluetooth/avrcpcontroller/" |
| "AvrcpItem;)V"); |
| method_handleGetPlayerItemsRsp = env->GetMethodID( |
| clazz, "handleGetPlayerItemsRsp", |
| "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V"); |
| |
| method_createFromNativeMediaItem = |
| env->GetMethodID(clazz, "createFromNativeMediaItem", |
| "([BJILjava/lang/String;[I[Ljava/lang/String;)Lcom/" |
| "android/bluetooth/avrcpcontroller/AvrcpItem;"); |
| method_createFromNativeFolderItem = env->GetMethodID( |
| clazz, "createFromNativeFolderItem", |
| "([BJILjava/lang/String;I)Lcom/android/bluetooth/avrcpcontroller/" |
| "AvrcpItem;"); |
| method_createFromNativePlayerItem = |
| env->GetMethodID(clazz, "createFromNativePlayerItem", |
| "([BILjava/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"); |
| method_onAvailablePlayerChanged = |
| env->GetMethodID(clazz, "onAvailablePlayerChanged", "([B)V"); |
| |
| ALOGI("%s: succeeds", __func__); |
| } |
| |
| static void initNative(JNIEnv* env, jobject object) { |
| std::unique_lock<std::shared_timed_mutex> lock(sCallbacks_mutex); |
| |
| jclass tmpAvrcpItem = |
| env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpItem"); |
| class_AvrcpItem = (jclass)env->NewGlobalRef(tmpAvrcpItem); |
| |
| 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 getCurrentMetadataNative(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_current_metadata_cmd(rawAddress); |
| if (status != BT_STATUS_SUCCESS) { |
| ALOGE("Failed sending getCurrentMetadataNative 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}, |
| {"getCurrentMetadataNative", "([B)V", (void*)getCurrentMetadataNative}, |
| {"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)); |
| } |
| } |