| /* |
| * 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 "com_android_bluetooth.h" |
| #include "hardware/bt_rc.h" |
| #include "utils/Log.h" |
| #include "android_runtime/AndroidRuntime.h" |
| |
| #include <string.h> |
| |
| #define CHECK_CALLBACK_ENV()\ |
| do { \ |
| if (!checkCallbackThread()) { \ |
| ALOGE("Callback: '%s' is not called on the correct thread", __func__); \ |
| return; \ |
| } \ |
| } while (0) |
| |
| |
| 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; |
| static JNIEnv *sCallbackEnv = 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 bool checkCallbackThread() { |
| // Always fetch the latest callbackEnv from AdapterService. |
| // Caching this could cause this sCallbackEnv to go out-of-sync |
| // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event |
| // is received |
| sCallbackEnv = getCallbackEnv(); |
| |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| if (sCallbackEnv != env || sCallbackEnv == NULL) return false; |
| return true; |
| } |
| |
| static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, |
| btrc_remote_features_t features) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Unable to allocate byte array for bd_addr"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| |
| /** Callback for play status request */ |
| static void btavrcp_get_play_status_callback(bt_bdaddr_t* bd_addr) { |
| ALOGI("%s", __func__); |
| |
| jbyteArray addr; |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for get_play_status command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs, |
| bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| jintArray attrs; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for get_element_attr command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr); |
| if (!attrs) { |
| ALOGE("Fail to new jintArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| |
| sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs); |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, addr, (jbyte)num_attr, |
| attrs); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param, |
| bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for register_notification command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification, |
| addr, (jint)event_id, (jint)param); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| |
| static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype, |
| bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for volume_change command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, addr, (jint)volume, |
| (jint)ctype); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| sCallbackEnv->DeleteLocalRef(addr); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| } |
| |
| static void btavrcp_passthrough_command_callback(int id, int pressed, |
| bt_bdaddr_t* bd_addr) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for passthrough_command command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, addr, (jint)id, |
| (jint)pressed); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_set_addressed_player_callback(uint16_t player_id, |
| bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for set_addressed_player command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayerCallback, addr, \ |
| (jint) player_id); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_set_browsed_player_callback(uint16_t player_id, bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for set_browsed_player command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->CallVoidMethod( |
| mCallbacksObj, method_setBrowsedPlayerCallback, addr, (jint) player_id); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| 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, bt_bdaddr_t *bd_addr) { |
| jbyteArray addr; |
| jintArray attr_ids = NULL; |
| uint32_t *puiAttr = (uint32_t *)p_attr_ids; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for get_folder_items command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| |
| /* 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 = (jintArray)sCallbackEnv->NewIntArray(num_attr); |
| if (!attr_ids) { |
| ALOGE("Fail to allocate new jintArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| sCallbackEnv->SetIntArrayRegion(attr_ids, 0, num_attr, (jint *)puiAttr); |
| } |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getFolderItemsCallback, addr, |
| (jbyte) scope, (jint) start_item, (jint) end_item, (jbyte) num_attr, attr_ids); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| if (attr_ids != NULL) sCallbackEnv->DeleteLocalRef(attr_ids); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_change_path_callback(uint8_t direction, uint8_t* folder_uid, |
| bt_bdaddr_t *bd_addr) { |
| |
| jbyteArray addr; |
| jbyteArray attrs;; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| attrs = sCallbackEnv->NewByteArray(BTRC_UID_SIZE); |
| if (!attrs) { |
| ALOGE("Fail to new jintArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for change_path command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->SetByteArrayRegion( |
| attrs, 0, sizeof(uint8_t)*BTRC_UID_SIZE, (jbyte *)folder_uid); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePathCallback, addr, |
| (jbyte) direction, attrs); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| 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, bt_bdaddr_t *bd_addr) { |
| |
| jbyteArray addr; |
| jintArray attrs; |
| jbyteArray attr_uid; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| attr_uid = sCallbackEnv->NewByteArray(BTRC_UID_SIZE); |
| if (!attr_uid) { |
| ALOGE("Fail to new jintArray for attr_uid"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for get_item_attr command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attr_uid); |
| return; |
| } |
| |
| attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr); |
| if (!attrs) { |
| ALOGE("Fail to new jintArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attr_uid); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs); |
| sCallbackEnv->SetByteArrayRegion(attr_uid, 0, sizeof(uint8_t)*BTRC_UID_SIZE, (jbyte *)uid); |
| |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getItemAttrCallback, addr, |
| (jbyte) scope, attr_uid, (jint) uid_counter, (jbyte)num_attr, attrs); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attr_uid); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_play_item_callback(uint8_t scope, uint16_t uid_counter, uint8_t* uid, |
| bt_bdaddr_t *bd_addr) { |
| |
| jbyteArray addr; |
| jbyteArray attrs; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| attrs = sCallbackEnv->NewByteArray(BTRC_UID_SIZE); |
| if (!attrs) { |
| ALOGE("%s:Fail to new jByteArray attrs for play_item command", __func__); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for play_item command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->SetByteArrayRegion(attrs, 0, sizeof(uint8_t)*BTRC_UID_SIZE, (jbyte *)uid); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItemCallback, addr, |
| (jbyte) scope, (jint) uid_counter, attrs); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_get_total_num_items_callback(uint8_t scope, bt_bdaddr_t *bd_addr) { |
| |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for get_total_num_items command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->CallVoidMethod( |
| mCallbacksObj, method_getTotalNumOfItemsCallback, addr, (jbyte) scope); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_search_callback(uint16_t charset_id, uint16_t str_len, uint8_t* p_str, |
| bt_bdaddr_t *bd_addr) { |
| |
| jbyteArray addr; |
| jbyteArray attrs; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| attrs = sCallbackEnv->NewByteArray(str_len); |
| if (!attrs) { |
| ALOGE("Fail to new jintArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for search command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->SetByteArrayRegion( |
| attrs, 0, str_len*sizeof(uint8_t), (jbyte *)p_str); |
| sCallbackEnv->CallVoidMethod( |
| mCallbacksObj, method_searchCallback, addr, (jint) charset_id, attrs); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| static void btavrcp_add_to_play_list_callback(uint8_t scope, |
| uint8_t* uid, uint16_t uid_counter, bt_bdaddr_t *bd_addr) { |
| jbyteArray attrs; |
| jbyteArray addr; |
| |
| CHECK_CALLBACK_ENV(); |
| |
| addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); |
| if (!addr) { |
| ALOGE("Fail to new jbyteArray bd addr for add_to_play_list command"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| return; |
| } |
| |
| attrs = sCallbackEnv->NewByteArray(BTRC_UID_SIZE); |
| if (!attrs) { |
| ALOGE("Fail to new jByteArray for attrs"); |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(addr); |
| return; |
| } |
| |
| if (mCallbacksObj) { |
| sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); |
| sCallbackEnv->SetByteArrayRegion(attrs, 0, sizeof(uint8_t)*BTRC_UID_SIZE, (jbyte *)uid); |
| sCallbackEnv->CallVoidMethod(mCallbacksObj, method_addToPlayListCallback, addr, |
| (jbyte) scope, attrs, (jint) uid_counter); |
| } else { |
| ALOGE("%s: mCallbacksObj is null", __func__); |
| } |
| |
| checkAndClearExceptionFromCallback(sCallbackEnv, __func__); |
| sCallbackEnv->DeleteLocalRef(attrs); |
| sCallbackEnv->DeleteLocalRef(addr); |
| } |
| |
| |
| 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", "([BBIIB[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; |
| bt_status_t status; |
| |
| if ( (btInf = getBluetoothInterface()) == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothAvrcpInterface !=NULL) { |
| ALOGW("Cleaning up Avrcp Interface before initializing..."); |
| sBluetoothAvrcpInterface->cleanup(); |
| sBluetoothAvrcpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| ALOGW("Cleaning up Avrcp callback object"); |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| |
| if ( (sBluetoothAvrcpInterface = (btrc_interface_t *) |
| btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) { |
| ALOGE("Failed to get Bluetooth Avrcp Interface"); |
| return; |
| } |
| |
| if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) != |
| 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; |
| |
| if ( (btInf = getBluetoothInterface()) == NULL) { |
| ALOGE("Bluetooth module is not loaded"); |
| return; |
| } |
| |
| if (sBluetoothAvrcpInterface !=NULL) { |
| sBluetoothAvrcpInterface->cleanup(); |
| sBluetoothAvrcpInterface = NULL; |
| } |
| |
| if (mCallbacksObj != NULL) { |
| env->DeleteGlobalRef(mCallbacksObj); |
| mCallbacksObj = NULL; |
| } |
| } |
| |
| static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, |
| jbyteArray address, jint playStatus, jint songLen, jint songPos) { |
| bt_status_t status; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((bt_bdaddr_t*)btAddr, |
| (btrc_play_status_t)playStatus,songLen, songPos)) != 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) { |
| jint *attr; |
| bt_status_t status; |
| jstring text; |
| int attr_cnt; |
| btrc_element_attr_val_t *pAttrs = NULL; |
| |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| 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; |
| } |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| 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; |
| } |
| |
| attr = env->GetIntArrayElements(attrIds, NULL); |
| if (!attr) { |
| delete[] pAttrs; |
| jniThrowIOException(env, EINVAL); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| return JNI_FALSE; |
| } |
| |
| for (attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) { |
| pAttrs[attr_cnt].attr_id = attr[attr_cnt]; |
| text = (jstring) env->GetObjectArrayElement(textArray, attr_cnt); |
| |
| if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text, env)) |
| break; |
| |
| env->DeleteLocalRef(text); |
| } |
| |
| if (attr_cnt < numAttr) { |
| delete[] pAttrs; |
| env->DeleteLocalRef(text); |
| env->ReleaseIntArrayElements(attrIds, attr, 0); |
| ALOGE("%s: Failed to copy attributes", __func__); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(btAddr, numAttr, pAttrs)) != |
| 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) { |
| jint *attr = NULL; |
| bt_status_t status; |
| int attr_cnt; |
| btrc_element_attr_val_t *pAttrs = NULL; |
| |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| 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; |
| } |
| |
| 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; |
| } |
| |
| if (attrIds != NULL) { |
| attr = env->GetIntArrayElements(attrIds, NULL); |
| if (!attr) { |
| delete[] pAttrs; |
| jniThrowIOException(env, EINVAL); |
| env->ReleaseByteArrayElements(address, addr, 0); |
| return JNI_FALSE; |
| } |
| } |
| |
| for (attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) { |
| pAttrs[attr_cnt].attr_id = attr[attr_cnt]; |
| jstring text = (jstring) env->GetObjectArrayElement(textArray, attr_cnt); |
| |
| if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text, env)) { |
| rspStatus = BTRC_STS_INTERNAL_ERR; |
| env->DeleteLocalRef(text); |
| ALOGE("%s: Failed to copy attributes", __func__); |
| break; |
| } |
| |
| env->DeleteLocalRef(text); |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_item_attr_rsp(btAddr, |
| (btrc_status_t)rspStatus, numAttr, pAttrs)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| param.play_status = (btrc_play_status_t)playStatus; |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| jbyte *trk; |
| int uid_idx; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| trk = env->GetByteArrayElements(track, NULL); |
| if (!trk) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| for (uid_idx = 0; uid_idx < BTRC_UID_SIZE; ++uid_idx) { |
| param.track[uid_idx] = trk[uid_idx]; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| param.song_pos = (uint32_t)playPos; |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp( |
| BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| param.uids_changed.uid_counter = (uint16_t)uidCounter; |
| |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp( |
| BTRC_EVT_UIDS_CHANGED, |
| (btrc_notification_type_t)type,¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| param.addr_player_changed.player_id = (uint16_t)playerId; |
| param.addr_player_changed.uid_counter = (uint16_t)uidCounter; |
| |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_ADDR_PLAYER_CHANGE, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| btrc_register_notification_t param; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_AVAL_PLAYER_CHANGE, |
| (btrc_notification_type_t)type, ¶m)) != 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) { |
| bt_status_t status; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != 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, |
| jbyteArray playerTypes,jintArray playerSubtypes, |
| jbyteArray playStatusValues, |
| jshortArray featureBitmask,jobjectArray textArray) { |
| bt_status_t status; |
| btrc_folder_items_t *p_items = NULL; |
| int itemIdx = 0, InnCnt = 0; |
| jbyte* p_playerTypes = NULL,*p_PlayStatusValues = NULL; |
| jshort *p_FeatBitMaskValues = NULL; |
| jint *p_playerSubTypes = NULL; |
| jstring text; |
| |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if (rspStatus == BTRC_STS_NO_ERROR) { |
| /* allocate memory */ |
| 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_playerTypes || !p_playerSubTypes || !p_PlayStatusValues || !p_FeatBitMaskValues |
| || !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); |
| 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 */ |
| for (itemIdx = 0; itemIdx < numItems; ++itemIdx) { |
| p_items[itemIdx].player.player_id = (uint16_t)(itemIdx+1); |
| 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; |
| |
| text = (jstring) env->GetObjectArrayElement(textArray, itemIdx); |
| /* copy player name */ |
| if (!copy_jstring(p_items[itemIdx].player.name, BTRC_MAX_ATTR_STR_LEN, text, env)) |
| break; |
| |
| env->DeleteLocalRef(text); |
| |
| /* Feature bit mask is 128-bit value each */ |
| for (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__); |
| } |
| |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(btAddr, |
| (btrc_status_t)rspStatus, uidCounter, numItems, p_items)) != 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) { |
| jstring text; /* folder or item name */ |
| bt_status_t status = BT_STATUS_SUCCESS; |
| 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; |
| int item_idx = 0; |
| |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| /* 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 (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]; |
| |
| text = (jstring) env->GetObjectArrayElement(displayNameArray, item_idx); |
| if (!copy_jstring(pitem->folder.name, BTRC_MAX_ATTR_STR_LEN, |
| text, env)) { |
| rspStatus = BTRC_STS_INTERNAL_ERR; |
| env->DeleteLocalRef(text); |
| ALOGE("%s: failed to copy display name of folder item", __func__); |
| break; |
| } |
| env->DeleteLocalRef(text); |
| } |
| 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; |
| |
| text = (jstring) env->GetObjectArrayElement(displayNameArray, item_idx); |
| if (!copy_jstring(pitem->media.name, BTRC_MAX_ATTR_STR_LEN, text, env)){ |
| rspStatus = BTRC_STS_INTERNAL_ERR; |
| env->DeleteLocalRef(text); |
| ALOGE("%s: failed to copy display name of media item", __func__); |
| break; |
| } |
| env->DeleteLocalRef(text); |
| |
| /* 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__); |
| } |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(btAddr, |
| (btrc_status_t)rspStatus, uidCounter,numItems,p_items)) != 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) { |
| bt_status_t status; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->set_addressed_player_rsp(btAddr, |
| (btrc_status_t)rspStatus)) != 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) { |
| bt_status_t status; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| uint16_t charset_id = BTRC_CHARSET_ID_UTF8; |
| uint8_t folder_depth = depth; /* folder_depth is 0 if current folder is root */ |
| |
| btrc_br_folder_name_t *p_folders = NULL; |
| int folder_idx = 0; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if (rspStatus == BTRC_STS_NO_ERROR) { |
| if (depth > 0) |
| p_folders = new btrc_br_folder_name_t[depth]; |
| |
| for (folder_idx = 0; folder_idx < depth; folder_idx++) { |
| /* copy folder names */ |
| jstring text = (jstring) env->GetObjectArrayElement(textArray, folder_idx); |
| |
| if (!copy_jstring(p_folders[folder_idx].p_str, BTRC_MAX_ATTR_STR_LEN, |
| text, env)) { |
| rspStatus = BTRC_STS_INTERNAL_ERR; |
| env->DeleteLocalRef(text); |
| 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); |
| |
| env->DeleteLocalRef(text); |
| } |
| |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->set_browsed_player_rsp(btAddr, |
| (btrc_status_t) rspStatus, numItems, charset_id, folder_depth, |
| p_folders)) != 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) { |
| bt_status_t status; |
| uint32_t nItems = (uint32_t)numItems; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->change_path_rsp(btAddr, (btrc_status_t)rspStatus, |
| (uint32_t) nItems)) != 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) { |
| bt_status_t status; |
| uint32_t nItems = (uint32_t)numItems; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->search_rsp(btAddr, (btrc_status_t)rspStatus, |
| (uint32_t) uidCounter, (uint32_t) nItems)) != 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) { |
| bt_status_t status; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->play_item_rsp(btAddr, (btrc_status_t)rspStatus)) |
| != 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) { |
| bt_status_t status; |
| uint32_t nItems = (uint32_t)numItems; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->get_total_num_of_items_rsp(btAddr, |
| (btrc_status_t)rspStatus, (uint32_t) uidCounter, (uint32_t) nItems)) |
| != 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) { |
| bt_status_t status; |
| bt_bdaddr_t * btAddr; |
| jbyte *addr; |
| |
| if (!sBluetoothAvrcpInterface) { |
| ALOGE("%s: sBluetoothAvrcpInterface is null", __func__); |
| return JNI_FALSE; |
| } |
| |
| addr = env->GetByteArrayElements(address, NULL); |
| btAddr = (bt_bdaddr_t *) addr; |
| if (!addr) { |
| jniThrowIOException(env, EINVAL); |
| return JNI_FALSE; |
| } |
| |
| if ((status = sBluetoothAvrcpInterface->add_to_now_playing_rsp(btAddr, |
| (btrc_status_t)rspStatus)) != 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[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; |
| jstring text; |
| |
| /* 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); |
| |
| if (!(pitem->media.p_attrs = new btrc_element_attr_val_t[num_attrs])) { |
| return false; |
| } |
| |
| for (int tempAtrCount = 0; tempAtrCount < pitem->media.num_attrs; ++tempAtrCount) { |
| pitem->media.p_attrs[tempAtrCount].attr_id = |
| p_attributesIds[attribCopiedIndex + tempAtrCount]; |
| |
| text = (jstring) env->GetObjectArrayElement(attributesArray, |
| attribCopiedIndex + tempAtrCount); |
| |
| if (!copy_jstring(pitem->media.p_attrs[tempAtrCount].text, BTRC_MAX_ATTR_STR_LEN, |
| text, env)) { |
| success = false; |
| env->DeleteLocalRef(text); |
| ALOGE("%s: failed to copy attributes", __func__); |
| break; |
| } |
| env->DeleteLocalRef(text); |
| } |
| } |
| 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) { |
| const char* p_str; |
| int len; |
| |
| if (str == NULL || jstr == NULL || env == NULL) |
| return false; |
| |
| memset(str, 0, maxBytes); |
| p_str = env->GetStringUTFChars(jstr, NULL); |
| 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; |
| } |
| } |
| |
| } |