Merge "Treat the buffering state as playing" into pi-dev
diff --git a/android/app/jni/com_android_bluetooth_avrcp.cpp b/android/app/jni/com_android_bluetooth_avrcp.cpp
index f0d829d..1eb3553 100644
--- a/android/app/jni/com_android_bluetooth_avrcp.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp.cpp
@@ -58,7 +58,7 @@
 
 static void cleanup_items(btrc_folder_items_t* p_items, int numItems);
 
-static void btavrcp_remote_features_callback(RawAddress* bd_addr,
+static void btavrcp_remote_features_callback(const RawAddress& bd_addr,
                                              btrc_remote_features_t features) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -76,13 +76,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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(RawAddress* bd_addr) {
+static void btavrcp_get_play_status_callback(const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -99,13 +99,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                              RawAddress* bd_addr) {
+                                              const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -131,14 +131,14 @@
   sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs);
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                                   RawAddress* bd_addr) {
+                                                   const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -155,13 +155,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                           RawAddress* bd_addr) {
+                                           const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -178,14 +178,14 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                                 RawAddress* bd_addr) {
+                                                 const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -201,14 +201,14 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                                  RawAddress* bd_addr) {
+                                                  const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -225,13 +225,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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,
-                                                RawAddress* bd_addr) {
+                                                const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
   if (!mCallbacksObj) {
@@ -246,7 +246,7 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
 
   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setBrowsedPlayerCallback,
                                addr.get(), (jint)player_id);
@@ -254,7 +254,7 @@
 
 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, RawAddress* bd_addr) {
+    uint32_t* p_attr_ids, const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -273,7 +273,7 @@
   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);
+                                   (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)) {
@@ -294,7 +294,7 @@
 }
 
 static void btavrcp_change_path_callback(uint8_t direction, uint8_t* folder_uid,
-                                         RawAddress* bd_addr) {
+                                         const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -318,7 +318,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
   sCallbackEnv->SetByteArrayRegion(
       attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)folder_uid);
   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePathCallback,
@@ -329,7 +329,7 @@
                                            uint16_t uid_counter,
                                            uint8_t num_attr,
                                            btrc_media_attr_t* p_attrs,
-                                           RawAddress* bd_addr) {
+                                           const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
 
@@ -360,7 +360,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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);
@@ -371,7 +371,8 @@
 }
 
 static void btavrcp_play_item_callback(uint8_t scope, uint16_t uid_counter,
-                                       uint8_t* uid, RawAddress* bd_addr) {
+                                       uint8_t* uid,
+                                       const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
   if (!mCallbacksObj) {
@@ -394,7 +395,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
   sCallbackEnv->SetByteArrayRegion(
       attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItemCallback,
@@ -403,7 +404,7 @@
 }
 
 static void btavrcp_get_total_num_items_callback(uint8_t scope,
-                                                 RawAddress* bd_addr) {
+                                                 const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
   if (!mCallbacksObj) {
@@ -419,13 +420,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (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, RawAddress* bd_addr) {
+                                    uint8_t* p_str, const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
   if (!mCallbacksObj) {
@@ -448,7 +449,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
   sCallbackEnv->SetByteArrayRegion(attrs.get(), 0, str_len * sizeof(uint8_t),
                                    (jbyte*)p_str);
   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_searchCallback, addr.get(),
@@ -457,7 +458,7 @@
 
 static void btavrcp_add_to_play_list_callback(uint8_t scope, uint8_t* uid,
                                               uint16_t uid_counter,
-                                              RawAddress* bd_addr) {
+                                              const RawAddress& bd_addr) {
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
   if (!mCallbacksObj) {
@@ -480,7 +481,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
   sCallbackEnv->SetByteArrayRegion(
       attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_addToPlayListCallback,
@@ -629,9 +630,11 @@
     jniThrowIOException(env, EINVAL);
     return JNI_FALSE;
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   bt_status_t status = sBluetoothAvrcpInterface->get_play_status_rsp(
-      (RawAddress*)addr, (btrc_play_status_t)playStatus, songLen, songPos);
+      rawAddress, (btrc_play_status_t)playStatus, songLen, songPos);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed get_play_status_rsp, status: %d", status);
   }
@@ -693,9 +696,10 @@
     return JNI_FALSE;
   }
 
-  RawAddress* btAddr = (RawAddress*)addr;
-  bt_status_t status =
-      sBluetoothAvrcpInterface->get_element_attr_rsp(btAddr, numAttr, pAttrs);
+  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);
   }
@@ -756,10 +760,11 @@
       break;
     }
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
-  RawAddress* btAddr = (RawAddress*)addr;
   bt_status_t status = sBluetoothAvrcpInterface->get_item_attr_rsp(
-      btAddr, (btrc_status_t)rspStatus, numAttr, pAttrs);
+      rawAddress, (btrc_status_t)rspStatus, numAttr, pAttrs);
   if (status != BT_STATUS_SUCCESS)
     ALOGE("Failed get_item_attr_rsp, status: %d", status);
 
@@ -1025,7 +1030,7 @@
 
   RawAddress* btAddr = (RawAddress*)addr;
   bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
-      btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
+      *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
   }
@@ -1160,7 +1165,7 @@
 
   RawAddress* btAddr = (RawAddress*)addr;
   bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
-      btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
+      *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
   if (status != BT_STATUS_SUCCESS)
     ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
 
@@ -1198,7 +1203,7 @@
 
   RawAddress* btAddr = (RawAddress*)addr;
   bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_rsp(
-      btAddr, (btrc_status_t)rspStatus);
+      *btAddr, (btrc_status_t)rspStatus);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed set_addressed_player_rsp, status: %d", status);
   }
@@ -1252,7 +1257,7 @@
   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,
+      *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);
@@ -1283,7 +1288,7 @@
   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);
+      *btAddr, (btrc_status_t)rspStatus, (uint32_t)nItems);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed change_path_rsp, status: %d", status);
   }
@@ -1309,7 +1314,8 @@
   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);
+      *btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter,
+      (uint32_t)nItems);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed search_rsp, status: %d", status);
   }
@@ -1333,8 +1339,8 @@
   }
 
   RawAddress* btAddr = (RawAddress*)addr;
-  bt_status_t status =
-      sBluetoothAvrcpInterface->play_item_rsp(btAddr, (btrc_status_t)rspStatus);
+  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);
   }
@@ -1360,7 +1366,8 @@
   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);
+      *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);
   }
@@ -1384,7 +1391,7 @@
 
   RawAddress* btAddr = (RawAddress*)addr;
   bt_status_t status = sBluetoothAvrcpInterface->add_to_now_playing_rsp(
-      btAddr, (btrc_status_t)rspStatus);
+      *btAddr, (btrc_status_t)rspStatus);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed add_to_now_playing_rsp, status: %d", status);
   }
diff --git a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
index 86932d3..7710a91 100644
--- a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
@@ -53,8 +53,8 @@
 static const btrc_ctrl_interface_t* sBluetoothAvrcpInterface = NULL;
 static jobject sCallbacksObj = NULL;
 
-static void btavrcp_passthrough_response_callback(RawAddress* bd_addr, int id,
-                                                  int pressed) {
+static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr,
+                                                  int id, int pressed) {
   ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -67,7 +67,7 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp,
                                (jint)id, (jint)pressed, addr.get());
 }
@@ -82,7 +82,7 @@
 }
 
 static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect,
-                                              RawAddress* bd_addr) {
+                                              const RawAddress& bd_addr) {
   ALOGI("%s: conn state: rc: %d br: %d", __func__, rc_connect, br_connect);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -95,13 +95,14 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_onConnectionStateChanged,
                                (jboolean)rc_connect, (jboolean)br_connect,
                                addr.get());
 }
 
-static void btavrcp_get_rcfeatures_callback(RawAddress* bd_addr, int features) {
+static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr,
+                                            int features) {
   ALOGV("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -114,13 +115,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr.get(),
                                (jint)features);
 }
 
 static void btavrcp_setplayerapplicationsetting_rsp_callback(
-    RawAddress* bd_addr, uint8_t accepted) {
+    const RawAddress& bd_addr, uint8_t accepted) {
   ALOGV("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -133,14 +134,15 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp,
                                addr.get(), (jint)accepted);
 }
 
 static void btavrcp_playerapplicationsetting_callback(
-    RawAddress* bd_addr, uint8_t num_attr, btrc_player_app_attr_t* app_attrs,
-    uint8_t num_ext_attr, btrc_player_app_ext_attr_t* ext_attrs) {
+    const RawAddress& bd_addr, uint8_t num_attr,
+    btrc_player_app_attr_t* app_attrs, uint8_t num_ext_attr,
+    btrc_player_app_ext_attr_t* ext_attrs) {
   ALOGI("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -152,7 +154,7 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   /* TODO ext attrs
    * Flattening defined attributes: <id,num_values,values[]>
    */
@@ -187,7 +189,7 @@
 }
 
 static void btavrcp_playerapplicationsetting_changed_callback(
-    RawAddress* bd_addr, btrc_player_settings_t* p_vals) {
+    const RawAddress& bd_addr, const btrc_player_settings_t& vals) {
   ALOGI("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -199,9 +201,9 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
 
-  int arraylen = p_vals->num_attr * 2;
+  int arraylen = vals.num_attr * 2;
   ScopedLocalRef<jbyteArray> playerattribs(
       sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen));
   if (!playerattribs.get()) {
@@ -211,12 +213,12 @@
   /*
    * Flatening format: <id,val>
    */
-  for (int i = 0, k = 0; (i < p_vals->num_attr) && (k < arraylen); i++) {
+  for (int i = 0, k = 0; (i < vals.num_attr) && (k < arraylen); i++) {
     sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
-                                     (jbyte*)&(p_vals->attr_ids[i]));
+                                     (jbyte*)&(vals.attr_ids[i]));
     k++;
     sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
-                                     (jbyte*)&(p_vals->attr_values[i]));
+                                     (jbyte*)&(vals.attr_values[i]));
     k++;
   }
   sCallbackEnv->CallVoidMethod(sCallbacksObj,
@@ -224,7 +226,7 @@
                                playerattribs.get(), (jint)arraylen);
 }
 
-static void btavrcp_set_abs_vol_cmd_callback(RawAddress* bd_addr,
+static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr,
                                              uint8_t abs_vol, uint8_t label) {
   ALOGI("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
@@ -238,13 +240,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume,
                                addr.get(), (jbyte)abs_vol, (jbyte)label);
 }
 
-static void btavrcp_register_notification_absvol_callback(RawAddress* bd_addr,
-                                                          uint8_t label) {
+static void btavrcp_register_notification_absvol_callback(
+    const RawAddress& bd_addr, uint8_t label) {
   ALOGI("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -257,13 +259,13 @@
   }
 
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj,
                                method_handleRegisterNotificationAbsVol,
                                addr.get(), (jbyte)label);
 }
 
-static void btavrcp_track_changed_callback(RawAddress* bd_addr,
+static void btavrcp_track_changed_callback(const RawAddress& bd_addr,
                                            uint8_t num_attr,
                                            btrc_element_attr_val_t* p_attrs) {
   /*
@@ -288,7 +290,7 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
 
   jclass strclazz = sCallbackEnv->FindClass("java/lang/String");
   ScopedLocalRef<jobjectArray> stringArray(
@@ -317,7 +319,7 @@
                                stringArray.get());
 }
 
-static void btavrcp_play_position_changed_callback(RawAddress* bd_addr,
+static void btavrcp_play_position_changed_callback(const RawAddress& bd_addr,
                                                    uint32_t song_len,
                                                    uint32_t song_pos) {
   ALOGI("%s", __func__);
@@ -331,13 +333,13 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaypositionchanged,
                                addr.get(), (jint)(song_len), (jint)song_pos);
 }
 
 static void btavrcp_play_status_changed_callback(
-    RawAddress* bd_addr, btrc_play_status_t play_status) {
+    const RawAddress& bd_addr, btrc_play_status_t play_status) {
   ALOGI("%s", __func__);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -349,13 +351,13 @@
     return;
   }
   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)bd_addr);
+                                   (jbyte*)&bd_addr.address);
   sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaystatuschanged,
                                addr.get(), (jbyte)play_status);
 }
 
 static void btavrcp_get_folder_items_callback(
-    RawAddress* bd_addr, btrc_status_t status,
+    const RawAddress& bd_addr, btrc_status_t status,
     const btrc_folder_items_t* folder_items, uint8_t count) {
   /* Folder items are list of items that can be either BTRC_ITEM_PLAYER
    * BTRC_ITEM_MEDIA, BTRC_ITEM_FOLDER. Here we translate them to their java
@@ -545,7 +547,8 @@
   }
 }
 
-static void btavrcp_change_path_callback(RawAddress* bd_addr, uint32_t count) {
+static void btavrcp_change_path_callback(const RawAddress& bd_addr,
+                                         uint32_t count) {
   ALOGI("%s count %d", __func__, count);
   CallbackEnv sCallbackEnv(__func__);
   if (!sCallbackEnv.valid()) return;
@@ -554,7 +557,7 @@
                                (jint)count);
 }
 
-static void btavrcp_set_browsed_player_callback(RawAddress* bd_addr,
+static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr,
                                                 uint8_t num_items,
                                                 uint8_t depth) {
   ALOGI("%s items %d depth %d", __func__, num_items, depth);
@@ -565,7 +568,7 @@
                                (jint)num_items, (jint)depth);
 }
 
-static void btavrcp_set_addressed_player_callback(RawAddress* bd_addr,
+static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr,
                                                   uint8_t status) {
   ALOGI("%s status %d", __func__, status);
 
@@ -738,8 +741,10 @@
     return JNI_FALSE;
   }
 
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
   bt_status_t status = sBluetoothAvrcpInterface->send_pass_through_cmd(
-      (RawAddress*)addr, (uint8_t)key_code, (uint8_t)key_state);
+      rawAddress, (uint8_t)key_code, (uint8_t)key_state);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending passthru command, status: %d", status);
   }
@@ -763,9 +768,11 @@
     jniThrowIOException(env, EINVAL);
     return JNI_FALSE;
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   bt_status_t status = sBluetoothAvrcpInterface->send_group_navigation_cmd(
-      (RawAddress*)addr, (uint8_t)key_code, (uint8_t)key_state);
+      rawAddress, (uint8_t)key_code, (uint8_t)key_state);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending Grp Navigation command, status: %d", status);
   }
@@ -810,9 +817,11 @@
     pAttrs[i] = (uint8_t)attr[i];
     pAttrsVal[i] = (uint8_t)attr_val[i];
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   bt_status_t status = sBluetoothAvrcpInterface->set_player_app_setting_cmd(
-      (RawAddress*)addr, (uint8_t)num_attrib, pAttrs, pAttrsVal);
+      rawAddress, (uint8_t)num_attrib, pAttrs, pAttrsVal);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending setPlAppSettValNative command, status: %d", status);
   }
@@ -834,8 +843,11 @@
   }
 
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
+
   bt_status_t status = sBluetoothAvrcpInterface->set_volume_rsp(
-      (RawAddress*)addr, (uint8_t)abs_vol, (uint8_t)label);
+      rawAddress, (uint8_t)abs_vol, (uint8_t)label);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status);
   }
@@ -853,8 +865,11 @@
     return;
   }
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
+
   bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp(
-      (RawAddress*)addr, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol,
+      rawAddress, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol,
       (uint8_t)label);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d",
@@ -873,8 +888,11 @@
     return;
   }
   ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
+
   bt_status_t status =
-      sBluetoothAvrcpInterface->get_playback_state_cmd((RawAddress*)addr);
+      sBluetoothAvrcpInterface->get_playback_state_cmd(rawAddress);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending getPlaybackStateNative command, status: %d", status);
   }
@@ -890,8 +908,11 @@
     return;
   }
   ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
+
   bt_status_t status = sBluetoothAvrcpInterface->get_now_playing_list_cmd(
-      (RawAddress*)addr, start, end);
+      rawAddress, start, end);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending getNowPlayingListNative command, status: %d", status);
   }
@@ -907,8 +928,11 @@
     return;
   }
   ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
-  bt_status_t status = sBluetoothAvrcpInterface->get_folder_list_cmd(
-      (RawAddress*)addr, start, end);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
+
+  bt_status_t status =
+      sBluetoothAvrcpInterface->get_folder_list_cmd(rawAddress, start, end);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending getFolderListNative command, status: %d", status);
   }
@@ -924,9 +948,11 @@
     return;
   }
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
-  bt_status_t status = sBluetoothAvrcpInterface->get_player_list_cmd(
-      (RawAddress*)addr, start, end);
+  bt_status_t status =
+      sBluetoothAvrcpInterface->get_player_list_cmd(rawAddress, start, end);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending getPlayerListNative command, status: %d", status);
   }
@@ -950,9 +976,11 @@
   }
 
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd(
-      (RawAddress*)addr, (uint8_t)direction, (uint8_t*)uid);
+      rawAddress, (uint8_t)direction, (uint8_t*)uid);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending changeFolderPathNative command, status: %d", status);
   }
@@ -967,10 +995,12 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
   bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd(
-      (RawAddress*)addr, (uint16_t)id);
+      rawAddress, (uint16_t)id);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status);
   }
@@ -985,10 +1015,12 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
   bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd(
-      (RawAddress*)addr, (uint16_t)id);
+      rawAddress, (uint16_t)id);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending setAddressedPlayerNative command, status: %d",
           status);
@@ -1010,10 +1042,12 @@
     jniThrowIOException(env, EINVAL);
     return;
   }
+  RawAddress rawAddress;
+  rawAddress.FromOctets((uint8_t*)addr);
 
   ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
   bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd(
-      (RawAddress*)addr, (uint8_t)scope, (uint8_t*)uid, (uint16_t)uidCounter);
+      rawAddress, (uint8_t)scope, (uint8_t*)uid, (uint16_t)uidCounter);
   if (status != BT_STATUS_SUCCESS) {
     ALOGE("Failed sending playItemNative command, status: %d", status);
   }
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 599e5df..34c3331 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -331,12 +331,15 @@
      * The check considers a number of factors during the evaluation.
      *
      * @param device the peer device to connect to
+     * @param isOutgoingRequest if true, the check is for outgoing connection
+     * request, otherwise is for incoming connection request
      * @return true if connection is allowed, otherwise false
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    public boolean okToConnect(BluetoothDevice device) {
+    public boolean okToConnect(BluetoothDevice device, boolean isOutgoingRequest) {
+        Log.i(TAG, "okToConnect: device " + device + " isOutgoingRequest: " + isOutgoingRequest);
         // Check if this is an incoming connection in Quiet mode.
-        if (mAdapterService.isQuietModeEnabled()) {
+        if (mAdapterService.isQuietModeEnabled() && !isOutgoingRequest) {
             Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
             return false;
         }
@@ -435,6 +438,11 @@
             if (DBG) {
                 Log.d(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice);
             }
+
+            if (previousActiveDevice != null && AvrcpTargetService.get() != null) {
+                AvrcpTargetService.get().storeVolumeForDevice(previousActiveDevice);
+            }
+
             if (device == null) {
                 // Clear the active device
                 mActiveDevice = null;
@@ -452,7 +460,7 @@
                             previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
                             BluetoothProfile.A2DP,
                             getConnectionState(previousActiveDevice)
-                                == BluetoothProfile.STATE_CONNECTED);
+                                == BluetoothProfile.STATE_CONNECTED, -1);
                     // Make sure the Active device in native layer is set to null and audio is off
                     if (!mA2dpNativeInterface.setActiveDevice(null)) {
                         Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
@@ -495,11 +503,21 @@
                 if (previousActiveDevice != null) {
                     mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
                             previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED,
-                            BluetoothProfile.A2DP, true);
+                            BluetoothProfile.A2DP, true, -1);
                 }
+
+                int rememberedVolume = -1;
+                if (AvrcpTargetService.get() != null) {
+                    AvrcpTargetService.get().volumeDeviceSwitched(mActiveDevice);
+
+                    rememberedVolume = AvrcpTargetService.get()
+                            .getRememberedVolumeForDevice(mActiveDevice);
+                }
+
                 mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
                         mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
-                        true);
+                        true, rememberedVolume);
+
                 // Inform the Audio Service about the codec configuration
                 // change, so the Audio Service can reset accordingly the audio
                 // feeding parameters in the Audio HAL to the Bluetooth stack.
@@ -803,14 +821,6 @@
             Log.d(TAG, "broadcastActiveDevice(" + device + ")");
         }
 
-        // We need to inform AVRCP that the device has switched before informing the audio service
-        // so that AVRCP can prepare and wait on audio service connecting the new stream before
-        // restoring the previous volume. Otherwise the updated volume could be applied to the
-        // old active device before the switch has fully completed.
-        if (AvrcpTargetService.get() != null) {
-            AvrcpTargetService.get().volumeDeviceSwitched(device);
-        }
-
         Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 26e9571..3ab26e1 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -185,7 +185,7 @@
                         Log.e(TAG, "Disconnected: error connecting to " + mDevice);
                         break;
                     }
-                    if (mA2dpService.okToConnect(mDevice)) {
+                    if (mA2dpService.okToConnect(mDevice, true)) {
                         transitionTo(mConnecting);
                     } else {
                         // Reject the request and stay in Disconnected state
@@ -226,7 +226,7 @@
                     Log.w(TAG, "Ignore A2DP DISCONNECTED event: " + mDevice);
                     break;
                 case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
-                    if (mA2dpService.okToConnect(mDevice)) {
+                    if (mA2dpService.okToConnect(mDevice, false)) {
                         Log.i(TAG, "Incoming A2DP Connecting request accepted: " + mDevice);
                         transitionTo(mConnecting);
                     } else {
@@ -237,7 +237,7 @@
                     break;
                 case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
                     Log.w(TAG, "A2DP Connected from Disconnected state: " + mDevice);
-                    if (mA2dpService.okToConnect(mDevice)) {
+                    if (mA2dpService.okToConnect(mDevice, false)) {
                         Log.i(TAG, "Incoming A2DP Connected request accepted: " + mDevice);
                         transitionTo(mConnected);
                     } else {
@@ -428,7 +428,7 @@
                     transitionTo(mDisconnected);
                     break;
                 case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
-                    if (mA2dpService.okToConnect(mDevice)) {
+                    if (mA2dpService.okToConnect(mDevice, false)) {
                         Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice);
                         transitionTo(mConnected);
                     } else {
@@ -438,7 +438,7 @@
                     }
                     break;
                 case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
-                    if (mA2dpService.okToConnect(mDevice)) {
+                    if (mA2dpService.okToConnect(mDevice, false)) {
                         Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice);
                         transitionTo(mConnecting);
                     } else {
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
index caf4864..f442c49 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
@@ -111,8 +111,14 @@
         }
         switch (message.what) {
             case SRC_STR_START:
-                // Audio stream has started, stop it if we don't have focus.
                 mStreamAvailable = true;
+                // Always request audio focus if on TV.
+                if (isTvDevice()) {
+                    if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
+                        requestAudioFocus();
+                    }
+                }
+                // Audio stream has started, stop it if we don't have focus.
                 if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
                     sendAvrcpPause();
                 } else {
@@ -142,7 +148,7 @@
             case SRC_PLAY:
                 // Remote play command.
                 // If is an iot device gain focus and start avrcp updates.
-                if (isIotDevice()) {
+                if (isIotDevice() || isTvDevice()) {
                     if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
                         requestAudioFocus();
                     }
@@ -367,4 +373,9 @@
     private boolean isIotDevice() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
     }
+
+    private boolean isTvDevice() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
 }
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 7edf182..3077664 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -91,8 +91,10 @@
 
     static final int ABS_VOL_TIMEOUT_MILLIS = 1000; //1s
     static final int CMD_TIMEOUT_MILLIS = 5000; // 5s
-    // Fetch only 5 items at a time.
-    static final int GET_FOLDER_ITEMS_PAGINATION_SIZE = 5;
+    // Fetch only 20 items at a time.
+    static final int GET_FOLDER_ITEMS_PAGINATION_SIZE = 20;
+    // Fetch no more than 1000 items per directory.
+    static final int MAX_FOLDER_ITEMS = 1000;
 
     /*
      * Base value for absolute volume from JNI
@@ -590,7 +592,7 @@
                 Log.d(STATE_TAG, "startInd " + startInd + " endInd " + endInd);
             }
             mStartInd = startInd;
-            mEndInd = endInd;
+            mEndInd = Math.min(endInd, MAX_FOLDER_ITEMS);
         }
 
         @Override
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index c71c0da..a0ad490 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -78,10 +78,10 @@
     private static final int MESSAGE_PROFILE_INIT_PRIORITIES = 2;
     private static final int MESSAGE_CONNECT_OTHER_PROFILES = 3;
     private static final int MESSAGE_ADAPTER_STATE_TURNED_ON = 4;
+    private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5;
 
     // Timeouts
-    @VisibleForTesting
-    static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
+    @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
 
     private final AdapterService mAdapterService;
     private final ServiceFactory mFactory;
@@ -110,6 +110,11 @@
                             BluetoothProfile.A2DP, -1, // No-op argument
                             intent).sendToTarget();
                     break;
+                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
+                    mHandler.obtainMessage(MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED,
+                            BluetoothProfile.A2DP, -1, // No-op argument
+                            intent).sendToTarget();
+                    break;
                 case BluetoothAdapter.ACTION_STATE_CHANGED:
                     // Only pass the message on if the adapter has actually changed state from
                     // non-ON to ON. NOTE: ON is the state depicting BREDR ON and not just BLE ON.
@@ -169,6 +174,14 @@
                 }
                 break;
 
+                case MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED: {
+                    Intent intent = (Intent) msg.obj;
+                    BluetoothDevice activeDevice =
+                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    processProfileActiveDeviceChanged(activeDevice, msg.arg1);
+                }
+                break;
+
                 case MESSAGE_CONNECT_OTHER_PROFILES: {
                     // Called when we try connect some profiles in processConnectOtherProfiles but
                     // we send a delayed message to try connecting the remaining profiles
@@ -195,6 +208,7 @@
         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_UUID);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
         mAdapterService.registerReceiver(mReceiver, filter);
     }
 
@@ -246,9 +260,9 @@
             panService.setPriority(device, BluetoothProfile.PRIORITY_ON);
         }
 
-        if ((hearingAidService != null)
-                && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)
-                && (hearingAidService.getPriority(device) == BluetoothProfile.PRIORITY_UNDEFINED)) {
+        if ((hearingAidService != null) && BluetoothUuid.isUuidPresent(uuids,
+                BluetoothUuid.HearingAid) && (hearingAidService.getPriority(device)
+                == BluetoothProfile.PRIORITY_UNDEFINED)) {
             debugLog("setting hearing aid profile priority for device " + device);
             hearingAidService.setPriority(device, BluetoothProfile.PRIORITY_ON);
         }
@@ -269,13 +283,46 @@
                         break;
                 }
                 connectOtherProfile(device);
-                setProfileAutoConnectionPriority(device, profileId, true);
             }
             if (prevState == BluetoothProfile.STATE_CONNECTING
                     && nextState == BluetoothProfile.STATE_DISCONNECTED) {
-                setProfileAutoConnectionPriority(device, profileId, false);
+                HeadsetService hsService = mFactory.getHeadsetService();
+                boolean hsDisconnected = hsService == null || hsService.getConnectionState(device)
+                        == BluetoothProfile.STATE_DISCONNECTED;
+                A2dpService a2dpService = mFactory.getA2dpService();
+                boolean a2dpDisconnected = a2dpService == null
+                        || a2dpService.getConnectionState(device)
+                        == BluetoothProfile.STATE_DISCONNECTED;
+                debugLog("processProfileStateChanged, device=" + device + ", a2dpDisconnected="
+                        + a2dpDisconnected + ", hsDisconnected=" + hsDisconnected);
+                if (hsDisconnected && a2dpDisconnected) {
+                    removeAutoConnectFromA2dpSink(device);
+                    removeAutoConnectFromHeadset(device);
+                }
             }
+        }
+    }
 
+    private void processProfileActiveDeviceChanged(BluetoothDevice activeDevice, int profileId) {
+        debugLog("processProfileActiveDeviceChanged, activeDevice=" + activeDevice + ", profile="
+                + profileId);
+        switch (profileId) {
+            // Tracking active device changed intent only for A2DP so that we always connect to a
+            // single device after toggling Bluetooth
+            case BluetoothProfile.A2DP:
+                // Ignore null active device since we don't know if the change is triggered by
+                // normal device disconnection during Bluetooth shutdown or user action
+                if (activeDevice == null) {
+                    warnLog("processProfileActiveDeviceChanged: ignore null A2DP active device");
+                    return;
+                }
+                for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
+                    removeAutoConnectFromA2dpSink(device);
+                    removeAutoConnectFromHeadset(device);
+                }
+                setAutoConnectForA2dpSink(activeDevice);
+                setAutoConnectForHeadset(activeDevice);
+                break;
         }
     }
 
@@ -286,67 +333,55 @@
 
     private void autoConnect() {
         if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
-            errorLog("autoConnect() - BT is not ON. Exiting autoConnect");
+            errorLog("autoConnect: BT is not ON. Exiting autoConnect");
             return;
         }
 
         if (!mAdapterService.isQuietModeEnabled()) {
-            debugLog("autoConnect() - Initiate auto connection on BT on...");
-            // Phone profiles.
-            autoConnectHeadset();
-            autoConnectA2dp();
+            debugLog("autoConnect: Initiate auto connection on BT on...");
+            final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+            if (bondedDevices == null) {
+                errorLog("autoConnect: bondedDevices are null");
+                return;
+            }
+            for (BluetoothDevice device : bondedDevices) {
+                autoConnectHeadset(device);
+                autoConnectA2dp(device);
+            }
         } else {
             debugLog("autoConnect() - BT is in quiet mode. Not initiating auto connections");
         }
     }
 
-    private void autoConnectHeadset() {
-        final HeadsetService hsService = mFactory.getHeadsetService();
-        if (hsService == null) {
-            errorLog("autoConnectHeadset, service is null");
+    private void autoConnectA2dp(BluetoothDevice device) {
+        final A2dpService a2dpService = mFactory.getA2dpService();
+        if (a2dpService == null) {
+            warnLog("autoConnectA2dp: service is null, failed to connect to " + device);
             return;
         }
-        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
-        if (bondedDevices == null) {
-            errorLog("autoConnectHeadset, bondedDevices are null");
-            return;
-        }
-        for (BluetoothDevice device : bondedDevices) {
-            int priority = hsService.getPriority(device);
-            debugLog("autoConnectHeadset, attempt auto-connect with device " + device
-                     + " priority " + priority);
-            if (priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
-                debugLog("autoConnectHeadset, Connecting HFP with " + device);
-                hsService.connect(device);
-            } else {
-                debugLog("autoConnectHeadset, skipped auto-connect with device " + device
-                         + " priority " + priority);
-            }
+        int a2dpPriority = a2dpService.getPriority(device);
+        if (a2dpPriority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            debugLog("autoConnectA2dp: connecting A2DP with " + device);
+            a2dpService.connect(device);
+        } else {
+            debugLog("autoConnectA2dp: skipped auto-connect A2DP with device " + device
+                    + " priority " + a2dpPriority);
         }
     }
 
-    private void autoConnectA2dp() {
-        final A2dpService a2dpService = mFactory.getA2dpService();
-        if (a2dpService == null) {
-            errorLog("autoConnectA2dp, service is null");
+    private void autoConnectHeadset(BluetoothDevice device) {
+        final HeadsetService hsService = mFactory.getHeadsetService();
+        if (hsService == null) {
+            warnLog("autoConnectHeadset: service is null, failed to connect to " + device);
             return;
         }
-        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
-        if (bondedDevices == null) {
-            errorLog("autoConnectA2dp, bondedDevices are null");
-            return;
-        }
-        for (BluetoothDevice device : bondedDevices) {
-            int priority = a2dpService.getPriority(device);
-            debugLog("autoConnectA2dp, attempt auto-connect with device " + device
-                     + " priority " + priority);
-            if (priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
-                debugLog("autoConnectA2dp, connecting A2DP with " + device);
-                a2dpService.connect(device);
-            } else {
-                debugLog("autoConnectA2dp, skipped auto-connect with device " + device
-                         + " priority " + priority);
-            }
+        int headsetPriority = hsService.getPriority(device);
+        if (headsetPriority == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            debugLog("autoConnectHeadset: Connecting HFP with " + device);
+            hsService.connect(device);
+        } else {
+            debugLog("autoConnectHeadset: skipped auto-connect HFP with device " + device
+                    + " priority " + headsetPriority);
         }
     }
 
@@ -444,62 +479,74 @@
         }
     }
 
-    private void setProfileAutoConnectionPriority(BluetoothDevice device, int profileId,
-            boolean autoConnect) {
-        debugLog("setProfileAutoConnectionPriority: device=" + device + ", profile=" + profileId
-                + ", autoConnect=" + autoConnect);
-        switch (profileId) {
-            case BluetoothProfile.HEADSET: {
-                HeadsetService hsService = mFactory.getHeadsetService();
-                if (hsService == null) {
-                    warnLog("setProfileAutoConnectionPriority: HEADSET service is null");
-                    break;
-                }
-                removeAutoConnectFromDisconnectedHeadsets(hsService);
-                if (autoConnect) {
-                    hsService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
-                }
-                break;
-            }
-            case BluetoothProfile.A2DP: {
-                A2dpService a2dpService = mFactory.getA2dpService();
-                if (a2dpService == null) {
-                    warnLog("setProfileAutoConnectionPriority: A2DP service is null");
-                    break;
-                }
-                removeAutoConnectFromDisconnectedA2dpSinks(a2dpService);
-                if (autoConnect) {
-                    a2dpService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
-                }
-                break;
-            }
-            default:
-                Log.w(TAG, "Tried to set AutoConnect priority on invalid profile " + profileId);
-                break;
+    /**
+     * Set a device's headset profile priority to PRIORITY_AUTO_CONNECT if device support that
+     * profile
+     *
+     * @param device device whose headset profile priority should be PRIORITY_AUTO_CONNECT
+     */
+    private void setAutoConnectForHeadset(BluetoothDevice device) {
+        HeadsetService hsService = mFactory.getHeadsetService();
+        if (hsService == null) {
+            warnLog("setAutoConnectForHeadset: HEADSET service is null");
+            return;
+        }
+        if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
+            debugLog("setAutoConnectForHeadset: device " + device + " PRIORITY_AUTO_CONNECT");
+            hsService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
         }
     }
 
-    private void removeAutoConnectFromDisconnectedHeadsets(HeadsetService hsService) {
-        List<BluetoothDevice> connectedDeviceList = hsService.getConnectedDevices();
-        for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
-            if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT
-                    && !connectedDeviceList.contains(device)) {
-                debugLog("removeAutoConnectFromDisconnectedHeadsets, device " + device
-                        + " PRIORITY_ON");
-                hsService.setPriority(device, BluetoothProfile.PRIORITY_ON);
-            }
+    /**
+     * Set a device's A2DP profile priority to PRIORITY_AUTO_CONNECT if device support that profile
+     *
+     * @param device device whose headset profile priority should be PRIORITY_AUTO_CONNECT
+     */
+    private void setAutoConnectForA2dpSink(BluetoothDevice device) {
+        A2dpService a2dpService = mFactory.getA2dpService();
+        if (a2dpService == null) {
+            warnLog("setAutoConnectForA2dpSink: A2DP service is null");
+            return;
+        }
+        if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
+            debugLog("setAutoConnectForA2dpSink: device " + device + " PRIORITY_AUTO_CONNECT");
+            a2dpService.setPriority(device, BluetoothProfile.PRIORITY_AUTO_CONNECT);
         }
     }
 
-    private void removeAutoConnectFromDisconnectedA2dpSinks(A2dpService a2dpService) {
-        List<BluetoothDevice> connectedDeviceList = a2dpService.getConnectedDevices();
-        for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
-            if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT
-                    && !connectedDeviceList.contains(device)) {
-                debugLog("removeAutoConnectFromDisconnectedA2dpSinks, device " + device
-                        + " PRIORITY_ON");
-                a2dpService.setPriority(device, BluetoothProfile.PRIORITY_ON);
-            }
+    /**
+     * Remove PRIORITY_AUTO_CONNECT from all headsets and set headset that used to have
+     * PRIORITY_AUTO_CONNECT to PRIORITY_ON
+     *
+     * @param device device whose PRIORITY_AUTO_CONNECT priority should be removed
+     */
+    private void removeAutoConnectFromHeadset(BluetoothDevice device) {
+        HeadsetService hsService = mFactory.getHeadsetService();
+        if (hsService == null) {
+            warnLog("removeAutoConnectFromHeadset: HEADSET service is null");
+            return;
+        }
+        if (hsService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            debugLog("removeAutoConnectFromHeadset: device " + device + " PRIORITY_ON");
+            hsService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+    }
+
+    /**
+     * Remove PRIORITY_AUTO_CONNECT from all A2DP sinks and set A2DP sink that used to have
+     * PRIORITY_AUTO_CONNECT to PRIORITY_ON
+     *
+     * @param device device whose PRIORITY_AUTO_CONNECT priority should be removed
+     */
+    private void removeAutoConnectFromA2dpSink(BluetoothDevice device) {
+        A2dpService a2dpService = mFactory.getA2dpService();
+        if (a2dpService == null) {
+            warnLog("removeAutoConnectFromA2dpSink: A2DP service is null");
+            return;
+        }
+        if (a2dpService.getPriority(device) >= BluetoothProfile.PRIORITY_AUTO_CONNECT) {
+            debugLog("removeAutoConnectFromA2dpSink: device " + device + " PRIORITY_ON");
+            a2dpService.setPriority(device, BluetoothProfile.PRIORITY_ON);
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index 76ff7b9..db115ca 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -1499,6 +1499,13 @@
         doForEachConnectedStateMachine(
                 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED,
                         new HeadsetCallState(numActive, numHeld, callState, number, type)));
+        mStateMachinesThread.getThreadHandler().post(() -> {
+            if (callState == HeadsetHalConstants.CALL_STATE_IDLE
+                    && !shouldCallAudioBeActive() && !isAudioOn()) {
+                // Resume A2DP when call ended and SCO is not connected
+                mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false");
+            }
+        });
 
     }
 
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index 266358c..844c470 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -120,7 +120,7 @@
     private static final long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds
 
     // Keep track of audio routing across all devices.
-    private static boolean sAudioIsRouted = true;
+    private static boolean sAudioIsRouted = false;
 
     private final Disconnected mDisconnected;
     private final Connecting mConnecting;
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
index 8991b81..eb0de8e 100644
--- a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
@@ -657,6 +657,11 @@
             Log.d(TAG, "stop()");
         }
 
+        if (sHidDeviceService == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
         setHidDeviceService(null);
         if (mNativeAvailable) {
             mHidDeviceNativeInterface.cleanup();
diff --git a/android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java
index 9e1e44e..6636d24 100644
--- a/android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java
@@ -169,6 +169,11 @@
     protected boolean stop() {
         Log.i(TAG, "Stopping the AVRCP Target Service");
 
+        if (sInstance == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
+
         sInstance = null;
         unregisterReceiver(mReceiver);
 
@@ -198,9 +203,9 @@
     }
 
     /**
-     * Signal to the service that the current audio out device has changed. The current volume
-     * for the old device is saved and the new device has its volume restored. If there is no
-     * saved volume use the current system volume.
+     * Signal to the service that the current audio out device has changed and to inform
+     * the audio service whether the new device supports absolute volume. If it does, also
+     * set the absolute volume level on the remote device.
      */
     public void volumeDeviceSwitched(BluetoothDevice device) {
         if (DEBUG) {
@@ -209,6 +214,25 @@
         mVolumeManager.volumeDeviceSwitched(device);
     }
 
+    /**
+     * Store the current system volume for a device in order to be retrieved later.
+     */
+    public void storeVolumeForDevice(BluetoothDevice device) {
+        if (device == null) return;
+
+        mVolumeManager.storeVolumeForDevice(device);
+    }
+
+    /**
+     * Retrieve the remembered volume for a device. Returns -1 if there is no volume for the
+     * device.
+     */
+    public int getRememberedVolumeForDevice(BluetoothDevice device) {
+        if (device == null) return -1;
+
+        return mVolumeManager.getVolume(device, -1);
+    }
+
     // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly.
     void setVolume(int avrcpVolume) {
         int deviceVolume =
diff --git a/android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java b/android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java
index 9652433..f1f99d2 100644
--- a/android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java
+++ b/android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java
@@ -22,8 +22,6 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.util.Log;
 
@@ -31,7 +29,7 @@
 import java.util.Map;
 import java.util.Objects;
 
-class AvrcpVolumeManager extends AudioDeviceCallback {
+class AvrcpVolumeManager {
     public static final String TAG = "NewAvrcpVolumeManager";
     public static final boolean DEBUG = true;
 
@@ -48,7 +46,6 @@
 
     HashMap<BluetoothDevice, Boolean> mDeviceMap = new HashMap();
     HashMap<BluetoothDevice, Integer> mVolumeMap = new HashMap();
-    BluetoothDevice mDeferredDevice = null;
     BluetoothDevice mCurrentDevice = null;
     boolean mAbsoluteVolumeSupported = false;
 
@@ -67,31 +64,17 @@
         return mContext.getSharedPreferences(VOLUME_MAP, Context.MODE_PRIVATE);
     }
 
-    private int getVolume(@NonNull BluetoothDevice device, int defaultValue) {
-        if (!mVolumeMap.containsKey(device)) {
-            Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + device);
-            return defaultValue;
-        }
-
-        return mVolumeMap.get(device);
-    }
-
     private void switchVolumeDevice(@NonNull BluetoothDevice device) {
         // Inform the audio manager that the device has changed
+        d("switchVolumeDevice: Set Absolute volume support to " + mDeviceMap.get(device));
         mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), mDeviceMap.get(device));
 
         // Get the current system volume and try to get the preference volume
         int currVolume = mAudioManager.getStreamVolume(STREAM_MUSIC);
         int savedVolume = getVolume(device, currVolume);
 
-        // If the preference volume isn't equal to the current stream volume then that means
-        // we had a stored preference.
         d("switchVolumeDevice: currVolume=" + currVolume + " savedVolume=" + savedVolume);
 
-        Log.i(TAG, "switchVolumeDevice: restoring volume level " + savedVolume);
-        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, savedVolume,
-                AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
-
         // If absolute volume for the device is supported, set the volume for the device
         if (mDeviceMap.get(device)) {
             int avrcpVolume = systemToAvrcpVolume(savedVolume);
@@ -100,43 +83,6 @@
         }
     }
 
-    @Override
-    public synchronized void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
-        if (mDeferredDevice == null) {
-            d("onAudioDevicesAdded: Not expecting device changed");
-            return;
-        }
-
-        boolean foundDevice = false;
-        d("onAudioDevicesAdded: size: " + addedDevices.length);
-        for (int i = 0; i < addedDevices.length; i++) {
-            d("onAudioDevicesAdded: address=" + addedDevices[i].getAddress());
-            if (addedDevices[i].getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
-                    && Objects.equals(addedDevices[i].getAddress(), mDeferredDevice.getAddress())) {
-                foundDevice = true;
-                break;
-            }
-        }
-
-        if (!foundDevice) {
-            d("Didn't find deferred device in list: device=" + mDeferredDevice);
-            return;
-        }
-
-        mCurrentDevice = mDeferredDevice;
-        mDeferredDevice = null;
-
-        // A2DP can sometimes connect and set a device to active before AVRCP has determined if the
-        // device supports absolute volume. Defer switching the device until AVRCP returns the
-        // info.
-        if (!mDeviceMap.containsKey(mCurrentDevice)) {
-            Log.w(TAG, "volumeDeviceSwitched: Device isn't connected: " + mCurrentDevice);
-            return;
-        }
-
-        switchVolumeDevice(mCurrentDevice);
-    }
-
     AvrcpVolumeManager(Context context, AudioManager audioManager,
             AvrcpNativeInterface nativeInterface) {
         mContext = context;
@@ -144,8 +90,6 @@
         mNativeInterface = nativeInterface;
         sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
 
-        mAudioManager.registerAudioDeviceCallback(this, null);
-
         // Load the stored volume preferences into a hash map since shared preferences are slow
         // to poll and update. If the device has been unbonded since last start remove it from
         // the map.
@@ -166,19 +110,29 @@
         volumeMapEditor.apply();
     }
 
-    void storeVolume() {
+    void storeVolumeForDevice(BluetoothDevice device) {
         SharedPreferences.Editor pref = getVolumeMap().edit();
         int storeVolume =  mAudioManager.getStreamVolume(STREAM_MUSIC);
-        Log.i(TAG, "storeVolume: Storing stream volume level for device " + mCurrentDevice
+        Log.i(TAG, "storeVolume: Storing stream volume level for device " + device
                 + " : " + storeVolume);
-        mVolumeMap.put(mCurrentDevice, storeVolume);
-        pref.putInt(mCurrentDevice.getAddress(), storeVolume);
+        mVolumeMap.put(device, storeVolume);
+        pref.putInt(device.getAddress(), storeVolume);
 
         // Always use apply() since it is asynchronous, otherwise the call can hang waiting for
         // storage to be written.
         pref.apply();
     }
 
+    int getVolume(@NonNull BluetoothDevice device, int defaultValue) {
+        if (!mVolumeMap.containsKey(device)) {
+            Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + device);
+            return defaultValue;
+        }
+
+        d("getVolume: Returning volume " + mVolumeMap.get(device));
+        return mVolumeMap.get(device);
+    }
+
     synchronized void deviceConnected(@NonNull BluetoothDevice device, boolean absoluteVolume) {
         d("deviceConnected: device=" + device + " absoluteVolume=" + absoluteVolume);
 
@@ -198,17 +152,23 @@
             return;
         }
 
-        // Store the previous volume if a device was active.
-        if (mCurrentDevice != null) {
-            storeVolume();
-        }
-
         // Wait until AudioManager informs us that the new device is connected
-        mDeferredDevice = device;
+        mCurrentDevice = device;
 
         // If device is null, that means that there is no active Bluetooth device
         if (device == null) {
             mCurrentDevice = null;
+            return;
+        }
+
+        // If the device is connected then switch the device which informs audio manager that
+        // absolute volume is supported and set the volume for the device. Otherwise disable
+        // absolute volume until the new device connects to prevent sending unattenuated audio.
+        if (mDeviceMap.containsKey(device)) {
+            switchVolumeDevice(device);
+        } else {
+            d("volumeDeviceSwitched: Set Absolute Volume support to false until device connects.");
+            mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), false);
         }
     }
 
@@ -220,7 +180,7 @@
     public void dump(StringBuilder sb) {
         sb.append("AvrcpVolumeManager:\n");
         sb.append("  mCurrentDevice: " + mCurrentDevice + "\n");
-        sb.append("  mDeferredDevice: " + mDeferredDevice + "\n");
+        sb.append("  Current System Volume: " + mAudioManager.getStreamVolume(STREAM_MUSIC) + "\n");
         sb.append("  Device Volume Memory Map:\n");
         sb.append(String.format("    %-17s : %-14s : %3s : %s\n",
                 "Device Address", "Device Name", "Vol", "AbsVol"));
diff --git a/android/app/src/com/android/bluetooth/newavrcp/MediaPlayerList.java b/android/app/src/com/android/bluetooth/newavrcp/MediaPlayerList.java
index 1316c77..0a6b946 100644
--- a/android/app/src/com/android/bluetooth/newavrcp/MediaPlayerList.java
+++ b/android/app/src/com/android/bluetooth/newavrcp/MediaPlayerList.java
@@ -194,10 +194,6 @@
             player.disconnect();
         }
         mBrowsablePlayers.clear();
-
-        mMediaPlayerIds = null;
-        mMediaPlayers = null;
-        mBrowsablePlayers = null;
     }
 
     int getCurrentPlayerId() {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
index 0cc9317..6b27256 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
@@ -90,6 +90,7 @@
 
         TestUtils.setAdapterService(mAdapterService);
         doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices();
+        doReturn(false).when(mAdapterService).isQuietModeEnabled();
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
@@ -851,18 +852,28 @@
     }
 
     /**
-     *  Helper function to test okToConnect() method
+     * Helper function to test okToConnect() method.
      *
-     *  @param device test device
-     *  @param bondState bond state value, could be invalid
-     *  @param priority value, could be invalid, coudl be invalid
-     *  @param expected expected result from okToConnect()
+     * @param device test device
+     * @param bondState bond state value, could be invalid
+     * @param priority value, could be invalid, coudl be invalid
+     * @param expected expected result from okToConnect()
      */
     private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority,
             boolean expected) {
         doReturn(bondState).when(mAdapterService).getBondState(device);
         Assert.assertTrue(mA2dpService.setPriority(device, priority));
-        Assert.assertEquals(expected, mA2dpService.okToConnect(device));
-    }
 
+        // Test when the AdapterService is in non-quiet mode: the result should not depend
+        // on whether the connection request is outgoing or incoming.
+        doReturn(false).when(mAdapterService).isQuietModeEnabled();
+        Assert.assertEquals(expected, mA2dpService.okToConnect(device, true));  // Outgoing
+        Assert.assertEquals(expected, mA2dpService.okToConnect(device, false)); // Incoming
+
+        // Test when the AdapterService is in quiet mode: the result should always be
+        // false when the connection request is incoming.
+        doReturn(true).when(mAdapterService).isQuietModeEnabled();
+        Assert.assertEquals(expected, mA2dpService.okToConnect(device, true));  // Outgoing
+        Assert.assertEquals(false, mA2dpService.okToConnect(device, false)); // Incoming
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
index 7bbdc39..0b825bf 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
@@ -107,7 +107,8 @@
      * @param allow if true, connection is allowed
      */
     private void allowConnection(boolean allow) {
-        doReturn(allow).when(mA2dpService).okToConnect(any(BluetoothDevice.class));
+        doReturn(allow).when(mA2dpService).okToConnect(any(BluetoothDevice.class),
+                                                       anyBoolean());
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
index b36b03d..37a9ded 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
@@ -151,8 +151,8 @@
         mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);
 
         // Check that we got a request to connect over HFP and A2DP
-        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(bondedDevices[0]));
         verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(bondedDevices[0]));
+        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connect(eq(bondedDevices[0]));
     }
 
     /**
@@ -173,9 +173,6 @@
         bondedDevices[3] = TestUtils.getTestDevice(mAdapter, 3);
         when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices);
 
-        ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>();
-        doAnswer(invocation -> connectedDevices).when(mHeadsetService).getConnectedDevices();
-
         // Make all devices auto connect
         when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn(
                 BluetoothProfile.PRIORITY_AUTO_CONNECT);
@@ -186,76 +183,76 @@
         when(mHeadsetService.getPriority(bondedDevices[3])).thenReturn(
                 BluetoothProfile.PRIORITY_OFF);
 
-        // Make one of the device connected
-        connectedDevices.add(bondedDevices[0]);
-        when(mHeadsetService.getConnectionState(bondedDevices[0])).thenReturn(
-                BluetoothProfile.STATE_CONNECTED);
-        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        // Make one of the device active
+        Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);
 
         // All other disconnected device's priority is set to ON, except disabled ones
         verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0],
-                BluetoothProfile.PRIORITY_AUTO_CONNECT);
+                BluetoothProfile.PRIORITY_ON);
         verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[1],
                 BluetoothProfile.PRIORITY_ON);
         verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[2],
                 BluetoothProfile.PRIORITY_ON);
+        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0],
+                BluetoothProfile.PRIORITY_AUTO_CONNECT);
         verify(mHeadsetService, never()).setPriority(eq(bondedDevices[3]), anyInt());
         when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn(
                 BluetoothProfile.PRIORITY_ON);
         when(mHeadsetService.getPriority(bondedDevices[2])).thenReturn(
                 BluetoothProfile.PRIORITY_ON);
 
-        // Make another device connected
-        connectedDevices.add(bondedDevices[1]);
+        // Make another device active
         when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn(
                 BluetoothProfile.STATE_CONNECTED);
-        intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[1]);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);
 
-        // This device should be set to auto connect
+        // This device should be set to auto connect while the first device is reset to ON
+        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0],
+                BluetoothProfile.PRIORITY_ON);
         verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[1],
                 BluetoothProfile.PRIORITY_AUTO_CONNECT);
         verify(mHeadsetService, never()).setPriority(eq(bondedDevices[3]), anyInt());
+        when(mHeadsetService.getPriority(bondedDevices[0])).thenReturn(
+                BluetoothProfile.PRIORITY_ON);
         when(mHeadsetService.getPriority(bondedDevices[1])).thenReturn(
                 BluetoothProfile.PRIORITY_AUTO_CONNECT);
 
-        // Make one of the device disconnect
-        connectedDevices.remove(bondedDevices[0]);
-        when(mHeadsetService.getConnectionState(bondedDevices[0])).thenReturn(
+        // Set active device to null
+        when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
-        intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
+        intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, (BluetoothDevice) null);
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);
 
         // This should not have any effect
-        verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).never()).setPriority(
-                bondedDevices[0], BluetoothProfile.PRIORITY_ON);
+        verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).setPriority(
+                bondedDevices[1], BluetoothProfile.PRIORITY_ON);
 
+        // Make the current active device fail to connect
+        when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn(
+                BluetoothProfile.STATE_DISCONNECTED);
+        when(mA2dpService.getConnectionState(bondedDevices[1])).thenReturn(
+                BluetoothProfile.STATE_DISCONNECTED);
         intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[0]);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bondedDevices[1]);
         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
         intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mPhonePolicy.getBroadcastReceiver().onReceive(null /* context */, intent);
 
         // This device should be set to ON
-        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).setPriority(bondedDevices[0],
-                BluetoothProfile.PRIORITY_ON);
+        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).setPriority(
+                bondedDevices[1], BluetoothProfile.PRIORITY_ON);
 
         // Verify that we are not setting priorities to random devices and values
-        verify(mHeadsetService, times(5)).setPriority(any(BluetoothDevice.class), anyInt());
+        verify(mHeadsetService, times(7)).setPriority(any(BluetoothDevice.class), anyInt());
     }
 
     /**
@@ -304,6 +301,10 @@
 
     /**
      * Test that a second device will auto-connect if there is already one connected device.
+     *
+     * Even though we currently only set one device to be auto connect. The consumer of the auto
+     * connect property works independently so that we will connect to all devices that are in
+     * auto connect mode.
      */
     @Test
     public void testAutoConnectMultipleDevices() {
@@ -472,13 +473,14 @@
 
         // Check the connect priorities for all devices
         // - testDevices[0] - connected for HFP and A2DP: setPriority() should not be called
-        // - testDevices[1] - connection state changed for HFP - auto-connect completed for A2DP
-        //                    expect setPriority(PRIORITY_AUTO_CONNECT) for HFP
+        // - testDevices[1] - connection state changed for HFP should no longer trigger auto
+        //                    connect priority change since it is now triggered by A2DP active
+        //                    device change intent
         // - testDevices[2] - connected for A2DP: setPriority() should not be called
         // - testDevices[3] - not connected for HFP nor A2DP: setPriority() should not be called
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[0]), anyInt());
         verify(mA2dpService, times(0)).setPriority(eq(testDevices[0]), anyInt());
-        verify(mHeadsetService, times(1)).setPriority(eq(testDevices[1]),
+        verify(mHeadsetService, times(0)).setPriority(eq(testDevices[1]),
                 eq(BluetoothProfile.PRIORITY_AUTO_CONNECT));
         verify(mA2dpService, times(0)).setPriority(eq(testDevices[1]), anyInt());
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[2]), anyInt());
@@ -508,15 +510,16 @@
         // Check the connect priorities for all devices
         // - testDevices[0] - connected for HFP and A2DP: setPriority() should not be called
         // - testDevices[1] - connected for HFP and A2DP: setPriority() should not be called
-        // - testDevices[2] - connection state changed for A2DP - auto-connect completed for HFP
-        //                    expected setPriority(PRIORITY_AUTO_CONNECT) for A2DP
+        // - testDevices[2] - connection state changed for A2DP should no longer trigger auto
+        //                    connect priority change since it is now triggered by A2DP
+        //                    active device change intent
         // - testDevices[3] - not connected for HFP nor A2DP: setPriority() should not be called
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[0]), anyInt());
         verify(mA2dpService, times(0)).setPriority(eq(testDevices[0]), anyInt());
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[1]), anyInt());
         verify(mA2dpService, times(0)).setPriority(eq(testDevices[1]), anyInt());
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[2]), anyInt());
-        verify(mA2dpService, times(1)).setPriority(eq(testDevices[2]),
+        verify(mA2dpService, times(0)).setPriority(eq(testDevices[2]),
                 eq(BluetoothProfile.PRIORITY_AUTO_CONNECT));
         verify(mHeadsetService, times(0)).setPriority(eq(testDevices[3]), anyInt());
         verify(mA2dpService, times(0)).setPriority(eq(testDevices[3]), anyInt());