blob: afe6320e8e762ffc21ee13adb58f700251416d21 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BluetoothAvrcpControllerJni"
#define LOG_NDEBUG 0
#include "android_runtime/AndroidRuntime.h"
#include "com_android_bluetooth.h"
#include "hardware/bt_rc.h"
#include "utils/Log.h"
#include <string.h>
namespace android {
static jmethodID method_handlePassthroughRsp;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_getRcFeatures;
static jmethodID method_setplayerappsettingrsp;
static jmethodID method_handleplayerappsetting;
static jmethodID method_handleplayerappsettingchanged;
static jmethodID method_handleSetAbsVolume;
static jmethodID method_handleRegisterNotificationAbsVol;
static jmethodID method_handletrackchanged;
static jmethodID method_handleplaypositionchanged;
static jmethodID method_handleplaystatuschanged;
static jmethodID method_handleGetFolderItemsRsp;
static jmethodID method_handleGetPlayerItemsRsp;
static jmethodID method_handleGroupNavigationRsp;
static jmethodID method_createFromNativeMediaItem;
static jmethodID method_createFromNativeFolderItem;
static jmethodID method_createFromNativePlayerItem;
static jmethodID method_handleChangeFolderRsp;
static jmethodID method_handleSetBrowsedPlayerRsp;
static jmethodID method_handleSetAddressedPlayerRsp;
static jclass class_MediaBrowser_MediaItem;
static jclass class_AvrcpPlayer;
static const btrc_ctrl_interface_t* sBluetoothAvrcpInterface = NULL;
static jobject sCallbacksObj = NULL;
static void btavrcp_passthrough_response_callback(bt_bdaddr_t* bd_addr, int id,
int pressed) {
ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr for passthrough response");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp,
(jint)id, (jint)pressed, addr);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_groupnavigation_response_callback(int id, int pressed) {
ALOGV("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGroupNavigationRsp,
(jint)id, (jint)pressed);
}
static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect,
bt_bdaddr_t* bd_addr) {
jbyteArray addr;
ALOGI("%s: conn state: rc: %d br: %d", __func__, rc_connect, br_connect);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr for connection state");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_onConnectionStateChanged,
(jboolean)rc_connect, (jboolean)br_connect,
addr);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_get_rcfeatures_callback(bt_bdaddr_t* bd_addr,
int features) {
ALOGV("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr,
(jint)features);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_setplayerapplicationsetting_rsp_callback(
bt_bdaddr_t* bd_addr, uint8_t accepted) {
ALOGV("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp,
addr, (jint)accepted);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_playerapplicationsetting_callback(
bt_bdaddr_t* bd_addr, uint8_t num_attr, btrc_player_app_attr_t* app_attrs,
uint8_t num_ext_attr, btrc_player_app_ext_attr_t* ext_attrs) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
/* TODO ext attrs
* Flattening defined attributes: <id,num_values,values[]>
*/
jint arraylen = 0;
for (int i = 0; i < num_attr; i++) {
/*2 bytes for id and num */
arraylen += 2 + app_attrs[i].num_val;
}
ALOGV(" arraylen %d", arraylen);
jbyteArray playerattribs = sCallbackEnv->NewByteArray(arraylen);
if (!playerattribs) {
ALOGE("Fail to new jbyteArray playerattribs ");
sCallbackEnv->DeleteLocalRef(addr);
return;
}
for (int i = 0, k = 0; (i < num_attr) && (k < arraylen); i++) {
sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1,
(jbyte*)&(app_attrs[i].attr_id));
k++;
sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1,
(jbyte*)&(app_attrs[i].num_val));
k++;
sCallbackEnv->SetByteArrayRegion(playerattribs, k, app_attrs[i].num_val,
(jbyte*)(app_attrs[i].attr_val));
k = k + app_attrs[i].num_val;
}
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplayerappsetting,
addr, playerattribs, (jint)arraylen);
sCallbackEnv->DeleteLocalRef(addr);
sCallbackEnv->DeleteLocalRef(playerattribs);
}
static void btavrcp_playerapplicationsetting_changed_callback(
bt_bdaddr_t* bd_addr, btrc_player_settings_t* p_vals) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
int arraylen = p_vals->num_attr * 2;
jbyteArray playerattribs = sCallbackEnv->NewByteArray(arraylen);
if (!playerattribs) {
ALOGE("Fail to new jbyteArray playerattribs ");
sCallbackEnv->DeleteLocalRef(addr);
return;
}
/*
* Flatening format: <id,val>
*/
for (int i = 0, k = 0; (i < p_vals->num_attr) && (k < arraylen); i++) {
sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1,
(jbyte*)&(p_vals->attr_ids[i]));
k++;
sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1,
(jbyte*)&(p_vals->attr_values[i]));
k++;
}
sCallbackEnv->CallVoidMethod(sCallbacksObj,
method_handleplayerappsettingchanged, addr,
playerattribs, (jint)arraylen);
sCallbackEnv->DeleteLocalRef(addr);
sCallbackEnv->DeleteLocalRef(playerattribs);
}
static void btavrcp_set_abs_vol_cmd_callback(bt_bdaddr_t* bd_addr,
uint8_t abs_vol, uint8_t label) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume, addr,
(jbyte)abs_vol, (jbyte)label);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_register_notification_absvol_callback(bt_bdaddr_t* bd_addr,
uint8_t label) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj,
method_handleRegisterNotificationAbsVol, addr,
(jbyte)label);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_track_changed_callback(bt_bdaddr_t* bd_addr,
uint8_t num_attr,
btrc_element_attr_val_t* p_attrs) {
/*
* byteArray will be formatted like this: id,len,string
* Assuming text feild to be null terminated.
*/
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
jintArray attribIds = sCallbackEnv->NewIntArray(num_attr);
if (!attribIds) {
ALOGE(" failed to set new array for attribIds");
sCallbackEnv->DeleteLocalRef(addr);
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
jclass strclazz = sCallbackEnv->FindClass("java/lang/String");
jobjectArray stringArray =
sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0);
if (!stringArray) {
ALOGE(" failed to get String array");
sCallbackEnv->DeleteLocalRef(addr);
sCallbackEnv->DeleteLocalRef(attribIds);
return;
}
for (jint i = 0; i < num_attr; i++) {
jstring str = sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text));
if (!str) {
ALOGE("Unable to get str");
sCallbackEnv->DeleteLocalRef(addr);
sCallbackEnv->DeleteLocalRef(attribIds);
sCallbackEnv->DeleteLocalRef(stringArray);
return;
}
sCallbackEnv->SetIntArrayRegion(attribIds, i, 1,
(jint*)&(p_attrs[i].attr_id));
sCallbackEnv->SetObjectArrayElement(stringArray, i, str);
sCallbackEnv->DeleteLocalRef(str);
}
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handletrackchanged, addr,
(jbyte)(num_attr), attribIds, stringArray);
sCallbackEnv->DeleteLocalRef(addr);
sCallbackEnv->DeleteLocalRef(attribIds);
/* TODO check do we need to delete str seperately or not */
sCallbackEnv->DeleteLocalRef(stringArray);
sCallbackEnv->DeleteLocalRef(strclazz);
}
static void btavrcp_play_position_changed_callback(bt_bdaddr_t* bd_addr,
uint32_t song_len,
uint32_t song_pos) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaypositionchanged,
addr, (jint)(song_len), (jint)song_pos);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_play_status_changed_callback(
bt_bdaddr_t* bd_addr, btrc_play_status_t play_status) {
ALOGI("%s", __func__);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to get new array ");
return;
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t),
(jbyte*)bd_addr);
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaystatuschanged,
addr, (jbyte)play_status);
sCallbackEnv->DeleteLocalRef(addr);
}
static void btavrcp_get_folder_items_callback(
bt_bdaddr_t* bd_addr, btrc_status_t status,
const btrc_folder_items_t* folder_items, uint8_t count) {
/* Folder items are list of items that can be either BTRC_ITEM_PLAYER
* BTRC_ITEM_MEDIA, BTRC_ITEM_FOLDER. Here we translate them to their java
* counterparts by calling the java constructor for each of the items.
*/
ALOGV("%s count %d", __func__, count);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
// Inspect if the first element is a folder/item or player listing. They are
// always exclusive.
bool isPlayerListing =
count > 0 && (folder_items[0].item_type == BTRC_ITEM_PLAYER);
// Initialize arrays for Folder OR Player listing.
jobjectArray playerItemArray = NULL;
jobjectArray folderItemArray = NULL;
if (isPlayerListing) {
playerItemArray =
sCallbackEnv->NewObjectArray((jint)count, class_AvrcpPlayer, 0);
if (!playerItemArray) {
ALOGE("%s playerItemArray allocation failed.", __func__);
return;
}
} else {
folderItemArray = sCallbackEnv->NewObjectArray(
(jint)count, class_MediaBrowser_MediaItem, 0);
if (!folderItemArray) {
ALOGE("%s folderItemArray is empty.", __func__);
return;
}
}
for (int i = 0; i < count; i++) {
const btrc_folder_items_t* item = &(folder_items[i]);
ALOGV("%s item type %d", __func__, item->item_type);
switch (item->item_type) {
case BTRC_ITEM_MEDIA: {
// Parse name
jstring mediaName =
sCallbackEnv->NewStringUTF((const char*)item->media.name);
// Parse UID
jbyteArray uidByteArray =
sCallbackEnv->NewByteArray(sizeof(uint8_t) * BTRC_UID_SIZE);
sCallbackEnv->SetByteArrayRegion(uidByteArray, 0,
BTRC_UID_SIZE * sizeof(uint8_t),
(jbyte*)item->media.uid);
// Parse Attrs
jintArray attrIdArray =
sCallbackEnv->NewIntArray(item->media.num_attrs);
jobjectArray attrValArray = sCallbackEnv->NewObjectArray(
item->media.num_attrs, sCallbackEnv->FindClass("java/lang/String"),
0);
for (int j = 0; j < item->media.num_attrs; j++) {
sCallbackEnv->SetIntArrayRegion(
attrIdArray, j, 1, (jint*)&(item->media.p_attrs[j].attr_id));
jstring attrValStr =
sCallbackEnv->NewStringUTF((char*)(item->media.p_attrs[j].text));
sCallbackEnv->SetObjectArrayElement(attrValArray, j, attrValStr);
sCallbackEnv->DeleteLocalRef(attrValStr);
}
jobject mediaObj = (jobject)sCallbackEnv->CallObjectMethod(
sCallbacksObj, method_createFromNativeMediaItem, uidByteArray,
(jint)item->media.type, mediaName, attrIdArray, attrValArray);
sCallbackEnv->DeleteLocalRef(uidByteArray);
sCallbackEnv->DeleteLocalRef(mediaName);
sCallbackEnv->DeleteLocalRef(attrIdArray);
sCallbackEnv->DeleteLocalRef(attrValArray);
if (!mediaObj) {
ALOGE("%s failed to create MediaItem for type ITEM_MEDIA", __func__);
return;
}
sCallbackEnv->SetObjectArrayElement(folderItemArray, i, mediaObj);
sCallbackEnv->DeleteLocalRef(mediaObj);
break;
}
case BTRC_ITEM_FOLDER: {
// Parse name
jstring folderName =
sCallbackEnv->NewStringUTF((const char*)item->folder.name);
// Parse UID
jbyteArray uidByteArray =
sCallbackEnv->NewByteArray(sizeof(uint8_t) * BTRC_UID_SIZE);
sCallbackEnv->SetByteArrayRegion(uidByteArray, 0,
BTRC_UID_SIZE * sizeof(uint8_t),
(jbyte*)item->folder.uid);
jobject folderObj = (jobject)sCallbackEnv->CallObjectMethod(
sCallbacksObj, method_createFromNativeFolderItem, uidByteArray,
(jint)item->folder.type, folderName, (jint)item->folder.playable);
sCallbackEnv->DeleteLocalRef(uidByteArray);
sCallbackEnv->DeleteLocalRef(folderName);
if (!folderObj) {
ALOGE("%s failed to create MediaItem for type ITEM_MEDIA", __func__);
return;
}
sCallbackEnv->SetObjectArrayElement(folderItemArray, i, folderObj);
sCallbackEnv->DeleteLocalRef(folderObj);
break;
}
case BTRC_ITEM_PLAYER: {
// Parse name
isPlayerListing = true;
jint id = (jint)item->player.player_id;
jint playerType = (jint)item->player.major_type;
jint playStatus = (jint)item->player.play_status;
jbyteArray featureBitArray = sCallbackEnv->NewByteArray(
BTRC_FEATURE_BIT_MASK_SIZE * sizeof(uint8_t));
if (!featureBitArray) {
ALOGE("%s failed to allocate featureBitArray", __func__);
sCallbackEnv->DeleteLocalRef(playerItemArray);
sCallbackEnv->DeleteLocalRef(folderItemArray);
return;
}
sCallbackEnv->SetByteArrayRegion(
featureBitArray, 0, sizeof(uint8_t) * BTRC_FEATURE_BIT_MASK_SIZE,
(jbyte*)item->player.features);
jstring playerName =
sCallbackEnv->NewStringUTF((const char*)item->player.name);
jobject playerObj = (jobject)sCallbackEnv->CallObjectMethod(
sCallbacksObj, method_createFromNativePlayerItem, id, playerName,
featureBitArray, playStatus, playerType);
sCallbackEnv->SetObjectArrayElement(playerItemArray, i, playerObj);
sCallbackEnv->DeleteLocalRef(featureBitArray);
sCallbackEnv->DeleteLocalRef(playerObj);
break;
}
default:
ALOGE("%s cannot understand type %d", __func__, item->item_type);
}
ALOGI("%s inserted %d elements uptil now", __func__, i);
}
ALOGI("%s returning the complete set now", __func__);
if (isPlayerListing) {
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetPlayerItemsRsp,
playerItemArray);
} else {
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetFolderItemsRsp,
status, folderItemArray);
}
if (isPlayerListing) {
sCallbackEnv->DeleteLocalRef(playerItemArray);
} else {
sCallbackEnv->DeleteLocalRef(folderItemArray);
}
}
static void btavrcp_change_path_callback(bt_bdaddr_t* bd_addr, uint8_t count) {
ALOGI("%s count %d", __func__, count);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleChangeFolderRsp,
(jint)count);
}
static void btavrcp_set_browsed_player_callback(bt_bdaddr_t* bd_addr,
uint8_t num_items,
uint8_t depth) {
ALOGI("%s items %d depth %d", __func__, num_items, depth);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetBrowsedPlayerRsp,
(jint)num_items, (jint)depth);
}
static void btavrcp_set_addressed_player_callback(bt_bdaddr_t* bd_addr,
uint8_t status) {
ALOGI("%s status %d", __func__, status);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
sCallbackEnv->CallVoidMethod(
sCallbacksObj, method_handleSetAddressedPlayerRsp, (jint)status);
}
static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
sizeof(sBluetoothAvrcpCallbacks),
btavrcp_passthrough_response_callback,
btavrcp_groupnavigation_response_callback,
btavrcp_connection_state_callback,
btavrcp_get_rcfeatures_callback,
btavrcp_setplayerapplicationsetting_rsp_callback,
btavrcp_playerapplicationsetting_callback,
btavrcp_playerapplicationsetting_changed_callback,
btavrcp_set_abs_vol_cmd_callback,
btavrcp_register_notification_absvol_callback,
btavrcp_track_changed_callback,
btavrcp_play_position_changed_callback,
btavrcp_play_status_changed_callback,
btavrcp_get_folder_items_callback,
btavrcp_change_path_callback,
btavrcp_set_browsed_player_callback,
btavrcp_set_addressed_player_callback};
static void classInitNative(JNIEnv* env, jclass clazz) {
method_handlePassthroughRsp =
env->GetMethodID(clazz, "handlePassthroughRsp", "(II[B)V");
method_handleGroupNavigationRsp =
env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V");
method_onConnectionStateChanged =
env->GetMethodID(clazz, "onConnectionStateChanged", "(ZZ[B)V");
method_getRcFeatures = env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
method_setplayerappsettingrsp =
env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V");
method_handleplayerappsetting =
env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V");
method_handleplayerappsettingchanged =
env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V");
method_handleSetAbsVolume =
env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V");
method_handleRegisterNotificationAbsVol =
env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V");
method_handletrackchanged =
env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");
method_handleplaypositionchanged =
env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V");
method_handleplaystatuschanged =
env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V");
method_handleGetFolderItemsRsp =
env->GetMethodID(clazz, "handleGetFolderItemsRsp",
"(I[Landroid/media/browse/MediaBrowser$MediaItem;)V");
method_handleGetPlayerItemsRsp = env->GetMethodID(
clazz, "handleGetPlayerItemsRsp",
"([Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V");
method_createFromNativeMediaItem =
env->GetMethodID(clazz, "createFromNativeMediaItem",
"([BILjava/lang/String;[I[Ljava/lang/String;)Landroid/"
"media/browse/MediaBrowser$MediaItem;");
method_createFromNativeFolderItem = env->GetMethodID(
clazz, "createFromNativeFolderItem",
"([BILjava/lang/String;I)Landroid/media/browse/MediaBrowser$MediaItem;");
method_createFromNativePlayerItem =
env->GetMethodID(clazz, "createFromNativePlayerItem",
"(ILjava/lang/String;[BII)Lcom/android/bluetooth/"
"avrcpcontroller/AvrcpPlayer;");
method_handleChangeFolderRsp =
env->GetMethodID(clazz, "handleChangeFolderRsp", "(I)V");
method_handleSetBrowsedPlayerRsp =
env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "(II)V");
method_handleSetAddressedPlayerRsp =
env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "(I)V");
ALOGI("%s: succeeds", __func__);
}
static void initNative(JNIEnv* env, jobject object) {
jclass tmpMediaItem =
env->FindClass("android/media/browse/MediaBrowser$MediaItem");
class_MediaBrowser_MediaItem = (jclass)env->NewGlobalRef(tmpMediaItem);
jclass tmpBtPlayer =
env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpPlayer");
class_AvrcpPlayer = (jclass)env->NewGlobalRef(tmpBtPlayer);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == NULL) {
ALOGE("Bluetooth module is not loaded");
return;
}
if (sBluetoothAvrcpInterface != NULL) {
ALOGW("Cleaning up Avrcp Interface before initializing...");
sBluetoothAvrcpInterface->cleanup();
sBluetoothAvrcpInterface = NULL;
}
if (sCallbacksObj != NULL) {
ALOGW("Cleaning up Avrcp callback object");
env->DeleteGlobalRef(sCallbacksObj);
sCallbacksObj = NULL;
}
sBluetoothAvrcpInterface =
(btrc_ctrl_interface_t*)btInf->get_profile_interface(
BT_PROFILE_AV_RC_CTRL_ID);
if (sBluetoothAvrcpInterface == NULL) {
ALOGE("Failed to get Bluetooth Avrcp Controller Interface");
return;
}
bt_status_t status =
sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d",
status);
sBluetoothAvrcpInterface = NULL;
return;
}
sCallbacksObj = env->NewGlobalRef(object);
}
static void cleanupNative(JNIEnv* env, jobject object) {
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == NULL) {
ALOGE("Bluetooth module is not loaded");
return;
}
if (sBluetoothAvrcpInterface != NULL) {
sBluetoothAvrcpInterface->cleanup();
sBluetoothAvrcpInterface = NULL;
}
if (sCallbacksObj != NULL) {
env->DeleteGlobalRef(sCallbacksObj);
sCallbacksObj = NULL;
}
}
static jboolean sendPassThroughCommandNative(JNIEnv* env, jobject object,
jbyteArray address, jint key_code,
jint key_state) {
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
ALOGI("key_code: %d, key_state: %d", key_code, key_state);
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
bt_status_t status = sBluetoothAvrcpInterface->send_pass_through_cmd(
(bt_bdaddr_t*)addr, (uint8_t)key_code, (uint8_t)key_state);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending passthru command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static jboolean sendGroupNavigationCommandNative(JNIEnv* env, jobject object,
jbyteArray address,
jint key_code,
jint key_state) {
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
ALOGI("key_code: %d, key_state: %d", key_code, key_state);
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
bt_status_t status = sBluetoothAvrcpInterface->send_group_navigation_cmd(
(bt_bdaddr_t*)addr, (uint8_t)key_code, (uint8_t)key_state);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending Grp Navigation command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static void setPlayerApplicationSettingValuesNative(JNIEnv* env, jobject object,
jbyteArray address,
jbyte num_attrib,
jbyteArray attrib_ids,
jbyteArray attrib_val) {
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
uint8_t* pAttrs = new uint8_t[num_attrib];
uint8_t* pAttrsVal = new uint8_t[num_attrib];
if ((!pAttrs) || (!pAttrsVal)) {
delete[] pAttrs;
ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory");
return;
}
jbyte* attr = env->GetByteArrayElements(attrib_ids, NULL);
jbyte* attr_val = env->GetByteArrayElements(attrib_val, NULL);
if ((!attr) || (!attr_val)) {
delete[] pAttrs;
delete[] pAttrsVal;
jniThrowIOException(env, EINVAL);
return;
}
int i;
for (i = 0; i < num_attrib; ++i) {
pAttrs[i] = (uint8_t)attr[i];
pAttrsVal[i] = (uint8_t)attr_val[i];
}
bt_status_t status = sBluetoothAvrcpInterface->set_player_app_setting_cmd(
(bt_bdaddr_t*)addr, (uint8_t)num_attrib, pAttrs, pAttrsVal);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending setPlAppSettValNative command, status: %d", status);
}
delete[] pAttrs;
delete[] pAttrsVal;
env->ReleaseByteArrayElements(attrib_ids, attr, 0);
env->ReleaseByteArrayElements(attrib_val, attr_val, 0);
env->ReleaseByteArrayElements(address, addr, 0);
}
static void sendAbsVolRspNative(JNIEnv* env, jobject object, jbyteArray address,
jint abs_vol, jint label) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->set_volume_rsp(
(bt_bdaddr_t*)addr, (uint8_t)abs_vol, (uint8_t)label);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void sendRegisterAbsVolRspNative(JNIEnv* env, jobject object,
jbyteArray address, jbyte rsp_type,
jint abs_vol, jint label) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp(
(bt_bdaddr_t*)addr, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol,
(uint8_t)label);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d",
status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void getPlaybackStateNative(JNIEnv* env, jobject object,
jbyteArray address) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status =
sBluetoothAvrcpInterface->get_playback_state_cmd((bt_bdaddr_t*)addr);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending getPlaybackStateNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void getNowPlayingListNative(JNIEnv* env, jobject object,
jbyteArray address, jbyte start,
jbyte items) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->get_now_playing_list_cmd(
(bt_bdaddr_t*)addr, (uint8_t)start, (uint8_t)items);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending getNowPlayingListNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void getFolderListNative(JNIEnv* env, jobject object, jbyteArray address,
jbyte start, jbyte items) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->get_folder_list_cmd(
(bt_bdaddr_t*)addr, (uint8_t)start, (uint8_t)items);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending getFolderListNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void getPlayerListNative(JNIEnv* env, jobject object, jbyteArray address,
jbyte start, jbyte items) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->get_player_list_cmd(
(bt_bdaddr_t*)addr, (uint8_t)start, (uint8_t)items);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending getPlayerListNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void changeFolderPathNative(JNIEnv* env, jobject object,
jbyteArray address, jbyte direction,
jbyteArray uidarr) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
jbyte* uid = env->GetByteArrayElements(uidarr, NULL);
if (!uid) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd(
(bt_bdaddr_t*)addr, (uint8_t)direction, (uint8_t*)uid);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending changeFolderPathNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void setBrowsedPlayerNative(JNIEnv* env, jobject object,
jbyteArray address, jint id) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd(
(bt_bdaddr_t*)addr, (uint16_t)id);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void setAddressedPlayerNative(JNIEnv* env, jobject object,
jbyteArray address, jint id) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd(
(bt_bdaddr_t*)addr, (uint16_t)id);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending setAddressedPlayerNative command, status: %d",
status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static void playItemNative(JNIEnv* env, jobject object, jbyteArray address,
jbyte scope, jbyteArray uidArr, jint uidCounter) {
if (!sBluetoothAvrcpInterface) return;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return;
}
jbyte* uid = env->GetByteArrayElements(uidArr, NULL);
if (!uid) {
jniThrowIOException(env, EINVAL);
return;
}
ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd(
(bt_bdaddr_t*)addr, (uint8_t)scope, (uint8_t*)uid, (uint16_t)uidCounter);
if (status != BT_STATUS_SUCCESS) {
ALOGE("Failed sending playItemNative command, status: %d", status);
}
env->ReleaseByteArrayElements(address, addr, 0);
}
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void*)classInitNative},
{"initNative", "()V", (void*)initNative},
{"cleanupNative", "()V", (void*)cleanupNative},
{"sendPassThroughCommandNative", "([BII)Z",
(void*)sendPassThroughCommandNative},
{"sendGroupNavigationCommandNative", "([BII)Z",
(void*)sendGroupNavigationCommandNative},
{"setPlayerApplicationSettingValuesNative", "([BB[B[B)V",
(void*)setPlayerApplicationSettingValuesNative},
{"sendAbsVolRspNative", "([BII)V", (void*)sendAbsVolRspNative},
{"sendRegisterAbsVolRspNative", "([BBII)V",
(void*)sendRegisterAbsVolRspNative},
{"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative},
{"getNowPlayingListNative", "([BBB)V", (void*)getNowPlayingListNative},
{"getFolderListNative", "([BBB)V", (void*)getFolderListNative},
{"getPlayerListNative", "([BBB)V", (void*)getPlayerListNative},
{"changeFolderPathNative", "([BB[B)V", (void*)changeFolderPathNative},
{"playItemNative", "([BB[BI)V", (void*)playItemNative},
{"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative},
{"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative},
};
int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) {
return jniRegisterNativeMethods(
env, "com/android/bluetooth/avrcpcontroller/AvrcpControllerService",
sMethods, NELEM(sMethods));
}
}