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