C++程序  |  1522行  |  53.78 KB

/*
 * 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 <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(bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Unable to allocate byte array for bd_addr");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr.get(),
                               (jint)features);
}

/** Callback for play status request */
static void btavrcp_get_play_status_callback(bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for get_play_status command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr.get());
}

static void btavrcp_get_element_attr_callback(uint8_t num_attr,
                                              btrc_media_attr_t* p_attrs,
                                              bt_bdaddr_t* 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(bt_bdaddr_t)));
  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(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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,
                                                   bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for register_notification command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
                               addr.get(), (jint)event_id, (jint)param);
}

static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype,
                                           bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for volume_change command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);

  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback,
                               addr.get(), (jint)volume, (jint)ctype);
}

static void btavrcp_passthrough_command_callback(int id, int pressed,
                                                 bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for passthrough_command command");
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);

  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
                               addr.get(), (jint)id, (jint)pressed);
}

static void btavrcp_set_addressed_player_callback(uint16_t player_id,
                                                  bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for set_addressed_player command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayerCallback,
                               addr.get(), (jint)player_id);
}

static void btavrcp_set_browsed_player_callback(uint16_t player_id,
                                                bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for set_browsed_player command");
    return;
  }
  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);

  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, bt_bdaddr_t* 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(bt_bdaddr_t)));
  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(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.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,
                                         bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for change_path command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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,
                                           bt_bdaddr_t* 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(bt_bdaddr_t)));
  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(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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, bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for play_item command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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,
                                                 bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for get_total_num_items command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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, bt_bdaddr_t* 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(bt_bdaddr_t)));
  if (!addr.get()) {
    ALOGE("Fail to new jbyteArray bd addr for search command");
    return;
  }

  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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,
                                              bt_bdaddr_t* 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(bt_bdaddr_t)));
  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(bt_bdaddr_t),
                                   (jbyte*)bd_addr);
  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;
  }

  bt_status_t status = sBluetoothAvrcpInterface->get_play_status_rsp(
      (bt_bdaddr_t*)addr, (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;
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
  bt_status_t status =
      sBluetoothAvrcpInterface->get_element_attr_rsp(btAddr, 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;
    }
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
  bt_status_t status = sBluetoothAvrcpInterface->get_item_attr_rsp(
      btAddr, (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, &param);
  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 -> %llu", __func__, type,
        uid);

  bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
      BTRC_EVT_TRACK_CHANGE, (btrc_notification_type_t)type, &param);
  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, &param);
  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,
      &param);
  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, &param);
  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, &param);
  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, &param);
  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__);
    }
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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__);
    }
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  }

  bt_bdaddr_t* btAddr = (bt_bdaddr_t*)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;
  }
}
}