Merge 242126c60df7843460161d9189ccb09c02f74a5f on remote branch

Change-Id: I355a05383e58b8f7ab67ea596bcf29c73824fca2
diff --git a/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp b/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp
new file mode 100644
index 0000000..fc72517
--- /dev/null
+++ b/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp
@@ -0,0 +1,1554 @@
+/*
+ * 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>
+#include <mutex>
+#include <shared_mutex>
+
+namespace android {
+static jmethodID method_getRcFeatures;
+static jmethodID method_getPlayStatus;
+static jmethodID method_getElementAttr;
+static jmethodID method_registerNotification;
+static jmethodID method_volumeChangeCallback;
+static jmethodID method_handlePassthroughCmd;
+static jmethodID method_getFolderItemsCallback;
+static jmethodID method_setAddressedPlayerCallback;
+
+static jmethodID method_setBrowsedPlayerCallback;
+static jmethodID method_changePathCallback;
+static jmethodID method_searchCallback;
+static jmethodID method_playItemCallback;
+static jmethodID method_getItemAttrCallback;
+static jmethodID method_addToPlayListCallback;
+static jmethodID method_getTotalNumOfItemsCallback;
+
+static const btrc_interface_t* sBluetoothAvrcpInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static std::shared_timed_mutex callbacks_mutex;
+
+/* 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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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) {
+  std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  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) {
+  std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  const bt_interface_t* btInf = getBluetoothInterface();
+  if (btInf == NULL) {
+    ALOGE("Bluetooth module is not loaded");
+    return;
+  }
+
+  if (sBluetoothAvrcpInterface != NULL) {
+    sBluetoothAvrcpInterface->cleanup();
+    sBluetoothAvrcpInterface = NULL;
+  }
+
+  if (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, &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 -> %" PRIu64, __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__);
+    }
+  }
+
+  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];
+
+      if (!p_folders ) {
+        jniThrowIOException(env, EINVAL);
+        ALOGE("%s: not have enough memeory", __func__);
+        return JNI_FALSE;
+      }
+      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;
+  }
+}
+}
diff --git a/packages_apps_bluetooth_ext/jni/Android.bp b/packages_apps_bluetooth_ext/jni/Android.bp
index 6848cb2..cb15b77 100644
--- a/packages_apps_bluetooth_ext/jni/Android.bp
+++ b/packages_apps_bluetooth_ext/jni/Android.bp
@@ -25,5 +25,9 @@
         "com_android_bluetooth_btservice_vendor_socket.cpp",
         "com_android_bluetooth_avrcp_ext.cpp",
         "com_android_bluetooth_ba.cpp",
+        "com_android_bluetooth_hf_vendor.cpp",
     ],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
index 9774d2a..cf4e968 100644
--- a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
@@ -59,6 +59,7 @@
 static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
 static jobject mCallbacksObj = NULL;
 static std::shared_timed_mutex callbacks_mutex;
+static std::shared_timed_mutex interface_mutex;
 
 /* Function declarations */
 static bool copy_item_attributes(JNIEnv* env, jobject object,
@@ -122,6 +123,7 @@
                                                      RawAddress* bd_addr) {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -145,6 +147,7 @@
 static void btavrcp_get_player_attribute_id_callback(RawAddress* bd_addr) {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -169,6 +172,7 @@
                                                           RawAddress* bd_addr) {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -202,6 +206,7 @@
 {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -242,6 +247,7 @@
 {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -274,6 +280,7 @@
 {
   ALOGI("%s", __FUNCTION__);
   CallbackEnv sCallbackEnv(__func__);
+  std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   if (!sCallbackEnv.valid()) return;
 
   if (!mCallbacksObj) {
@@ -822,6 +829,7 @@
 static void initNative(JNIEnv* env, jobject object,
         jint maxAvrcpConnections) {
   std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   const bt_interface_t* btInf = getBluetoothInterface();
   if (btInf == NULL) {
     ALOGE("Bluetooth module is not loaded");
@@ -861,6 +869,7 @@
 
 static void cleanupNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   const bt_interface_t* btInf = getBluetoothInterface();
   if (btInf == NULL) {
     ALOGE("Bluetooth module is not loaded");
@@ -881,6 +890,7 @@
 static jboolean getPlayStatusRspNative(JNIEnv* env, jobject object,
                                        jbyteArray address, jint playStatus,
                                        jint songLen, jint songPos) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -907,6 +917,7 @@
 }
 static jboolean updatePlayStatusToStack(JNIEnv *env ,jobject object, jint playStatus) {
   ALOGE("%s",__func__);
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null ", __func__);
     return JNI_FALSE;
@@ -926,6 +937,7 @@
     int i;
     jbyte *attr;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -983,6 +995,7 @@
     int i;
     jbyte *attr;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1041,6 +1054,7 @@
     int i;
     jbyte *attr;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1092,6 +1106,7 @@
     jbyte *addr;
     btrc_status_t player_rsp = (btrc_status_t) attr_status;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1124,6 +1139,7 @@
     const char* textStr;
     jbyte *arr ;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1191,7 +1207,8 @@
     const char* textStr;
     jbyte *arr ;
 
-    //ALOGE("sendValueTextRspNative");
+    ALOGE("sendValueTextRspNative");
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1250,6 +1267,7 @@
                                         jbyteArray address, jbyte numAttr,
                                         jintArray attrIds,
                                         jobjectArray textArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1322,6 +1340,7 @@
                                      jbyteArray address, jint rspStatus,
                                      jbyte numAttr, jintArray attrIds,
                                      jobjectArray textArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1395,6 +1414,7 @@
     jbyte *attr;
     btrc_register_notification_t *param= NULL;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
@@ -1448,6 +1468,7 @@
                                                         jint type,
                                                         jint playStatus,
                                                         jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1483,6 +1504,7 @@
                                                          jint type,
                                                          jbyteArray track,
                                                          jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1531,6 +1553,7 @@
                                                      jobject object, jint type,
                                                      jint playPos,
                                                      jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1565,6 +1588,7 @@
                                                                jobject object,
                                                                jint type,
                                                                jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1598,6 +1622,7 @@
                                                          jint type,
                                                          jint uidCounter,
                                                          jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1631,6 +1656,7 @@
 static jboolean registerNotificationRspAddrPlayerChangedNative(
     JNIEnv* env, jobject object, jint type, jint playerId, jint uidCounter,
     jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1667,6 +1693,7 @@
                                                           jobject object,
                                                           jint type,
                                                           jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1699,6 +1726,7 @@
 }
 
 static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume, jbyteArray address) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1733,6 +1761,7 @@
     jbyteArray playerTypes, jintArray playerSubtypes,
     jbyteArray playStatusValues, jshortArray featureBitmask,
     jobjectArray textArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1841,6 +1870,7 @@
     jbyteArray playable, jbyteArray itemType, jbyteArray itemUidArray,
     jobjectArray displayNameArray, jintArray numAttrs, jintArray attributesIds,
     jobjectArray attributesArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -1980,6 +2010,7 @@
 static jboolean setAddressedPlayerRspNative(JNIEnv* env, jobject object,
                                             jbyteArray address,
                                             jint rspStatus) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2011,6 +2042,7 @@
                                           jbyteArray address, jint rspStatus,
                                           jbyte depth, jint numItems,
                                           jobjectArray textArray) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2080,6 +2112,7 @@
 static jboolean changePathRspNative(JNIEnv* env, jobject object,
                                     jbyteArray address, jint rspStatus,
                                     jint numItems) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2111,6 +2144,7 @@
 static jboolean searchRspNative(JNIEnv* env, jobject object, jbyteArray address,
                                 jint rspStatus, jint uidCounter,
                                 jint numItems) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2142,6 +2176,7 @@
 
 static jboolean playItemRspNative(JNIEnv* env, jobject object,
                                   jbyteArray address, jint rspStatus) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2172,6 +2207,7 @@
 static jboolean getTotalNumOfItemsRspNative(JNIEnv* env, jobject object,
                                             jbyteArray address, jint rspStatus,
                                             jint uidCounter, jint numItems) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2202,6 +2238,7 @@
 
 static jboolean addToNowPlayingRspNative(JNIEnv* env, jobject object,
                                          jbyteArray address, jint rspStatus) {
+  std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   if (!sBluetoothAvrcpInterface) {
     ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
     return JNI_FALSE;
@@ -2235,6 +2272,7 @@
     bt_status_t status = BT_STATUS_SUCCESS;
     jbyte *addr;
 
+    std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
 
     if (!address) {
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
index 66b00c8..e4b775d 100644
--- a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
@@ -477,12 +477,13 @@
 
 int load_bt_configstore_lib() {
     const char* sym = BT_CONFIG_STORE_INTERFACE_STRING;
+    const char* err = "error unknown";
 
     bt_configstore_lib_handle = dlopen("libbtconfigstore.so", RTLD_NOW);
     if (!bt_configstore_lib_handle) {
         const char* err_str = dlerror();
         LOG(ERROR) << __func__ << ": failed to load Bt Config store library, error="
-                   << (err_str ? err_str : "error unknown");
+                   << (err_str ? err_str : err);
         goto error;
     }
 
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
new file mode 100644
index 0000000..f11d4cd
--- /dev/null
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "BluetoothVendorHfJni"
+
+#include "com_android_bluetooth.h"
+#include <hardware/vendor_hf.h>
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+static btvendor_hf_interface_t *sBluetoothVendorHfInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static jmethodID method_onSWB;
+
+static jbyteArray marshall_bda(RawAddress* bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+  if (!sCallbackEnv.valid()) return nullptr;
+
+  jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
+  if (!addr) {
+    ALOGE("Fail to new jbyteArray bd addr");
+    return nullptr;
+  }
+  sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress),
+                                   (jbyte*)bd_addr);
+  return addr;
+}
+
+static void SwbCallback(uint16_t swb_config, RawAddress* bd_addr) {
+
+    ALOGI("%s", __FUNCTION__);
+    CallbackEnv sCallbackEnv(__func__);
+    if (!sCallbackEnv.valid()) return;
+
+    ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
+    if (addr.get() == nullptr) return;
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSWB, swb_config,
+                                 addr.get());
+}
+
+static btvendor_hf_callbacks_t sBluetoothVendorHfCallbacks = {
+    sizeof(sBluetoothVendorHfCallbacks),
+    SwbCallback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+    method_onSWB = env->GetMethodID(clazz, "onSWB", "(I[B)V");
+    ALOGI("%s: succeeds", __FUNCTION__);
+}
+
+static void initNative(JNIEnv *env, jobject object) {
+    const bt_interface_t* btInf;
+    bt_status_t status;
+
+    if ( (btInf = getBluetoothInterface()) == NULL) {
+        ALOGE("Bluetooth module is not loaded");
+        return;
+    }
+
+    if (mCallbacksObj != NULL) {
+        ALOGW("Cleaning up Bluetooth Vendor callback object");
+        env->DeleteGlobalRef(mCallbacksObj);
+        mCallbacksObj = NULL;
+    }
+
+    if ( (sBluetoothVendorHfInterface = (btvendor_hf_interface_t *)
+          btInf->get_profile_interface(BT_PROFILE_VENDOR_HF_ID)) == NULL) {
+        ALOGE("Failed to get Bluetooth Vendor Interface");
+        return;
+    }
+
+    if ( (status = sBluetoothVendorHfInterface->init(&sBluetoothVendorHfCallbacks))
+                 != BT_STATUS_SUCCESS) {
+        ALOGE("Failed to initialize Bluetooth Vendor, status: %d", status);
+        sBluetoothVendorHfInterface = NULL;
+        return;
+    }
+    mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+    const bt_interface_t* btInf;
+
+    if ( (btInf = getBluetoothInterface()) == NULL) {
+        ALOGE("Bluetooth module is not loaded");
+        return;
+    }
+
+    if (sBluetoothVendorHfInterface !=NULL) {
+        ALOGW("Cleaning up Bluetooth Vendor Interface...");
+        sBluetoothVendorHfInterface->cleanup();
+        sBluetoothVendorHfInterface = NULL;
+    }
+
+    if (mCallbacksObj != NULL) {
+        ALOGW("Cleaning up Bluetooth Vendor callback object");
+        env->DeleteGlobalRef(mCallbacksObj);
+        mCallbacksObj = NULL;
+    }
+
+}
+
+/* native interface */
+static jint enableSwbNative(JNIEnv* env, jobject thiz, jboolean enable)
+{
+    if (sBluetoothVendorHfInterface == NULL) {
+        ALOGE("No Interface initialized");
+        return JNI_FALSE;
+    }
+
+    int ret = sBluetoothVendorHfInterface->enable_swb(enable);
+
+    if (ret != 0) {
+        ALOGE("%s: Failure", __func__);
+        return JNI_FALSE;
+    } else {
+        ALOGV("%s: Success :%d", __func__, enable);
+    }
+
+    return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+    {"classInitNative", "()V", (void *) classInitNative},
+    {"initNative", "()V", (void *) initNative},
+    {"cleanupNative", "()V", (void *) cleanupNative},
+    { "enableSwbNative", "(Z)I", (void*)enableSwbNative},
+};
+
+int register_com_android_bluetooth_hfp_vendorhfservice(JNIEnv* env)
+{
+    ALOGE("%s:",__FUNCTION__);
+    return jniRegisterNativeMethods(env, "com/android/bluetooth/hfp/vendorhfservice",
+                                    sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java
new file mode 100644
index 0000000..a650069
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java
@@ -0,0 +1,646 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.MediaSession.QueueItem;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*************************************************************************************************
+ * Provides functionality required for Addressed Media Player, like Now Playing List related
+ * browsing commands, control commands to the current addressed player(playItem, play, pause, etc)
+ * Acts as an Interface to communicate with media controller APIs for NowPlayingItems.
+ ************************************************************************************************/
+
+public class AddressedMediaPlayer_ext {
+    private static final String TAG = "AddressedMediaPlayer_ext";
+    private static final Boolean DEBUG = true;
+
+    private static final long SINGLE_QID = 1;
+    private static final String UNKNOWN_TITLE = "(unknown)";
+
+    static private final String GPM_BUNDLE_METADATA_KEY =
+            "com.google.android.music.mediasession.music_metadata";
+
+    private AvrcpMediaRspInterface_ext mMediaInterface;
+    private Avrcp_ext mAvrcp = null;
+    @NonNull private List<MediaSession.QueueItem> mNowPlayingList;
+
+    private final List<MediaSession.QueueItem> mEmptyNowPlayingList;
+
+    private long mLastTrackIdSent;
+
+    public AddressedMediaPlayer_ext(AvrcpMediaRspInterface_ext mediaInterface) {
+        mEmptyNowPlayingList = new ArrayList<MediaSession.QueueItem>();
+        mNowPlayingList = mEmptyNowPlayingList;
+        mMediaInterface = mediaInterface;
+        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+    }
+
+    public AddressedMediaPlayer_ext(AvrcpMediaRspInterface_ext mediaInterface, Avrcp_ext mAvrcp_ext) {
+        this(mediaInterface);
+        mAvrcp = mAvrcp_ext;
+    }
+
+    void cleanup() {
+        if (DEBUG) {
+            Log.v(TAG, "cleanup");
+        }
+        mNowPlayingList = mEmptyNowPlayingList;
+        mMediaInterface = null;
+        mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+    }
+
+    /* get now playing list from addressed player */
+    void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd reqObj,
+            @Nullable MediaController mediaController) {
+        if (mediaController == null) {
+            // No players (if a player exists, we would have selected it)
+            Log.e(TAG, "mediaController = null, sending no available players response");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, null);
+            return;
+        }
+        List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+        getFolderItemsFilterAttr(bdaddr, reqObj, items, AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING,
+                reqObj.mStartItem, reqObj.mEndItem, mediaController);
+    }
+
+    /* get item attributes for item in now playing list */
+    void getItemAttr(byte[] bdaddr, AvrcpCmd_ext.ItemAttrCmd itemAttr,
+            @Nullable MediaController mediaController) {
+        int status = AvrcpConstants_ext.RSP_NO_ERROR;
+        long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong();
+        List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+
+        // NOTE: this is out-of-spec (AVRCP 1.6.1 sec 6.10.4.3, p90) but we answer it anyway
+        // because some CTs ask for it.
+        if (Arrays.equals(itemAttr.mUid, AvrcpConstants_ext.TRACK_IS_SELECTED)) {
+            mediaId = getActiveQueueItemId(mediaController);
+            if (DEBUG) {
+                Log.d(TAG, "getItemAttr: Remote requests for now playing contents, sending UID: "
+                        + mediaId);
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "getItemAttr-UID: 0x" + Utils.byteArrayToString(itemAttr.mUid));
+        }
+        for (MediaSession.QueueItem item : items) {
+            if (item.getQueueId() == mediaId) {
+                getItemAttrFilterAttr(bdaddr, itemAttr, item, mediaController);
+                return;
+            }
+        }
+
+        // Couldn't find it, so the id is invalid
+        mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM, null);
+    }
+
+    /* Refresh and get the queue of now playing.
+     */
+    @NonNull
+    List<MediaSession.QueueItem> updateNowPlayingList(@Nullable MediaController mediaController) {
+        if (mediaController == null) {
+            return mEmptyNowPlayingList;
+        }
+        List<MediaSession.QueueItem> items = mediaController.getQueue();
+        if (items == null) {
+            Log.i(TAG, "null queue from " + mediaController.getPackageName()
+                    + ", constructing single-item list");
+
+            // Because we are database-unaware, we can just number the item here whatever we want
+            // because they have to re-poll it every time.
+            MediaMetadata metadata = mediaController.getMetadata();
+            if (metadata == null) {
+                Log.w(TAG, "Controller has no metadata!? Making an empty one");
+                metadata = (new MediaMetadata.Builder()).build();
+            }
+
+            MediaDescription.Builder bob = new MediaDescription.Builder();
+            MediaDescription desc = metadata.getDescription();
+
+            // set the simple ones that MediaMetadata builds for us
+            bob.setMediaId(desc.getMediaId());
+            bob.setTitle(desc.getTitle());
+            bob.setSubtitle(desc.getSubtitle());
+            bob.setDescription(desc.getDescription());
+            // fill the ones that we use later
+            bob.setExtras(fillBundle(metadata, desc.getExtras()));
+
+            // build queue item with the new metadata
+            MediaSession.QueueItem current = new QueueItem(bob.build(), SINGLE_QID);
+
+            items = new ArrayList<MediaSession.QueueItem>();
+            items.add(current);
+        }
+
+        if (!items.equals(mNowPlayingList)) {
+            sendNowPlayingListChanged();
+        }
+        mNowPlayingList = items;
+
+        return mNowPlayingList;
+    }
+
+    private void sendNowPlayingListChanged() {
+        if (mMediaInterface == null) {
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "sendNowPlayingListChanged()");
+        }
+        mMediaInterface.nowPlayingChangedRsp(AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED);
+    }
+
+    private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) {
+        if (metadata == null) {
+            Log.i(TAG, "fillBundle: metadata is null");
+            return currentExtras;
+        }
+
+        Bundle bundle = currentExtras;
+        if (bundle == null) {
+            bundle = new Bundle();
+        }
+
+        String[] stringKeys = {
+                MediaMetadata.METADATA_KEY_TITLE,
+                MediaMetadata.METADATA_KEY_ARTIST,
+                MediaMetadata.METADATA_KEY_ALBUM,
+                MediaMetadata.METADATA_KEY_GENRE
+        };
+        for (String key : stringKeys) {
+            String current = bundle.getString(key);
+            if (current == null) {
+                bundle.putString(key, metadata.getString(key));
+            }
+        }
+
+        String[] longKeys = {
+                MediaMetadata.METADATA_KEY_TRACK_NUMBER,
+                MediaMetadata.METADATA_KEY_NUM_TRACKS,
+                MediaMetadata.METADATA_KEY_DURATION
+        };
+        for (String key : longKeys) {
+            if (!bundle.containsKey(key)) {
+                bundle.putLong(key, metadata.getLong(key));
+            }
+        }
+        return bundle;
+    }
+
+    /* Instructs media player to play particular media item */
+    void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) {
+        long qid = ByteBuffer.wrap(uid).getLong();
+        List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+
+        if (mediaController == null) {
+            Log.e(TAG, "No mediaController when PlayItem " + qid + " requested");
+            mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+            return;
+        }
+
+        MediaController.TransportControls mediaControllerCntrl =
+                mediaController.getTransportControls();
+
+        if (items == null) {
+            Log.w(TAG, "nowPlayingItems is null");
+            mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+            return;
+        }
+
+        for (MediaSession.QueueItem item : items) {
+            if (qid == item.getQueueId()) {
+                if (DEBUG) {
+                    Log.d(TAG, "Skipping to ID " + qid);
+                }
+                mediaControllerCntrl.skipToQueueItem(qid);
+                mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
+                return;
+            }
+        }
+
+        Log.w(TAG, "Invalid now playing Queue ID " + qid);
+        mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM);
+    }
+
+    void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) {
+        List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+        if (DEBUG) {
+            Log.d(TAG, "getTotalNumOfItems: " + items.size() + " items.");
+        }
+        mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, 0, items.size());
+    }
+
+    void sendTrackChangeWithId(int type, @Nullable MediaController mediaController) {
+        Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
+        long qid = getActiveQueueItemId(mediaController);
+        byte[] track = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+        Log.d(TAG, "qid: " + qid );
+        if ((type == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
+            (qid != MediaSession.QueueItem.UNKNOWN_ID) &&
+            (qid != mLastTrackIdSent)) {
+             byte[] lastTrack =
+                    ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(mLastTrackIdSent).array();
+             mMediaInterface.trackChangedRsp(type, lastTrack);
+             type = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        }
+        // The nowPlayingList changed: the new list has the full data for the current item
+        Log.d(TAG, "last_sent_qid: " + mLastTrackIdSent);
+        mMediaInterface.trackChangedRsp(type, track);
+        mLastTrackIdSent = qid;
+    }
+
+    void sendTrackChangeWithId(int type, @Nullable MediaController mediaController, byte[] bdaddr) {
+        Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
+        if(mAvrcp == null) {
+            sendTrackChangeWithId(type, mediaController);
+            return;
+        }
+        long qid = getActiveQueueItemId(mediaController);
+        byte[] track = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+        Log.d(TAG, "qid: " + qid );
+        if ((type == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
+            (qid != MediaSession.QueueItem.UNKNOWN_ID) &&
+            (qid != mLastTrackIdSent)) {
+             byte[] lastTrack =
+                    ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(mLastTrackIdSent).array();
+             mAvrcp.trackChangedAddressedRsp(type, lastTrack, bdaddr);
+             type = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        }
+        // The nowPlayingList changed: the new list has the full data for the current item
+        Log.d(TAG, "last_sent_qid: " + mLastTrackIdSent);
+        mAvrcp.trackChangedAddressedRsp(type, track, bdaddr);
+        mLastTrackIdSent = qid;
+    }
+
+    /*
+     * helper method to check if startItem and endItem index is with range of
+     * MediaItem list. (Resultset containing all items in current path)
+     */
+    @Nullable
+    private List<MediaSession.QueueItem> getQueueSubset(@NonNull List<MediaSession.QueueItem> items,
+            long startItem, long endItem) {
+        if (endItem > items.size()) {
+            endItem = items.size() - 1;
+        }
+        if (startItem > Integer.MAX_VALUE) {
+            startItem = Integer.MAX_VALUE;
+        }
+        try {
+            List<MediaSession.QueueItem> selected =
+                    items.subList((int) startItem, (int) Math.min(items.size(), endItem + 1));
+            if (selected.isEmpty()) {
+                Log.i(TAG, "itemsSubList is empty.");
+                return null;
+            }
+            return selected;
+        } catch (IndexOutOfBoundsException ex) {
+            Log.i(TAG, "Range (" + startItem + ", " + endItem + ") invalid");
+        } catch (IllegalArgumentException ex) {
+            Log.i(TAG, "Range start " + startItem + " > size (" + items.size() + ")");
+        }
+        return null;
+    }
+
+    /*
+     * helper method to filter required attibutes before sending GetFolderItems
+     * response
+     */
+    private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd folderItemsReqObj,
+            @NonNull List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem,
+            @NonNull MediaController mediaController) {
+        if (DEBUG) {
+            Log.d(TAG,
+                    "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem);
+        }
+
+        List<MediaSession.QueueItem> resultItems = getQueueSubset(items, startItem, endItem);
+        /* check for index out of bound errors */
+        if (resultItems == null) {
+            Log.w(TAG, "getFolderItemsFilterAttr: resultItems is empty");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+            return;
+        }
+
+        FolderItemsData_ext folderDataNative = new FolderItemsData_ext(resultItems.size());
+
+        /* variables to accumulate attrs */
+        ArrayList<String> attrArray = new ArrayList<String>();
+        ArrayList<Integer> attrId = new ArrayList<Integer>();
+
+        for (int itemIndex = 0; itemIndex < resultItems.size(); itemIndex++) {
+            MediaSession.QueueItem item = resultItems.get(itemIndex);
+            // get the queue id
+            long qid = item.getQueueId();
+            byte[] uid = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+
+            // get the array of uid from 2d to array 1D array
+            for (int idx = 0; idx < AvrcpConstants_ext.UID_SIZE; idx++) {
+                folderDataNative.mItemUid[itemIndex * AvrcpConstants_ext.UID_SIZE + idx] = uid[idx];
+            }
+
+            /* Set display name for current item */
+            folderDataNative.mDisplayNames[itemIndex] =
+                    getAttrValue(bdaddr, AvrcpConstants_ext.ATTRID_TITLE, item, mediaController);
+
+            int maxAttributesRequested = 0;
+            boolean isAllAttribRequested = false;
+            /* check if remote requested for attributes */
+            if (folderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+                int attrCnt = 0;
+
+                /* add requested attr ids to a temp array */
+                if (folderItemsReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+                    isAllAttribRequested = true;
+                    maxAttributesRequested = AvrcpConstants_ext.MAX_NUM_ATTR;
+                } else {
+                    /* get only the requested attribute ids from the request */
+                    maxAttributesRequested = folderItemsReqObj.mNumAttr;
+                }
+
+                /* lookup and copy values of attributes for ids requested above */
+                for (int idx = 0; idx < maxAttributesRequested; idx++) {
+                    /* check if media player provided requested attributes */
+                    String value = null;
+
+                    int attribId =
+                            isAllAttribRequested ? (idx + 1) : folderItemsReqObj.mAttrIDs[idx];
+                    value = getAttrValue(bdaddr, attribId, item, mediaController);
+                    if (value != null) {
+                        attrArray.add(value);
+                        attrId.add(attribId);
+                        attrCnt++;
+                    }
+                }
+                /* add num attr actually received from media player for a particular item */
+                folderDataNative.mAttributesNum[itemIndex] = attrCnt;
+            }
+        }
+
+        /* copy filtered attr ids and attr values to response parameters */
+        if (folderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+            folderDataNative.mAttrIds = new int[attrId.size()];
+            for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+                folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
+            }
+            folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
+        }
+        for (int attrIndex = 0; attrIndex < folderDataNative.mAttributesNum.length; attrIndex++) {
+            if (DEBUG) {
+                Log.d(TAG, "folderDataNative.mAttributesNum"
+                        + folderDataNative.mAttributesNum[attrIndex] + " attrIndex " + attrIndex);
+            }
+        }
+
+        /* create rsp object and send response to remote device */
+        FolderItemsRsp_ext rspObj =
+                new FolderItemsRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, Avrcp_ext.sUIDCounter, scope,
+                        folderDataNative.mNumItems, folderDataNative.mFolderTypes,
+                        folderDataNative.mPlayable, folderDataNative.mItemTypes,
+                        folderDataNative.mItemUid, folderDataNative.mDisplayNames,
+                        folderDataNative.mAttributesNum, folderDataNative.mAttrIds,
+                        folderDataNative.mAttrValues);
+        mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+    }
+
+    private String getAttrValue(byte []bdaddr, int attr, MediaSession.QueueItem item,
+            @Nullable MediaController mediaController) {
+        String attrValue = null;
+        if (item == null) {
+            if (DEBUG) {
+                Log.d(TAG, "getAttrValue received null item");
+            }
+            return null;
+        }
+        try {
+            MediaDescription desc = item.getDescription();
+            Bundle extras = desc.getExtras();
+            boolean isCurrentTrack = item.getQueueId() == getActiveQueueItemId(mediaController);
+            MediaMetadata data = null;
+            if (isCurrentTrack) {
+                if (DEBUG) {
+                    Log.d(TAG, "getAttrValue: item is active, using current data");
+                }
+                data = mediaController.getMetadata();
+                if (data == null) {
+                    Log.e(TAG, "getMetadata didn't give us any metadata for the current track");
+                }
+            }
+
+            if (data == null) {
+                // TODO: This code can be removed when b/63117921 is resolved
+                data = (MediaMetadata) extras.get(GPM_BUNDLE_METADATA_KEY);
+                extras = null; // We no longer need the data in here
+            }
+
+            extras = fillBundle(data, extras);
+
+            if (DEBUG) {
+                Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
+            }
+            switch (attr) {
+                case AvrcpConstants_ext.ATTRID_TITLE:
+                    /* Title is mandatory attribute */
+                    if (isCurrentTrack) {
+                        attrValue = extras.getString(MediaMetadata.METADATA_KEY_TITLE);
+                    } else {
+                        attrValue = desc.getTitle().toString();
+                    }
+                    if (attrValue == null)
+                        attrValue = "<Unknown Title>";
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_ARTIST:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_ALBUM:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_TRACK_NUM:
+                    attrValue =
+                            Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_NUM_TRACKS:
+                    attrValue =
+                            Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_GENRE:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_PLAY_TIME:
+                    attrValue = Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_DURATION));
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_COVER_ART:
+                    if (mAvrcp != null) {
+                        attrValue = mAvrcp.getImgHandleFromTitle(bdaddr,
+                                desc.getTitle().toString());
+                    } else {
+                        if (DEBUG) Log.d(TAG, " mAvrcp null ");
+                    }
+                    break;
+
+                default:
+                    Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+                    return null;
+            }
+        } catch (NullPointerException ex) {
+            Log.w(TAG, "getAttrValue: attr id not found in result");
+            /* checking if attribute is title, then it is mandatory and cannot send null */
+            if (attr == AvrcpConstants_ext.ATTRID_TITLE) {
+                attrValue = "<Unknown Title>";
+            } else {
+                return null;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + ", attr id: " + attr);
+        }
+        return attrValue;
+    }
+
+    private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd_ext.ItemAttrCmd mItemAttrReqObj,
+            MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) {
+        /* Response parameters */
+        int[] attrIds = null; /* array of attr ids */
+        String[] attrValues = null; /* array of attr values */
+
+        /* variables to temperorily add attrs */
+        ArrayList<String> attrArray = new ArrayList<String>();
+        ArrayList<Integer> attrId = new ArrayList<Integer>();
+        ArrayList<Integer> attrTempId = new ArrayList<Integer>();
+
+        /* check if remote device has requested for attributes */
+        if (mItemAttrReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+            if (mItemAttrReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+                for (int idx = 1; idx < AvrcpConstants_ext.MAX_NUM_ATTR; idx++) {
+                    attrTempId.add(idx); /* attr id 0x00 is unused */
+                }
+            } else {
+                /* get only the requested attribute ids from the request */
+                for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) {
+                    if (DEBUG) {
+                        Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :"
+                                + mItemAttrReqObj.mAttrIDs[idx]);
+                    }
+                    attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]);
+                }
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size());
+        }
+        /* lookup and copy values of attributes for ids requested above */
+        for (int idx = 0; idx < attrTempId.size(); idx++) {
+            /* check if media player provided requested attributes */
+            String value = getAttrValue(bdaddr, attrTempId.get(idx), mediaItem, mediaController);
+            if (value != null) {
+                attrArray.add(value);
+                attrId.add(attrTempId.get(idx));
+            }
+        }
+
+        /* copy filtered attr ids and attr values to response parameters */
+        if (mItemAttrReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+            attrIds = new int[attrId.size()];
+
+            for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+                attrIds[attrIndex] = attrId.get(attrIndex);
+            }
+
+            attrValues = attrArray.toArray(new String[attrId.size()]);
+
+            /* create rsp object and send response */
+            ItemAttrRsp_ext rspObj = new ItemAttrRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, attrIds, attrValues);
+            mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+            return;
+        }
+    }
+
+    private long getActiveQueueItemId(@Nullable MediaController controller) {
+        if (controller == null) {
+            return MediaSession.QueueItem.UNKNOWN_ID;
+        }
+        PlaybackState state = controller.getPlaybackState();
+        if (state == null || state.getState() == PlaybackState.STATE_NONE) {
+            return MediaSession.QueueItem.UNKNOWN_ID;
+        }
+        long qid = state.getActiveQueueItemId();
+        if (qid != MediaSession.QueueItem.UNKNOWN_ID) {
+            return qid;
+        }
+        // Check if we're presenting a "one item queue"
+        if (controller.getMetadata() != null) {
+            return SINGLE_QID;
+        }
+        return MediaSession.QueueItem.UNKNOWN_ID;
+    }
+
+    String displayMediaItem(MediaSession.QueueItem item) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("#");
+        sb.append(item.getQueueId());
+        sb.append(": ");
+        sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_TITLE, item, null)));
+        sb.append(" - ");
+        sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_ALBUM, item, null)));
+        sb.append(" by ");
+        sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_ARTIST, item, null)));
+        sb.append(" (");
+        sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_PLAY_TIME, item, null));
+        sb.append(" ");
+        sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_TRACK_NUM, item, null));
+        sb.append("/");
+        sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_NUM_TRACKS, item, null));
+        sb.append(") ");
+        sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_GENRE, item, null));
+        return sb.toString();
+    }
+
+    public void dump(StringBuilder sb, @Nullable MediaController mediaController) {
+        ProfileService.println(sb, "AddressedPlayer info:");
+        ProfileService.println(sb, "mLastTrackIdSent: " + mLastTrackIdSent);
+        ProfileService.println(sb, "mNowPlayingList: " + mNowPlayingList.size() + " elements");
+        long currentQueueId = getActiveQueueItemId(mediaController);
+        for (MediaSession.QueueItem item : mNowPlayingList) {
+            long itemId = item.getQueueId();
+            ProfileService.println(sb,
+                    (itemId == currentQueueId ? "*" : " ") + displayMediaItem(item));
+        }
+    }
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java
new file mode 100644
index 0000000..82ef436
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.util.Log;
+
+/*************************************************************************************************
+ * Grouped all HAL constants into a file to be consistent with the stack.
+ * Moved the constants used in Avrcp to this new file to be used across multiple files.
+ * Helps in easier modifications and future enhancements in the constants.
+ ************************************************************************************************/
+
+/*
+ * @hide
+ */
+final class AvrcpConstants_ext {
+
+    /* Do not modify without upating the HAL bt_rc.h file */
+    /** Response Error codes **/
+    static final byte RSP_BAD_CMD = 0x00; /* Invalid command */
+    static final byte RSP_BAD_PARAM = 0x01; /* Invalid parameter */
+    static final byte RSP_NOT_FOUND = 0x02; /* Specified parameter is
+                                                              * wrong or not found */
+    static final byte RSP_INTERNAL_ERR = 0x03; /* Internal Error */
+    static final byte RSP_NO_ERROR = 0x04; /* Operation Success */
+    static final byte RSP_UID_CHANGED = 0x05; /* UIDs changed */
+    static final byte RSP_RESERVED = 0x06; /* Reserved */
+    static final byte RSP_INV_DIRN = 0x07; /* Invalid direction */
+    static final byte RSP_INV_DIRECTORY = 0x08; /* Invalid directory */
+    static final byte RSP_INV_ITEM = 0x09; /* Invalid Item */
+    static final byte RSP_INV_SCOPE = 0x0a; /* Invalid scope */
+    static final byte RSP_INV_RANGE = 0x0b; /* Invalid range */
+    static final byte RSP_DIRECTORY = 0x0c; /* UID is a directory */
+    static final byte RSP_MEDIA_IN_USE = 0x0d; /* Media in use */
+    static final byte RSP_PLAY_LIST_FULL = 0x0e; /* Playing list full */
+    static final byte RSP_SRCH_NOT_SPRTD = 0x0f; /* Search not supported */
+    static final byte RSP_SRCH_IN_PROG = 0x10; /* Search in progress */
+    static final byte RSP_INV_PLAYER = 0x11; /* Invalid player */
+    static final byte RSP_PLAY_NOT_BROW = 0x12; /* Player not browsable */
+    static final byte RSP_PLAY_NOT_ADDR = 0x13; /* Player not addressed */
+    static final byte RSP_INV_RESULTS = 0x14; /* Invalid results */
+    static final byte RSP_NO_AVBL_PLAY = 0x15; /* No available players */
+    static final byte RSP_ADDR_PLAY_CHGD = 0x16; /* Addressed player changed */
+
+    /* valid scopes for get_folder_items */
+    static final byte BTRC_SCOPE_PLAYER_LIST = 0x00; /* Media Player List */
+    static final byte BTRC_SCOPE_FILE_SYSTEM = 0x01; /* Virtual File System */
+    static final byte BTRC_SCOPE_SEARCH = 0x02; /* Search */
+    static final byte BTRC_SCOPE_NOW_PLAYING = 0x03; /* Now Playing */
+
+    /* valid directions for change path */
+    static final byte DIR_UP = 0x00;
+    static final byte DIR_DOWN = 0x01;
+
+    /* item type to browse */
+    static final byte BTRC_ITEM_PLAYER = 0x01;
+    static final byte BTRC_ITEM_FOLDER = 0x02;
+    static final byte BTRC_ITEM_MEDIA = 0x03;
+
+    /* valid folder types */
+    static final byte FOLDER_TYPE_MIXED = 0x00;
+    static final byte FOLDER_TYPE_TITLES = 0x01;
+    static final byte FOLDER_TYPE_ALBUMS = 0x02;
+    static final byte FOLDER_TYPE_ARTISTS = 0x03;
+    static final byte FOLDER_TYPE_GENRES = 0x04;
+    static final byte FOLDER_TYPE_PLAYLISTS = 0x05;
+    static final byte FOLDER_TYPE_YEARS = 0x06;
+
+    /* valid playable flags */
+    static final byte ITEM_NOT_PLAYABLE = 0x00;
+    static final byte ITEM_PLAYABLE = 0x01;
+
+    /* valid Attribute ids for media elements */
+    static final int ATTRID_TITLE = 0x01;
+    static final int ATTRID_ARTIST = 0x02;
+    static final int ATTRID_ALBUM = 0x03;
+    static final int ATTRID_TRACK_NUM = 0x04;
+    static final int ATTRID_NUM_TRACKS = 0x05;
+    static final int ATTRID_GENRE = 0x06;
+    static final int ATTRID_PLAY_TIME = 0x07;
+    static final int ATTRID_COVER_ART = 0x08;
+
+    /* constants to send in Track change response */
+    static final byte[] NO_TRACK_SELECTED = {
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF,
+            (byte) 0xFF
+    };
+    static final byte[] TRACK_IS_SELECTED = {
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00,
+            (byte) 0x00
+    };
+
+    /* UID size */
+    static final int UID_SIZE = 8;
+
+    static final short DEFAULT_UID_COUNTER = 0x0000;
+
+    /* Bitmask size for Media Players */
+    static final int AVRC_FEATURE_MASK_SIZE = 16;
+
+    /* Maximum attributes for media item */
+    static final int MAX_NUM_ATTR = 8;
+
+    /* notification types for remote device */
+    static final int NOTIFICATION_TYPE_INTERIM = 0;
+    static final int NOTIFICATION_TYPE_CHANGED = 1;
+
+    static final int TRACK_ID_SIZE = 8;
+
+    /* player feature bit mask constants */
+    static final short AVRC_PF_PLAY_BIT_NO = 40;
+    static final short AVRC_PF_STOP_BIT_NO = 41;
+    static final short AVRC_PF_PAUSE_BIT_NO = 42;
+    static final short AVRC_PF_REWIND_BIT_NO = 44;
+    static final short AVRC_PF_FAST_FWD_BIT_NO = 45;
+    static final short AVRC_PF_FORWARD_BIT_NO = 47;
+    static final short AVRC_PF_BACKWARD_BIT_NO = 48;
+    static final short AVRC_PF_ADV_CTRL_BIT_NO = 58;
+    static final short AVRC_PF_BROWSE_BIT_NO = 59;
+    static final short AVRC_PF_ADD2NOWPLAY_BIT_NO = 61;
+    static final short AVRC_PF_UID_UNIQUE_BIT_NO = 62;
+    static final short AVRC_PF_NOW_PLAY_BIT_NO = 65;
+    static final short AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO = 67;
+    static final short AVRC_PF_COVER_ART_BIT_NO = 68;
+    static final byte PLAYER_TYPE_AUDIO = 1;
+    static final int PLAYER_SUBTYPE_NONE = 0;
+
+    // match up with btrc_play_status_t enum of bt_rc.h
+    static final int PLAYSTATUS_STOPPED = 0;
+    static final int PLAYSTATUS_PLAYING = 1;
+    static final int PLAYSTATUS_PAUSED = 2;
+    static final int PLAYSTATUS_FWD_SEEK = 3;
+    static final int PLAYSTATUS_REV_SEEK = 4;
+    static final int PLAYSTATUS_ERROR = 255;
+
+    static final byte NUM_ATTR_ALL = (byte) 0x00;
+    static final byte NUM_ATTR_NONE = (byte) 0xFF;
+
+    static final int KEY_STATE_PRESS = 1;
+    static final int KEY_STATE_RELEASE = 0;
+
+    static final int GET_ATTRIBUTE_IDS = 0;
+    static final int GET_VALUE_IDS = 1;
+    static final int GET_ATTRIBUTE_TEXT = 2;
+    static final int GET_VALUE_TEXT     = 3;
+    static final int GET_ATTRIBUTE_VALUES = 4;
+    static final int NOTIFY_ATTRIBUTE_VALUES = 5;
+    static final int SET_ATTRIBUTE_VALUES  = 6;
+    static final int GET_INVALID = 0xff;
+
+    public static final String TAG = "Avrcp";
+    public static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java
new file mode 100644
index 0000000..8aca354
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java
@@ -0,0 +1,451 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.bluetooth.Utils;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collection;
+
+/*************************************************************************************************
+ * Helper classes used for callback/response of browsing commands:-
+ *     1) To bundle parameters for  native callbacks/response.
+ *     2) Stores information of Addressed and Browsed Media Players.
+ ************************************************************************************************/
+
+class AvrcpCmd_ext {
+
+    AvrcpCmd_ext() {}
+
+    /* Helper classes to pass parameters from callbacks to Avrcp handler */
+    class FolderItemsCmd {
+        byte mScope;
+        long mStartItem;
+        long mEndItem;
+        byte mNumAttr;
+        int[] mAttrIDs;
+        public byte[] mAddress;
+
+        FolderItemsCmd(byte[] address, byte scope, long startItem, long endItem, byte numAttr,
+                int[] attrIds) {
+            mAddress = address;
+            this.mScope = scope;
+            this.mStartItem = startItem;
+            this.mEndItem = endItem;
+            this.mNumAttr = numAttr;
+            this.mAttrIDs = attrIds;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[FolderItemCmd: scope " + mScope);
+            sb.append(" start " + mStartItem);
+            sb.append(" end " + mEndItem);
+            sb.append(" numAttr " + mNumAttr);
+            sb.append(" attrs: ");
+            for (int i = 0; i < mNumAttr; i++) {
+                sb.append(mAttrIDs[i] + " ");
+            }
+            return sb.toString();
+        }
+    }
+
+    class ItemAttrCmd {
+        byte mScope;
+        byte[] mUid;
+        int mUidCounter;
+        byte mNumAttr;
+        int[] mAttrIDs;
+        public byte[] mAddress;
+
+        ItemAttrCmd(byte[] address, byte scope, byte[] uid, int uidCounter, byte numAttr,
+                int[] attrIDs) {
+            mAddress = address;
+            mScope = scope;
+            mUid = uid;
+            mUidCounter = uidCounter;
+            mNumAttr = numAttr;
+            mAttrIDs = attrIDs;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[ItemAttrCmd: scope " + mScope);
+            sb.append(" uid " + Utils.byteArrayToString(mUid));
+            sb.append(" numAttr " + mNumAttr);
+            sb.append(" attrs: ");
+            for (int i = 0; i < mNumAttr; i++) {
+                sb.append(mAttrIDs[i] + " ");
+            }
+            return sb.toString();
+        }
+    }
+
+    class ElementAttrCmd {
+        byte mNumAttr;
+        int[] mAttrIDs;
+        public byte[] mAddress;
+
+        ElementAttrCmd(byte[] address, byte numAttr, int[] attrIDs) {
+            mAddress = address;
+            mNumAttr = numAttr;
+            mAttrIDs = attrIDs;
+        }
+    }
+}
+
+/* Helper classes to pass parameters to native response */
+class MediaPlayerListRsp_ext {
+    byte mStatus;
+    short mUIDCounter;
+    byte mItemType;
+    int[] mPlayerIds;
+    byte[] mPlayerTypes;
+    int[] mPlayerSubTypes;
+    byte[] mPlayStatusValues;
+    short[] mFeatureBitMaskValues;
+    String[] mPlayerNameList;
+    int mNumItems;
+
+    MediaPlayerListRsp_ext(byte status, short uidCounter, int numItems, byte itemType, int[] playerIds,
+            byte[] playerTypes, int[] playerSubTypes, byte[] playStatusValues,
+            short[] featureBitMaskValues, String[] playerNameList) {
+        this.mStatus = status;
+        this.mUIDCounter = uidCounter;
+        this.mNumItems = numItems;
+        this.mItemType = itemType;
+        this.mPlayerIds = playerIds;
+        this.mPlayerTypes = playerTypes;
+        this.mPlayerSubTypes = new int[numItems];
+        this.mPlayerSubTypes = playerSubTypes;
+        this.mPlayStatusValues = new byte[numItems];
+        this.mPlayStatusValues = playStatusValues;
+        int bitMaskSize = AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE;
+        this.mFeatureBitMaskValues = new short[numItems * bitMaskSize];
+        for (int bitMaskIndex = 0; bitMaskIndex < (numItems * bitMaskSize); bitMaskIndex++) {
+            this.mFeatureBitMaskValues[bitMaskIndex] = featureBitMaskValues[bitMaskIndex];
+        }
+        this.mPlayerNameList = playerNameList;
+    }
+}
+
+class FolderItemsRsp_ext {
+    byte mStatus;
+    short mUIDCounter;
+    byte mScope;
+    int mNumItems;
+    byte[] mFolderTypes;
+    byte[] mPlayable;
+    byte[] mItemTypes;
+    byte[] mItemUid;
+    String[] mDisplayNames; /* display name of the item. Eg: Folder name or song name */
+    int[] mAttributesNum;
+    int[] mAttrIds;
+    String[] mAttrValues;
+
+    FolderItemsRsp_ext(byte status, short uidCounter, byte scope, int numItems, byte[] folderTypes,
+            byte[] playable, byte[] itemTypes, byte[] itemsUid, String[] displayNameArray,
+            int[] attributesNum, int[] attrIds, String[] attrValues) {
+        this.mStatus = status;
+        this.mUIDCounter = uidCounter;
+        this.mScope = scope;
+        this.mNumItems = numItems;
+        this.mFolderTypes = folderTypes;
+        this.mPlayable = playable;
+        this.mItemTypes = itemTypes;
+        this.mItemUid = itemsUid;
+        this.mDisplayNames = displayNameArray;
+        this.mAttributesNum = attributesNum;
+        this.mAttrIds = attrIds;
+        this.mAttrValues = attrValues;
+    }
+}
+
+class ItemAttrRsp_ext {
+    byte mStatus;
+    byte mNumAttr;
+    int[] mAttributesIds;
+    String[] mAttributesArray;
+
+    ItemAttrRsp_ext(byte status, int[] attributesIds, String[] attributesArray) {
+        mStatus = status;
+        mNumAttr = (byte) attributesIds.length;
+        mAttributesIds = attributesIds;
+        mAttributesArray = attributesArray;
+    }
+}
+
+/* stores information of Media Players in the system */
+class MediaPlayerInfo_ext {
+
+    private byte mMajorType;
+    private int mSubType;
+    private byte mPlayStatus;
+    private short[] mFeatureBitMask;
+    @NonNull private String mPackageName;
+    @NonNull private String mDisplayableName;
+    @Nullable private MediaController mMediaController;
+
+    MediaPlayerInfo_ext(@Nullable MediaController controller, byte majorType, int subType,
+            byte playStatus, short[] featureBitMask, @NonNull String packageName,
+            @Nullable String displayableName) {
+        this.setMajorType(majorType);
+        this.setSubType(subType);
+        this.mPlayStatus = playStatus;
+        // store a copy the FeatureBitMask array
+        this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+        Arrays.sort(this.mFeatureBitMask);
+        this.setPackageName(packageName);
+        this.setDisplayableName(displayableName);
+        this.setMediaController(controller);
+    }
+
+    /* getters and setters */
+    byte getPlayStatus() {
+        return mPlayStatus;
+    }
+
+    void setPlayStatus(byte playStatus) {
+        this.mPlayStatus = playStatus;
+    }
+
+    MediaController getMediaController() {
+        return mMediaController;
+    }
+
+    void setMediaController(MediaController mediaController) {
+        if (mediaController != null) {
+            this.mPackageName = mediaController.getPackageName();
+        }
+        this.mMediaController = mediaController;
+    }
+
+    void setPackageName(@NonNull String name) {
+        // Controller determines package name when it is set.
+        if (mMediaController != null) {
+            return;
+        }
+        this.mPackageName = name;
+    }
+
+    String getPackageName() {
+        if (mMediaController != null) {
+            return mMediaController.getPackageName();
+        } else if (mPackageName != null) {
+            return mPackageName;
+        }
+        return null;
+    }
+
+    byte getMajorType() {
+        return mMajorType;
+    }
+
+    void setMajorType(byte majorType) {
+        this.mMajorType = majorType;
+    }
+
+    int getSubType() {
+        return mSubType;
+    }
+
+    void setSubType(int subType) {
+        this.mSubType = subType;
+    }
+
+    String getDisplayableName() {
+        return mDisplayableName;
+    }
+
+    void setDisplayableName(@Nullable String displayableName) {
+        if (displayableName == null) {
+            displayableName = "";
+        }
+        this.mDisplayableName = displayableName;
+    }
+
+    short[] getFeatureBitMask() {
+        return mFeatureBitMask;
+    }
+
+    void setFeatureBitMask(short[] featureBitMask) {
+        synchronized (this) {
+            this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+            Arrays.sort(this.mFeatureBitMask);
+        }
+    }
+
+    boolean isBrowseSupported() {
+        synchronized (this) {
+            if (this.mFeatureBitMask == null) {
+                return false;
+            }
+            for (short bit : this.mFeatureBitMask) {
+                if (bit == AvrcpConstants_ext.AVRC_PF_BROWSE_BIT_NO) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Tests if the view of this player presented to the controller is different enough to
+     *  justify sending an Available Players Changed update */
+    public boolean equalView(MediaPlayerInfo_ext other) {
+        return (this.mMajorType == other.getMajorType()) && (this.mSubType == other.getSubType())
+                && Arrays.equals(this.mFeatureBitMask, other.getFeatureBitMask())
+                && this.mDisplayableName.equals(other.getDisplayableName());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("MediaPlayerInfo ");
+        sb.append(getPackageName());
+        sb.append(" (as '" + getDisplayableName() + "')");
+        sb.append(" Type = " + getMajorType());
+        sb.append(", SubType = " + getSubType());
+        sb.append(", Status = " + mPlayStatus);
+        sb.append(" Feature Bits [");
+        short[] bits = getFeatureBitMask();
+        for (int i = 0; i < bits.length; i++) {
+            if (i != 0) {
+                sb.append(" ");
+            }
+            sb.append(bits[i]);
+        }
+        sb.append("] Controller: ");
+        sb.append(getMediaController());
+        return sb.toString();
+    }
+}
+
+/* stores information for browsable Media Players available in the system */
+class BrowsePlayerInfo_ext {
+    public String packageName;
+    public String displayableName;
+    public String serviceClass;
+
+    BrowsePlayerInfo_ext(String packageName, String displayableName, String serviceClass) {
+        this.packageName = packageName;
+        this.displayableName = displayableName;
+        this.serviceClass = serviceClass;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("BrowsePlayerInfo_ext ");
+        sb.append(packageName);
+        sb.append(" ( as '" + displayableName + "')");
+        sb.append(" service " + serviceClass);
+        return sb.toString();
+    }
+}
+
+class FolderItemsData_ext {
+    /* initialize sizes for rsp parameters */ int mNumItems;
+    int[] mAttributesNum;
+    byte[] mFolderTypes;
+    byte[] mItemTypes;
+    byte[] mPlayable;
+    byte[] mItemUid;
+    String[] mDisplayNames;
+    int[] mAttrIds;
+    String[] mAttrValues;
+    int mAttrCounter;
+
+    FolderItemsData_ext(int size) {
+        mNumItems = size;
+        mAttributesNum = new int[size];
+
+        mFolderTypes = new byte[size]; /* folderTypes */
+        mItemTypes = new byte[size]; /* folder or media item */
+        mPlayable = new byte[size];
+        Arrays.fill(mFolderTypes, AvrcpConstants_ext.FOLDER_TYPE_MIXED);
+        Arrays.fill(mItemTypes, AvrcpConstants_ext.BTRC_ITEM_MEDIA);
+        Arrays.fill(mPlayable, AvrcpConstants_ext.ITEM_PLAYABLE);
+
+        mItemUid = new byte[size * AvrcpConstants_ext.UID_SIZE];
+        mDisplayNames = new String[size];
+
+        mAttrIds = null; /* array of attr ids */
+        mAttrValues = null; /* array of attr values */
+    }
+}
+
+/** A queue that evicts the first element when you add an element to the end when it reaches a
+ * maximum size.
+ * This is useful for keeping a FIFO queue of items where the items drop off the front, i.e. a log
+ * with a maximum size.
+ */
+class EvictingQueue_ext<E> extends ArrayDeque<E> {
+    private int mMaxSize;
+
+    EvictingQueue_ext(int maxSize) {
+        super();
+        mMaxSize = maxSize;
+    }
+
+    EvictingQueue_ext(int maxSize, int initialElements) {
+        super(initialElements);
+        mMaxSize = maxSize;
+    }
+
+    EvictingQueue_ext(int maxSize, Collection<? extends E> c) {
+        super(c);
+        mMaxSize = maxSize;
+    }
+
+    @Override
+    public void addFirst(E e) {
+        if (super.size() == mMaxSize) {
+            return;
+        }
+        super.addFirst(e);
+    }
+
+    @Override
+    public void addLast(E e) {
+        if (super.size() == mMaxSize) {
+            super.remove();
+        }
+        super.addLast(e);
+    }
+
+    @Override
+    public boolean offerFirst(E e) {
+        if (super.size() == mMaxSize) {
+            return false;
+        }
+        return super.offerFirst(e);
+    }
+
+    @Override
+    public boolean offerLast(E e) {
+        if (super.size() == mMaxSize) {
+            super.remove();
+        }
+        return super.offerLast(e);
+    }
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java
new file mode 100644
index 0000000..0eaa2fa
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+
+/*************************************************************************************************
+ * Interface for classes which handle callbacks from AvrcpMediaManager.
+ * These callbacks should map to native responses and used to communicate with the native layer.
+ ************************************************************************************************/
+
+public interface AvrcpMediaRspInterface_ext {
+    void setAddrPlayerRsp(byte[] address, int rspStatus);
+
+    void setBrowsedPlayerRsp(byte[] address, int rspStatus, byte depth, int numItems,
+            String[] textArray);
+
+    void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp_ext rspObj);
+
+    void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp_ext rspObj);
+
+    void changePathRsp(byte[] address, int rspStatus, int numItems);
+
+    void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp_ext rspObj);
+
+    void playItemRsp(byte[] address, int rspStatus);
+
+    void getTotalNumOfItemsRsp(byte[] address, int rspStatus, int uidCounter, int numItems);
+
+    void addrPlayerChangedRsp(int type, int playerId, int uidCounter);
+
+    void avalPlayerChangedRsp(byte[] address, int type);
+
+    void uidsChangedRsp(int type);
+
+    void nowPlayingChangedRsp(int type);
+
+    void trackChangedRsp(int type, byte[] uid);
+}
+
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
index 18fcd7e..0ca60a0 100644
--- a/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
@@ -31,7 +31,7 @@
 import android.content.Context;
 import android.bluetooth.BluetoothDevice;
 import com.android.bluetooth.avrcp.AvrcpPlayerAppSettingsRspInterface;
-import com.android.bluetooth.avrcp.AvrcpConstants;
+import com.android.bluetooth.avrcp.AvrcpConstants_ext;
 import android.content.BroadcastReceiver;
 import com.android.bluetooth.Utils;
 import java.util.ArrayList;
@@ -164,7 +164,7 @@
             String action = intent.getAction();
             if (action.equals(PLAYERSETTINGS_RESPONSE)) {
             int getResponse = intent.getIntExtra(EXTRA_GET_RESPONSE,
-                                                      AvrcpConstants.GET_INVALID);
+                                                      AvrcpConstants_ext.GET_INVALID);
                 byte [] data;
                 String [] text;
                 boolean isSetAttrValRsp = false;
@@ -173,7 +173,7 @@
                 synchronized (mPendingCmds) {
                     Integer val = new Integer(getResponse);
                     if (mPendingCmds.contains(val)) {
-                        if (getResponse == AvrcpConstants.SET_ATTRIBUTE_VALUES) {
+                        if (getResponse == AvrcpConstants_ext.SET_ATTRIBUTE_VALUES) {
                             isSetAttrValRsp = true;
                             if (DEBUG) Log.v(TAG,"Response received for SET_ATTRIBUTE_VALUES");
                         }
@@ -187,7 +187,7 @@
                 if (DEBUG)
                     Log.v(TAG,"getResponse" + getResponse);
                 switch (getResponse) {
-                    case AvrcpConstants.GET_ATTRIBUTE_IDS:
+                    case AvrcpConstants_ext.GET_ATTRIBUTE_IDS:
                         if (device == null) {
                             Log.e(TAG,"ERROR!!! device is null");
                             return;
@@ -200,7 +200,7 @@
                                 data ,getByteAddress(device));
 
                     break;
-                    case AvrcpConstants.GET_VALUE_IDS:
+                    case AvrcpConstants_ext.GET_VALUE_IDS:
                         if (device == null) {
                             Log.e(TAG,"ERROR!!! device is null");
                             return;
@@ -220,7 +220,7 @@
                         mAvrcpPlayerAppSettingsRspInterface.getPlayerAppValueRsp(numAttr, data,
                                 getByteAddress(device));
                     break;
-                    case AvrcpConstants.GET_ATTRIBUTE_VALUES:
+                    case AvrcpConstants_ext.GET_ATTRIBUTE_VALUES:
                         if (device == null) {
                             Log.e(TAG,"ERROR!!! device is null");
                             return;
@@ -233,7 +233,7 @@
                         mAvrcpPlayerAppSettingsRspInterface.SendCurrentPlayerValueRsp(numAttr ,
                                 data, getByteAddress(device));
                     break;
-                    case AvrcpConstants.SET_ATTRIBUTE_VALUES:
+                    case AvrcpConstants_ext.SET_ATTRIBUTE_VALUES:
                         boolean send_change_rsp_only = true;
                         data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
                         updateLocalPlayerSettings(data);
@@ -243,19 +243,19 @@
                             Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request");
                             if (checkPlayerAttributeResponse(data)) {
                                 mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
-                                        AvrcpConstants.RSP_NO_ERROR, getByteAddress(device));
+                                        AvrcpConstants_ext.RSP_NO_ERROR, getByteAddress(device));
                                 } else {
                                 mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
-                                        AvrcpConstants.RSP_INTERNAL_ERR, getByteAddress(device));
+                                        AvrcpConstants_ext.RSP_INTERNAL_ERR, getByteAddress(device));
                             }
                             mPendingSetAttributes.clear();
                         }
                         if (send_change_rsp_only) {
                             mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
-                                    AvrcpConstants.RSP_NO_ERROR, null);
+                                    AvrcpConstants_ext.RSP_NO_ERROR, null);
                         }
                     break;
-                    case AvrcpConstants.GET_ATTRIBUTE_TEXT:
+                    case AvrcpConstants_ext.GET_ATTRIBUTE_TEXT:
                         text = intent.getStringArrayExtra(EXTRA_ATTRIBUTE_STRING_ARRAY);
                         if (device == null) {
                             Log.e(TAG," ERROR!!! device is null");
@@ -268,7 +268,7 @@
                             Log.v(TAG,"mPlayerSettings.attrIds"
                                     + mPlayerSettings.attrIds.length);
                     break;
-                    case AvrcpConstants.GET_VALUE_TEXT:
+                    case AvrcpConstants_ext.GET_VALUE_TEXT:
                         text = intent.getStringArrayExtra(EXTRA_VALUE_STRING_ARRAY);
                         if (device == null) {
                             Log.e(TAG,"ERROR!!! device is null");
@@ -293,12 +293,12 @@
             mPendingCmds.remove(val);
         }
         switch (cmd) {
-            case AvrcpConstants.GET_ATTRIBUTE_IDS:
+            case AvrcpConstants_ext.GET_ATTRIBUTE_IDS:
                 mAvrcpPlayerAppSettingsRspInterface.getListPlayerappAttrRsp(
                         (byte)def_attrib.length ,
                         def_attrib, getByteAddress(device));
                 break;
-            case AvrcpConstants.GET_VALUE_IDS:
+            case AvrcpConstants_ext.GET_VALUE_IDS:
                 byte attrib = 0;
                 if (!mPlayerSettingCmds.isEmpty()) {
                     attrib = mPlayerSettingCmds.get(0);
@@ -326,7 +326,7 @@
                     break;
                 }
                 break;
-            case AvrcpConstants.GET_ATTRIBUTE_VALUES:
+            case AvrcpConstants_ext.GET_ATTRIBUTE_VALUES:
                 int j = 0;
                 byte [] retVal = new byte [mPlayerSettings.attrIds.length*2];
                 for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
@@ -342,11 +342,11 @@
                  mAvrcpPlayerAppSettingsRspInterface.SendCurrentPlayerValueRsp((byte)retVal.length,
                         retVal, getByteAddress(device));
                 break;
-            case AvrcpConstants.SET_ATTRIBUTE_VALUES :
+            case AvrcpConstants_ext.SET_ATTRIBUTE_VALUES :
                  mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
-                        AvrcpConstants.RSP_INTERNAL_ERR, getByteAddress(device));
+                        AvrcpConstants_ext.RSP_INTERNAL_ERR, getByteAddress(device));
                 break;
-            case AvrcpConstants.GET_ATTRIBUTE_TEXT:
+            case AvrcpConstants_ext.GET_ATTRIBUTE_TEXT:
                 String [] attribText = new String [mPlayerSettings.attrIds.length];
                 for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
                     attribText[i] = "";
@@ -355,7 +355,7 @@
                         mPlayerSettings.attrIds.length, mPlayerSettings.attrIds,
                         attribText.length, attribText, getByteAddress(device));
                 break;
-            case AvrcpConstants.GET_VALUE_TEXT:
+            case AvrcpConstants_ext.GET_VALUE_TEXT:
                 String [] valueText = new String [mPlayerSettings.attrIds.length];
                 for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
                     valueText[i] = "";
@@ -374,19 +374,19 @@
     public void onListPlayerAttributeRequest(byte[] address) {
         Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
         intent.putExtra(COMMAND, CMDGET);
-        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_IDS);
+        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_IDS);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-        mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_IDS));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_IDS));
     }
 
     public void onListPlayerAttributeValues (byte attr, byte[] address) {
         Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
         intent.putExtra(COMMAND, CMDGET);
-        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_VALUE_IDS);
+        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_VALUE_IDS);
         intent.putExtra(EXTRA_ATTRIBUTE_ID, attr);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mPlayerSettingCmds.add(attr);
-        mPendingCmds.add(new Integer(AvrcpConstants.GET_VALUE_IDS));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_VALUE_IDS));
     }
 
     public void onGetPlayerAttributeValues (byte attr ,int[] arr ,
@@ -400,10 +400,10 @@
             mPlayerSettings.attrIds[i] = barray[i];
         Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
         intent.putExtra(COMMAND, CMDGET);
-        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_VALUES);
+        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_VALUES);
         intent.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, barray);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-        mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_VALUES));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES));
     }
 
     public void setPlayerAppSetting( byte num, byte [] attr_id, byte [] attr_val,
@@ -419,34 +419,34 @@
         intent.putExtra(COMMAND, CMDSET);
         intent.putExtra(EXTRA_ATTRIB_VALUE_PAIRS, array);
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-        mPendingCmds.add(new Integer(AvrcpConstants.SET_ATTRIBUTE_VALUES));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES));
     }
 
     public void getplayerattribute_text(byte attr , byte [] attrIds,
             byte[] address) {
         Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
         intent.putExtra(COMMAND, CMDGET);
-        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_TEXT);
+        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_TEXT);
         intent.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, attrIds);
         mPlayerSettings.attrIds = new byte [attr];
         for (int i = 0; i < attr; i++)
             mPlayerSettings.attrIds[i] = attrIds[i];
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-        mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_TEXT));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT));
    }
 
     public void getplayervalue_text(byte attr_id , byte num_value , byte [] value,
             byte[] address) {
         Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
         intent.putExtra(COMMAND, CMDGET);
-        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_VALUE_TEXT);
+        intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_VALUE_TEXT);
         intent.putExtra(EXTRA_ATTRIBUTE_ID, attr_id);
         intent.putExtra(EXTRA_VALUE_ID_ARRAY, value);
         mPlayerSettings.attrIds = new byte [num_value];
         for (int i = 0; i < num_value; i++)
             mPlayerSettings.attrIds[i] = value[i];
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-        mPendingCmds.add(new Integer(AvrcpConstants.GET_VALUE_TEXT));
+        mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_VALUE_TEXT));
     }
 
     public void sendPlayerAppChangedRsp(int rsptype, BluetoothDevice device) {
diff --git a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
index 0e127bd..8903fcf 100644
--- a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
+++ b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
@@ -8,7 +8,7 @@
  * 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
@@ -169,7 +169,7 @@
     private static final String [] BlacklistDeviceAddrToMediaAttr = {"00:17:53"/*Toyota Etios*/};
     private boolean ignore_play;
     private byte changePathFolderType;
-    private FolderItemsRsp saveRspObj;
+    private FolderItemsRsp_ext saveRspObj;
     private int changePathDepth;
     private byte changePathDirection;
     HashMap<BluetoothDevice, Integer> mVolumeMap = new HashMap();
@@ -191,7 +191,7 @@
     };
 
     /* UID counter to be shared across different files. */
-    static short sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
+    static short sUIDCounter = AvrcpConstants_ext.DEFAULT_UID_COUNTER;
 
     /* BTRC features */
     public static final int BTRC_FEAT_METADATA = 0x01;
@@ -236,7 +236,6 @@
     private final static int MESSAGE_SET_MEDIA_SESSION = 24;
     private final static int MSG_SET_AVRCP_CONNECTED_DEVICE = 25;
     private final static int MESSAGE_UPDATE_ABS_VOLUME_STATUS = 31;
-    private final static int MESSAGE_UPDATE_ABSOLUTE_VOLUME = 32;
     private static final int MSG_PLAY_STATUS_CMD_TIMEOUT = 33;
     private final static int MESSAGE_START_SHO = 34;
 
@@ -251,17 +250,17 @@
     private static final int SET_MEDIA_SESSION_DELAY = 300;
 
     /* Communicates with MediaPlayer to fetch media content */
-    private BrowsedMediaPlayer mBrowsedMediaPlayer;
+    private BrowsedMediaPlayer_ext mBrowsedMediaPlayer;
 
     /* Addressed player handling */
-    private AddressedMediaPlayer mAddressedMediaPlayer;
+    private AddressedMediaPlayer_ext mAddressedMediaPlayer;
 
     /* List of Media player instances, useful for retrieving MediaPlayerList or MediaPlayerInfo */
-    private SortedMap<Integer, MediaPlayerInfo> mMediaPlayerInfoList;
+    private SortedMap<Integer, MediaPlayerInfo_ext> mMediaPlayerInfoList;
     private boolean mAvailablePlayerViewChanged;
 
     /* List of media players which supports browse */
-    private List<BrowsePlayerInfo> mBrowsePlayerInfoList;
+    private List<BrowsePlayerInfo_ext> mBrowsePlayerInfoList;
 
     /* Manage browsed players */
     private AvrcpBrowseManager mAvrcpBrowseManager;
@@ -278,7 +277,7 @@
 
     /* Recording passthrough key dispatches */
     static private final int PASSTHROUGH_LOG_MAX_SIZE = DEBUG ? 50 : 10;
-    private EvictingQueue<MediaKeyLog> mPassthroughLogs; // Passthorugh keys dispatched
+    private EvictingQueue_ext<MediaKeyLog> mPassthroughLogs; // Passthorugh keys dispatched
     private List<MediaKeyLog> mPassthroughPending; // Passthrough keys sent not dispatched yet
     private int mPassthroughDispatched; // Number of keys dispatched
 
@@ -369,16 +368,16 @@
             mContext = context;
             mCurrentDevice = null;
             mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();;
-            mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mPlayerStatusChangeNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+            mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mPlayerStatusChangeNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
             mNextPosMs = -1;
             mPrevPosMs = -1;
             mPlaybackIntervalMs = 0L;
             mLastReportedPosition = -1;
-            mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+            mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
             mFeatures = 0;
             mLastDirection = 0;
             mAbsoluteVolume = -1;
@@ -388,12 +387,12 @@
             mVolCmdSetInProgress = false;
             isAbsoluteVolumeSupportingDevice = false;
             mAbsVolRetryTimes = 0;
-            keyPressState = AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+            keyPressState = AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
             mRemoteVolume = -1;
             mMusicAppCmdResponsePending = new HashMap<Integer, Integer>();
-            mAvailablePlayersChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-            mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+            mAvailablePlayersChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+            mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
             mLastStateUpdate = -1;
             mInitialRemoteVolume = -1;
             mBlackListVolume = -1;
@@ -432,12 +431,12 @@
         mReportedPlayStatus = PLAYSTATUS_ERROR;
         mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
         mAudioManagerIsPlaying = false;
-        mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        mPlayerStatusChangeNT  = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        mPlayerStatusChangeNT  = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         mPlaybackIntervalMs = 0L;
         mLastReportedPosition = -1;
         mNextPosMs = -1;
@@ -538,11 +537,11 @@
         mMediaControllerCb = new MediaControllerListener();
         mAvrcpMediaRsp = new AvrcpMediaRsp();
         mAvrcpPlayerAppSettingsRsp = new AvrcpPlayerAppSettingsRsp();
-        mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo>();
+        mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo_ext>();
         mAvailablePlayerViewChanged = false;
-        mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo>());
+        mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo_ext>());
         mPassthroughDispatched = 0;
-        mPassthroughLogs = new EvictingQueue<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE);
+        mPassthroughLogs = new EvictingQueue_ext<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE);
         mPassthroughPending = Collections.synchronizedList(new ArrayList<MediaKeyLog>());
         if (mMediaSessionManager != null) {
             mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveSessionListener, null,
@@ -582,7 +581,7 @@
         }
 
         /* create object to communicate with addressed player */
-        mAddressedMediaPlayer = new AddressedMediaPlayer(mAvrcpMediaRsp, mAvrcp);
+        mAddressedMediaPlayer = new AddressedMediaPlayer_ext(mAvrcpMediaRsp, mAvrcp);
 
         /* initialize BrowseMananger which manages Browse commands and response */
         mAvrcpBrowseManager = new AvrcpBrowseManager(mContext, mAvrcpMediaRsp);
@@ -675,7 +674,7 @@
         Log.d(TAG, "Enter clearDeviceDependentFeature()");
         for (int i = 0; i < maxAvrcpConnections; i++) {
             deviceFeatures[i].keyPressState =
-                AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+                AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
             if (deviceFeatures[i].mMusicAppCmdResponsePending != null)
                 deviceFeatures[i].mMusicAppCmdResponsePending.clear();
         }
@@ -805,20 +804,17 @@
                     break;
                 mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
                                                           isAbsoluteVolumeSupported(deviceIndex));
-                if (isAbsoluteVolumeSupported(deviceIndex)) {
-                    if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
+                if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
                         vol > mAbsVolThreshold) {
                         if (DEBUG) Log.v(TAG, "remote inital volume too high " + vol + ">" +
                             mAbsVolThreshold);
                         vol = mAbsVolThreshold;
                         notifyVolumeChanged(vol, false);
-                    }
                 }
                 if (vol >= 0) {
                     vol = convertToAvrcpVolume(vol);
                     Log.d(TAG,"vol = " + vol + "rem vol = " + deviceFeatures[deviceIndex].mRemoteVolume);
                     if(vol != deviceFeatures[deviceIndex].mRemoteVolume &&
-                       deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice &&
                        deviceFeatures[deviceIndex].mCurrentDevice != null) {
                        setVolumeNative(vol , getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                        if (deviceFeatures[deviceIndex].mCurrentDevice.isTwsPlusDevice()) {
@@ -838,19 +834,6 @@
                 break;
             }
 
-           case MESSAGE_UPDATE_ABSOLUTE_VOLUME:
-            {
-                int vol = msg.arg2;
-                deviceIndex = msg.arg1;
-                Log.e(TAG, "Device switch: setting volume: " + vol);
-                if(deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice) {
-                    notifyVolumeChanged(vol, false);
-                } else {
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);
-                }
-                break;
-            }
-
             case MSG_NATIVE_REQ_GET_RC_FEATURES:
             {
                 String address = (String) msg.obj;
@@ -898,7 +881,7 @@
                         }
                     } else
                         mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), true);
-                } else {
+                } else if (deviceFeatures[deviceIndex].isActiveDevice) {
                     mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
                         isAbsoluteVolumeSupported(deviceIndex));
                     Log.v(TAG,"update audio manager for abs vol state = "
@@ -1018,7 +1001,7 @@
             case MSG_NATIVE_REQ_GET_ELEM_ATTRS:
             {
                 String[] textArray;
-                AvrcpCmd.ElementAttrCmd elem = (AvrcpCmd.ElementAttrCmd) msg.obj;
+                AvrcpCmd_ext.ElementAttrCmd elem = (AvrcpCmd_ext.ElementAttrCmd) msg.obj;
                 byte numAttr = elem.mNumAttr;
                 int[] attrIds = elem.mAttrIDs;
                 byte[] remoteAddr = elem.mAddress;
@@ -1052,9 +1035,9 @@
                             textArray[i]= new String();
                     }
                     responseDebug.append("[" + attrIds[i] + "=");
-                    if (attrIds[i] == AvrcpConstants.ATTRID_TITLE
-                            || attrIds[i] == AvrcpConstants.ATTRID_ARTIST
-                            || attrIds[i] == AvrcpConstants.ATTRID_ALBUM) {
+                    if (attrIds[i] == AvrcpConstants_ext.ATTRID_TITLE
+                            || attrIds[i] == AvrcpConstants_ext.ATTRID_ARTIST
+                            || attrIds[i] == AvrcpConstants_ext.ATTRID_ALBUM) {
                         responseDebug.append(Utils.ellipsize(textArray[i]) + "] ");
                     } else {
                         responseDebug.append(textArray[i] + "] ");
@@ -1182,8 +1165,10 @@
                     //Don't show media UI when device connected.
                     isShowUI = false;
                     deviceFeatures[deviceIndex].mInitialRemoteVolume = absVol;
-                    //Avoid fluction of volume during device add in blacklist
-                    if(deviceFeatures[deviceIndex].mBlackListVolume != -1) {
+                    //Avoid fluctuation of volume during device added in blacklist
+                    // use send setAbsolute volume for blacklisted volume
+                    if(deviceFeatures[deviceIndex].mBlackListVolume != -1 &&
+                       deviceFeatures[deviceIndex].isActiveDevice) {
                         resetBlackList(address);
                         if (DEBUG) Log.v(TAG, "remote initial volume as audio stream volume : " +
                             deviceFeatures[deviceIndex].mBlackListVolume);
@@ -1195,18 +1180,29 @@
                         deviceFeatures[deviceIndex].mLocalVolume = deviceFeatures[deviceIndex].mBlackListVolume;
                         deviceFeatures[deviceIndex].mBlackListVolume = -1;
                         break;
-                    }
-                    else if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
-                        volIndex > mAbsVolThreshold) {
-                        if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" +
-                            mAbsVolThreshold);
-                        Message msg1 = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME,
-                            mAbsVolThreshold , 0);
-                        mHandler.sendMessage(msg1);
-                        deviceFeatures[deviceIndex].mRemoteVolume = absVol;
-                        deviceFeatures[deviceIndex].mLocalVolume = volIndex;
-                        deviceFeatures[deviceIndex].mLastRequestedVolume = -1;
-                        break;
+                    } else if (deviceFeatures[deviceIndex].isActiveDevice) {
+                        /*Avoid send set absolute volume for store volume untill volume registration
+                        complete and making synchronization to send only one setAbsolute volume
+                        during connection*/
+                        if(getVolume(deviceFeatures[deviceIndex].mCurrentDevice) != -1) {
+                            setAbsVolumeFlag(deviceFeatures[deviceIndex].mCurrentDevice);
+                            break;
+                        }
+                        /*if volume is stored than no need to send setAbsoluteVolume use register volume*/
+                        else if(mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
+                          volIndex > mAbsVolThreshold) {
+                            //To handle volume when device volume is not stored during
+                            // paring
+                            if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" +
+                                mAbsVolThreshold);
+                            Message msg1 = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME,
+                                mAbsVolThreshold , 0);
+                            mHandler.sendMessage(msg1);
+                            deviceFeatures[deviceIndex].mRemoteVolume = absVol;
+                            deviceFeatures[deviceIndex].mLocalVolume = volIndex;
+                            deviceFeatures[deviceIndex].mLastRequestedVolume = -1;
+                            break;
+                        }
                     }
                 }
                 if (deviceFeatures[deviceIndex].mLocalVolume != volIndex &&
@@ -1423,21 +1419,21 @@
                 break;
 
             case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
-                AvrcpCmd.FolderItemsCmd folderObj = (AvrcpCmd.FolderItemsCmd) msg.obj;
+                AvrcpCmd_ext.FolderItemsCmd folderObj = (AvrcpCmd_ext.FolderItemsCmd) msg.obj;
                 if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_FOLDER_ITEMS " + folderObj);
                 switch (folderObj.mScope) {
-                    case AvrcpConstants.BTRC_SCOPE_PLAYER_LIST:
+                    case AvrcpConstants_ext.BTRC_SCOPE_PLAYER_LIST:
                         handleMediaPlayerListRsp(folderObj);
                         break;
-                    case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM:
-                    case AvrcpConstants.BTRC_SCOPE_NOW_PLAYING:
+                    case AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM:
+                    case AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING:
                         handleGetFolderItemBrowseResponse(folderObj, folderObj.mAddress);
                         break;
                     default:
                         Log.e(TAG, "unknown scope for getfolderitems. scope = "
                                 + folderObj.mScope);
                         getFolderItemsRspNative(folderObj.mAddress,
-                                AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0, 0,
+                                AvrcpConstants_ext.RSP_INV_SCOPE, (short) 0, (byte) 0, 0,
                                 null, null, null, null, null, null, null, null);
                 }
                 break;
@@ -1451,7 +1447,7 @@
 
             case MSG_NATIVE_REQ_GET_ITEM_ATTR:
                 // msg object contains the item attribute object
-                AvrcpCmd.ItemAttrCmd cmd = (AvrcpCmd.ItemAttrCmd) msg.obj;
+                AvrcpCmd_ext.ItemAttrCmd cmd = (AvrcpCmd_ext.ItemAttrCmd) msg.obj;
                 if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_ITEM_ATTR " + cmd);
                 handleGetItemAttr(cmd);
                 break;
@@ -1470,7 +1466,7 @@
                 byte[] bdaddr = data.getByteArray("BdAddress");
                 byte[] folderUid = data.getByteArray("folderUid");
                 byte direction = data.getByte("direction");
-                byte[] tempUid = new byte[AvrcpConstants.UID_SIZE];
+                byte[] tempUid = new byte[AvrcpConstants_ext.UID_SIZE];
                 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
                     changePathDirection = direction;
                     if(direction == 1)
@@ -1479,8 +1475,8 @@
                         direction);
                     if ((direction == 1) && (changePathDepth > 0)) {
                        for (int index=0; index<saveRspObj.mDisplayNames.length; index++) {
-                            for (int size=0; size < AvrcpConstants.UID_SIZE; size++)
-                                tempUid[size] = saveRspObj.mItemUid[index* AvrcpConstants.UID_SIZE + size];
+                            for (int size=0; size < AvrcpConstants_ext.UID_SIZE; size++)
+                                tempUid[size] = saveRspObj.mItemUid[index* AvrcpConstants_ext.UID_SIZE + size];
                             if (Arrays.equals(folderUid, tempUid)) {
                                 changePathFolderType = saveRspObj.mFolderTypes[index];
                                 break;
@@ -1495,7 +1491,7 @@
 
                 } else {
                     Log.e(TAG, "Remote requesting change path before setbrowsedplayer");
-                    changePathRspNative(bdaddr, AvrcpConstants.RSP_BAD_CMD, 0);
+                    changePathRspNative(bdaddr, AvrcpConstants_ext.RSP_BAD_CMD, 0);
                 }
                 break;
             }
@@ -1607,7 +1603,7 @@
         byte newStatus = getBluetoothPlayState(state);
 
         /* update play status in global media player list */
-        MediaPlayerInfo player = getAddressedPlayerInfo();
+        MediaPlayerInfo_ext player = getAddressedPlayerInfo();
         if (player != null) {
             player.setPlayStatus(newStatus);
         }
@@ -1654,11 +1650,11 @@
         deviceFeatures[deviceIndex].mLastPassthroughcmd = KeyEvent.KEYCODE_UNKNOWN;
 
         if ((deviceFeatures[deviceIndex].mPlayStatusChangedNT ==
-                AvrcpConstants.NOTIFICATION_TYPE_INTERIM) &&
+                AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
                (oldPlayStatus != newPlayStatus) && deviceFeatures[deviceIndex].mCurrentDevice != null) {
             Log.w(TAG, "Sending PlayStatus CHANGED Rsp !!!");
             deviceFeatures[deviceIndex].mPlayStatusChangedNT =
-                AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
             registerNotificationRspPlayStatusNative(
                     deviceFeatures[deviceIndex].mPlayStatusChangedNT,
                     newPlayStatus,
@@ -2104,31 +2100,31 @@
                 Log.v(TAG, "Update player id: " + deviceFeatures[index].mReportedPlayerID +
                         "-> " + mCurrAddrPlayerID);
                 if (deviceFeatures[index].mAvailablePlayersChangedNT ==
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                     registerNotificationRspAvalPlayerChangedNative(
-                            AvrcpConstants.NOTIFICATION_TYPE_CHANGED, addr);
+                            AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, addr);
                     mAvailablePlayerViewChanged = false;
                     deviceFeatures[index].mAvailablePlayersChangedNT =
-                            AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                            AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                 }
                 if (deviceFeatures[index].mAddrPlayerChangedNT ==
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                     registerNotificationRspAddrPlayerChangedNative(
-                            AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
+                            AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
                             sUIDCounter, addr);
                     deviceFeatures[index].mAddrPlayerChangedNT =
-                            AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                            AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                 }
                 deviceFeatures[index].mReportedPlayerID = mCurrAddrPlayerID;
 
                 // Update the now playing list without sending the notification
-                deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                 mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
-                deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
             }
 
             // Dont send now playing list changed if the player doesn't support browsing
-            MediaPlayerInfo info = getAddressedPlayerInfo();
+            MediaPlayerInfo_ext info = getAddressedPlayerInfo();
             if (info != null && info.isBrowseSupported()) {
                 Log.v(TAG, "Check if NowPlayingList is updated");
                 mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
@@ -2144,7 +2140,7 @@
                 mLastQueueId = newQueueId;
                 for (int i = 0; i < maxAvrcpConnections; i++) {
                     if ((deviceFeatures[i].mCurrentDevice != null) &&
-                        (deviceFeatures[i].mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
+                        (deviceFeatures[i].mTrackChangedNT == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM)) {
                          deviceFeatures[i].mTracksPlayed++;
                          Log.v(TAG,"sending track change for device " + i);
                          sendTrackChangedRsp(false, deviceFeatures[i].mCurrentDevice);
@@ -2201,8 +2197,8 @@
 
     private void getElementAttrRequestFromNative(byte[] address, byte numAttr, int[] attrs) {
         if (DEBUG) Log.v(TAG, "getElementAttrRequestFromNative: numAttr=" + numAttr);
-        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
-        AvrcpCmd.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
+        AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
+        AvrcpCmd_ext.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
         Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ELEM_ATTRS);
         msg.obj = elemAttr;
         mHandler.sendMessage(msg);
@@ -2245,7 +2241,7 @@
                   return;
                 }
                 deviceFeatures[deviceIndex].mPlayStatusChangedNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 mHandler.removeMessages(MSG_PLAY_STATUS_CMD_TIMEOUT);
                 deviceFeatures[deviceIndex].isPlayStatusTimeOut = false;
                 if(avrcp_playstatus_blacklist && isPlayerStateUpdateBlackListed(
@@ -2281,7 +2277,7 @@
                     Log.d(TAG, "playback Status has changed from last playstatus response " +
                                     "send CHANGED event with current playback status");
                     deviceFeatures[deviceIndex].mPlayStatusChangedNT =
-                                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                     if (!deviceFeatures[deviceIndex].isPlayStatusTimeOut) {
                         Message msg = mHandler.obtainMessage(MSG_PLAY_STATUS_CMD_TIMEOUT,
                                                  0, 0, deviceFeatures[deviceIndex].mCurrentDevice);
@@ -2296,7 +2292,7 @@
                                 getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                     Log.v(TAG, "Sending Stopped in INTERIM response when current_play_status is playing and device just got connected");
                     deviceFeatures[deviceIndex].mPlayStatusChangedNT =
-                                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                     if (!deviceFeatures[deviceIndex].isPlayStatusTimeOut) {
                         Message msg = mHandler.obtainMessage(MSG_PLAY_STATUS_CMD_TIMEOUT,
                                                  0, 0, deviceFeatures[deviceIndex].mCurrentDevice);
@@ -2317,7 +2313,7 @@
                   return;
                 }
                 deviceFeatures[deviceIndex].mTrackChangedNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 sendTrackChangedRsp(true, deviceFeatures[deviceIndex].mCurrentDevice);
                 break;
 
@@ -2352,7 +2348,7 @@
                     update_interval = SystemProperties.getLong("persist.vendor.btstack.avrcp.pos_time", 1000L);
                 }
                 deviceFeatures[deviceIndex].mPlayPosChangedNT =
-                                             AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                                             AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 update_interval = Math.max((long)param * 1000L, update_interval);
                 deviceFeatures[deviceIndex].mPlaybackIntervalMs = update_interval;
                 sendPlayPosNotificationRsp(true, deviceIndex);
@@ -2363,7 +2359,7 @@
 
             case EVT_APP_SETTINGS_CHANGED:
                 deviceFeatures[deviceIndex].mPlayerStatusChangeNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 mAvrcpPlayerAppSettings.sendPlayerAppChangedRsp(
                         deviceFeatures[deviceIndex].mPlayerStatusChangeNT, device);
                 break;
@@ -2376,9 +2372,9 @@
                   return;
                 }
                 deviceFeatures[deviceIndex].mAvailablePlayersChangedNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 registerNotificationRspAvalPlayerChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                 break;
 
@@ -2390,9 +2386,9 @@
                   return;
                 }
                 deviceFeatures[deviceIndex].mAddrPlayerChangedNT =
-                                             AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                                             AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 registerNotificationRspAddrPlayerChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
                         mCurrAddrPlayerID, sUIDCounter,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                 deviceFeatures[deviceIndex].mReportedPlayerID = mCurrAddrPlayerID;
@@ -2405,9 +2401,9 @@
                   return;
                 }
                 deviceFeatures[deviceIndex].mUidsChangedNT =
-                                             AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                                             AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 registerNotificationRspUIDsChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM, sUIDCounter,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM, sUIDCounter,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                 break;
 
@@ -2418,9 +2414,9 @@
                   return;
                 }
                 /* send interim response to remote device */
-                mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+                mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
                 if (!registerNotificationRspNowPlayingChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice))) {
                     Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " +
                             "registerNotificationRspNowPlayingChangedNative for Interim rsp failed!");
@@ -2443,23 +2439,23 @@
     private void sendTrackChangedRsp(boolean registering, BluetoothDevice device) {
         int deviceIndex = getIndexForDevice(device);
         Log.d(TAG, "Enter sendTrackChangedRsp");
-        if (deviceFeatures[deviceIndex].mTrackChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM
+        if (deviceFeatures[deviceIndex].mTrackChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM
                 && !registering) {
             if (DEBUG) Log.d(TAG, "sendTrackChangedRsp: Not registered or registering.");
             return;
         }
 
-        deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         if (registering)
-            deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+            deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
 
-        MediaPlayerInfo info = getAddressedPlayerInfo();
+        MediaPlayerInfo_ext info = getAddressedPlayerInfo();
         byte[] byteAddr = getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice);
         // for non-browsable players or no player
         if ((info != null && !info.isBrowseSupported()) ||
                 (deviceFeatures[deviceIndex].mFeatures & BTRC_FEAT_BROWSE) == 0) {
-            byte[] track = AvrcpConstants.TRACK_IS_SELECTED;
-            if (!mMediaAttributes.exists) track = AvrcpConstants.NO_TRACK_SELECTED;
+            byte[] track = AvrcpConstants_ext.TRACK_IS_SELECTED;
+            if (!mMediaAttributes.exists) track = AvrcpConstants_ext.NO_TRACK_SELECTED;
             registerNotificationRspTrackChangeNative(
                               deviceFeatures[deviceIndex].mTrackChangedNT,
                               track,
@@ -2596,7 +2592,7 @@
      */
     private void sendPlayPosNotificationRsp(boolean requested, int i) {
         Log.d(TAG, "Enter sendPlayPosNotificationRsp");
-        if (!requested && deviceFeatures[i].mPlayPosChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+        if (!requested && deviceFeatures[i].mPlayPosChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
             if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting.");
             return;
         }
@@ -2625,7 +2621,7 @@
         }
         if (requested || ((deviceFeatures[i].mLastReportedPosition != playPositionMs) &&
              ((playPositionMs >= deviceFeatures[i].mNextPosMs) ||
-             (playPositionMs <= deviceFeatures[i].mPrevPosMs)))) {
+             (playPositionMs <= deviceFeatures[i].mPrevPosMs))) && deviceFeatures[i].isActiveDevice) {
             if (!requested) deviceFeatures[i].mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
             if (deviceFeatures[i].mCurrentDevice != null)
                 registerNotificationRspPlayPosNative(deviceFeatures[i].mPlayPosChangedNT,
@@ -2642,7 +2638,7 @@
 
         mHandler.removeMessages(currMsgPlayIntervalTimeout);
         if ((deviceFeatures[i].mCurrentDevice != null) &&
-            (deviceFeatures[i].mPlayPosChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM) &&
+            (deviceFeatures[i].mPlayPosChangedNT == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
                  (isPlayingState(deviceFeatures[i].mCurrentPlayState)) && !isPlayerPaused()) {
             Message msg = mHandler.obtainMessage(currMsgPlayIntervalTimeout, 0, 0,
                                                  deviceFeatures[i].mCurrentDevice);
@@ -2737,18 +2733,18 @@
                             Log.d(TAG, "Vol Passthrough Up");
                             avrcpCtrlService.sendPassThroughCmd(
                                 deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_UP,
-                                AvrcpConstants.KEY_STATE_PRESS);
+                                AvrcpConstants_ext.KEY_STATE_PRESS);
                             avrcpCtrlService.sendPassThroughCmd(
                                 deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_UP,
-                                AvrcpConstants.KEY_STATE_RELEASE);
+                                AvrcpConstants_ext.KEY_STATE_RELEASE);
                         } else if (volume < mLocalVolume) {
                            Log.d(TAG, "Vol Passthrough Down");
                            avrcpCtrlService.sendPassThroughCmd(
                                 deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_DOWN,
-                                AvrcpConstants.KEY_STATE_PRESS);
+                                AvrcpConstants_ext.KEY_STATE_PRESS);
                            avrcpCtrlService.sendPassThroughCmd(
                                 deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_DOWN,
-                                AvrcpConstants.KEY_STATE_RELEASE);
+                                AvrcpConstants_ext.KEY_STATE_RELEASE);
                         }
                         mLocalVolume = volume;
                     }
@@ -2784,9 +2780,9 @@
     private void getFolderItemsRequestFromNative(
         byte[] address, byte scope, long startItem, long endItem, byte numAttr, int[] attrIds) {
         if (DEBUG) Log.v(TAG, "getFolderItemsRequestFromNative: scope=" + scope + ", numAttr=" + numAttr);
-        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
+        AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
         Log.v(TAG, "Enter getFolderItemsRequestFromNative");
-        AvrcpCmd.FolderItemsCmd folderObj = avrcpCmdobj.new FolderItemsCmd(address, scope,
+        AvrcpCmd_ext.FolderItemsCmd folderObj = avrcpCmdobj.new FolderItemsCmd(address, scope,
                 startItem, endItem, numAttr, attrIds);
         Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_FOLDER_ITEMS, 0, 0);
         msg.obj = folderObj;
@@ -2824,9 +2820,9 @@
 
     private void getItemAttrRequestFromNative(byte[] address, byte scope, byte[] itemUid, int uidCounter,
             byte numAttr, int[] attrs) {
-        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
+        AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
         Log.v(TAG, "Enter getItemAttrRequestFromNative");
-        AvrcpCmd.ItemAttrCmd itemAttr = avrcpCmdobj.new ItemAttrCmd(address, scope,
+        AvrcpCmd_ext.ItemAttrCmd itemAttr = avrcpCmdobj.new ItemAttrCmd(address, scope,
                 itemUid, uidCounter, numAttr, attrs);
         Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ITEM_ATTR);
         msg.obj = itemAttr;
@@ -2837,7 +2833,7 @@
     private void searchRequestFromNative(byte[] address, int charsetId, byte[] searchStr) {
         /* Search is not supported */
         Log.w(TAG, "searchRequestFromNative: search is not supported");
-        searchRspNative(address, AvrcpConstants.RSP_SRCH_NOT_SPRTD, 0, 0);
+        searchRspNative(address, AvrcpConstants_ext.RSP_SRCH_NOT_SPRTD, 0, 0);
     }
 
     private void playItemRequestFromNative(byte[] address, byte scope, int uidCounter, byte[] uid) {
@@ -2856,7 +2852,7 @@
     private void addToPlayListRequestFromNative(byte[] address, byte scope, byte[] uid, int uidCounter) {
         /* add to NowPlaying not supported */
         Log.w(TAG, "addToPlayListRequestFromNative: not supported! scope=" + scope);
-        addToNowPlayingRspNative(address, AvrcpConstants.RSP_INTERNAL_ERR);
+        addToNowPlayingRspNative(address, AvrcpConstants_ext.RSP_INTERNAL_ERR);
     }
 
     private void getTotalNumOfItemsRequestFromNative(byte[] address, byte scope) {
@@ -2899,10 +2895,10 @@
             Log.e(TAG,"invalid index for device");
             return;
         }
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_IDS,
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_IDS,
                 deviceIndex, true);
         mAvrcpPlayerAppSettings.onListPlayerAttributeRequest(address);
-        SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_IDS, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_IDS, address);
    }
 
     //PDU ID 0x12
@@ -2915,9 +2911,9 @@
             Log.e(TAG,"invalid index for device");
             return;
         }
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_VALUE_IDS, deviceIndex, true);
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_VALUE_IDS, deviceIndex, true);
         mAvrcpPlayerAppSettings.onListPlayerAttributeValues(attr, address);
-        SendPlayerSettingMsg(AvrcpConstants.GET_VALUE_IDS, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.GET_VALUE_IDS, address);
     }
 
     //PDU ID 0x13
@@ -2933,10 +2929,10 @@
             Log.e(TAG,"invalid index for device");
             return;
         }
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_VALUES,
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES,
                 deviceIndex, true);
         mAvrcpPlayerAppSettings.onGetPlayerAttributeValues(attr, arr, address);
-        SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_VALUES, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES, address);
     }
 
     //PDU 0x14
@@ -2953,15 +2949,15 @@
             return;
         }
         if (!deviceFeatures[deviceIndex].isActiveDevice) {
-            SendSetPlayerAppRspNative(AvrcpConstants.RSP_INTERNAL_ERR, address);
+            SendSetPlayerAppRspNative(AvrcpConstants_ext.RSP_INTERNAL_ERR, address);
             Log.e(TAG,"Set Command from inactive device reject it");
             return;
         }
 
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.SET_ATTRIBUTE_VALUES,
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES,
                 deviceIndex, true);
         mAvrcpPlayerAppSettings.setPlayerAppSetting(num, attr_id, attr_val, address);
-        SendPlayerSettingMsg(AvrcpConstants.SET_ATTRIBUTE_VALUES, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES, address);
     }
 
     //PDU 0x15
@@ -2977,10 +2973,10 @@
             Log.e(TAG,"invalid index for device");
             return;
         }
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_TEXT,
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT,
                 deviceIndex, true);
         mAvrcpPlayerAppSettings.getplayerattribute_text(attr, attrIds, address);
-        SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_TEXT, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT, address);
     }
 
     //PDU 0x16
@@ -2994,9 +2990,9 @@
             Log.e(TAG,"invalid index for device");
             return;
         }
-        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_VALUE_TEXT, deviceIndex, true);
+        CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_VALUE_TEXT, deviceIndex, true);
         mAvrcpPlayerAppSettings.getplayervalue_text(attr_id, num_value, value, address);
-        SendPlayerSettingMsg(AvrcpConstants.GET_VALUE_TEXT, address);
+        SendPlayerSettingMsg(AvrcpConstants_ext.GET_VALUE_TEXT, address);
     }
 
     private void SendPlayerSettingMsg(Integer cmd, byte[] address) {
@@ -3217,8 +3213,6 @@
              (mDevice.isTwsPlusDevice() && device.isTwsPlusDevice()))) {
             setActiveDevice(mDevice);
             //below line to send setAbsolute volume if device is suporting absolute volume
-            if (mDevice.equals(deviceFeatures[index].mCurrentDevice))
-                setAbsVolumeFlag(mDevice);//Do not call this funciton for second EB connect
             //When A2dp playing on DUT and Remote got connected, send proper playstatus
             if (isPlayingState(mCurrentPlayerState) &&
                 mA2dpService.isA2dpPlaying(device)) {
@@ -3400,7 +3394,7 @@
         int browseInfoID = 0;
         synchronized (this) {
             synchronized (mBrowsePlayerInfoList) {
-                for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
+                for (BrowsePlayerInfo_ext info : mBrowsePlayerInfoList) {
                     if (info.packageName.equals(packageName)) {
                         response = true;
                         break;
@@ -3427,34 +3421,34 @@
             synchronized (mMediaPlayerInfoList) {
                 if (mMediaPlayerInfoList.isEmpty()) {
                     Log.w(TAG, functionTag + "no players, send no available players");
-                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY);
+                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY);
                     return;
                 }
                 if (selectedId == NO_PLAYER_ID) {
                     Log.w(TAG, functionTag + "Respond dummy pass response ");
-                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
                     return;
                 }
                 if (!mMediaPlayerInfoList.containsKey(selectedId)) {
                     Log.w(TAG, functionTag + "invalid id, sending response back ");
-                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INV_PLAYER);
+                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_PLAYER);
                     return;
                 }
 
                 if (isPlayerAlreadyAddressed(selectedId)) {
-                    MediaPlayerInfo info = getAddressedPlayerInfo();
+                    MediaPlayerInfo_ext info = getAddressedPlayerInfo();
                     Log.i(TAG, functionTag + "player already addressed: " + info);
-                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
                     return;
                 }
                 // register new Media Controller Callback and update the current IDs
                 if (!updateCurrentController(selectedId, mCurrBrowsePlayerID)) {
                     Log.e(TAG, functionTag + "updateCurrentController failed!");
-                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+                    setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
                     return;
                 }
                 // If we don't have a controller, try to launch the player
-                MediaPlayerInfo info = getAddressedPlayerInfo();
+                MediaPlayerInfo_ext info = getAddressedPlayerInfo();
                 if (info.getMediaController() == null) {
                     Intent launch = mPackageManager.getLaunchIntentForPackage(info.getPackageName());
                     Log.i(TAG, functionTag + "launching player " + launch);
@@ -3462,12 +3456,12 @@
                 }
             }
         }
-        setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+        setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
         Log.d(TAG, "Exit setAddressedPlayer");
     }
 
     private void setBrowsedPlayer(byte[] bdaddr, int selectedId) {
-        int status = AvrcpConstants.RSP_NO_ERROR;
+        int status = AvrcpConstants_ext.RSP_NO_ERROR;
 
         Log.d(TAG, "Enter setBrowsedPlayer");
         String address = Utils.getAddressStringFromByte(bdaddr);
@@ -3475,10 +3469,10 @@
         // checking for error cases
         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
         if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
-            status = AvrcpConstants.RSP_INTERNAL_ERR;
+            status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
             Log.w(TAG, "setBrowsedPlayer: Cmd from browse inactive device reject it");
         } else if (mMediaPlayerInfoList.isEmpty()) {
-            status = AvrcpConstants.RSP_NO_AVBL_PLAY;
+            status = AvrcpConstants_ext.RSP_NO_AVBL_PLAY;
             Log.w(TAG, "setBrowsedPlayer: No available players! ");
         } else {
             // Workaround for broken controllers selecting ID 0
@@ -3493,19 +3487,19 @@
 
             if (!isPackageNameValid(browsedPackage)) {
                 Log.w(TAG, " Invalid package for id:" + mCurrBrowsePlayerID);
-                status = AvrcpConstants.RSP_INV_PLAYER;
+                status = AvrcpConstants_ext.RSP_INV_PLAYER;
             } else if (!isBrowseSupported(browsedPackage)) {
                 Log.w(TAG, "Browse unsupported for id:" + mCurrBrowsePlayerID
                         + ", packagename : " + browsedPackage);
-                status = AvrcpConstants.RSP_PLAY_NOT_BROW;
+                status = AvrcpConstants_ext.RSP_PLAY_NOT_BROW;
             } else if (!startBrowseService(bdaddr, browsedPackage)) {
                 Log.e(TAG, "service cannot be started for browse player id:" + mCurrBrowsePlayerID
                         + ", packagename : " + browsedPackage);
-                status = AvrcpConstants.RSP_INTERNAL_ERR;
+                status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
             }
         }
 
-        if (status != AvrcpConstants.RSP_NO_ERROR) {
+        if (status != AvrcpConstants_ext.RSP_NO_ERROR) {
             setBrowsedPlayerRspNative(bdaddr, status, (byte) 0x00, 0, null);
         }
 
@@ -3570,7 +3564,7 @@
 
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
                     if (entry.getValue().getPackageName().equals(packageName)) {
                         int newAddrID = entry.getKey();
                         if (DEBUG) Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue());
@@ -3676,10 +3670,10 @@
                     String packageName = info.serviceInfo.packageName;
 
                     if (DEBUG) Log.d(TAG, "Adding " + serviceName + " to list of browsable players");
-                    BrowsePlayerInfo currentPlayer =
-                            new BrowsePlayerInfo(packageName, displayableName, serviceName);
+                    BrowsePlayerInfo_ext currentPlayer =
+                            new BrowsePlayerInfo_ext(packageName, displayableName, serviceName);
                     mBrowsePlayerInfoList.add(currentPlayer);
-                    MediaPlayerInfo playerInfo = getMediaPlayerInfo(packageName);
+                    MediaPlayerInfo_ext playerInfo = getMediaPlayerInfo(packageName);
                     MediaController controller =
                             (playerInfo == null) ? null : playerInfo.getMediaController();
                     // Refresh the media player entry so it notices we can browse
@@ -3730,7 +3724,7 @@
                 new ArrayList<android.media.session.MediaController>();
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
-                for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) {
+                for (MediaPlayerInfo_ext info : mMediaPlayerInfoList.values()) {
                      MediaController controller = info.getMediaController();
                     if (controller != null) {
                         controllers.add(controller.getWrappedInstance());
@@ -3743,8 +3737,8 @@
 
     /** Add (or update) a player to the media player list without a controller */
     private boolean addMediaPlayerPackage(String packageName) {
-        MediaPlayerInfo info = new MediaPlayerInfo(null, AvrcpConstants.PLAYER_TYPE_AUDIO,
-                AvrcpConstants.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED,
+        MediaPlayerInfo_ext info = new MediaPlayerInfo_ext(null, AvrcpConstants_ext.PLAYER_TYPE_AUDIO,
+                AvrcpConstants_ext.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED,
                 getFeatureBitMask(packageName), packageName, getAppLabel(packageName));
         return addMediaPlayerInfo(info);
     }
@@ -3752,8 +3746,8 @@
     /** Add (or update) a player to the media player list given an active controller */
     private boolean addMediaPlayerController(android.media.session.MediaController controller) {
         String packageName = controller.getPackageName();
-        MediaPlayerInfo info = new MediaPlayerInfo(MediaControllerFactory.wrap(controller), /*MediaController not present*/
-                AvrcpConstants.PLAYER_TYPE_AUDIO, AvrcpConstants.PLAYER_SUBTYPE_NONE,
+        MediaPlayerInfo_ext info = new MediaPlayerInfo_ext(MediaControllerFactory.wrap(controller), /*MediaController not present*/
+                AvrcpConstants_ext.PLAYER_TYPE_AUDIO, AvrcpConstants_ext.PLAYER_SUBTYPE_NONE,
                 getBluetoothPlayState(controller.getPlaybackState()),
                 getFeatureBitMask(packageName), controller.getPackageName(),
                 getAppLabel(packageName));
@@ -3763,7 +3757,7 @@
     /** Add or update a player to the media player list given the MediaPlayerInfo object.
      *  @return true if an item was updated, false if it was added instead
      */
-    private boolean addMediaPlayerInfo(MediaPlayerInfo info) {
+    private boolean addMediaPlayerInfo(MediaPlayerInfo_ext info) {
         int updateId = -1;
         boolean updated = false;
         boolean currentRemoved = false;
@@ -3773,8 +3767,8 @@
         }
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
-                    MediaPlayerInfo current = entry.getValue();
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
+                    MediaPlayerInfo_ext current = entry.getValue();
                     int id = entry.getKey();
                     if (info.getPackageName().equals(current.getPackageName())) {
                         if (!current.equalView(info)) {
@@ -3806,11 +3800,11 @@
     }
 
     /** Remove all players related to |packageName| from the media player info list */
-    private MediaPlayerInfo removeMediaPlayerInfo(String packageName) {
+    private MediaPlayerInfo_ext removeMediaPlayerInfo(String packageName) {
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 int removeKey = -1;
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
                     if (entry.getValue().getPackageName().equals(packageName)) {
                         removeKey = entry.getKey();
                         break;
@@ -3833,8 +3827,8 @@
         if (controller == null) return;
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
-                    MediaPlayerInfo info = entry.getValue();
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
+                    MediaPlayerInfo_ext info = entry.getValue();
                     MediaController c = info.getMediaController();
                     if (c != null && c.equals(controller)) {
                         info.setMediaController(null);
@@ -3894,23 +3888,23 @@
         ArrayList<Short> featureBitsList = new ArrayList<Short>();
 
         /* adding default feature bits */
-        featureBitsList.add(AvrcpConstants.AVRC_PF_PLAY_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_STOP_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_PAUSE_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_REWIND_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_FAST_FWD_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_FORWARD_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_BACKWARD_BIT_NO);
-        featureBitsList.add(AvrcpConstants.AVRC_PF_ADV_CTRL_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_PLAY_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_STOP_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_PAUSE_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_REWIND_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_FAST_FWD_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_FORWARD_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_BACKWARD_BIT_NO);
+        featureBitsList.add(AvrcpConstants_ext.AVRC_PF_ADV_CTRL_BIT_NO);
 
         /* Add/Modify browse player supported features. */
         if (isBrowseSupported(packageName)) {
-            featureBitsList.add(AvrcpConstants.AVRC_PF_BROWSE_BIT_NO);
-            featureBitsList.add(AvrcpConstants.AVRC_PF_UID_UNIQUE_BIT_NO);
-            featureBitsList.add(AvrcpConstants.AVRC_PF_NOW_PLAY_BIT_NO);
-            featureBitsList.add(AvrcpConstants.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO);
+            featureBitsList.add(AvrcpConstants_ext.AVRC_PF_BROWSE_BIT_NO);
+            featureBitsList.add(AvrcpConstants_ext.AVRC_PF_UID_UNIQUE_BIT_NO);
+            featureBitsList.add(AvrcpConstants_ext.AVRC_PF_NOW_PLAY_BIT_NO);
+            featureBitsList.add(AvrcpConstants_ext.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO);
             if (mAvrcpBipRsp != null)
-                featureBitsList.add(AvrcpConstants.AVRC_PF_COVER_ART_BIT_NO);
+                featureBitsList.add(AvrcpConstants_ext.AVRC_PF_COVER_ART_BIT_NO);
         }
 
         // converting arraylist to array for response
@@ -3933,7 +3927,7 @@
         synchronized (this) {
             synchronized (mBrowsePlayerInfoList) {
                 /* check if Browsable Player's list contains this package name */
-                for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
+                for (BrowsePlayerInfo_ext info : mBrowsePlayerInfoList) {
                     if (info.packageName.equals(packageName)) {
                         if (DEBUG) Log.v(TAG, "isBrowseSupported for " + packageName + ": true");
                         return true;
@@ -3947,7 +3941,7 @@
     }
 
     private String getPackageName(int id) {
-        MediaPlayerInfo player = null;
+        MediaPlayerInfo_ext player = null;
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 player = mMediaPlayerInfoList.getOrDefault(id, null);
@@ -3970,7 +3964,7 @@
     private String getCurrentBrowsedPlayer(byte[] bdaddr) {
         String browsedPlayerPackage = "";
 
-        Map<String, BrowsedMediaPlayer> connList = mAvrcpBrowseManager.getConnList();
+        Map<String, BrowsedMediaPlayer_ext> connList = mAvrcpBrowseManager.getConnList();
         String bdaddrStr = new String(bdaddr);
         if(connList.containsKey(bdaddrStr)){
             browsedPlayerPackage = connList.get(bdaddrStr).getPackageName();
@@ -3980,7 +3974,7 @@
     }
 
     /* Returns the MediaPlayerInfo for the currently addressed media player */
-    private MediaPlayerInfo getAddressedPlayerInfo() {
+    private MediaPlayerInfo_ext getAddressedPlayerInfo() {
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 return mMediaPlayerInfoList.getOrDefault(mCurrAddrPlayerID, null);
@@ -3992,7 +3986,7 @@
      * Utility function to get the Media player info from package name returns
      * null if package name not found in media players list
      */
-    private MediaPlayerInfo getMediaPlayerInfo(String packageName) {
+    private MediaPlayerInfo_ext getMediaPlayerInfo(String packageName) {
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 if (mMediaPlayerInfoList.isEmpty()) {
@@ -4000,7 +3994,7 @@
                     return null;
                 }
 
-                for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) {
+                for (MediaPlayerInfo_ext info : mMediaPlayerInfoList.values()) {
                     if (packageName.equals(info.getPackageName())) {
                         if (DEBUG) Log.v(TAG, "getMediaPlayerInfo: Found " + packageName);
                         return info;
@@ -4013,7 +4007,7 @@
     }
 
     /* prepare media list & return the media player list response object */
-    private MediaPlayerListRsp prepareMediaPlayerRspObj() {
+    private MediaPlayerListRsp_ext prepareMediaPlayerRspObj() {
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 // TODO(apanicke): This hack will go away as soon as a developer
@@ -4029,18 +4023,18 @@
                 String[] displayableNameArray = new String[numPlayers];
                 byte[] playStatusValues = new byte[numPlayers];
                 short[] featureBitMaskValues =
-                        new short[numPlayers * AvrcpConstants.AVRC_FEATURE_MASK_SIZE];
+                        new short[numPlayers * AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE];
 
                 // Reserve the first spot for the currently addressed player if
                 // we have one
                 int players = mMediaPlayerInfoList.containsKey(mCurrAddrPlayerID) ? 1 : 0;
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
                     int idx = players;
                     if (entry.getKey() == mCurrAddrPlayerID)
                         idx = 0;
                     else
                         continue; // TODO(apanicke): Remove, see above note
-                    MediaPlayerInfo info = entry.getValue();
+                    MediaPlayerInfo_ext info = entry.getValue();
                     playerIds[idx] = entry.getKey();
                     playerTypes[idx] = info.getMajorType();
                     playerSubTypes[idx] = info.getSubType();
@@ -4053,7 +4047,7 @@
                         byte octet = (byte) (featureBits[numBit] / 8);
                         /* gives the bit position within the octet */
                         byte bit = (byte) (featureBits[numBit] % 8);
-                        featureBitMaskValues[(idx * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |=
+                        featureBitMaskValues[(idx * AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE) + octet] |=
                                 (1 << bit);
                     }
 
@@ -4069,28 +4063,28 @@
 
                 if (DEBUG) Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers);
 
-                return new MediaPlayerListRsp(AvrcpConstants.RSP_NO_ERROR, sUIDCounter, players,
-                        AvrcpConstants.BTRC_ITEM_PLAYER, playerIds, playerTypes, playerSubTypes,
+                return new MediaPlayerListRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, sUIDCounter, players,
+                        AvrcpConstants_ext.BTRC_ITEM_PLAYER, playerIds, playerTypes, playerSubTypes,
                         playStatusValues, featureBitMaskValues, displayableNameArray);
             }
         }
     }
 
      /* build media player list and send it to remote. */
-    private void handleMediaPlayerListRsp(AvrcpCmd.FolderItemsCmd folderObj) {
-        MediaPlayerListRsp rspObj = null;
+    private void handleMediaPlayerListRsp(AvrcpCmd_ext.FolderItemsCmd folderObj) {
+        MediaPlayerListRsp_ext rspObj = null;
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
                 int numPlayers = mMediaPlayerInfoList.size();
                 if (numPlayers == 0) {
-                    mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY,
+                    mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants_ext.RSP_NO_AVBL_PLAY,
                             (short) 0, (byte) 0, 0, null, null, null, null, null, null);
                     return;
                 }
                 if (folderObj.mStartItem >= numPlayers || folderObj.mStartItem >= 1) {
                     Log.i(TAG, "handleMediaPlayerListRsp: start = " + folderObj.mStartItem
                                     + " > num of items = " + numPlayers);
-                    mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_INV_RANGE,
+                    mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants_ext.RSP_INV_RANGE,
                             (short) 0, (byte) 0, 0, null, null, null, null, null, null);
                     return;
                 }
@@ -4098,9 +4092,9 @@
                     short[] featureBitsArray = {0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x01, 0x04,
                                                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
                     Log.i(TAG, "handleMediaPlayerListRsp: Send dummy player response");
-                    mediaPlayerListRspNative(folderObj.mAddress, (int)AvrcpConstants.RSP_NO_ERROR,
-                            (int)sUIDCounter, AvrcpConstants.BTRC_ITEM_PLAYER, 1, new int[] {0},
-                            new byte[] {AvrcpConstants.PLAYER_TYPE_AUDIO}, new int[] {1},
+                    mediaPlayerListRspNative(folderObj.mAddress, (int)AvrcpConstants_ext.RSP_NO_ERROR,
+                            (int)sUIDCounter, AvrcpConstants_ext.BTRC_ITEM_PLAYER, 1, new int[] {0},
+                            new byte[] {AvrcpConstants_ext.PLAYER_TYPE_AUDIO}, new int[] {1},
                             new byte[] {PLAYSTATUS_STOPPED}, featureBitsArray,
                             new String[] {"Dummy Player"});
                     return;
@@ -4124,7 +4118,7 @@
         updateNewIds(addrId, browseId);
 
         MediaController newController = null;
-        MediaPlayerInfo info = getAddressedPlayerInfo();
+        MediaPlayerInfo_ext info = getAddressedPlayerInfo();
         if (info != null) newController = info.getMediaController();
 
         if (DEBUG)
@@ -4148,36 +4142,36 @@
     }
 
     /* Handle getfolderitems for scope = VFS, Search, NowPlayingList */
-    private void handleGetFolderItemBrowseResponse(AvrcpCmd.FolderItemsCmd folderObj, byte[] bdaddr) {
-        int status = AvrcpConstants.RSP_NO_ERROR;
+    private void handleGetFolderItemBrowseResponse(AvrcpCmd_ext.FolderItemsCmd folderObj, byte[] bdaddr) {
+        int status = AvrcpConstants_ext.RSP_NO_ERROR;
         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
 
         /* Browsed player is already set */
-        if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
+        if (folderObj.mScope == AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
             if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
                 Log.e(TAG, "handleGetFolderItemBrowse: Cmd from browse inactive device, reject it");
-                getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, (short) 0,
+                getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (short) 0,
                         (byte) 0x00, 0, null, null, null, null, null, null, null, null);
                 return;
             }
             if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) == null) {
                 Log.e(TAG, "handleGetFolderItemBrowseResponse: no browsed player set for "
                                 + Utils.getAddressStringFromByte(bdaddr));
-                getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, (short) 0,
+                getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (short) 0,
                         (byte) 0x00, 0, null, null, null, null, null, null, null, null);
                 return;
             }
             mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getFolderItemsVFS(folderObj);
             return;
         }
-        if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+        if (folderObj.mScope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
             mAddressedMediaPlayer.getFolderItemsNowPlaying(bdaddr, folderObj, mMediaController);
             return;
         }
 
         /* invalid scope */
         Log.e(TAG, "handleGetFolderItemBrowseResponse: unknown scope " + folderObj.mScope);
-        getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0x00, 0,
+        getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_SCOPE, (short) 0, (byte) 0x00, 0,
                 null, null, null, null, null, null, null, null);
     }
 
@@ -4208,24 +4202,24 @@
         HeadsetService mService = HeadsetService.getHeadsetService();
         if ((mService != null) && mService.isScoOrCallActive()) {
             Log.w(TAG, "Remote requesting play item while call is active");
-            playItemRspNative(bdaddr, AvrcpConstants.RSP_MEDIA_IN_USE);
+            playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_MEDIA_IN_USE);
             return;
         }
 
         if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
             Log.w(TAG, "play item Cmd from browse inactive device, reject it");
-            playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+            playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
             return;
         }
 
-        if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+        if (scope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
             mAddressedMediaPlayer.playItem(bdaddr, uid, mMediaController);
         }
         else {
             if(!isAddrPlayerSameAsBrowsed(bdaddr)) {
                 Log.w(TAG, "Remote requesting play item on uid which may not be recognized by" +
                         "current addressed player");
-                playItemRspNative(bdaddr, AvrcpConstants.RSP_INV_ITEM);
+                playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM);
                 return;
             }
 
@@ -4234,12 +4228,12 @@
             } else {
                 Log.e(TAG, "handlePlayItemResponse: Remote requested playitem " +
                         "before setbrowsedplayer");
-                playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+                playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
             }
         }
     }
 
-    private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
+    private void handleGetItemAttr(AvrcpCmd_ext.ItemAttrCmd itemAttr) {
         if (itemAttr.mUidCounter != sUIDCounter) {
             itemAttr.mUidCounter = sUIDCounter;
             Log.e(TAG, "handleGetItemAttr: invalid uid counter, assign new value = " + itemAttr.mUidCounter);
@@ -4249,13 +4243,13 @@
         if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
             Log.e(TAG, "Item attributes from browse inactive device, reject it");
             getItemAttrRspNative(
-                    itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null);
+                    itemAttr.mAddress, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0, null, null);
             return;
         }
-        if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+        if (itemAttr.mScope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
             if (mCurrAddrPlayerID == NO_PLAYER_ID) {
                 getItemAttrRspNative(
-                        itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, null, null);
+                        itemAttr.mAddress, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, (byte) 0, null, null);
                 return;
             }
             mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController);
@@ -4267,20 +4261,20 @@
         } else {
             Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null");
             getItemAttrRspNative(
-                    itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null);
+                    itemAttr.mAddress, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0, null, null);
         }
     }
 
     private void handleGetTotalNumOfItemsResponse(byte[] bdaddr, byte scope) {
         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
         if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
-            getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
+            getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
             Log.w(TAG, "GetTotalNumOfItems: Cmd from browse inactive device reject it");
             return;
         }
 
         // for scope as media player list
-        if (scope == AvrcpConstants.BTRC_SCOPE_PLAYER_LIST) {
+        if (scope == AvrcpConstants_ext.BTRC_SCOPE_PLAYER_LIST) {
             int numPlayers = 0;
             synchronized(this) {
                 synchronized (mMediaPlayerInfoList) {
@@ -4288,11 +4282,11 @@
                 }
             }
             if (DEBUG) Log.d(TAG, "handleGetTotalNumOfItemsResponse: " + numPlayers + " players.");
-            getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, numPlayers);
-        } else if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+            getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, 0, numPlayers);
+        } else if (scope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
             if (mMediaController == null) {
                 Log.e(TAG, "Could not get Total NumOfItems. mMediaController is null");
-                getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, 0, 0);
+                getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, 0, 0);
                 return;
             }
             mAddressedMediaPlayer.getTotalNumOfItems(bdaddr, mMediaController);
@@ -4302,7 +4296,7 @@
                 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getTotalNumOfItems(scope);
             } else {
                 Log.e(TAG, "Could not get Total NumOfItems. mBrowsedMediaPlayer is null");
-                getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
+                getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
             }
         }
 
@@ -4317,7 +4311,7 @@
             return false;
         }
 
-        MediaPlayerInfo info = getAddressedPlayerInfo();
+        MediaPlayerInfo_ext info = getAddressedPlayerInfo();
         String packageName = (info == null) ? "<none>" : info.getPackageName();
         if (info == null || !packageName.equals(browsedPlayer)) {
             if (DEBUG) Log.d(TAG, browsedPlayer + " is not addressed player " + packageName);
@@ -4350,12 +4344,12 @@
         Log.i(TAG,"cleanupDeviceFeaturesIndex index:" + index);
         deviceFeatures[index].mCurrentDevice = null;
         deviceFeatures[index].mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();;
-        deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        deviceFeatures[index].mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        deviceFeatures[index].mPlayerStatusChangeNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        deviceFeatures[index].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mPlayerStatusChangeNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         deviceFeatures[index].mPlaybackIntervalMs = 0L;
-        deviceFeatures[index].mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         deviceFeatures[index].mFeatures = 0;
         deviceFeatures[index].mAbsoluteVolume = -1;
         deviceFeatures[index].mLastRspPlayStatus = -1;
@@ -4364,13 +4358,13 @@
         deviceFeatures[index].mVolCmdSetInProgress = false;
         deviceFeatures[index].mVolCmdAdjustInProgress = false;
         deviceFeatures[index].mAbsVolRetryTimes = 0;
-        deviceFeatures[index].mAvailablePlayersChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mAvailablePlayersChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         deviceFeatures[index].isActiveDevice = false;
-        deviceFeatures[index].mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
-        deviceFeatures[index].mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+        deviceFeatures[index].mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         deviceFeatures[index].mLastPassthroughcmd = KeyEvent.KEYCODE_UNKNOWN;
         deviceFeatures[index].isAbsoluteVolumeSupportingDevice = false;
-        deviceFeatures[index].keyPressState = AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+        deviceFeatures[index].keyPressState = AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
         deviceFeatures[index].mReportedPlayerID = NO_PLAYER_ID;
         deviceFeatures[index].isPlayStatusTimeOut = false;
         deviceFeatures[index].mInitialRemoteVolume = -1;
@@ -4440,7 +4434,7 @@
         ProfileService.println(sb, "Media Players:");
         synchronized (this) {
             synchronized (mMediaPlayerInfoList) {
-                for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+                for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
                     int key = entry.getKey();
                     ProfileService.println(sb, ((mCurrAddrPlayerID == key) ? " *#" : "  #")
                                     + entry.getKey() + ": " + entry.getValue());
@@ -4482,11 +4476,11 @@
     }
 
     public class AvrcpBrowseManager {
-        Map<String, BrowsedMediaPlayer> connList = new HashMap<String, BrowsedMediaPlayer>();
-        private AvrcpMediaRspInterface mMediaInterface;
+        Map<String, BrowsedMediaPlayer_ext> connList = new HashMap<String, BrowsedMediaPlayer_ext>();
+        private AvrcpMediaRspInterface_ext mMediaInterface;
         private Context mContext;
 
-        public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface mediaInterface) {
+        public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface_ext mediaInterface) {
             mContext = context;
             mMediaInterface = mediaInterface;
         }
@@ -4495,7 +4489,7 @@
             Iterator entries = connList.entrySet().iterator();
             while (entries.hasNext()) {
                 Map.Entry entry = (Map.Entry) entries.next();
-                BrowsedMediaPlayer browsedMediaPlayer = (BrowsedMediaPlayer) entry.getValue();
+                BrowsedMediaPlayer_ext browsedMediaPlayer = (BrowsedMediaPlayer_ext) entry.getValue();
                 if (browsedMediaPlayer != null) {
                     browsedMediaPlayer.cleanup();
                 }
@@ -4507,13 +4501,13 @@
         // get the a free media player interface based on the passed bd address
         // if the no items is found for the passed media player then it assignes a
         // available media player interface
-        public BrowsedMediaPlayer getBrowsedMediaPlayer(byte[] bdaddr) {
-            BrowsedMediaPlayer mediaPlayer;
+        public BrowsedMediaPlayer_ext getBrowsedMediaPlayer(byte[] bdaddr) {
+            BrowsedMediaPlayer_ext mediaPlayer;
             String bdaddrStr = new String(bdaddr);
             if (connList.containsKey(bdaddrStr)) {
                 mediaPlayer = connList.get(bdaddrStr);
             } else {
-                mediaPlayer = new BrowsedMediaPlayer(bdaddr, mContext, mMediaInterface);
+                mediaPlayer = new BrowsedMediaPlayer_ext(bdaddr, mContext, mMediaInterface);
                 connList.put(bdaddrStr, mediaPlayer);
             }
             return mediaPlayer;
@@ -4529,7 +4523,7 @@
             return false;
         }
 
-        public Map<String, BrowsedMediaPlayer> getConnList() {
+        public Map<String, BrowsedMediaPlayer_ext> getConnList() {
             return connList;
         }
 
@@ -4567,14 +4561,14 @@
         }
 
         public void SendSetPlayerAppRsp(int attr_status, byte[] address) {
-            if (attr_status != AvrcpConstants.RSP_INTERNAL_ERR) {
+            if (attr_status != AvrcpConstants_ext.RSP_INTERNAL_ERR) {
                 for (int i = 0; i < maxAvrcpConnections; i++) {
                     if (deviceFeatures[i].mCurrentDevice != null &&
                         deviceFeatures[i].mPlayerStatusChangeNT ==
-                            AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+                            AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                         Log.v(TAG,"device has registered for mPlayerAppSettingStatusChangeNT");
                         deviceFeatures[i].mPlayerStatusChangeNT =
-                                AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                                AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                         mAvrcpPlayerAppSettings.sendPlayerAppChangedRsp(
                                 deviceFeatures[i].mPlayerStatusChangeNT,
                                 deviceFeatures[i].mCurrentDevice);
@@ -4629,7 +4623,7 @@
      * private class which handles responses from AvrcpMediaManager. Maps responses to native
      * responses. This class implements the AvrcpMediaRspInterface interface.
      */
-    private class AvrcpMediaRsp implements AvrcpMediaRspInterface {
+    private class AvrcpMediaRsp implements AvrcpMediaRspInterface_ext {
         private static final String TAG = "AvrcpMediaRsp";
 
         public void setAddrPlayerRsp(byte[] address, int rspStatus) {
@@ -4651,8 +4645,8 @@
             }
         }
 
-        public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp rspObj) {
-            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+        public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp_ext rspObj) {
+            if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
                 if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter, rspObj.mItemType,
                             rspObj.mNumItems, rspObj.mPlayerIds, rspObj.mPlayerTypes,
                             rspObj.mPlayerSubTypes, rspObj.mPlayStatusValues,
@@ -4666,24 +4660,24 @@
             }
         }
 
-        public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp rspObj) {
-            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+        public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp_ext rspObj) {
+            if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
                 String Album = new String("Albums");
                 String Artist = new String("Artists");
                 String Playlist = new String("Playlists");
                 for (int index = 0; index < rspObj.mDisplayNames.length; index++) {
                     if (rspObj.mDisplayNames[index].equals(Album))
-                        rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_ALBUMS;
+                        rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_ALBUMS;
                     else if (rspObj.mDisplayNames[index].equals(Artist))
-                        rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_ARTISTS;
+                        rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_ARTISTS;
                     else if (rspObj.mDisplayNames[index].equals(Playlist))
-                        rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_PLAYLISTS;
+                        rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_PLAYLISTS;
                     /*by default for every folder filling folder type Titles*/
                     else
                         if (changePathFolderType > 0)
                             rspObj.mFolderTypes[index] = changePathFolderType;
                         else
-                            rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_TITLES;
+                            rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_TITLES;
                 }
                 Log.v(TAG, " changePathDepth " + changePathDepth +
                         " changePathFolderType " + changePathFolderType);
@@ -4707,7 +4701,7 @@
 
         public void changePathRsp(byte[] address, int rspStatus, int numItems) {
             /*to handle changePath invalid uid scenario or any error sceanrio */
-            if (rspStatus != AvrcpConstants.RSP_NO_ERROR && changePathDepth>0) {
+            if (rspStatus != AvrcpConstants_ext.RSP_NO_ERROR && changePathDepth>0) {
                 if(changePathDirection == 1)
                     changePathDepth--;
                 else
@@ -4717,8 +4711,8 @@
                 Log.e(TAG, "changePathRspNative failed!");
         }
 
-        public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp rspObj) {
-            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+        public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp_ext rspObj) {
+            if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
                 if (!getItemAttrRspNative(address, rspStatus, rspObj.mNumAttr,
                         rspObj.mAttributesIds, rspObj.mAttributesArray))
                     Log.e(TAG, "getItemAttrRspNative failed!");
@@ -4739,7 +4733,7 @@
                 return;
             }
 
-            if((rspStatus == AvrcpConstants.RSP_NO_ERROR) && ((mA2dpService != null) &&
+            if((rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) && ((mA2dpService != null) &&
                     !Objects.equals(mA2dpService.getActiveDevice(), device))) {
                 Log.d(TAG, "Trigger Handoff by playItem");
                 startSHO(device, true);
@@ -4786,7 +4780,7 @@
             }
 
             if ((index != INVALID_DEVICE_INDEX) &&
-                    (deviceFeatures[index].mUidsChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
+                    (deviceFeatures[index].mUidsChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM)) {
                 if (DEBUG) Log.d(TAG, "uidsChangedRsp: Not registered or requesting.");
                 return;
             }
@@ -4799,7 +4793,7 @@
                 Log.e(TAG, "registerNotificationRspUIDsChangedNative failed!");
             }
             if (index != INVALID_DEVICE_INDEX)
-                deviceFeatures[index].mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                deviceFeatures[index].mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         }
 
         public void nowPlayingChangedRsp(int type) {
@@ -4819,7 +4813,7 @@
                 Log.e(TAG,"uidsChangedRsp:No active device found");
                 return;
             }
-            if (mNowPlayingListChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+            if (mNowPlayingListChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                 if (DEBUG) Log.d(TAG, "NowPlayingListChanged: Not registered or requesting.");
                 return;
             }
@@ -4827,7 +4821,7 @@
             if (!registerNotificationRspNowPlayingChangedNative(type, addr)) {
                 Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!");
             }
-            mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+            mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
         }
 
         public void trackChangedRsp(int type, byte[] uid) {
@@ -5004,12 +4998,6 @@
 
         //to keep volume copy for setting volume
         deviceFeatures[deviceIndex].mLocalVolume = getVolume(device);
-        if(deviceFeatures[deviceIndex].mLocalVolume == -1) {
-            //volume is not stored so need to copy stream music
-            //to send setAbsolute request
-            deviceFeatures[deviceIndex].mLocalVolume =
-                mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
-        }
         if ((maxAvrcpConnections > 1) && (deviceFeatures[deviceIndex].mCurrentDevice != null) &&
                 (deviceFeatures[deviceIndex].mReportedPlayerID != mCurrAddrPlayerID)) {
             Log.d(TAG,"Update cached browsing events to latest active device, deviceFeatures[" +
@@ -5017,21 +5005,21 @@
                     deviceFeatures[deviceIndex].mReportedPlayerID +
                     ", mCurrAddrPlayerID: " + mCurrAddrPlayerID);
             if (deviceFeatures[deviceIndex].mAvailablePlayersChangedNT ==
-                    AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+                    AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                 registerNotificationRspAvalPlayerChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                 mAvailablePlayerViewChanged = false;
                 deviceFeatures[deviceIndex].mAvailablePlayersChangedNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
             }
             if (deviceFeatures[deviceIndex].mAddrPlayerChangedNT ==
-                    AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+                    AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
                 registerNotificationRspAddrPlayerChangedNative(
-                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
                         sUIDCounter, getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
                 deviceFeatures[deviceIndex].mAddrPlayerChangedNT =
-                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                        AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                 // send track change event becasue some carkits will refresh metadata
                 // while receive addressed player change event. Track change event to
                 // make remote get metadata correctly.
@@ -5083,16 +5071,22 @@
             Log.e(TAG,"Invalid device index for setAbsVolumeFlag");
             return;
         }
-        /* Delay updating absVolume support notification to audiomanager
-         * as absVolume flag in audio audioservice is reset when device disconnect is
-         * notified to audiomanager in SHO is processed late. Resulting in not sending
-         * setABsVolume cmd to remote
-         */
+        //updating abs volume supported or not to audio when active device change is success
+        mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
+            isAbsoluteVolumeSupported(deviceIndex));
+        if(deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice == false) {
+            Log.d(TAG,"isAbsoluteVolumeSupportingDevice is false or volume is not stored");
+            return;
+        }
+        if(deviceFeatures[deviceIndex].mInitialRemoteVolume == -1 || getVolume(device) == -1) {
+            Log.e(TAG,"intial volume is not updated or volume is not stored");
+            return;
+        }
         Message msg = mHandler.obtainMessage();
         msg.what = MESSAGE_UPDATE_ABS_VOLUME_STATUS;
         msg.arg1 = deviceIndex;
         msg.arg2 = deviceFeatures[deviceIndex].mLocalVolume;
-        mHandler.sendMessageDelayed(msg, 100);
+        mHandler.sendMessage(msg);
         Log.d(TAG,"setAbsVolumeFlag = " + isAbsoluteVolumeSupported(deviceIndex));
         return;
     }
@@ -5129,7 +5123,7 @@
             Log.d(TAG, "Active device: " + mA2dpService.getActiveDevice());
 
         int action = KeyEvent.ACTION_DOWN;
-        if (state == AvrcpConstants.KEY_STATE_RELEASE) action = KeyEvent.ACTION_UP;
+        if (state == AvrcpConstants_ext.KEY_STATE_RELEASE) action = KeyEvent.ACTION_UP;
         BluetoothDevice a2dp_active_device = null;
         boolean skip = false;;
         if (mA2dpService != null) a2dp_active_device = mA2dpService.getActiveDevice();
@@ -5224,7 +5218,7 @@
         } else {
             if (code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
                 if ((state == deviceFeatures[deviceIndex].keyPressState) &&
-                        (state == AvrcpConstants.KEY_STATE_RELEASE)) {
+                        (state == AvrcpConstants_ext.KEY_STATE_RELEASE)) {
                     Log.e(TAG, "Ignore fast forward key release event");
                     return;
                 }
@@ -5236,7 +5230,7 @@
                 deviceFeatures[deviceIndex].keyPressState = state;
             } else if (code == KeyEvent.KEYCODE_MEDIA_REWIND) {
                 if ((state == deviceFeatures[deviceIndex].keyPressState) &&
-                        (state == AvrcpConstants.KEY_STATE_RELEASE)) {
+                        (state == AvrcpConstants_ext.KEY_STATE_RELEASE)) {
                     Log.e(TAG, "Ignore rewind key release event");
                     return;
                 }
@@ -5256,7 +5250,7 @@
          * changed response at the time of Release of Fast-Forward/Rewind Button */
         if ((code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || code == KeyEvent.KEYCODE_MEDIA_REWIND)
                 && (deviceFeatures[deviceIndex].mPlayStatusChangedNT ==
-                AvrcpConstants.NOTIFICATION_TYPE_INTERIM) && (action == KeyEvent.ACTION_UP)) {
+                AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) && (action == KeyEvent.ACTION_UP)) {
             int currentPlayState =
                     convertPlayStateToPlayStatus(deviceFeatures[deviceIndex].mCurrentPlayState);
             Log.d(TAG, " currentPlayState: " + currentPlayState + " mLastRspPlayStatus: " +
@@ -5264,7 +5258,7 @@
             if (deviceFeatures[deviceIndex].mCurrentDevice != null &&
                     deviceFeatures[deviceIndex].mLastRspPlayStatus != currentPlayState) {
                 deviceFeatures[deviceIndex].mPlayStatusChangedNT =
-                                    AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+                                    AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
                 registerNotificationRspPlayStatusNative(deviceFeatures[deviceIndex].mPlayStatusChangedNT
                        ,currentPlayState,
                         getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
diff --git a/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java b/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java
new file mode 100644
index 0000000..8f1801d
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java
@@ -0,0 +1,1075 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+
+/*************************************************************************************************
+ * Provides functionality required for Browsed Media Player like browsing Virtual File System, get
+ * Item Attributes, play item from the file system, etc.
+ * Acts as an Interface to communicate with Media Browsing APIs for browsing FileSystem.
+ ************************************************************************************************/
+
+class BrowsedMediaPlayer_ext {
+    private static final boolean DEBUG = true;
+    private static final String TAG = "BrowsedMediaPlayer_ext";
+
+    /* connection state with MediaBrowseService */
+    private static final int DISCONNECTED = 0;
+    private static final int CONNECTED = 1;
+    private static final int SUSPENDED = 2;
+
+    private static final int BROWSED_ITEM_ID_INDEX = 2;
+    private static final int BROWSED_FOLDER_ID_INDEX = 4;
+    private static final String[] ROOT_FOLDER = {"root"};
+    private static boolean mPlayerRoot = false;
+    /*  package and service name of target Media Player which is set for browsing */
+    private String mPackageName;
+    private String mConnectingPackageName;
+    private String mClassName;
+    private Context mContext;
+    private AvrcpMediaRspInterface_ext mMediaInterface;
+    private byte[] mBDAddr;
+
+    private String mCurrentBrowsePackage;
+    private String mCurrentBrowseClass;
+
+    /* Object used to connect to MediaBrowseService of Media Player */
+    private MediaBrowser mMediaBrowser = null;
+    private MediaController mMediaController = null;
+
+    /* The mediaId to be used for subscribing for children using the MediaBrowser */
+    private String mMediaId = null;
+    private String mRootFolderUid = null;
+    private int mConnState = DISCONNECTED;
+
+    /* stores the path trail during changePath */
+    private Stack<String> mPathStack = null;
+    private Stack<String> mLocalPathCache = null;
+    /* Number of items in current folder */
+    private int mCurrFolderNumItems = 0;
+
+    /* store mapping between uid(Avrcp) and mediaId(Media Player) for Media Item */
+    private HashMap<Integer, String> mMediaHmap = new HashMap<Integer, String>();
+
+    /* store mapping between uid(Avrcp) and mediaId(Media Player) for Folder Item */
+    private HashMap<Integer, String> mFolderHmap = new HashMap<Integer, String>();
+
+    /* command objects from avrcp handler */
+    private AvrcpCmd_ext.FolderItemsCmd mFolderItemsReqObj;
+
+    /* store result of getfolderitems with scope="vfs" */
+    private List<MediaBrowser.MediaItem> mFolderItems = null;
+
+    /* Connection state callback handler */
+    class MediaConnectionCallback extends MediaBrowser.ConnectionCallback {
+        private String mCallbackPackageName;
+        private MediaBrowser mBrowser;
+
+        MediaConnectionCallback(String packageName) {
+            this.mCallbackPackageName = packageName;
+        }
+
+        public void setBrowser(MediaBrowser b) {
+            mBrowser = b;
+        }
+
+        @Override
+        public void onConnected() {
+            mConnState = CONNECTED;
+            if (DEBUG) {
+                Log.d(TAG, "mediaBrowser CONNECTED to " + mPackageName);
+            }
+            /* perform init tasks and set player as browsed player on successful connection */
+            onBrowseConnect(mCallbackPackageName, mBrowser);
+
+            // Remove what could be a circular dependency causing GC to never happen on this object
+            mBrowser = null;
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            mConnState = DISCONNECTED;
+            // Remove what could be a circular dependency causing GC to never happen on this object
+            mBrowser = null;
+            Log.e(TAG, "mediaBrowser Connection failed with " + mPackageName
+                    + ", Sending fail response!");
+            mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+                    (byte) 0x00, 0, null);
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            mBrowser = null;
+            mConnState = SUSPENDED;
+            Log.e(TAG, "mediaBrowser SUSPENDED connection with " + mPackageName);
+        }
+    }
+
+    /* Subscription callback handler. Subscribe to a folder to get its contents */
+    private MediaBrowser.SubscriptionCallback mFolderItemsCb =
+            new MediaBrowser.SubscriptionCallback() {
+
+                @Override
+                public void onChildrenLoaded(String parentId,
+                        List<MediaBrowser.MediaItem> children) {
+                    if (DEBUG) {
+                        Log.d(TAG, "OnChildren Loaded folder items: childrens= " + children.size());
+                    }
+
+            /*
+             * cache current folder items and send as rsp when remote requests
+             * get_folder_items (scope = vfs)
+             */
+                    if (mFolderItems == null) {
+                        if (DEBUG) {
+                            Log.d(TAG, "sending setbrowsed player rsp");
+                        }
+                        Log.w(TAG, "sending setbrowsed player rsp");
+                        mFolderItems = children;
+                        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR,
+                                (byte) 0x00, children.size(), ROOT_FOLDER);
+                    } else {
+                        mFolderItems = children;
+                        mCurrFolderNumItems = mFolderItems.size();
+                        mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR,
+                                mCurrFolderNumItems);
+                    }
+                    refreshFolderItems(mFolderItems);
+                    mMediaBrowser.unsubscribe(parentId);
+                }
+
+                /* UID is invalid */
+                @Override
+                public void onError(String id) {
+                    Log.e(TAG, "set browsed player rsp. Could not get root folder items");
+                    mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+                            (byte) 0x00, 0, null);
+                }
+            };
+
+    /* callback from media player in response to getitemAttr request */
+    private class ItemAttribSubscriber extends MediaBrowser.SubscriptionCallback {
+        private String mMediaId;
+        private AvrcpCmd_ext.ItemAttrCmd mAttrReq;
+
+        ItemAttribSubscriber(@NonNull AvrcpCmd_ext.ItemAttrCmd attrReq, @NonNull String mediaId) {
+            mAttrReq = attrReq;
+            mMediaId = mediaId;
+        }
+
+        @Override
+        public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+            String logprefix = "ItemAttribSubscriber(" + mMediaId + "): ";
+            if (DEBUG) {
+                Log.d(TAG, logprefix + "OnChildren Loaded");
+            }
+            int status = AvrcpConstants_ext.RSP_INV_ITEM;
+
+            if (children == null) {
+                Log.w(TAG, logprefix + "children list is null parentId: " + parentId);
+            } else {
+                /* find the item in the folder */
+                for (MediaBrowser.MediaItem item : children) {
+                    if (item.getMediaId().equals(mMediaId)) {
+                        if (DEBUG) {
+                            Log.d(TAG, logprefix + "found item");
+                        }
+                        getItemAttrFilterAttr(item);
+                        status = AvrcpConstants_ext.RSP_NO_ERROR;
+                        break;
+                    }
+                }
+            }
+            /* Send only error from here, in case of success, getItemAttrFilterAttr sends */
+            if (status != AvrcpConstants_ext.RSP_NO_ERROR) {
+                Log.e(TAG, logprefix + "not able to find item from " + parentId);
+                mMediaInterface.getItemAttrRsp(mBDAddr, status, null);
+            }
+            mMediaBrowser.unsubscribe(parentId);
+        }
+
+        @Override
+        public void onError(String id) {
+            Log.e(TAG, "Could not get attributes from media player id: " + id);
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+        }
+
+        /* helper method to filter required attibuteand send GetItemAttr response */
+        private void getItemAttrFilterAttr(@NonNull MediaBrowser.MediaItem mediaItem) {
+            /* Response parameters */
+            int[] attrIds = null; /* array of attr ids */
+            String[] attrValues = null; /* array of attr values */
+
+            /* variables to temperorily add attrs */
+            ArrayList<Integer> attrIdArray = new ArrayList<Integer>();
+            ArrayList<String> attrValueArray = new ArrayList<String>();
+            ArrayList<Integer> attrReqIds = new ArrayList<Integer>();
+
+            if (mAttrReq.mNumAttr == AvrcpConstants_ext.NUM_ATTR_NONE) {
+                // Note(jamuraa): the stack should never send this, remove?
+                Log.i(TAG, "getItemAttrFilterAttr: No attributes requested");
+                mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_BAD_PARAM, null);
+                return;
+            }
+
+            /* check if remote device has requested all attributes */
+            if (mAttrReq.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL
+                    || mAttrReq.mNumAttr == AvrcpConstants_ext.MAX_NUM_ATTR) {
+                for (int idx = 1; idx <= AvrcpConstants_ext.MAX_NUM_ATTR; idx++) {
+                    attrReqIds.add(idx); /* attr id 0x00 is unused */
+                }
+            } else {
+                /* get only the requested attribute ids from the request */
+                for (int idx = 0; idx < mAttrReq.mNumAttr; idx++) {
+                    attrReqIds.add(mAttrReq.mAttrIDs[idx]);
+                }
+            }
+
+            /* lookup and copy values of attributes for ids requested above */
+            for (int attrId : attrReqIds) {
+                /* check if media player provided requested attributes */
+                String value = getAttrValue(mBDAddr, attrId, mediaItem);
+                if (value != null) {
+                    attrIdArray.add(attrId);
+                    attrValueArray.add(value);
+                }
+            }
+
+            /* copy filtered attr ids and attr values to response parameters */
+            attrIds = new int[attrIdArray.size()];
+            for (int i = 0; i < attrIdArray.size(); i++) {
+                attrIds[i] = attrIdArray.get(i);
+            }
+
+            attrValues = attrValueArray.toArray(new String[attrIdArray.size()]);
+
+            /* create rsp object and send response */
+            ItemAttrRsp_ext rspObj = new ItemAttrRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, attrIds, attrValues);
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+        }
+    }
+
+    /* Constructor */
+    BrowsedMediaPlayer_ext(byte[] address, Context context,
+            AvrcpMediaRspInterface_ext mAvrcpMediaRspInterface) {
+        mContext = context;
+        mMediaInterface = mAvrcpMediaRspInterface;
+        mBDAddr = address;
+    }
+
+    /* initialize mediacontroller in order to communicate with media player. */
+    private void onBrowseConnect(String connectedPackage, MediaBrowser browser) {
+        if (!connectedPackage.equals(mConnectingPackageName)) {
+            Log.w(TAG, "onBrowseConnect: recieved callback for package" + mConnectingPackageName +
+                    "we aren't connecting to " + connectedPackage);
+            mMediaInterface.setBrowsedPlayerRsp(
+                    mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
+            return;
+        }
+        mConnectingPackageName = null;
+
+        if (browser == null) {
+            Log.e(TAG, "onBrowseConnect: received a null browser for " + connectedPackage);
+            mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+                    (byte) 0x00, 0, null);
+            return;
+        }
+
+        MediaSession.Token token = null;
+        try {
+            if (!browser.isConnected()) {
+                Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
+            } else if ((token = browser.getSessionToken()) == null) {
+                Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "no Session token");
+            } else {
+                /* update to the new MediaBrowser */
+                if (mMediaBrowser != null) {
+                    mMediaBrowser.disconnect();
+                }
+                mMediaBrowser = browser;
+                mPackageName = connectedPackage;
+
+                /* get rootfolder uid from media player */
+                if (mMediaId == null) {
+                    mMediaId = mMediaBrowser.getRoot();
+                    Log.d(TAG, "media browser root = " + mMediaId);
+
+                    if (mMediaId == null || mMediaId.length() == 0) {
+                        Log.e(TAG, "onBrowseConnect: root value is empty or null");
+                        mMediaInterface.setBrowsedPlayerRsp(
+                                mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
+                        return;
+                    }
+
+                    /*
+                     * assuming that root folder uid will not change on uids changed
+                     */
+                    mRootFolderUid = mMediaId;
+                    /* store root folder uid to stack */
+                    mPathStack.push(mMediaId);
+                    String [] ExternalPath = mMediaId.split("/");
+                    if (ExternalPath != null) {
+                        Log.d(TAG,"external path length: " + ExternalPath.length);
+                        if (ExternalPath.length == 1) {
+                            mLocalPathCache.push(mMediaId);
+                        } else if (ExternalPath.length == 0 && mMediaId.equals("/")) {
+                            mPlayerRoot = true;
+                            mLocalPathCache.push(mMediaId);
+                        } else {
+                            //to trim the root in GMP which comes as "com.google.android.music.generic/root"
+                            mLocalPathCache.push(ExternalPath[ExternalPath.length - 1]);
+                        }
+                    }
+                    /* get root folder items */
+                    Log.e(TAG, "onBrowseConnect: subscribe event for FolderCb");
+                    mMediaBrowser.subscribe(mRootFolderUid, mFolderItemsCb);
+                }
+
+                mMediaController = MediaControllerFactory.make(mContext, token);
+                return;
+            }
+        } catch (NullPointerException ex) {
+            Log.e(TAG, "setBrowsedPlayer : Null pointer during init");
+            ex.printStackTrace();
+        }
+
+        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00,
+                0, null);
+    }
+
+    public void setBrowsed(String packageName, String cls) {
+        Log.w(TAG, "!! In setBrowse function !!" + mFolderItems);
+        if ((mPackageName != null && packageName != null
+                && !mPackageName.equals(packageName)) || (mFolderItems == null)) {
+            Log.d(TAG, "setBrowse for packageName = " + packageName);
+            mConnectingPackageName = packageName;
+            mPackageName = packageName;
+            mClassName = cls;
+
+           /* cleanup variables from previous browsed calls */
+           mFolderItems = null;
+           mMediaId = null;
+           mRootFolderUid = null;
+           mPlayerRoot = false;
+           /*
+            * create stack to store the navigation trail (current folder ID). This
+            * will be required while navigating up the folder
+            */
+           mPathStack = new Stack<String>();
+           mLocalPathCache = new Stack<String>();
+           /* Bind to MediaBrowseService of MediaPlayer */
+           MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
+           MediaBrowser tempBrowser = new MediaBrowser(
+                   mContext, new ComponentName(packageName, mClassName), callback, null);
+           callback.setBrowser(tempBrowser);
+           tempBrowser.connect();
+        } else if (mFolderItems != null) {
+            mPackageName = packageName;
+            mClassName = cls;
+            int rsp_status = AvrcpConstants_ext.RSP_NO_ERROR;
+            int folder_depth = (mPathStack.size() > 0) ? (mPathStack.size() - 1) : 0;
+            if (!mPathStack.empty()) {
+                Log.d(TAG, "~~current Path = " + mPathStack.peek());
+                if (mPathStack.size() > 1) {
+                    String top = mPathStack.peek();
+                    mPathStack.pop();
+                    String path = mPathStack.peek();
+                    mPathStack.push(top);
+                    String [] ExternalPath = path.split("/");
+                    if (!mPlayerRoot && ExternalPath != null && ExternalPath.length > 1) {
+                        Log.d(TAG,"external path length: " + ExternalPath.length);
+                        String [] folderPath = new String[ExternalPath.length - 1];
+                        for (int i = 0; i < (ExternalPath.length - 1); i++) {
+                             folderPath[i] = ExternalPath[i + 1];
+                             Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+                        }
+                        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+                            (byte)folder_depth, mFolderItems.size(), folderPath);
+                    } else if (!mPlayerRoot && mLocalPathCache.size() > 1 && ExternalPath.length == 1) {
+                        String [] folderPath = new String[mLocalPathCache.size() - 1];
+                        folderPath = mLocalPathCache.toArray(folderPath);
+                        for (int i = 0; i < mLocalPathCache.size() - 1; i++) {
+                             Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+                        }
+                        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+                            (byte)folder_depth, mFolderItems.size(), folderPath);
+                    } else if (mPlayerRoot && mLocalPathCache.size() > 1) {
+                        String [] folderPath = new String[mLocalPathCache.size() - 1];
+                        folderPath = mLocalPathCache.toArray(folderPath);
+                        for (int i = 0; i < mLocalPathCache.size() - 1; i++) {
+                             Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+                        }
+                        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+                            (byte)folder_depth, mFolderItems.size(), folderPath);
+                    } else {
+                        Log.e(TAG, "sending internal error !!!");
+                        rsp_status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
+                        mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)0x00, 0, null);
+                    }
+                } else if (mPathStack.size() == 1) {
+                    Log.d(TAG, "On root send SetBrowse response with root properties");
+                    mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)folder_depth,
+                            mFolderItems.size(), ROOT_FOLDER);
+                }
+            } else {
+                Log.e(TAG, "Path Stack empty sending internal error !!!");
+                rsp_status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
+                mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)0x00, 0, null);
+            }
+            Log.d(TAG, "send setbrowse rsp status=" + rsp_status + " folder_depth=" + folder_depth);
+        }
+    }
+
+    public void TryReconnectBrowse(String packageName, String cls) {
+        Log.w(TAG, "Try reconnection with Browser service for package = " + packageName);
+        mConnectingPackageName = packageName;
+        mPackageName = packageName;
+        mClassName = cls;
+
+        /* cleanup variables from previous browsed calls */
+        mFolderItems = null;
+        mMediaId = null;
+        mRootFolderUid = null;
+        mPlayerRoot = false;
+
+        if (mPathStack != null)
+            mPathStack = null;
+        mPathStack = new Stack<String>();
+
+        if (mLocalPathCache != null)
+            mLocalPathCache = null;
+        mLocalPathCache = new Stack<String>();
+
+        MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
+        MediaBrowser tempBrowser = new MediaBrowser(
+                mContext, new ComponentName(packageName, cls), callback, null);
+        callback.setBrowser(tempBrowser);
+        tempBrowser.connect();
+        Log.w(TAG, "Reconnected with Browser service");
+    }
+
+    public void setCurrentPackage(String packageName, String cls) {
+        Log.w(TAG, "Set current Browse based on Addr Player as " + packageName);
+        mCurrentBrowsePackage = packageName;
+        mCurrentBrowseClass = cls;
+    }
+
+    /* called when connection to media player is closed */
+    public void cleanup() {
+        if (DEBUG) {
+            Log.d(TAG, "cleanup");
+        }
+
+        if (mConnState != DISCONNECTED) {
+            if (mMediaBrowser != null) mMediaBrowser.disconnect();
+        }
+
+        mMediaHmap = null;
+        mFolderHmap = null;
+        mMediaController = null;
+        mMediaBrowser = null;
+        mPathStack = null;
+        mLocalPathCache = null;
+        mPlayerRoot = false;
+    }
+
+    public boolean isPlayerConnected() {
+        if (mMediaBrowser == null) {
+            if (DEBUG) {
+                Log.d(TAG, "isPlayerConnected: mMediaBrowser = null!");
+            }
+            return false;
+        }
+
+        return mMediaBrowser.isConnected();
+    }
+
+    /* returns number of items in new path as reponse */
+    public void changePath(byte[] folderUid, byte direction) {
+        if (DEBUG) {
+            Log.d(TAG, "changePath.direction = " + direction);
+        }
+        String newPath = "";
+
+        if (!isPlayerConnected()) {
+            Log.w(TAG, "changePath: disconnected from player service, sending internal error");
+            mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0);
+            return;
+        }
+
+        if (mMediaBrowser == null) {
+            Log.e(TAG, "Media browser is null, sending internal error");
+            mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0);
+            return;
+        }
+
+        /* check direction and change the path */
+        if (direction == AvrcpConstants_ext.DIR_DOWN) { /* move down */
+            if ((newPath = byteToStringFolder(folderUid)) == null) {
+                Log.e(TAG, "Could not get media item from folder Uid, sending err response");
+                mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM, 0);
+            } else if (!isBrowsableFolderDn(newPath)) {
+                /* new path is not browsable */
+                Log.e(TAG, "ItemUid received from changePath cmd is not browsable");
+                mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRECTORY, 0);
+            } else if (mPathStack.peek().equals(newPath)) {
+                /* new_folder is same as current folder */
+                Log.e(TAG, "new_folder is same as current folder, Invalid direction!");
+                mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+            } else {
+                mMediaBrowser.subscribe(newPath, mFolderItemsCb);
+                String [] ExternalPath = newPath.split("/");
+                if (ExternalPath != null) {
+                    Log.d(TAG,"external path length: " + ExternalPath.length);
+                    if (ExternalPath.length == 1) {
+                        //when external path length is 1 then extract the folder name from index 4
+                        String folder_name = parseQueueId(newPath, BROWSED_FOLDER_ID_INDEX);
+                        Log.d(TAG,"folder path: " + folder_name);
+                        if (folder_name != null) {
+                            mLocalPathCache.push(folder_name);
+                        } else {
+                            mLocalPathCache.push(newPath);
+                        }
+                    } else {
+                        String folderPath = ExternalPath[ExternalPath.length - 1];
+                        if (folderPath != null) {
+                            Log.d(TAG,"folder path: " + folderPath);
+                            mLocalPathCache.push(folderPath);
+                        }
+                    }
+                }
+                /* assume that call is success and update stack with new folder path */
+                mPathStack.push(newPath);
+            }
+        } else if (direction == AvrcpConstants_ext.DIR_UP) { /* move up */
+            if (!isBrowsableFolderUp()) {
+                /* Already on the root, cannot allow up: PTS: test case TC_TG_MCN_CB_BI_02_C
+                 * This is required, otherwise some CT will keep on sending change path up
+                 * until they receive error */
+                Log.w(TAG, "Cannot go up from now, already in the root, Invalid direction!");
+                mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+            } else {
+                /* move folder up */
+                mPathStack.pop();
+                mLocalPathCache.pop();
+                newPath = mPathStack.peek();
+                mMediaBrowser.subscribe(newPath, mFolderItemsCb);
+            }
+        } else { /* invalid direction */
+            Log.w(TAG, "changePath : Invalid direction " + direction);
+            mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+        }
+    }
+
+    public void getItemAttr(AvrcpCmd_ext.ItemAttrCmd itemAttr) {
+        String mediaID;
+        if (DEBUG) {
+            Log.d(TAG, "getItemAttr");
+        }
+
+        /* check if uid is valid by doing a lookup in hashmap */
+        mediaID = byteToStringMedia(itemAttr.mUid);
+        if (mediaID == null) {
+            Log.e(TAG, "uid is invalid");
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM, null);
+            return;
+        }
+
+        /* check scope */
+        if (itemAttr.mScope != AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+            Log.e(TAG, "invalid scope");
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE, null);
+            return;
+        }
+
+        if (mMediaBrowser == null) {
+            Log.e(TAG, "mMediaBrowser is null");
+            mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+            return;
+        }
+
+        /* Subscribe to the parent to list items and retrieve the right one */
+        mMediaBrowser.subscribe(mPathStack.peek(), new ItemAttribSubscriber(itemAttr, mediaID));
+    }
+
+    public void getTotalNumOfItems(byte scope) {
+        if (DEBUG) {
+            Log.d(TAG, "getTotalNumOfItems scope = " + scope);
+        }
+        if (scope != AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+            Log.e(TAG, "getTotalNumOfItems error" + scope);
+            mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE, 0, 0);
+            return;
+        }
+
+        if (mFolderItems == null) {
+            Log.e(TAG, "mFolderItems is null, sending internal error");
+            /* folderitems were not fetched during change path */
+            mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
+            return;
+        }
+
+        /* find num items using size of already cached folder items */
+        mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR, 0,
+                mFolderItems.size());
+    }
+
+    public void getFolderItemsVFS(AvrcpCmd_ext.FolderItemsCmd reqObj) {
+        if (DEBUG) {
+            Log.d(TAG, "getFolderItemsVFS");
+        }
+        mFolderItemsReqObj = reqObj;
+
+        if ((mCurrentBrowsePackage != null) && (!mCurrentBrowsePackage.equals(mPackageName))) {
+            Log.w(TAG, "Try reconnection with Browser service as addressed pkg is changed = "
+                    + mCurrentBrowsePackage + "from " + mPackageName);
+            TryReconnectBrowse(mCurrentBrowsePackage, mCurrentBrowseClass);
+        }
+
+        if (mFolderItems == null) {
+            /* Failed to fetch folder items from media player. Send error to remote device */
+            Log.e(TAG, "Failed to fetch folder items during getFolderItemsVFS");
+            mMediaInterface.folderItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+            return;
+        }
+
+        /* Filter attributes based on the request and send response to remote device */
+        getFolderItemsFilterAttr(mBDAddr, reqObj, mFolderItems,
+                AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM, mFolderItemsReqObj.mStartItem,
+                mFolderItemsReqObj.mEndItem);
+    }
+
+    /* Instructs media player to play particular media item */
+    public void playItem(byte[] uid, byte scope) {
+        String folderUid;
+
+        if (isPlayerConnected()) {
+            /* check if uid is valid */
+            if ((folderUid = byteToStringMedia(uid)) == null) {
+                Log.e(TAG, "uid is invalid!");
+                mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM);
+                return;
+            }
+
+            if (mMediaController != null) {
+                MediaController.TransportControls mediaControllerCntrl =
+                        mMediaController.getTransportControls();
+                if (DEBUG) {
+                    Log.d(TAG, "Sending playID: " + folderUid);
+                }
+
+                if (scope == AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+                    mediaControllerCntrl.playFromMediaId(folderUid, null);
+                    mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR);
+                } else {
+                    Log.e(TAG, "playItem received for invalid scope!");
+                    mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE);
+                }
+            } else {
+                Log.e(TAG, "mediaController is null");
+                mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+            }
+        } else {
+            Log.e(TAG, "playItem: Not connected to media player");
+            mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+        }
+    }
+
+    /*
+     * helper method to check if startItem and endItem index is with range of
+     * MediaItem list. (Resultset containing all items in current path)
+     */
+    private List<MediaBrowser.MediaItem> checkIndexOutofBounds(byte[] bdaddr,
+            List<MediaBrowser.MediaItem> children, long startItem, long endItem) {
+        if (endItem >= children.size()) {
+            endItem = children.size() - 1;
+        }
+        if (startItem >= Integer.MAX_VALUE) {
+            startItem = Integer.MAX_VALUE;
+        }
+        try {
+            List<MediaBrowser.MediaItem> childrenSubList =
+                    children.subList((int) startItem, (int) endItem + 1);
+            if (childrenSubList.isEmpty()) {
+                Log.i(TAG, "childrenSubList is empty.");
+                throw new IndexOutOfBoundsException();
+            }
+            return childrenSubList;
+        } catch (IndexOutOfBoundsException ex) {
+            Log.w(TAG, "Index out of bounds start item =" + startItem + " end item = " + Math.min(
+                    children.size(), endItem + 1));
+            return null;
+        } catch (IllegalArgumentException ex) {
+            Log.i(TAG, "Index out of bounds start item =" + startItem + " > size");
+            return null;
+        }
+    }
+
+
+    /*
+     * helper method to filter required attibutes before sending GetFolderItems response
+     */
+    public void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd mFolderItemsReqObj,
+            List<MediaBrowser.MediaItem> children, byte scope, long startItem, long endItem) {
+        if (DEBUG) {
+            Log.d(TAG,
+                    "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem);
+        }
+
+        List<MediaBrowser.MediaItem> resultItems = new ArrayList<MediaBrowser.MediaItem>();
+
+        if (children == null) {
+            Log.e(TAG, "Error: children are null in getFolderItemsFilterAttr");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+            return;
+        }
+
+        /* check for index out of bound errors */
+        resultItems = checkIndexOutofBounds(bdaddr, children, startItem, endItem);
+        if (resultItems == null) {
+            Log.w(TAG, "resultItems is null.");
+            mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+            return;
+        }
+        FolderItemsData_ext folderDataNative = new FolderItemsData_ext(resultItems.size());
+
+        /* variables to temperorily add attrs */
+        ArrayList<String> attrArray = new ArrayList<String>();
+        ArrayList<Integer> attrId = new ArrayList<Integer>();
+
+        for (int itemIndex = 0; itemIndex < resultItems.size(); itemIndex++) {
+            /* item type. Needs to be set by media player */
+            MediaBrowser.MediaItem item = resultItems.get(itemIndex);
+            int flags = item.getFlags();
+            if ((flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
+                folderDataNative.mItemTypes[itemIndex] = AvrcpConstants_ext.BTRC_ITEM_FOLDER;
+            } else {
+                folderDataNative.mItemTypes[itemIndex] = AvrcpConstants_ext.BTRC_ITEM_MEDIA;
+            }
+
+            /* set playable */
+            if ((flags & MediaBrowser.MediaItem.FLAG_PLAYABLE) != 0) {
+                folderDataNative.mPlayable[itemIndex] = AvrcpConstants_ext.ITEM_PLAYABLE;
+            } else {
+                folderDataNative.mPlayable[itemIndex] = AvrcpConstants_ext.ITEM_NOT_PLAYABLE;
+            }
+            /* set uid for current item */
+            byte[] uid;
+            if (folderDataNative.mItemTypes[itemIndex] == AvrcpConstants_ext.BTRC_ITEM_MEDIA)
+                uid = stringToByteMedia(item.getDescription().getMediaId(), BROWSED_ITEM_ID_INDEX);
+            else
+                uid = stringToByteFolder(item.getDescription().getMediaId());
+
+            for (int idx = 0; idx < AvrcpConstants_ext.UID_SIZE; idx++) {
+                folderDataNative.mItemUid[itemIndex * AvrcpConstants_ext.UID_SIZE + idx] = uid[idx];
+            }
+
+            /* Set display name for current item */
+            folderDataNative.mDisplayNames[itemIndex] =
+                    getAttrValue(bdaddr, AvrcpConstants_ext.ATTRID_TITLE, item);
+
+            int maxAttributesRequested = 0;
+            boolean isAllAttribRequested = false;
+            /* check if remote requested for attributes */
+            if (mFolderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+                int attrCnt = 0;
+
+                /* add requested attr ids to a temp array */
+                if (mFolderItemsReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+                    isAllAttribRequested = true;
+                    maxAttributesRequested = AvrcpConstants_ext.MAX_NUM_ATTR;
+                } else {
+                    /* get only the requested attribute ids from the request */
+                    maxAttributesRequested = mFolderItemsReqObj.mNumAttr;
+                }
+
+                /* lookup and copy values of attributes for ids requested above */
+                for (int idx = 0; idx < maxAttributesRequested; idx++) {
+                    /* check if media player provided requested attributes */
+                    String value = null;
+
+                    int attribId =
+                            isAllAttribRequested ? (idx + 1) : mFolderItemsReqObj.mAttrIDs[idx];
+                    value = getAttrValue(bdaddr, attribId, resultItems.get(itemIndex));
+                    if (value != null) {
+                        attrArray.add(value);
+                        attrId.add(attribId);
+                        attrCnt++;
+                    }
+                }
+                /* add num attr actually received from media player for a particular item */
+                folderDataNative.mAttributesNum[itemIndex] = attrCnt;
+            }
+        }
+
+        /* copy filtered attr ids and attr values to response parameters */
+        if (attrId.size() > 0) {
+            folderDataNative.mAttrIds = new int[attrId.size()];
+            for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+                folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
+            }
+            folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
+        }
+
+        /* create rsp object and send response to remote device */
+        FolderItemsRsp_ext rspObj =
+                new FolderItemsRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, Avrcp_ext.sUIDCounter, scope,
+                        folderDataNative.mNumItems, folderDataNative.mFolderTypes,
+                        folderDataNative.mPlayable, folderDataNative.mItemTypes,
+                        folderDataNative.mItemUid, folderDataNative.mDisplayNames,
+                        folderDataNative.mAttributesNum, folderDataNative.mAttrIds,
+                        folderDataNative.mAttrValues);
+        mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+    }
+
+    public String getAttrValue(byte []bdaddr, int attr, MediaBrowser.MediaItem item) {
+        String attrValue = null;
+        try {
+            MediaDescription desc = item.getDescription();
+            Bundle extras = desc.getExtras();
+            switch (attr) {
+                /* Title is mandatory attribute */
+                case AvrcpConstants_ext.ATTRID_TITLE:
+                    attrValue = desc.getTitle().toString();
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_ARTIST:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_ALBUM:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_TRACK_NUM:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_NUM_TRACKS:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_NUM_TRACKS);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_GENRE:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_PLAY_TIME:
+                    attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
+                    break;
+
+                case AvrcpConstants_ext.ATTRID_COVER_ART:
+                    attrValue = Avrcp_ext.getImgHandleFromTitle(bdaddr,
+                            desc.getTitle().toString());
+                    break;
+
+                default:
+                    Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+                    return null;
+            }
+        } catch (NullPointerException ex) {
+            Log.w(TAG, "getAttrValue: attr id not found in result");
+            /* checking if attribute is title, then it is mandatory and cannot send null */
+            if (attr == AvrcpConstants_ext.ATTRID_TITLE) {
+                attrValue = "<Unknown Title>";
+            } else {
+                return null;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + " attr id: " + attr);
+        }
+        return attrValue;
+    }
+
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /* Helper methods */
+
+    /* check if item is browsable Down*/
+    private boolean isBrowsableFolderDn(String uid) {
+        for (MediaBrowser.MediaItem item : mFolderItems) {
+            if (item.getMediaId().equals(uid) && (
+                    (item.getFlags() & MediaBrowser.MediaItem.FLAG_BROWSABLE)
+                            == MediaBrowser.MediaItem.FLAG_BROWSABLE)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* check if browsable Up*/
+    private boolean isBrowsableFolderUp() {
+        if (mPathStack.peek().equals(mRootFolderUid)) {
+            /* Already on the root, cannot go up */
+            return false;
+        }
+        return true;
+    }
+
+    private String parseQueueId(String mediaId, int mId) {
+        if (isNumeric(mediaId)) {
+            Log.d(TAG, "Get queue id: " + mediaId);
+            return mediaId.trim();
+        } else {
+            String[] mediaIdItems = mediaId.split(",");
+            if (mediaIdItems != null && mediaIdItems.length > mId) {
+                Log.d(TAG, "Get queue id: " + mediaIdItems[mId]);
+                return mediaIdItems[mId].trim();
+            }
+        }
+
+        Log.d(TAG, "Unkown queue id");
+        return null;
+    }
+
+    /* convert uid to mediaId for Media item*/
+    private String byteToStringMedia(byte[] byteArray) {
+        int uid = new BigInteger(byteArray).intValue();
+        String mediaId = mMediaHmap.get(uid);
+        return mediaId;
+    }
+
+    /* convert uid to mediaId for Folder item*/
+    private String byteToStringFolder(byte[] byteArray) {
+        int uid = new BigInteger(byteArray).intValue();
+        String mediaId = mFolderHmap.get(uid);
+        return mediaId;
+    }
+
+    /* convert mediaId to uid for Media item*/
+    private byte[] stringToByteMedia(String mediaId, int id) {
+        /* check if this mediaId already exists in hashmap */
+        if (!mMediaHmap.containsValue(mediaId)) { /* add to hashmap */
+            int uid;
+            String queueId = parseQueueId(mediaId, id);
+            if (queueId == null) {
+                uid = mMediaHmap.size() + 1;
+            } else {
+                uid = Integer.valueOf(queueId).intValue();
+            }
+
+            mMediaHmap.put(uid, mediaId);
+            return intToByteArray(uid);
+        } else { /* search key for give mediaId */
+            for (int uid : mMediaHmap.keySet()) {
+                if (mMediaHmap.get(uid).equals(mediaId)) {
+                    return intToByteArray(uid);
+                }
+            }
+        }
+        return null;
+    }
+
+    /* convert mediaId to uid for Folder item*/
+    private byte[] stringToByteFolder(String mediaId) {
+        /* check if this mediaId already exists in hashmap */
+        if (!mFolderHmap.containsValue(mediaId)) { /* add to hashmap */
+            // Offset by one as uid 0 is reserved
+            int uid = mFolderHmap.size() + 1;
+            mFolderHmap.put(uid, mediaId);
+            return intToByteArray(uid);
+        } else { /* search key for give mediaId */
+            for (int uid : mFolderHmap.keySet()) {
+                if (mFolderHmap.get(uid).equals(mediaId)) {
+                    return intToByteArray(uid);
+                }
+            }
+        }
+        return null;
+    }
+
+    private void refreshFolderItems(List<MediaBrowser.MediaItem> folderItems) {
+        for (int itemIndex = 0; itemIndex < folderItems.size(); itemIndex++) {
+            MediaBrowser.MediaItem item = folderItems.get(itemIndex);
+            int flags = item.getFlags();
+            if ((flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
+                Log.d(TAG, "Folder item, no need refresh hashmap from mediaId to uid");
+            } else {
+                Log.d(TAG, "Media item, refresh haspmap from mediaId to uid");
+                stringToByteMedia(item.getDescription().getMediaId(), BROWSED_ITEM_ID_INDEX);
+            }
+        }
+    }
+
+    /* converts queue item received from getQueue call, to MediaItem used by FilterAttr method */
+    private List<MediaBrowser.MediaItem> queueItem2MediaItem(
+            List<MediaSession.QueueItem> tempItems) {
+
+        List<MediaBrowser.MediaItem> tempMedia = new ArrayList<MediaBrowser.MediaItem>();
+        for (int itemCount = 0; itemCount < tempItems.size(); itemCount++) {
+            MediaDescription.Builder build = new MediaDescription.Builder();
+            build.setMediaId(Long.toString(tempItems.get(itemCount).getQueueId()));
+            build.setTitle(tempItems.get(itemCount).getDescription().getTitle());
+            build.setExtras(tempItems.get(itemCount).getDescription().getExtras());
+            MediaDescription des = build.build();
+            MediaItem item = new MediaItem((des), MediaItem.FLAG_PLAYABLE);
+            tempMedia.add(item);
+        }
+        return tempMedia;
+    }
+
+    /* convert integer to byte array of size 8 bytes */
+    public byte[] intToByteArray(int value) {
+        int index = 0;
+        byte[] encodedValue = new byte[AvrcpConstants_ext.UID_SIZE];
+
+        encodedValue[index++] = (byte) 0x00;
+        encodedValue[index++] = (byte) 0x00;
+        encodedValue[index++] = (byte) 0x00;
+        encodedValue[index++] = (byte) 0x00;
+        encodedValue[index++] = (byte) (value >> 24);
+        encodedValue[index++] = (byte) (value >> 16);
+        encodedValue[index++] = (byte) (value >> 8);
+        encodedValue[index++] = (byte) value;
+
+        return encodedValue;
+    }
+
+    public boolean isNumeric(String mediaId) {
+        String trimStr = mediaId.trim();
+        int length = trimStr.length();
+
+        for(int i = 0; i < length; i++) {
+            char c = trimStr.charAt(i);
+            if (!((c >= '0' && c <= '9'))) {
+                Log.v(TAG, "Non-Numeric media Id");
+                return false;
+            }
+        }
+        Log.v(TAG, "Numeric media Id");
+        return true;
+    }
+}
diff --git a/packages_apps_bluetooth_ext/src/ba/BATService.java b/packages_apps_bluetooth_ext/src/ba/BATService.java
index 1cd459f..9d20265 100644
--- a/packages_apps_bluetooth_ext/src/ba/BATService.java
+++ b/packages_apps_bluetooth_ext/src/ba/BATService.java
@@ -88,7 +88,7 @@
     public static final int MESSAGE_BAT_STREAMING_ID_EVT = 104;
 
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
-    public static final String mBAAddress = "FA:CE:FA:CE:FA:CE";
+    public static final String mBAAddress = "CE:FA:CE:FA:CE:FA";
     public static BluetoothDevice mBADevice;
     // we will listen for vol change intent from audio manager.
     // this intent is called in all following 3 cases
@@ -452,13 +452,8 @@
         // check for transition from PENDING to PAUSED
         if((mPrevStackBATState == BA_STACK_STATE_PENDING) &&
                 (mCurrStackBATState == BA_STACK_STATE_PAUSED)) {
-            if (a2dpActiveDevice != null) {
-                Log.d(TAG," updating AudioManager: Disconnect for A2dp ");
-                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                    a2dpActiveDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP,
-                    true, -1);
-            }
-            mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+            Log.d(TAG," updating AudioManager: Connected for BA ");
+            mAudioManager.handleBluetoothA2dpActiveDeviceChange(
                 mBADevice, BluetoothProfile.STATE_CONNECTED,BluetoothProfile.A2DP, true, -1);
             //BA audio works on the principal of absVol
             //Currently mm-audio tracks value of last updated absVol support,
@@ -475,18 +470,14 @@
                 // inform BA device as disconnected, don't send noisy intent
                 // as a2dp has to be updated as well. Switching should happen to
                 // A2DP in this case.
-                Log.d(TAG," updating AudioManager: DisConnected for BA ");
-                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
-                        mBADevice, BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.A2DP,
-                        true, -1);
                 Log.d(TAG," updating AudioManager: Connected for A2DP ");
-                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
                     a2dpActiveDevice, BluetoothProfile.STATE_CONNECTED,BluetoothProfile.A2DP,
                     true, -1);
             } else {// a2dp active device is null.
                 // inform BA device as disconnected. we have to send noisy intent
                 // because BA seems to be last device.
-                mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
                         mBADevice, BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.A2DP,
                         false, -1);
             }
diff --git a/packages_apps_bluetooth_ext/src/btservice/Vendor.java b/packages_apps_bluetooth_ext/src/btservice/Vendor.java
index 54f8979..caa6647 100644
--- a/packages_apps_bluetooth_ext/src/btservice/Vendor.java
+++ b/packages_apps_bluetooth_ext/src/btservice/Vendor.java
@@ -234,7 +234,7 @@
     }
 
     public String getA2apOffloadCapability() {
-        return socName;
+        return a2dpOffloadCap;
     }
 
     public boolean isSplitA2dpEnabled() {
diff --git a/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
new file mode 100644
index 0000000..e34999a
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.hfp;
+
+import android.util.Log;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import com.android.bluetooth.Utils;
+import android.content.Intent;
+import android.content.Context;
+
+final class vendorhfservice {
+    private static final String TAG = "BluetoothVendorHfService";
+    private HeadsetService mService;
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+    static {
+        classInitNative();
+    }
+
+    public vendorhfservice(HeadsetService service) {
+        mService = service;
+    }
+
+    public void init(){
+        initNative();
+    }
+
+    public void cleanup() {
+        cleanupNative();
+    }
+
+    public int enableSwb(boolean enable) {
+        int ret = enableSwbNative(enable);
+        return ret;
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        BluetoothDevice local = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
+        return local;
+    }
+
+    private void onSWB(int codec, byte[] address) {
+        /*HeadsetStackEvent event =
+                new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SWB, codec, getDevice(address));
+        sendMessageToService(event);*/
+    }
+
+    private void sendMessageToService(HeadsetStackEvent event) {
+        HeadsetService service = HeadsetService.getHeadsetService();
+        if (service != null) {
+            service.messageFromNative(event);
+        } else {
+            Log.d(TAG,"FATAL: Stack sent event while service is not available: " + event);
+        }
+    }
+
+    private native void initNative();
+    private native static void classInitNative();
+    private native void cleanupNative();
+    private native int enableSwbNative(boolean enable);
+}
diff --git a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
index 40da4eb..655fbf1 100644
--- a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
+++ b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
@@ -120,11 +120,9 @@
     private final BluetoothMapAccountItem mAccount;
     /* The MasInstance reference is used to update persistent (over a connection) version counters*/
     private final BluetoothMapMasInstance mMasInstance;
-    private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR;
     private final boolean EMAIL_ATTACHMENT_IMPLEMENTED = false;
 
     private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK;
-    private int mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10;
 
     private class FilterInfo {
             public static final int TYPE_SMS    = 0;
diff --git a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
index 005dc42..a99f7a0 100644
--- a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
+++ b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
@@ -111,7 +111,7 @@
     private static final long EVENT_FILTER_CONVERSATION_CHANGED         = 1L<<10;
     private static final long EVENT_FILTER_PARTICIPANT_PRESENCE_CHANGED = 1L<<11;
     private static final long EVENT_FILTER_PARTICIPANT_CHATSTATE_CHANGED= 1L<<12;
-    private static final long EVENT_FILTER_MESSAGE_REMOVED              = 1L<<13;
+    private static final long EVENT_FILTER_MESSAGE_REMOVED              = 1L<<14;
 
     // TODO: If we are requesting a large message from the network, on a slow connection
     //       20 seconds might not be enough... But then again 20 seconds is long for other
@@ -141,14 +141,6 @@
 
     private boolean mTransmitEvents = true;
 
-    /* To make the filter update atomic, we declare it volatile.
-     * To avoid a penalty when using it, copy the value to a local
-     * non-volatile variable when used more than once.
-     * Actually we only ever use the lower 4 bytes of this variable,
-     * hence we could manage without the volatile keyword, but as
-     * we tend to copy ways of doing things, we better do it right:-) */
-    private volatile long mEventFilter = 0xFFFFFFFFL;
-
     public static final int DELETED_THREAD_ID = -1;
 
     // X-Mms-Message-Type field types. These are from PduHeaders.java
@@ -254,8 +246,8 @@
     }
 
     public int getObserverRemoteFeatureMask() {
-        if (V) Log.v(TAG, "getObserverRemoteFeatureMask Email: " + mMapEventReportVersion
-            + " mMapSupportedFeatures Email: " + mMapSupportedFeatures);
+        if (V) Log.v(TAG, "getObserverRemoteFeatureMask " + mMapEventReportVersion
+            + " mMapSupportedFeatures :" + mMapSupportedFeatures);
         return mMapSupportedFeatures;
     }
 
@@ -398,7 +390,7 @@
 
         /* Enable use of the cache for checking the filter */
         long eventFilter = mEventFilter;
-
+        if (V) Log.v(TAG," eventFilter " + eventFilter);
         /* This should have been a switch on the string, but it is not allowed in Java 1.6 */
         /* WARNING: Here we do pointer compare for the string to speed up things, that is.
          * HENCE: always use the EVENT_TYPE_"defines" */
@@ -550,7 +542,7 @@
                             msgList.put(id, msg);
                             Event evt;
                             /* Incoming message from the network */
-                            if (mMapEventReportVersion == BluetoothMapUtils.MAP_EVENT_REPORT_V11) {
+                            if (mMapEventReportVersion >= BluetoothMapUtils.MAP_EVENT_REPORT_V11) {
                                 String date = BluetoothMapUtils.getDateTimeString(c.getLong(c
                                     .getColumnIndex(BluetoothMapEmailContract
                                     .ExtEmailMessageColumns.TIMESTAMP)));
@@ -691,7 +683,12 @@
                 if (!msg.transparent ) {
                     if(msg.localInitiatedShift == false ) {
                         // "old_folder" used only for MessageShift event
-                        Event evt = new Event(EVENT_TYPE_DELETE, msg.id, oldFolder,
+                        String eventType = EVENT_TYPE_DELETE;
+                        if (mMapEventReportVersion >= BluetoothMapUtils.MAP_EVENT_REPORT_V12) {
+                            eventType = EVENT_TYPE_REMOVED;
+                            if (V) Log.v(TAG," send EVENT_TYPE_REMOVED");
+                        }
+                        Event evt = new Event(eventType, msg.id, oldFolder,
                             null, mAccount.getType());
                         sendEvent(evt);
                     } else {
diff --git a/system_bt_ext/bta/Android.bp b/system_bt_ext/bta/Android.bp
index 5d79a17..b87a3ac 100644
--- a/system_bt_ext/bta/Android.bp
+++ b/system_bt_ext/bta/Android.bp
@@ -28,6 +28,7 @@
         "ba/bta_ba.cc",
         "tws_plus/ag/bta_ag_twsp_dev.cc",
         "tws_plus/ag/bta_ag_twsp_sco.cc",
+        "swb/bta_ag_swb.cc"
     ],
     shared_libs: [
         "libcutils",
@@ -39,4 +40,7 @@
         "libbt-utils",
     ],
     cflags: ["-DBUILDCFG"],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/system_bt_ext/bta/include/bta_ag_swb.h b/system_bt_ext/bta/include/bta_ag_swb.h
new file mode 100644
index 0000000..a65f3c3
--- /dev/null
+++ b/system_bt_ext/bta/include/bta_ag_swb.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BTA_AG_SWB_H_
+#define _BTA_AG_SWB_H_
+
+#include "bta_ag_int.h"
+#include "device/include/esco_parameters.h"
+
+#define BTA_AG_SCO_SWB_SETTINGS_Q0_MASK 4
+#define BTA_AG_SCO_SWB_SETTINGS_Q1_MASK 8
+#define BTA_AG_SCO_SWB_SETTINGS_Q2_MASK 16
+#define BTA_AG_SCO_SWB_SETTINGS_Q3_MASK 32
+
+/* Events originated from HF side */
+#define  BTA_AG_AT_QAC_EVT 253
+#define  BTA_AG_AT_QCS_EVT 254
+#define  BTA_AG_SWB_EVT 100 /* SWB SCO codec info */
+#define  BTA_AG_LOCAL_RES_QAC 0x108
+#define  BTA_AG_LOCAL_RES_QCS 0x109
+
+#define LEGACY_CODECS 2
+#define SWB_ESCO_NUM_CODECS 1
+
+void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg, tBTA_AG_VAL val);
+void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data);
+void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data);
+tBTA_AG_PEER_CODEC bta_ag_parse_qac(tBTA_AG_SCB* p_scb, char* p_s);
+
+const enh_esco_params_t default_esco_swb_parameters[SWB_ESCO_NUM_CODECS] = {
+     {.transmit_bandwidth = TXRX_64KBITS_RATE,
+     .receive_bandwidth = TXRX_64KBITS_RATE,
+     .transmit_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS,
+                                .company_id = 0x000A,
+                                .vendor_specific_codec_id = 0x0000},
+     .receive_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS,
+                               .company_id = 0x000A,
+                               .vendor_specific_codec_id = 0x0000},
+     .transmit_codec_frame_size = 60,
+     .receive_codec_frame_size = 60,
+     .input_bandwidth = INPUT_OUTPUT_128K_RATE,
+     .output_bandwidth = INPUT_OUTPUT_128K_RATE,
+     .input_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR,
+                             .company_id = 0x0000,
+                             .vendor_specific_codec_id = 0x0000},
+     .output_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR,
+                              .company_id = 0x0000,
+                              .vendor_specific_codec_id = 0x0000},
+     .input_coded_data_size = 16,
+     .output_coded_data_size = 16,
+     .input_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP,
+     .output_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP,
+     .input_pcm_payload_msb_position = 0,
+     .output_pcm_payload_msb_position = 0,
+     .input_data_path = ESCO_DATA_PATH_PCM,
+     .output_data_path = ESCO_DATA_PATH_PCM,
+     .input_transport_unit_size = 0x00,
+     .output_transport_unit_size = 0x00,
+     .max_latency_ms = 14,
+     .packet_types = 0x0380,
+     .retransmission_effort = ESCO_RETRANSMISSION_QUALITY}};
+
+#endif//_BTA_AG_SWB_H_
diff --git a/system_bt_ext/bta/include/bta_ag_twsp_dev.h b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
index 592038c..c1e2b23 100644
--- a/system_bt_ext/bta/include/bta_ag_twsp_dev.h
+++ b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
@@ -64,10 +64,11 @@
 #define TWSPLUS_QDSP_ECHO_CANCELLATION 1
 
 enum {
-  TWSPLUS_EB_STATE_OFF,
+  TWSPLUS_EB_STATE_UNKNOWN,
   TWSPLUS_EB_STATE_INCASE,
   TWSPLUS_EB_STATE_OUT_OF_EAR,
   TWSPLUS_EB_STATE_INEAR,
+  TWSPLUS_EB_STATE_OFF
 };
 
 enum {
@@ -100,6 +101,7 @@
 bool twsp_get_right_eb_addr(RawAddress& eb_addr);
 bool twsp_get_left_eb_addr(RawAddress& eb_addr);
 uint8_t get_twsp_role(tBTA_AG_SCB *p_scb);
+uint8_t get_twsp_state(tBTA_AG_SCB *p_scb);
 tBTA_AG_SCB* twsp_get_best_mic_scb ();
 int twsp_get_idx_by_scb(tBTA_AG_SCB* p_scb);
 
diff --git a/system_bt_ext/bta/swb/bta_ag_swb.cc b/system_bt_ext/bta/swb/bta_ag_swb.cc
new file mode 100644
index 0000000..496f110
--- /dev/null
+++ b/system_bt_ext/bta/swb/bta_ag_swb.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include "bta_ag_swb.h"
+#include "internal_include/bt_trace.h"
+#include "bta_ag_int.h"
+#include "utl.h"
+#include "device/include/interop.h"
+#include <hardware/vendor_hf.h>
+
+#if (SWB_ENABLED == TRUE)
+
+#define SWB_CODECS_SUPPORTED "0,4,6,7"
+#define SWB_CODECS_UNSUPPORTD "0xFFFF"
+
+void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg, tBTA_AG_VAL val)
+{
+  APPL_TRACE_DEBUG("%s: p_scb : %x cmd : %d", __func__, p_scb, cmd);
+  switch(cmd) {
+    case BTA_AG_AT_QAC_EVT:
+      if (!get_swb_codec_status()) {
+        bta_ag_send_qac(p_scb, NULL);
+        break;
+      }
+      p_scb->codec_updated = true;
+      if (p_scb->peer_codecs &  BTA_AG_SCO_SWB_SETTINGS_Q0_MASK) {
+        p_scb->sco_codec = BTA_AG_SCO_SWB_SETTINGS_Q0;
+      } else if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
+        p_scb->sco_codec = UUID_CODEC_MSBC;
+      }
+      bta_ag_send_qac(p_scb, NULL);
+      APPL_TRACE_DEBUG("Received AT+QAC, updating sco codec to SWB: %d", p_scb->sco_codec);
+      val.num = p_scb->peer_codecs;
+      break;
+    case BTA_AG_AT_QCS_EVT: {
+      tBTA_AG_PEER_CODEC codec_type, codec_sent;
+      alarm_cancel(p_scb->codec_negotiation_timer);
+
+      switch (int_arg) {
+        case BTA_AG_SCO_SWB_SETTINGS_Q0:
+          codec_type = BTA_AG_SCO_SWB_SETTINGS_Q0;
+          break;
+        case BTA_AG_SCO_SWB_SETTINGS_Q1:
+          codec_type = BTA_AG_SCO_SWB_SETTINGS_Q1;
+          break;
+        case BTA_AG_SCO_SWB_SETTINGS_Q2:
+          codec_type = BTA_AG_SCO_SWB_SETTINGS_Q2;
+          break;
+        case BTA_AG_SCO_SWB_SETTINGS_Q3:
+          codec_type = BTA_AG_SCO_SWB_SETTINGS_Q3;
+          break;
+        default:
+          APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg);
+          p_scb->is_swb_codec = false;
+          codec_type = BTA_AG_CODEC_MSBC;
+          p_scb->codec_fallback = true;
+          p_scb->sco_codec = BTA_AG_CODEC_MSBC;
+          break;
+      }
+
+      if (p_scb->codec_fallback)
+        codec_sent = BTA_AG_CODEC_MSBC;
+      else
+        codec_sent = p_scb->sco_codec;
+
+      if (codec_type == codec_sent)
+        bta_ag_sco_codec_nego(p_scb, true);
+      else
+        bta_ag_sco_codec_nego(p_scb, false);
+
+      /* send final codec info to callback */
+      val.num = codec_sent;
+      break;
+    }
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_ag_send_qcs
+ *
+ * Description      Send +%QCS AT command to peer.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) {
+  uint16_t codec_uuid;
+  if (p_scb->codec_fallback) {
+    if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
+      codec_uuid = UUID_CODEC_MSBC;
+    } else {
+      codec_uuid = UUID_CODEC_CVSD;
+    }
+  } else {
+    codec_uuid = BTA_AG_SCO_SWB_SETTINGS_Q0;
+  }
+
+  /* send +BCS */
+  APPL_TRACE_DEBUG("send +QCS codec is %d", codec_uuid);
+  bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QCS, NULL, codec_uuid);
+}
+
+/*******************************************************************************
+ *
+ * Function         bta_ag_send_qac
+ *
+ * Description      Send +%QAC AT command to peer.
+ *
+ * Returns          void
+ *
+ ******************************************************************************/
+void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) {
+  /* send +BCS */
+  APPL_TRACE_DEBUG("send +QAC codecs suuported");
+  if (!get_swb_codec_status()) {
+    bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QAC, SWB_CODECS_UNSUPPORTD, 0);
+    return;
+  } else{
+    bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QAC, SWB_CODECS_SUPPORTED, 0);
+  }
+  if (p_scb->sco_codec == BTA_AG_SCO_SWB_SETTINGS_Q0) {
+      p_scb->is_swb_codec = true;
+  }
+}
+
+tBTA_AG_PEER_CODEC bta_ag_parse_qac(tBTA_AG_SCB* p_scb, char* p_s) {
+  tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE;
+  uint16_t codec_modes;
+  bool cont = false; /* Continue processing */
+  char* p;
+
+  while (p_s) {
+    /* skip to comma delimiter */
+    for (p = p_s; *p != ',' && *p != 0; p++)
+      ;
+
+    /* get integre value */
+    if (*p != 0) {
+      *p = 0;
+      cont = true;
+    } else
+      cont = false;
+
+    codec_modes = utl_str2int(p_s);
+    switch (codec_modes) {
+      case BTA_AG_SCO_SWB_SETTINGS_Q0:
+        retval |= BTA_AG_SCO_SWB_SETTINGS_Q0_MASK;
+        break;
+      case BTA_AG_SCO_SWB_SETTINGS_Q1:
+        retval |= BTA_AG_SCO_SWB_SETTINGS_Q1_MASK;
+        break;
+      case BTA_AG_SCO_SWB_SETTINGS_Q2:
+        retval |= BTA_AG_SCO_SWB_SETTINGS_Q2_MASK;
+        break;
+      case BTA_AG_SCO_SWB_SETTINGS_Q3:
+        retval |= BTA_AG_SCO_SWB_SETTINGS_Q3_MASK;
+        break;
+      default:
+        APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", codec_modes);
+        break;
+    }
+
+    if (cont)
+      p_s = p + 1;
+    else
+      break;
+  }
+
+  return (retval);
+}
+#endif //#if (SWB_ENABLED == TRUE)
+
diff --git a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
index 125e889..11642e3 100644
--- a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
+++ b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
@@ -63,6 +63,18 @@
    return NULL;
 }
 
+bool twsp_is_odd_eb_addr(tBTA_AG_SCB* p_scb) {
+    bool ret;
+    if ((p_scb->peer_addr.address[5] & 0x01) == 0) {
+        ret = false;
+    } else {
+        ret = true;
+    }
+    APPL_TRACE_DEBUG("%s: eb addr: %s, ret: %d", __func__,
+            p_scb->peer_addr.ToString().c_str(), ret);
+    return ret;
+}
+
 void reset_twsp_device(int  eb_idx) {
     if (eb_idx < PRIMARY_EB_IDX || eb_idx > SECONDARY_EB_IDX) {
         APPL_TRACE_WARNING("%s: Invalid eb_idx: %d\n", __func__, eb_idx);
@@ -84,7 +96,7 @@
 
     twsp_devices[eb_idx].p_scb = NULL;
     twsp_devices[eb_idx].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
-    twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_OFF;
+    twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_UNKNOWN;
     twsp_devices[eb_idx].role =  TWSPLUS_EB_ROLE_INVALID;
     twsp_devices[eb_idx].mic_path_delay = TWSPLUS_INVALID_MICPATH_DELAY;
     twsp_devices[eb_idx].mic_quality = TWSPLUS_MIN_MIC_QUALITY;
@@ -99,14 +111,18 @@
             APPL_TRACE_WARNING("%s: idx: %d, p_scb: %x", __func__, i, p_scb);
             twsp_devices[i].p_scb = p_scb;
             twsp_devices[i].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
-            twsp_devices[i].state = TWSPLUS_EB_STATE_OFF;
+            twsp_devices[i].state = TWSPLUS_EB_STATE_UNKNOWN;
 
             int other_idx = (i == PRIMARY_EB_IDX) ? SECONDARY_EB_IDX : PRIMARY_EB_IDX;
-            if (twsp_devices[other_idx].p_scb != NULL &&
-                    twsp_devices[other_idx].role == TWSPLUS_EB_ROLE_LEFT) {
-                twsp_devices[i].role = TWSPLUS_EB_ROLE_RIGHT;
+            if (twsp_devices[other_idx].p_scb != NULL) {
+                twsp_devices[i].role =
+                    (twsp_devices[other_idx].role == TWSPLUS_EB_ROLE_LEFT) ?
+                    TWSPLUS_EB_ROLE_RIGHT : TWSPLUS_EB_ROLE_LEFT;
+
             } else {
-                twsp_devices[i].role = TWSPLUS_EB_ROLE_LEFT;
+                twsp_devices[i].role =
+                    (twsp_is_odd_eb_addr(p_scb) == true) ?
+                    TWSPLUS_EB_ROLE_LEFT : TWSPLUS_EB_ROLE_RIGHT;
             }
 
             APPL_TRACE_WARNING("%s: idx: %d, role: %d", __func__, i, twsp_devices[i].role);
@@ -387,7 +403,7 @@
         return false;
     }
 
-    if (state < TWSPLUS_EB_STATE_OFF || state > TWSPLUS_EB_STATE_INEAR) {
+    if (state < TWSPLUS_EB_STATE_UNKNOWN || state > TWSPLUS_EB_STATE_INEAR) {
         APPL_TRACE_WARNING("%s: Invalid state: %d\n", __func__, state);
         return false;
     }
@@ -459,6 +475,20 @@
     return role;
 }
 
+uint8_t get_twsp_state(tBTA_AG_SCB *p_scb) {
+    APPL_TRACE_DEBUG("%s: p_scb : %d\n", __func__, p_scb);
+    uint8_t state = TWSPLUS_EB_STATE_UNKNOWN;
+    for (int i=0; i<=SECONDARY_EB_IDX; i++) {
+        if (p_scb == twsp_devices[i].p_scb)
+        {
+           state = twsp_devices[i].state;
+           break;
+        }
+    }
+    APPL_TRACE_DEBUG("%s: returns  : %d", __func__, state);
+    return state;
+}
+
 bool twsp_is_ring_sent(tBTA_AG_SCB *p_scb) {
     int sel_idx = -1;
     bool ret = false;
diff --git a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
index 5f6af9b..21c4023 100644
--- a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
+++ b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
@@ -315,7 +315,8 @@
                 bta_ag_create_sco(p_scb, false);
                 p_sco->state = BTA_AG_SCO_LISTEN_ST;
                 other_scb = get_other_twsp_scb((p_scb->peer_addr));
-                if (other_scb && twsp_sco_active(other_scb) == false) {
+                if (other_scb && twsp_sco_active(other_scb) == false &&
+                     get_twsp_state(other_scb) == TWSPLUS_EB_STATE_INEAR) {
                     //Atleast try bringing up the other EB eSCO
                     APPL_TRACE_WARNING("Calling SCO open for other EB");
                     dispatch_event_primary_peer_device(p_scb, BTA_AG_SCO_OPEN_E);
@@ -702,7 +703,8 @@
    uint8_t *p_param = param;
    std::string leb, reb;
 
-   APPL_TRACE_DEBUG("%s: left_addr: %x, right_addr: %x, selected_mic: %d", __func__, left_eb_addr, rght_eb_addr, selected_mic);
+   APPL_TRACE_DEBUG("%s: left_addr: %s, right_addr: %s, selected_mic: %d", __func__,
+           left_eb_addr.ToString().c_str(), rght_eb_addr.ToString().c_str(), selected_mic);
 
    *p_param++ = VS_QHCI_TWS_ESCO_SETUP_SUBOPCODE;
 
diff --git a/system_bt_ext/btconfigstore/Android.bp b/system_bt_ext/btconfigstore/Android.bp
index 2c82531..c1de2b1 100644
--- a/system_bt_ext/btconfigstore/Android.bp
+++ b/system_bt_ext/btconfigstore/Android.bp
@@ -33,4 +33,7 @@
     required: [
         "bt_configstore.conf",
     ],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/system_bt_ext/btconfigstore/bt_configstore.cc b/system_bt_ext/btconfigstore/bt_configstore.cc
index d76d05e..4f91bbf 100644
--- a/system_bt_ext/btconfigstore/bt_configstore.cc
+++ b/system_bt_ext/btconfigstore/bt_configstore.cc
@@ -401,7 +401,7 @@
         break;
 
       default:
-        LOG_INFO(LOG_TAG, "%s:: prop type not handled %d", __func__, vProp.type);
+        LOG_INFO(LOG_TAG, "%s:: prop type not handled %d", __func__, vPropType);
         config_free(config);
         return status;
     }
diff --git a/system_bt_ext/btif/Android.bp b/system_bt_ext/btif/Android.bp
index 76cfb3a..59cfaf2 100644
--- a/system_bt_ext/btif/Android.bp
+++ b/system_bt_ext/btif/Android.bp
@@ -34,6 +34,7 @@
         "src/btif_ba.cc",
         "src/btif_twsp_hf.cc",
         "src/btif_iot_config.cc",
+        "src/btif_vendor_hf.cc",
     ],
     shared_libs: [
         "libcutils",
@@ -51,5 +52,7 @@
         "-DBUILDCFG",
         "-DHAS_NO_BDROID_BUILDCFG",
     ],
-
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/system_bt_ext/btif/include/btif_bat.h b/system_bt_ext/btif/include/btif_bat.h
index 29166dd..3b8d635 100644
--- a/system_bt_ext/btif/include/btif_bat.h
+++ b/system_bt_ext/btif/include/btif_bat.h
@@ -48,6 +48,17 @@
  ******************************************************************************/
 
 typedef enum {
+    BA_CTRL_ACK_SUCCESS,
+    BA_CTRL_ACK_FAILURE,
+    BA_CTRL_ACK_INCALL_FAILURE, /* Failure when in Call*/
+    BA_CTRL_ACK_UNSUPPORTED,
+    BA_CTRL_ACK_PENDING,
+    BA_CTRL_ACK_DISCONNECT_IN_PROGRESS,
+    BA_CTRL_SKT_DISCONNECTED,
+    BA_CTRL_ACK_UNKNOWN,
+} tBA_CTRL_ACK;
+
+typedef enum {
     BTIF_BA_STATE_IDLE_AUDIO_PENDING = 0,
     BTIF_BA_STATE_IDLE_AUDIO_STREAMING,
     BTIF_BA_STATE_IDLE_AUDIO_NS,
@@ -123,6 +134,14 @@
 void ba_send_message(uint8_t event, uint8_t size, char* ptr, bool is_btif_thread);
 uint16_t btif_get_ba_latency();
 bool btif_ba_is_active();
+uint8_t btif_ba_get_sample_rate();
+uint8_t btif_ba_get_channel_mode();
+uint8_t btif_ba_get_frame_size();
+uint8_t btif_ba_get_complexity();
+uint8_t btif_ba_get_prediction_mode();
+uint8_t btif_ba_get_vbr_flag();
+uint32_t btif_ba_get_bitrate();
+
 /*
 uint8_t get_curr_vol_level();
 uint8_t get_max_vol_level();
diff --git a/system_bt_ext/btif/src/btif_ba.cc b/system_bt_ext/btif/src/btif_ba.cc
index c96fa8b..4ee9219 100644
--- a/system_bt_ext/btif/src/btif_ba.cc
+++ b/system_bt_ext/btif/src/btif_ba.cc
@@ -32,7 +32,7 @@
 #include <string.h>
 
 #define LOG_TAG "bt_btif_vendor"
-
+#include "btif_a2dp.h"
 #include <cutils/properties.h>
 #include "bt_utils.h"
 #include "btif_common.h"
@@ -52,17 +52,6 @@
 #include "btif_av.h"
 #include "btif_config.h"
 
-typedef enum {
-    BA_CTRL_ACK_SUCCESS,
-    BA_CTRL_ACK_FAILURE,
-    BA_CTRL_ACK_INCALL_FAILURE, /* Failure when in Call*/
-    BA_CTRL_ACK_UNSUPPORTED,
-    BA_CTRL_ACK_PENDING,
-    BA_CTRL_ACK_DISCONNECT_IN_PROGRESS,
-    BA_CTRL_SKT_DISCONNECTED,
-    BA_CTRL_ACK_UNKNOWN,
-} tBA_CTRL_ACK;
-
 extern void btif_av_trigger_suspend();
 
 static ba_transmitter_callbacks_t *ba_transmitter_callback = NULL;
@@ -264,11 +253,13 @@
 
 bool btif_ba_is_active()
 {
-    LOG_INFO(LOG_TAG,"%s:",__func__);
+    bool ret = false;
     if (btif_ba_get_state() > BTIF_BA_STATE_IDLE_AUDIO_NS)
-        return true;
+        ret = true;
     else
-        return false;
+        ret = false;
+    LOG_INFO(LOG_TAG,"%s: %d",__func__, ret);
+    return ret;
 }
 
 btif_ba_state_t btif_ba_get_state()
@@ -323,11 +314,11 @@
     {
         if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_PAUSE_REQ_EVT)
         {
-            btif_a2dp_audio_on_suspended(result);
+            btif_ba_audio_on_suspended(result);
         }
         else if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_STOP_REQ_EVT)
         {
-            btif_a2dp_audio_on_stopped(result);
+            btif_ba_audio_on_stopped(result);
         }
         else
         {
@@ -338,7 +329,7 @@
     {
         if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_START_REQ_EVT)
         {
-            btif_a2dp_audio_on_started(result);
+            btif_ba_audio_on_started(result);
         }
         else
         {
@@ -372,6 +363,41 @@
     *p_codec_config = bit_rate;p_codec_config ++;
 }
 
+uint8_t btif_ba_get_sample_rate()
+{
+  return btif_ba_cb.sampl_freq;
+}
+
+uint8_t btif_ba_get_channel_mode()
+{
+  return btif_ba_cb.ch_mode;
+}
+
+uint8_t btif_ba_get_frame_size()
+{
+  return btif_ba_cb.frame_size;
+}
+
+uint8_t btif_ba_get_complexity()
+{
+  return btif_ba_cb.complexity;
+}
+
+uint8_t btif_ba_get_prediction_mode()
+{
+  return btif_ba_cb.prediction_mode;
+}
+
+uint8_t btif_ba_get_vbr_flag()
+{
+  return btif_ba_cb.vbr_mode;
+}
+
+uint32_t btif_ba_get_bitrate()
+{
+  return btif_ba_cb.bit_rate;
+}
+
 static void memorize_msg(uint8_t event, btif_ba_state_t state)
 {
     BTIF_TRACE_DEBUG(" %s  event = %s, state = %d", __FUNCTION__,
diff --git a/system_bt_ext/btif/src/btif_vendor_hf.cc b/system_bt_ext/btif/src/btif_vendor_hf.cc
new file mode 100644
index 0000000..dc4895e
--- /dev/null
+++ b/system_bt_ext/btif/src/btif_vendor_hf.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***********************************************************************/
+
+/************************************************************************************
+ *
+ *  Filename:      btif_vendor_hf.cc
+ *
+ *  Description:   Vendor Bluetooth Interface
+ *
+ *
+ ***********************************************************************************/
+
+#define LOG_TAG "bt_btif_vendor_hf"
+
+#include <hardware/vendor_hf.h>
+#include "btif_api.h"
+
+btvendor_hf_callbacks_t *bt_vendor_hf_callbacks = NULL;
+static bool swb_codec_status = false;
+
+/*******************************************************************************
+** VENDOR INTERFACE FUNCTIONS
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** Function         init
+**
+** Description     initializes the vendor interface for HF
+**
+** Returns         bt_status_t
+**
+*******************************************************************************/
+
+static bt_status_t init( btvendor_hf_callbacks_t* callbacks)
+{
+    bt_vendor_hf_callbacks = callbacks;
+    LOG_INFO(LOG_TAG,"init done");
+    return BT_STATUS_SUCCESS;
+}
+
+static void cleanup(void)
+{
+    LOG_INFO(LOG_TAG,"cleanup");
+    if (bt_vendor_hf_callbacks)
+        bt_vendor_hf_callbacks = NULL;
+}
+
+int enable_swb(bool enable) {
+  LOG_INFO(LOG_TAG,"%s: %d", __func__, enable);
+  swb_codec_status = enable;
+  return 0;
+}
+
+static const btvendor_hf_interface_t btvendorhfInterface = {
+    sizeof(btvendorhfInterface),
+    init,
+    cleanup,
+    enable_swb,
+};
+
+void btif_handle_vendor_hf_events(uint16_t event, uint16_t swb_config, RawAddress *bd_addr) {
+    HAL_CBACK(bt_vendor_hf_callbacks, swb_codec_cb, swb_config, bd_addr);
+}
+
+bool get_swb_codec_status() {
+    return swb_codec_status;
+}
+
+/*******************************************************************************
+**
+** Function         btif_vendor_hf_get_interface
+**
+** Description      Get the vendor callback interface
+**
+** Returns          btvendor_hf_interface_t
+**
+*******************************************************************************/
+const btvendor_hf_interface_t *btif_vendor_hf_get_interface()
+{
+    BTIF_TRACE_EVENT("%s", __FUNCTION__);
+    return &btvendorhfInterface;
+}
+
diff --git a/system_bt_ext/conf/interop_database.conf b/system_bt_ext/conf/interop_database.conf
index b80522f..e53da04 100644
--- a/system_bt_ext/conf/interop_database.conf
+++ b/system_bt_ext/conf/interop_database.conf
@@ -8,7 +8,7 @@
 #1. Below are the four tags for blacklist
 #   A. Address_Based   C. Manufacturer_based
 #   B. Name_Based      D. Vndr_Prdt_Based
-#   E. SSR_Max_Lat_Based
+#   E. SSR_Max_Lat_Based F: Version_Based
 ##
 #   A. Address_Based :  This tag refers to Address based blacklist
 #   Input Type       :   Input value should be only 3 to 6 bytes of BD address
@@ -28,16 +28,21 @@
 #####
 #   D. Vndr_Prdt_Based : This tag refers to vendor and product based blacklist
 #   Input type  : Input should be in Hexadecimal value
-#   Format Type : 4 bytes hex value( 2bytes of vendor  and 2 bytes of product),
+#   Format Type : 4 bytes hex value( 2 bytes of vendor  and 2 bytes of product),
 #                 Vendor and product hex values should be separated with delimiter(-).
 #   Examples    : 0X00AB-0X00BC = Vndr_Prdt_Based
 #####
-#   E. SSR_Max_Lat_Based : This tag refers to SSR Max LAtency based blacklist
+#   E. SSR_Max_Lat_Based : This tag refers to SSR Max Latency based blacklist
 #   Input type  : Input value should be combination of first 3 bytes of BD address and
 #                 Hexadecimal value of SSR Max Latency
 #   Format Type : Address should be in XX:XX:XX format followed by 2 bytes hex value
 #                 of max latency Address and Max Latency should be separated with delimiter(-).
 #   Examples    : 00:01:03-0X00AB = SSR_Max_Lat_Based
+#####
+#   F. Version_Based : This tag refers to DID Version based blacklist
+#   Input type  : Input value should be Hexadecimal value
+#   Format Type : 2 bytes hex value
+#   Examples    : 0X00AB = Version_Based
 # ******************************* Start of Blacklist Database ********************************
 #Disable secure connections
 #This is for pre BT 4.1/2 devices that do not handle secure mode very well.
@@ -73,6 +78,7 @@
 04:F8:C2 = Address_Based
 00:18:91 = Address_Based
 00:24:1C = Address_Based
+00:08:8b = Address_Based
 
 # Disable automatic pairing with headsets/car-kits
 # Some car kits do not react kindly to a failed pairing attempt and
@@ -114,6 +120,8 @@
 00:1E:7C = Address_Based
 C8:84:47 = Address_Based
 D0:8A:55 = Address_Based
+48:F0:7B = Address_Based
+00:0A:08 = Address_Based
 
 # Some HID pointing devices have proven problematic behaviour if pairing is initiated with
 # them, resulting in no response for authentication request and ultimately resulting
@@ -211,10 +219,10 @@
 # disable AAC for those headsets so that it switch to SBC
 # 1. byte alignment issues with encoder (solo3::20:3c:ae,airpods::4c:32:75)
 # 2. remote sending 128 instead of 128k as bitrate (cadillac::28:a1:83, Chevrolet Tahoe,
-#    buick_verona::ac:7a:4d, maruti_brezzai:28:a1:83,cadillac cue::e0:75:0a, 30:C3:D9)
+#    buick_verano::ac:7a:4d(moved to name based, maruti_brezzai:28:a1:83,
+#    cadillac cue::e0:75:0a, 30:C3:D9)
 # 3. remote supporting only 44.1Khz (BIG-JAM-BOX::00-21-3c,JVC CK::00:1D:86,BMW CK::9C:DF:03)
 [INTEROP_DISABLE_AAC_CODEC]
-AC:7A:4D = Address_Based
 28:A1:83 = Address_Based
 A0:14:3D = Address_Based
 90:03:B7 = Address_Based
@@ -226,6 +234,7 @@
 30:C3:D9 = Address_Based
 00:E0:4C = Address_Based
 abramtek M1 = Name_Based
+Buick Verano = Name_Based
 
 #Enable AAC encoder only for whitelist devices
 
diff --git a/system_bt_ext/device/Android.bp b/system_bt_ext/device/Android.bp
index 44babef..8a83a90 100644
--- a/system_bt_ext/device/Android.bp
+++ b/system_bt_ext/device/Android.bp
@@ -28,4 +28,7 @@
         "libosi_qti",
         "libbluetooth-types",
     ],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/system_bt_ext/device/include/interop_config.h b/system_bt_ext/device/include/interop_config.h
index a2c5e62..35f3e8c 100644
--- a/system_bt_ext/device/include/interop_config.h
+++ b/system_bt_ext/device/include/interop_config.h
@@ -34,6 +34,7 @@
 void interop_database_add_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
 void interop_database_add_addr_max_lat(const interop_feature_t feature, const RawAddress *addr,
           size_t length, uint16_t max_lat);
+void interop_database_add_version(const interop_feature_t feature, uint16_t version);
 
 // API's for removing entries from dynamic interop database
 bool interop_database_remove_addr(const interop_feature_t feature, const RawAddress *addr);
@@ -42,6 +43,7 @@
 bool interop_database_remove_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
 bool interop_database_remove_addr_max_lat(const interop_feature_t feature,
           const RawAddress *addr, size_t length, uint16_t max_lat);
+bool interop_database_remove_version(const interop_feature_t feature, uint16_t version);
 
 // API's to match entries with in dynamic interop database
 bool interop_database_match_addr(const interop_feature_t feature, const RawAddress *addr);
@@ -50,4 +52,5 @@
 bool interop_database_match_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
 bool interop_database_match_addr_get_max_lat(const interop_feature_t feature,
           const RawAddress *addr, uint16_t *max_lat);
+bool interop_database_match_version(const interop_feature_t feature, uint16_t version);
 
diff --git a/system_bt_ext/device/src/interop.cc b/system_bt_ext/device/src/interop.cc
index 637fe44..2381669 100644
--- a/system_bt_ext/device/src/interop.cc
+++ b/system_bt_ext/device/src/interop.cc
@@ -69,6 +69,7 @@
 #define VALID_VNDR_PRDT_LEN   (13)
 #define VALID_MNFR_STR_LEN    (6)
 #define VALID_SSR_LAT_LEN   (15)
+#define VALID_VERSION_LEN   (6)
 #define VENDOR_VALUE_SEPARATOR  "-"
 
 #define ADDR_BASED    "Address_Based"
@@ -76,6 +77,7 @@
 #define MNFR_BASED    "Manufacturer_Based"
 #define VNDR_PRDT_BASED   "Vndr_Prdt_Based"
 #define SSR_MAX_LAT_BASED   "SSR_Max_Lat_Based"
+#define VERSION_BASED   "Version_Based"
 
 struct config_t {
   list_t *sections;
@@ -98,12 +100,18 @@
   interop_feature_t feature;
 } interop_hid_ssr_max_lat_t;
 
+typedef struct {
+  uint16_t version;
+  interop_feature_t feature;
+} interop_version_t;
+
 typedef enum {
     INTEROP_BL_TYPE_ADDR = 0,
     INTEROP_BL_TYPE_NAME,
     INTEROP_BL_TYPE_MANUFACTURE,
     INTEROP_BL_TYPE_VNDR_PRDT,
     INTEROP_BL_TYPE_SSR_MAX_LAT,
+    INTEROP_BL_TYPE_VERSION,
 
 } interop_bl_type;
 
@@ -123,6 +131,7 @@
         interop_manufacturer_t mnfr_entry;
         interop_hid_multitouch_t vnr_pdt_entry;
         interop_hid_ssr_max_lat_t ssr_max_lat_entry;
+        interop_version_t version_entry;
     } entry_type;
 
 } interop_db_entry_t;
@@ -488,6 +497,18 @@
         interop_config_flush();
         break;
       }
+    case INTEROP_BL_TYPE_VERSION:
+      {
+        interop_feature_t feature =
+          db_entry->entry_type.version_entry.feature;
+        char m_vendor[KEY_MAX_LENGTH] = { '\0' };
+        snprintf(m_vendor, sizeof(m_vendor), "0x%04x",
+            db_entry->entry_type.version_entry.version);
+        interop_config_set_str(interop_feature_string_(feature),
+            m_vendor, VERSION_BASED);
+        interop_config_flush();
+        break;
+      }
   }
 }
 
@@ -594,6 +615,20 @@
           }
           break;
         }
+      case INTEROP_BL_TYPE_VERSION:
+        {
+          interop_version_t *src = &entry->entry_type.version_entry;
+          interop_version_t *cur = &db_entry->entry_type.version_entry;
+
+          if ((src->feature == cur->feature) &&
+              (src->version == cur->version)) {
+            if (ret_entry) {
+              *ret_entry = db_entry;
+            }
+            found = true;
+          }
+          break;
+        }
     }
 
     if (found) {
@@ -690,6 +725,19 @@
         interop_config_flush();
         break;
       }
+    case INTEROP_BL_TYPE_VERSION:
+      {
+        interop_version_t *src = &entry->entry_type.version_entry;
+
+        interop_feature_t feature = src->feature;
+        char m_version[KEY_MAX_LENGTH] = { '\0' };
+        snprintf(m_version, sizeof(m_version), "0x%04x",
+            src->version);
+        interop_config_remove(interop_feature_string_(feature),
+            m_version);
+        interop_config_flush();
+        break;
+      }
     default:
     status = false;
   }
@@ -923,6 +971,28 @@
     entry->entry_type.ssr_max_lat_entry.length = len;
     entry->entry_type.ssr_max_lat_entry.max_lat = max_lat;
     interop_database_add_(entry, false);
+  } else if ( !strncasecmp( value, VERSION_BASED, strlen(VERSION_BASED))) {
+
+    uint16_t version;
+    char *e;
+
+    if ( strlen(key) != VALID_VERSION_LEN ) {
+      LOG_WARN(LOG_TAG,
+      " ignoring %s due to invalid version in config file", key);
+      return false;
+    }
+
+    version = (uint16_t)strtoul(key, &e, 16);
+    errno = 0;
+    if( *e || errno == EINVAL || errno == ERANGE )
+       return false;
+
+    interop_db_entry_t *entry = (interop_db_entry_t *)osi_calloc(sizeof(interop_db_entry_t));
+    entry->bl_type = INTEROP_BL_TYPE_VERSION;
+    entry->bl_entry_type = entry_type;
+    entry->entry_type.version_entry.feature = (interop_feature_t)feature;
+    entry->entry_type.version_entry.version = version;
+    interop_database_add_(entry, false);
   }
   LOG_WARN(LOG_TAG, " feature:: %d, key :: %s, value :: %s",
                     feature, key, value);
@@ -1065,6 +1135,17 @@
   interop_database_add_(entry, true);
 }
 
+void interop_database_add_version(const interop_feature_t feature, uint16_t version)
+{
+
+  interop_db_entry_t *entry = (interop_db_entry_t *)osi_calloc(sizeof(interop_db_entry_t));
+  entry->bl_type = INTEROP_BL_TYPE_VERSION;
+  entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
+  entry->entry_type.version_entry.feature = (interop_feature_t)feature;
+  entry->entry_type.version_entry.version = version;
+  interop_database_add_(entry, true);
+}
+
 bool interop_database_match_manufacturer(const interop_feature_t feature,
                       uint16_t manufacturer)
 {
@@ -1178,6 +1259,27 @@
   return false;
 }
 
+bool interop_database_match_version(const interop_feature_t feature, uint16_t version)
+{
+
+  interop_db_entry_t entry;
+  interop_db_entry_t *ret_entry = NULL;
+
+  entry.bl_type = INTEROP_BL_TYPE_VERSION;
+
+  entry.entry_type.version_entry.feature = (interop_feature_t)feature;
+  entry.entry_type.version_entry.version = version;
+  if (interop_database_match_(&entry, &ret_entry,  (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC  |
+    INTEROP_ENTRY_TYPE_DYNAMIC))) {
+    LOG_WARN(LOG_TAG,
+      "%s() Device with version: 0x%04x is a match for interop workaround %s", __func__, version,
+      interop_feature_string_(feature));
+    return true;
+  }
+
+  return false;
+}
+
 bool interop_database_remove_name( const interop_feature_t feature, const char *name)
 {
   assert(name);
@@ -1291,3 +1393,25 @@
   return false;
 }
 
+bool interop_database_remove_version(const interop_feature_t feature, uint16_t version)
+{
+
+  interop_db_entry_t entry;
+
+  entry.bl_type = INTEROP_BL_TYPE_VERSION;
+  entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
+
+  entry.entry_type.version_entry.feature = (interop_feature_t)feature;
+  entry.entry_type.version_entry.version = version;
+
+  if (interop_database_remove_(&entry)) {
+    LOG_WARN(LOG_TAG,
+      "%s() Device with version: 0x%04x is removed from"
+      "interop workaround %s", __func__, version,
+      interop_feature_string_(feature));
+    return true;
+  }
+  return false;
+}
+
+
diff --git a/system_bt_ext/osi/Android.bp b/system_bt_ext/osi/Android.bp
index 456ab11..438e8fe 100644
--- a/system_bt_ext/osi/Android.bp
+++ b/system_bt_ext/osi/Android.bp
@@ -24,4 +24,7 @@
         "libbt-utils",
     ],
     cflags: ["-DBUILDCFG"],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/system_bt_ext/stack/Android.bp b/system_bt_ext/stack/Android.bp
index cca03cb..97b18e6 100644
--- a/system_bt_ext/stack/Android.bp
+++ b/system_bt_ext/stack/Android.bp
@@ -39,4 +39,7 @@
         "libcutils",
         "liblog",
     ],
+    sanitize: {
+        never: true,
+    },
 }
diff --git a/vhal/include/hardware/vendor_hf.h b/vhal/include/hardware/vendor_hf.h
new file mode 100644
index 0000000..6b07c5c
--- /dev/null
+++ b/vhal/include/hardware/vendor_hf.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_VENDOR_H
+#define ANDROID_INCLUDE_BT_HF_VENDOR_H
+
+#include <hardware/bluetooth.h>
+
+__BEGIN_DECLS
+
+#define BT_PROFILE_VENDOR_HF_ID "vendorhfservice"
+
+bool get_swb_codec_status();
+
+void btif_handle_vendor_hf_events(uint16_t event, uint16_t swb_config, RawAddress *bd_addr);
+
+/* SWB callback events */
+typedef void (* bt_swb_update_callback)(uint16_t swb_codec_config, RawAddress *bd_addr);
+
+/** BT-Vendor hf callback structure. */
+typedef struct {
+    /** set to sizeof(BtVendorhfCallbacks) */
+    size_t      size;
+    bt_swb_update_callback  swb_codec_cb;
+} btvendor_hf_callbacks_t;
+
+
+/** Represents the standard BT-Vendor hf interface.
+ */
+typedef struct {
+    /** set to sizeof(BtVendorHfInterface) */
+    size_t  size;
+
+    /**
+     * Register the BtVendorhf callbacks
+     */
+    bt_status_t (*init)( btvendor_hf_callbacks_t* callbacks );
+
+    /** Closes the interface. */
+    void  (*cleanup)( void );
+
+    int   (*enable_swb) (bool enable);
+} btvendor_hf_interface_t;
+
+__END_DECLS
+#endif /* ANDROID_INCLUDE_BT_VENDOR_H */
+