Merge "A2dp: Support A2dp Source and A2dpSink profiles concurrency." into q-keystone-qcom-dev
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0fecd58..4a4eaaa 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -111,12 +111,12 @@
     <string name="outbound_noti_title" msgid="8051906709452260849">"Bluetooth: archivos enviados"</string>
     <string name="inbound_noti_title" msgid="4143352641953027595">"Bluetooth: archivos recibidos"</string>
     <plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
-      <item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> con error</item>
-      <item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_0">%1$d</xliff:g> con error</item>
+      <item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> incompletos</item>
+      <item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_0">%1$d</xliff:g> incompleto </item>
     </plurals>
     <plurals name="noti_caption_success" formatted="false" msgid="1572472450257645181">
-      <item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> correctos, %2$s</item>
-      <item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_0">%1$d</xliff:g> correcto, %2$s</item>
+      <item quantity="other"><xliff:g id="SUCCESSFUL_NUMBER_1">%1$d</xliff:g> completos, %2$s</item>
+      <item quantity="one"><xliff:g id="SUCCESSFUL_NUMBER_0">%1$d</xliff:g> completo, %2$s</item>
     </plurals>
     <string name="transfer_menu_clear_all" msgid="790017462957873132">"Eliminar lista"</string>
     <string name="transfer_menu_open" msgid="3368984869083107200">"Abrir"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 3d98cb3..dbc425a 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -23,7 +23,7 @@
     <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
     <string name="unknown_device" msgid="9221903979877041009">"Tundmatu seade"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Tundmatu"</string>
-    <string name="airplane_error_title" msgid="2683839635115739939">"Lennurežiim"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Lennukirežiim"</string>
     <string name="airplane_error_msg" msgid="8698965595254137230">"Te ei saa Bluetoothi lennurežiimis kasutada."</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">"Bluetoothi teenuste kasutamiseks peate esmalt Bluetoothi sisse lülitama."</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 9adbeff..cc6e417 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -21,7 +21,7 @@
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Jarri onartutakoen zerrendan Bluetooth bidezko gailua."</string>
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan jartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe."</string>
     <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth-a"</string>
-    <string name="unknown_device" msgid="9221903979877041009">"Gailu ezezaguna"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Identifikatu ezin den gailua"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Ezezaguna"</string>
     <string name="airplane_error_title" msgid="2683839635115739939">"Hegaldi modua"</string>
     <string name="airplane_error_msg" msgid="8698965595254137230">"Ezin duzu erabili Bluetooth-a Hegaldi moduan."</string>
@@ -84,7 +84,7 @@
     <string name="bt_toast_4" msgid="4678812947604395649">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" hartzaileari fitxategia bidaltzen"</string>
     <string name="bt_toast_5" msgid="2846870992823019494">"\"<xliff:g id="RECIPIENT">%2$s</xliff:g>\" hartzaileari <xliff:g id="NUMBER">%1$s</xliff:g> fitxategi bidaltzen"</string>
     <string name="bt_toast_6" msgid="1855266596936622458">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" hartzaileari fitxategia bidaltzeari utzi zaio."</string>
-    <string name="bt_sm_2_1_nosdcard" msgid="1791835163301501637">"Ez dago \"<xliff:g id="SENDER">%1$s</xliff:g>\" erabiltzailearen fitxategia gordetzeko behar adina toki USB memorian"</string>
+    <string name="bt_sm_2_1_nosdcard" msgid="1791835163301501637">"Ez dago \"<xliff:g id="SENDER">%1$s</xliff:g>\" erabiltzailearen fitxategia gordetzeko behar adina toki USB bidezko memorian"</string>
     <string name="bt_sm_2_1_default" msgid="9115512207909504071">"Ez dago \"<xliff:g id="SENDER">%1$s</xliff:g>\" erabiltzailearen fitxategia gordetzeko behar adina toki SD txartelean"</string>
     <string name="bt_sm_2_2" msgid="2965243265852680543">"Beharrezko memoria: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="ErrorTooManyRequests" msgid="8578277541472944529">"Eskaera gehiegi prozesatzen ari dira. Saiatu berriro geroago."</string>
@@ -95,7 +95,7 @@
     <string name="status_forbidden" msgid="613956401054050725">"Xede-gailuak transferentzia debekatu du."</string>
     <string name="status_canceled" msgid="6664490318773098285">"Erabiltzaileak bertan behera utzi du transferentzia."</string>
     <string name="status_file_error" msgid="3671917770630165299">"Memoria-arazoa."</string>
-    <string name="status_no_sd_card_nosdcard" msgid="573631036356922221">"Ez dago USB memoriarik."</string>
+    <string name="status_no_sd_card_nosdcard" msgid="573631036356922221">"Ez dago USB bidezko memoriarik."</string>
     <string name="status_no_sd_card_default" msgid="396564893716701954">"Ez dago SD txartelik. Transferitutako fitxategiak gordetzeko, sartu SD txartel bat."</string>
     <string name="status_connection_error" msgid="947681831523219891">"Ezin izan da konektatu."</string>
     <string name="status_protocol_error" msgid="3245444473429269539">"Ezin da eskaera behar bezala kudeatu."</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 30563fb..d01d137 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"डाउनलोड प्रबंधक में पहुंच प्राप्त करें."</string>
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"डाउनलोड मैनेजर में पहुंच पाएं."</string>
     <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"ऐप्लिकेशन को BluetoothShare प्रबंधक के इस्तेमाल की मंज़ूरी देता है और फ़ाइलों को ट्रांसफ़र करने के लिए उसका उपयोग करने देता है."</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"श्वेतसूची bluetooth डिवाइस पहुंच."</string>
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"ऐप को, ब्लूटूथ डिवाइस को उपयोगकर्ता की पुष्टि के बिना इस डिवाइस पर फ़ाइल भेजने की‍ अनुमति देकर, कुछ देर के लिए अनुमति देता है"</string>
@@ -38,9 +38,9 @@
     <string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" से आने वाली फ़ाइल स्वीकार करते हुए टाइम आउट हो गया."</string>
     <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"आवक फ़ाइल"</string>
     <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> भेजने के लिए तैयार है"</string>
-    <string name="notification_receiving" msgid="4674648179652543984">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त कर रहा है"</string>
-    <string name="notification_received" msgid="3324588019186687985">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> प्राप्त"</string>
-    <string name="notification_received_fail" msgid="3619350997285714746">"ब्लूटूथ शेयर: फ़ाइल <xliff:g id="FILE">%1$s</xliff:g> प्राप्त नहीं हुई"</string>
+    <string name="notification_receiving" msgid="4674648179652543984">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> पा रहा है"</string>
+    <string name="notification_received" msgid="3324588019186687985">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> पाई गई"</string>
+    <string name="notification_received_fail" msgid="3619350997285714746">"ब्लूटूथ शेयर: फ़ाइल <xliff:g id="FILE">%1$s</xliff:g> नहीं मिली"</string>
     <string name="notification_sending" msgid="3035748958534983833">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> भेज रहा है"</string>
     <string name="notification_sent" msgid="9218710861333027778">"ब्लूटूथ शेयर: <xliff:g id="FILE">%1$s</xliff:g> भेजा गया"</string>
     <string name="notification_sent_complete" msgid="302943281067557969">"100% पूरा"</string>
@@ -50,17 +50,17 @@
     <string name="download_line2" msgid="5876973543019417712">"फ़ाइल: <xliff:g id="FILE">%1$s</xliff:g>"</string>
     <string name="download_line3" msgid="4384821622908676061">"फ़ाइल आकार: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="download_line4" msgid="8535996869722666525"></string>
-    <string name="download_line5" msgid="3069560415845295386">"फ़ाइल प्राप्त कर रहा है…"</string>
+    <string name="download_line5" msgid="3069560415845295386">"फ़ाइल पा रहा है…"</string>
     <string name="download_cancel" msgid="9177305996747500768">"रोकें"</string>
     <string name="download_ok" msgid="5000360731674466039">"छुपाएं"</string>
     <string name="incoming_line1" msgid="2127419875681087545">"प्रेषक"</string>
     <string name="incoming_line2" msgid="3348994249285315873">"फ़ाइल नाम"</string>
     <string name="incoming_line3" msgid="7954237069667474024">"आकार"</string>
-    <string name="download_fail_line1" msgid="3846450148862894552">"फ़ाइल प्राप्त नहीं हुई"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"फ़ाइल नहीं मिली"</string>
     <string name="download_fail_line2" msgid="8950394574689971071">"फ़ाइल: <xliff:g id="FILE">%1$s</xliff:g>"</string>
     <string name="download_fail_line3" msgid="3451040656154861722">"कारण: <xliff:g id="REASON">%1$s</xliff:g>"</string>
     <string name="download_fail_ok" msgid="1521733664438320300">"ठीक है"</string>
-    <string name="download_succ_line5" msgid="4509944688281573595">"फ़ाइल प्राप्त की गई"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"फ़ाइल मिली"</string>
     <string name="download_succ_ok" msgid="7053688246357050216">"खोलें"</string>
     <string name="upload_line1" msgid="2055952074059709052">"प्रति: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
     <string name="upload_line3" msgid="4920689672457037437">"फ़ाइल प्रकार: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string>
@@ -79,8 +79,8 @@
     <string name="enabling_progress_title" msgid="436157952334723406">"कृपया प्रतीक्षा करें..."</string>
     <string name="enabling_progress_content" msgid="4601542238119927904">"ब्लूटूथ चालू कर रहा है…"</string>
     <string name="bt_toast_1" msgid="972182708034353383">"फ़ाइल मिलेगी. सूचना पैनल में प्रगति देखें."</string>
-    <string name="bt_toast_2" msgid="8602553334099066582">"फ़ाइल प्राप्त नहीं की जा सकती."</string>
-    <string name="bt_toast_3" msgid="6707884165086862518">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" से फ़ाइल प्राप्त करना रोका गया"</string>
+    <string name="bt_toast_2" msgid="8602553334099066582">"फ़ाइल पाई नहीं जा सकती."</string>
+    <string name="bt_toast_3" msgid="6707884165086862518">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" से फ़ाइल पाना रोका गया"</string>
     <string name="bt_toast_4" msgid="4678812947604395649">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" को फ़ाइल भेज रहा है"</string>
     <string name="bt_toast_5" msgid="2846870992823019494">"\"<xliff:g id="RECIPIENT">%2$s</xliff:g>\" को <xliff:g id="NUMBER">%1$s</xliff:g> फ़ाइलें भेज रहा है"</string>
     <string name="bt_toast_6" msgid="1855266596936622458">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" को फ़ाइल भेजना रोका गया"</string>
@@ -109,7 +109,7 @@
     <string name="no_transfers" msgid="3482965619151865672">"कुछ भी ट्रांसफ़र नहीं किया गया."</string>
     <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"सूची से सभी आइटम साफ़ कर दिए जाएंगे."</string>
     <string name="outbound_noti_title" msgid="8051906709452260849">"ब्लूटूथ शेयर: भेजी गई फ़ाइलें"</string>
-    <string name="inbound_noti_title" msgid="4143352641953027595">"ब्लूटूथ शेयर: प्राप्त फ़ाइलें"</string>
+    <string name="inbound_noti_title" msgid="4143352641953027595">"ब्लूटूथ शेयर: पाई गई फ़ाइलें"</string>
     <plurals name="noti_caption_unsuccessful" formatted="false" msgid="2020750076679526122">
       <item quantity="one"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> असफल.</item>
       <item quantity="other"><xliff:g id="UNSUCCESSFUL_NUMBER_1">%1$d</xliff:g> असफल.</item>
@@ -125,7 +125,7 @@
     <string name="bluetooth_a2dp_sink_queue_name" msgid="6864149958708669766">"अभी चल रहा है"</string>
     <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"सेव करें"</string>
     <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"रद्द करें"</string>
-    <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"वे खाते चुनें जिन्हें आप ब्लूटूथ के ज़रिये शेयर करना चाहते हैं. आपको अब भी कनेक्ट करते समय खातों के किसी भी एक्सेस को स्वीकार करना होगा."</string>
+    <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"वे खाते चुनें जिन्हें आप ब्लूटूथ के ज़रिये शेयर करना चाहते हैं. आपको अब भी कनेक्ट करते समय खातों के किसी भी ऐक्सेस को स्वीकार करना होगा."</string>
     <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"शेष स्लॉट:"</string>
     <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ऐप्लिकेशन आइकॉन"</string>
     <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ब्लूटूथ संदेश साझाकरण सेटिंग"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 27ccaac..6171772 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ダウンロードマネージャーにアクセスします。"</string>
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ダウンロード マネージャーにアクセスします。"</string>
     <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"BluetoothShareマネージャーへのアクセスとそれを利用したファイル転送をアプリに許可します。"</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Bluetoothデバイスによるアクセスを許可します。"</string>
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Bluetoothデバイスによるアクセスを一時的に許可して、ユーザーの確認を受けずにそのデバイスからこのデバイスにファイルを送信することをアプリに許可します。"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index f188ae0..011c39d 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -23,7 +23,7 @@
     <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
     <string name="unknown_device" msgid="9221903979877041009">"Непознат уред"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Непознат"</string>
-    <string name="airplane_error_title" msgid="2683839635115739939">"Режим на работа во авион"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Авионски режим"</string>
     <string name="airplane_error_msg" msgid="8698965595254137230">"Не можете да користите Bluetooth во режим на авион."</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">"За Bluetooth услуги, прво мора да вклучите Bluetooth."</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 54677ac..e584f9f 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -127,7 +127,7 @@
     <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"ரத்துசெய்"</string>
     <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"புளூடூத் வழியாகப் பகிர விரும்பும் கணக்குகளைத் தேர்ந்தெடுக்கவும். இணைக்கும் போது கணக்குகளுக்கான அணுகலை மீண்டும் ஏற்க வேண்டும்."</string>
     <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"மீதமுள்ள ஸ்லாட்கள்:"</string>
-    <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"பயன்பாட்டு ஐகான்"</string>
+    <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"ஆப்ஸ் ஐகான்"</string>
     <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"புளூடூத் செய்தி பகிர்தல் அமைப்புகள்"</string>
     <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"கணக்கைத் தேர்வுசெய்ய முடியாது. ஸ்லாட்கள் எதுவுமில்லை"</string>
     <string name="bluetooth_connected" msgid="6718623220072656906">"புளூடூத் ஆடியோ இணைக்கப்பட்டது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index cb26069..7aaeaa4 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -36,7 +36,7 @@
     <string name="incoming_file_confirm_ok" msgid="281462442932231475">"అంగీకరిస్తున్నాను"</string>
     <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"సరే"</string>
     <string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" పంపిన ఇన్‌కమింగ్ ఫైల్‌ను అంగీకరిస్తున్నప్పుడు గడువు సమయం ముగిసింది"</string>
-    <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ఫైల్ స్వీకరణ"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="5573329005298936903">"ఇన్‌క‌మింగ్‌ ఫైల్"</string>
     <string name="incoming_file_confirm_Notification_content" msgid="3359694069319644738">"<xliff:g id="SENDER">%1$s</xliff:g> <xliff:g id="FILE">%2$s</xliff:g> ఫైల్ పంపడానికి సిద్ధంగా ఉన్నారు"</string>
     <string name="notification_receiving" msgid="4674648179652543984">"బ్లూటూత్ భాగస్వామ్యం: <xliff:g id="FILE">%1$s</xliff:g>ను స్వీకరిస్తోంది"</string>
     <string name="notification_received" msgid="3324588019186687985">"బ్లూటూత్ భాగస్వామ్యం: <xliff:g id="FILE">%1$s</xliff:g> స్వీకరించబడింది"</string>
@@ -104,7 +104,7 @@
     <string name="opp_notification_group" msgid="3486303082135789982">"బ్లూటూత్ భాగస్వామ్యం"</string>
     <string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> స్వీకరించడం పూర్తయింది."</string>
     <string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> పంపడం పూర్తయింది."</string>
-    <string name="inbound_history_title" msgid="6940914942271327563">"స్వీకృత బదిలీలు"</string>
+    <string name="inbound_history_title" msgid="6940914942271327563">"ఇన్‌బౌండ్ బదిలీలు"</string>
     <string name="outbound_history_title" msgid="4279418703178140526">"అవుట్‌బౌండ్ బదిలీలు"</string>
     <string name="no_transfers" msgid="3482965619151865672">"బదిలీ చరిత్ర ఖాళీగా ఉంది."</string>
     <string name="transfer_clear_dlg_msg" msgid="1712376797268438075">"జాబితా నుండి అన్ని అంశాలు క్లియర్ చేయబడతాయి."</string>
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 306939c..521b00c 100755
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -57,6 +57,7 @@
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 /**
  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
@@ -81,6 +82,7 @@
     private final Object mBtAvrcpLock = new Object();
     private final Object mActiveDeviceLock = new Object();
     private final Object mVariableLock = new Object();
+    private final ReentrantReadWriteLock mA2dpNativeInterfaceLock = new ReentrantReadWriteLock();
 
     @VisibleForTesting
     A2dpNativeInterface mA2dpNativeInterface;
@@ -179,8 +181,16 @@
         synchronized (mVariableLock) {
             mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when A2dpService starts");
+        }
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
             mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
                 "A2dpNativeInterface cannot be null when A2dpService starts");
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+        BluetoothCodecConfig[] OffloadCodecConfig;
+        synchronized (mVariableLock) {
             mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
             Objects.requireNonNull(mAudioManager,
                                "AudioManager cannot be null when A2dpService starts");
@@ -233,10 +243,20 @@
             // Step 6: Initialize native interface
             List<BluetoothCodecConfig> mCodecConfigOffload;
             mCodecConfigOffload = mAudioManager.getHwOffloadEncodingFormatsSupportedForA2DP();
-            BluetoothCodecConfig[] OffloadCodecConfig  = new BluetoothCodecConfig[mCodecConfigOffload.size()];
+            OffloadCodecConfig  = new BluetoothCodecConfig[mCodecConfigOffload.size()];
             OffloadCodecConfig  = mCodecConfigOffload.toArray(OffloadCodecConfig);
-             mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
-                           mA2dpCodecConfig.codecConfigPriorities(),OffloadCodecConfig);
+        }
+
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
+            if (mA2dpNativeInterface != null)
+                mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
+                        mA2dpCodecConfig.codecConfigPriorities(),OffloadCodecConfig);
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+
+        synchronized (mVariableLock) {
 
             // Step 7: Check if A2DP is in offload mode
             mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
@@ -287,10 +307,14 @@
         unregisterReceiver(mBondStateChangedReceiver);
         mBondStateChangedReceiver = null;
         // Step 6: Cleanup native interface
-        synchronized (mVariableLock) {
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
             if (mA2dpNativeInterface != null)
                 mA2dpNativeInterface.cleanup();
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
         }
+
         // Step 5: Clear codec config
         mA2dpCodecConfig = null;
 
@@ -333,10 +357,16 @@
             mSetMaxConnectedAudioDevices = 1;
             // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
             mAudioManager = null;
-            mA2dpNativeInterface = null;
             mAdapterService = null;
         }
 
+        try {
+            mA2dpNativeInterfaceLock.writeLock().lock();
+            mA2dpNativeInterface = null;
+        } finally {
+            mA2dpNativeInterfaceLock.writeLock().unlock();
+        }
+
         return true;
     }
 
@@ -738,11 +768,14 @@
            }
         }
         // Make sure the Active device in native layer is set to null and audio is off
-        synchronized (mVariableLock ) {
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(null)) {
                  Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
                         + "layer");
             }
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
     }
 
@@ -764,13 +797,18 @@
             // Set the device as the active device if currently no active device.
             setActiveDevice(device);
         }
-        synchronized (mVariableLock) {
+
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null &&
                !mA2dpNativeInterface.setSilenceDevice(device, silence)) {
                 Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
                 return false;
             }
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
+
         return true;
     }
 
@@ -799,8 +837,17 @@
     public boolean setActiveDevice(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
 
+        synchronized (mBtA2dpLock) {
+            if(Objects.equals(device, mActiveDevice)) {
+                Log.e(TAG, "setActiveDevice(" + device + "): already set to active ");
+                return true;
+            }
+        }
+
+        boolean playReq = device != null &&
+                        mActiveDevice != null && isA2dpPlaying(mActiveDevice);
         if(mAvrcp_ext != null) {
-            return mAvrcp_ext.startSHO(device, false);
+            return mAvrcp_ext.startSHO(device, playReq);
         }
 
         return false;
@@ -813,32 +860,28 @@
     }
 
     private boolean setActiveDeviceInternal(BluetoothDevice device) {
-        boolean deviceChanged;
         BluetoothCodecStatus codecStatus = null;
         BluetoothDevice previousActiveDevice = mActiveDevice;
         boolean isBAActive = false;
         boolean tws_switch = false;
         Log.w(TAG, "setActiveDevice(" + device + "): previous is " + previousActiveDevice);
 
+        if (device == null) {
+            // Remove active device and continue playing audio only if necessary.
+            removeActiveDevice(false);
+            synchronized(mBtAvrcpLock) {
+                if(mAvrcp_ext != null)
+                    mAvrcp_ext.setActiveDevice(device);
+            }
+            return true;
+        }
+
         synchronized (mBtA2dpLock) {
             BATService mBatService = BATService.getBATService();
             isBAActive = (mBatService != null) && (mBatService.isBATActive());
             Log.d(TAG," setActiveDevice: BA active " + isBAActive);
 
-            if (device == null) {
-                // Remove active device and continue playing audio only if necessary.
-                removeActiveDevice(false);
-                if(mAvrcp_ext != null)
-                    mAvrcp_ext.setActiveDevice(device);
-                return true;
-            }
-
             A2dpStateMachine sm = mStateMachines.get(device);
-            deviceChanged = !Objects.equals(device, mActiveDevice);
-            if (!deviceChanged) {
-                Log.e(TAG, "setActiveDevice(" + device + "): already set to active ");
-                return true;
-            }
             if (sm == null) {
                 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active: "
                           + "no state machine");
@@ -849,24 +892,34 @@
                           + "device is not connected");
                 return false;
             }
+
             synchronized (mVariableLock) {
-               if (mActiveDevice != null && mAdapterService != null &&
-                    mAdapterService.isTwsPlusDevice(device) &&
-                    mAdapterService.isTwsPlusDevice(mActiveDevice) &&
-                    !Objects.equals(device, mActiveDevice) &&
-                    getConnectionState(mActiveDevice) == BluetoothProfile.STATE_CONNECTED) {
-                    Log.d(TAG,"Ignore setActiveDevice request");
-                    return false;
+                if (mAdapterService != null && previousActiveDevice != null &&
+                            (mAdapterService.isTwsPlusDevice(device) &&
+                             mAdapterService.isTwsPlusDevice(previousActiveDevice))) {
+                    if(getConnectionState(previousActiveDevice) == BluetoothProfile.STATE_CONNECTED) {
+                        Log.d(TAG,"Ignore setActiveDevice request for pair-earbud of active earbud");
+                        return false;
+                    }
+                    Log.d(TAG,"TWS+ active device disconnected, setting its pair-earbud as active");
+                    tws_switch = true;
                 }
             }
+
             codecStatus = sm.getCodecStatus();
 
-            if (deviceChanged) {
-                // Switch from one A2DP to another A2DP device
-                if (DBG) {
-                    Log.d(TAG, "Switch A2DP devices to " + device + " from " + mActiveDevice);
+            // Switch from one A2DP to another A2DP device
+            if (DBG) {
+                Log.d(TAG, "Switch A2DP devices to " + device + " from " + mActiveDevice);
+            }
+            storeActiveDeviceVolume();
+
+            if(previousActiveDevice != null && !tws_switch && isA2dpPlaying(previousActiveDevice)) {
+                if (mAudioManager != null && !mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)) {
+                    mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
+                            AudioManager.ADJUST_MUTE,
+                            mAudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
                 }
-                storeActiveDeviceVolume();
             }
 
             // This needs to happen before we inform the audio manager that the device
@@ -874,75 +927,71 @@
             Log.w(TAG, "setActiveDevice coming out of mutex lock");
         }
 
-        synchronized (mVariableLock) {
+        try {
+            mA2dpNativeInterfaceLock.readLock().lock();
             if (mA2dpNativeInterface != null && !mA2dpNativeInterface.setActiveDevice(device)) {
                 Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native layer");
                 return false;
             }
+
+        } finally {
+            mA2dpNativeInterfaceLock.readLock().unlock();
         }
 
+
         updateAndBroadcastActiveDevice(device);
         Log.d(TAG, "setActiveDevice(" + device + "): completed");
 
-        if (deviceChanged) {
-            if(mAvrcp_ext != null)
-                mAvrcp_ext.setActiveDevice(device);
-            synchronized (mVariableLock) {
-                if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
-                    (previousActiveDevice != null && mAdapterService.isTwsPlusDevice(previousActiveDevice))) {
-                    Log.d(TAG,"TWS+ active device disconnected, setting other pair as active");
-                    tws_switch = true;
-                }
-            }
-            // Send an intent with the active device codec config
-            if (codecStatus != null) {
-                broadcastCodecConfig(mActiveDevice, codecStatus);
-            }
-            int rememberedVolume = -1;
-            synchronized (mVariableLock) {
-                if (mFactory.getAvrcpTargetService() != null) {
-                    rememberedVolume = mFactory.getAvrcpTargetService()
-                           .getRememberedVolumeForDevice(mActiveDevice);
-                } else if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
-                    rememberedVolume = mAvrcp_ext.getVolume(device);
-                    Log.d(TAG,"volume = " + rememberedVolume);
-                }
-                // Make sure the Audio Manager knows the previous Active device is disconnected,
-                // and the new Active device is connected.
-
-                if (!isBAActive && mAudioManager != null) {
-                // Make sure the Audio Manager knows the previous
-                // Active device is disconnected, and the new Active
-                // device is connected.
-                // Also, provide information about codec used by
-                // new active device so that Audio Service
-                // can reset accordingly the audio feeding parameters
-                // in the Audio HAL to the Bluetooth stack.
-                    mAudioManager.handleBluetoothA2dpActiveDeviceChange(
-                            mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
-                              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.
-
-            // Split A2dp will be enabled by default
-                boolean isSplitA2dpEnabled = true;
-                AdapterService adapterService = AdapterService.getAdapterService();
-
-                if (adapterService != null){
-                    isSplitA2dpEnabled = adapterService.isSplitA2dpEnabled();
-                    Log.v(TAG,"isSplitA2dpEnabled: " + isSplitA2dpEnabled);
-                } else {
-                    Log.e(TAG,"adapterService is null");
-                }
-            }
-            if (mAvrcp_ext != null && !tws_switch) {
-                mAvrcp_ext.setAbsVolumeFlag(device);
-            }
-            tws_switch = false;
+        if(mAvrcp_ext != null)
+            mAvrcp_ext.setActiveDevice(device);
+        // Send an intent with the active device codec config
+        if (codecStatus != null) {
+            broadcastCodecConfig(mActiveDevice, codecStatus);
         }
+        int rememberedVolume = -1;
+        synchronized (mVariableLock) {
+            if (mFactory.getAvrcpTargetService() != null) {
+                rememberedVolume = mFactory.getAvrcpTargetService()
+                       .getRememberedVolumeForDevice(mActiveDevice);
+            } else if (mAdapterService != null && mAdapterService.isVendorIntfEnabled()) {
+                rememberedVolume = mAvrcp_ext.getVolume(device);
+                Log.d(TAG,"volume = " + rememberedVolume);
+            }
+            // Make sure the Audio Manager knows the previous Active device is disconnected,
+            // and the new Active device is connected.
+
+            if (!isBAActive && mAudioManager != null) {
+            // Make sure the Audio Manager knows the previous
+            // Active device is disconnected, and the new Active
+            // device is connected.
+            // Also, provide information about codec used by
+            // new active device so that Audio Service
+            // can reset accordingly the audio feeding parameters
+            // in the Audio HAL to the Bluetooth stack.
+                mAudioManager.handleBluetoothA2dpActiveDeviceChange(
+                        mActiveDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP,
+                          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.
+
+        // Split A2dp will be enabled by default
+            boolean isSplitA2dpEnabled = true;
+            AdapterService adapterService = AdapterService.getAdapterService();
+
+            if (adapterService != null){
+                isSplitA2dpEnabled = adapterService.isSplitA2dpEnabled();
+                Log.v(TAG,"isSplitA2dpEnabled: " + isSplitA2dpEnabled);
+            } else {
+                Log.e(TAG,"adapterService is null");
+            }
+        }
+        if (mAvrcp_ext != null && !tws_switch) {
+            mAvrcp_ext.setAbsVolumeFlag(device);
+        }
+        tws_switch = false;
         return true;
     }
 
@@ -972,7 +1021,7 @@
 
         synchronized (mVariableLock) {
             if(mAdapterService != null)
-       	        mAdapterService.getDatabase()
+                mAdapterService.getDatabase()
                 .setProfilePriority(device, BluetoothProfile.A2DP, priority);
         }
         return true;
@@ -1317,7 +1366,8 @@
                 if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
                     switch (stackEvent.valueInt) {
                         case A2dpStackEvent.CONNECTION_STATE_CONNECTED:
-                        case A2dpStackEvent.CONNECTION_STATE_CONNECTING:
+                        case A2dpStackEvent.CONNECTION_STATE_CONNECTING: {
+                            boolean connectionAllowed;
                             synchronized (mVariableLock) {
                                 // Create a new state machine only when connecting to a device
                                 if (mAdapterService != null && mAdapterService.isVendorIntfEnabled())
@@ -1326,15 +1376,24 @@
                                     sm = getOrCreateStateMachine(device);
                                     break;
                                 }
-                                if (!connectionAllowedCheckMaxDevices(device) && mA2dpNativeInterface != null) {
-                                    Log.e(TAG, "Cannot connect to " + device
-                                            + " : too many connected devices");
-                                    mA2dpNativeInterface.disconnectA2dp(device);
-                                    return;
+                                connectionAllowed = connectionAllowedCheckMaxDevices(device);
+                            }
+                            if (!connectionAllowed) {
+                                Log.e(TAG, "Cannot connect to " + device
+                                        + " : too many connected devices");
+                                try {
+                                    mA2dpNativeInterfaceLock.readLock().lock();
+                                    if (mA2dpNativeInterface != null) {
+                                        mA2dpNativeInterface.disconnectA2dp(device);
+                                        return;
+                                    }
+                                } finally {
+                                    mA2dpNativeInterfaceLock.readLock().unlock();
                                 }
                             }
                             sm = getOrCreateStateMachine(device);
                             break;
+                        }
                         default:
                             synchronized (mVariableLock) {
                                 if (mAdapterService!= null && mAdapterService.isVendorIntfEnabled() &&
@@ -1619,6 +1678,9 @@
             if (mFactory.getAvrcpTargetService() != null) {
                 mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
             }
+            if (mAvrcp_ext != null) {
+                mAvrcp_ext.removeVolumeForDevice(device);
+            }
 
             removeStateMachine(device);
         }
@@ -1715,7 +1777,9 @@
                     if (mFactory.getAvrcpTargetService() != null) {
                         mFactory.getAvrcpTargetService().removeStoredVolumeForDevice(device);
                     }
-
+                    if (mAvrcp_ext != null) {
+                        mAvrcp_ext.removeVolumeForDevice(device);
+                    }
                     removeStateMachine(device);
                 }
             }
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
index daa5312..0145260 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
@@ -24,6 +24,8 @@
 import android.media.AudioManager;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.media.MediaPlayer;
+import android.media.session.PlaybackState;
+
 import android.os.Handler;
 import android.os.Message;
 import android.util.Log;
@@ -242,7 +244,8 @@
                 break;
 
             case DELAYED_PAUSE:
-                if (mStreamAvailable && !inCallFromStreamingDevice()) {
+                if (BluetoothMediaBrowserService.getPlaybackState()
+                            == PlaybackState.STATE_PLAYING && !inCallFromStreamingDevice()) {
                     sendAvrcpPause();
                     mSentPause = true;
                     mStreamAvailable = false;
diff --git a/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index b48f353..bdc930b 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothAvrcpTarget;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -98,6 +99,19 @@
 
                 // Update all the playback status info for each connected device
                 mNativeInterface.sendMediaUpdate(false, true, false);
+            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                if (mNativeInterface == null) return;
+
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (device == null) return;
+
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                if (state == BluetoothProfile.STATE_DISCONNECTED) {
+                    // If there is no connection, disconnectDevice() will do nothing
+                    if (mNativeInterface.disconnectDevice(device.getAddress())) {
+                        Log.d(TAG, "request to disconnect device " + device);
+                    }
+                }
             }
         }
     }
@@ -175,6 +189,7 @@
         mReceiver = new AvrcpBroadcastReceiver();
         IntentFilter filter = new IntentFilter();
         filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         registerReceiver(mReceiver, filter);
 
         // Only allow the service to be used once it is initialized
diff --git a/src/com/android/bluetooth/avrcp/MediaPlayerList.java b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
index 10d99bf..29f4cf9 100644
--- a/src/com/android/bluetooth/avrcp/MediaPlayerList.java
+++ b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
@@ -626,6 +626,12 @@
                     android.media.session.MediaController controller =
                             new android.media.session.MediaController(mContext, token);
 
+                    if (mMediaSessionManager == null) {
+                        Log.w(TAG, "onAddressedPlayerChanged(Token): Unexpected callback "
+                                + "from the MediaSessionManager");
+                        return;
+                    }
+
                     if (!mMediaPlayerIds.containsKey(controller.getPackageName())) {
                         // Since we have a controller, we can try to to recover by adding the
                         // player and then setting it as active.
@@ -640,6 +646,12 @@
 
                 @Override
                 public void onAddressedPlayerChanged(ComponentName receiver) {
+                    if (mMediaSessionManager == null) {
+                        Log.w(TAG, "onAddressedPlayerChanged(Component): Unexpected callback "
+                                + "from the MediaSessionManager");
+                        return;
+                    }
+
                     if (receiver == null) {
                         return;
                     }
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index a0fd09c..3b9056e 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -333,7 +333,8 @@
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
-                    AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION);
+                    AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION,
+                    (int) label);
         }
     }
 
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index dc9f76a..ec85592 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -100,6 +100,8 @@
     private static final byte NOTIFICATION_RSP_TYPE_CHANGED = 0x01;
 
     private final AudioManager mAudioManager;
+    private final boolean mIsVolumeFixed;
+
     protected final BluetoothDevice mDevice;
     private BluetoothDevice mA2dpDevice = null;
     protected final byte[] mDeviceAddress;
@@ -122,6 +124,7 @@
     private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();
     // Only accessed from State Machine processMessage
     private int mVolumeChangedNotificationsToIgnore = 0;
+    private int mVolumeNotificationLabel = -1;
 
     GetFolderList mGetFolderList = null;
 
@@ -154,6 +157,7 @@
         mRemoteDevice = new RemoteDevice(device);
 
         mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE);
+        mIsVolumeFixed = mAudioManager.isVolumeFixed();
         IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
         mService.registerReceiver(mBroadcastReceiver, filter);
 
@@ -404,7 +408,8 @@
                     return true;
 
                 case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: {
-                    mRemoteDevice.setNotificationLabel(msg.arg1);
+                    mVolumeNotificationLabel = msg.arg1;
+                    mRemoteDevice.setNotificationLabel(mVolumeNotificationLabel);
                     mRemoteDevice.setAbsVolNotificationRequested(true);
                     int percentageVol = getVolumePercentage();
                     if (DBG) {
@@ -657,24 +662,9 @@
                     }
                     break;
 
-                case CONNECT:
-                case DISCONNECT:
-                case MSG_AVRCP_PASSTHRU:
-                case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
-                case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
-                case MESSAGE_PROCESS_TRACK_CHANGED:
-                case MESSAGE_PROCESS_PLAY_POS_CHANGED:
-                case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
-                case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
-                case MESSAGE_PLAY_ITEM:
-                case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED:
+                default:
                     // All of these messages should be handled by parent state immediately.
                     return false;
-
-                default:
-                    logD(STATE_TAG + " deferring message " + msg.what
-                                + " to connected!");
-                    deferMessage(msg);
             }
             return true;
         }
@@ -846,7 +836,7 @@
             mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex,
                     AudioManager.FLAG_SHOW_UI);
         }
-        AvrcpControllerService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
+        AvrcpControllerService.sendAbsVolRspNative(mDeviceAddress, getAbsVolumeResponse(), label);
     }
 
     private int getVolumePercentage() {
@@ -858,6 +848,13 @@
         return percentageVol;
     }
 
+    private int getAbsVolumeResponse() {
+        if (mIsVolumeFixed) {
+            return ABS_VOL_BASE;
+        }
+        return getVolumePercentage();
+    }
+
     MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {
         @Override
         public void onPlay() {
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index ae5b769..150a569 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -177,6 +177,20 @@
     }
 
     /**
+     * Get playback state
+     */
+    public static synchronized int getPlaybackState() {
+        if (sBluetoothMediaBrowserService != null) {
+            PlaybackState currentPlaybackState =
+                    sBluetoothMediaBrowserService.mSession.getController().getPlaybackState();
+            if (currentPlaybackState != null) {
+                return currentPlaybackState.getState();
+            }
+        }
+        return PlaybackState.STATE_ERROR;
+    }
+
+    /**
      * Set Media session active whenever we have Focus of any kind
      */
     public static synchronized void setActive(boolean active) {
diff --git a/src/com/android/bluetooth/btservice/AbstractionLayer.java b/src/com/android/bluetooth/btservice/AbstractionLayer.java
index 30493f1..3da2440 100644
--- a/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -126,7 +126,6 @@
     public static final int PBAP = 2;
     public static final int MAP = 3;
     public static final int MAX_POW = 4;
-    public static final int HEARING_AID = 5;
 
     // Profile features supported in profile_conf
     public static final int PROFILE_VERSION =1;
@@ -139,7 +138,6 @@
     public static final int BR_MAX_POW_SUPPORT = 8;
     public static final int EDR_MAX_POW_SUPPORT = 9;
     public static final int BLE_MAX_POW_SUPPORT = 10;
-    public static final int HEARING_AID_SUPPORT = 11;
 
 
     static final int BT_VENDOR_PROPERTY_TWS_PLUS_DEVICE_TYPE = 0x01;
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index c7eb53c..beff7c8 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -86,18 +86,7 @@
     private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices =
             new CopyOnWriteArrayList<BluetoothDevice>();
 
-    private final class ProfilesConnectionState {
-        public int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
-
-        ProfilesConnectionState(int connecting, int connected, int disconnecting) {
-          mProfilesConnecting    = connecting;
-          mProfilesConnected     = connected;
-          mProfilesDisconnecting = disconnecting;
-        }
-    }
-    private final HashMap<BluetoothDevice, ProfilesConnectionState> mDevicesConnectionState =
-            new HashMap<>();
-
+    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
     private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState =
             new HashMap<>();
 
@@ -229,7 +218,6 @@
 
     public void init(RemoteDevices remoteDevices) {
         mProfileConnectionState.clear();
-        mDevicesConnectionState.clear();
         mRemoteDevices = remoteDevices;
 
         // Get default max connected audio devices from config.xml in frameworks/base/core
@@ -286,7 +274,6 @@
         }
         mService = null;
         mBondedDevices.clear();
-        mDevicesConnectionState.clear();
     }
 
     @Override
@@ -800,10 +787,6 @@
                     debugLog("Adding bonded device:" + device);
                     mBondedDevices.add(device);
                 }
-                if (!mDevicesConnectionState.containsKey(device)) {
-                    debugLog("Adding connection state:" + device);
-                    mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
-                }
             } else if (state == BluetoothDevice.BOND_NONE) {
                 // remove device from list
                 if (mBondedDevices.remove(device)) {
@@ -811,10 +794,6 @@
                 } else {
                     debugLog("Failed to remove device: " + device);
                 }
-                if (mDevicesConnectionState.containsKey(device)) {
-                    debugLog("Removing connection state:" + device);
-                    mDevicesConnectionState.remove(device);
-                }
             }
         } catch (Exception ee) {
             Log.w(TAG, "onBondStateChanged: Exception ", ee);
@@ -889,7 +868,7 @@
 
             try {
                 validateConnectionState =
-                   updateCountersAndCheckForConnectionStateChange(device, state, prevState);
+                   updateCountersAndCheckForConnectionStateChange(state, prevState);
             } catch (IllegalStateException ee) {
                 Log.w(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: unexpected transition for profile="
                         + profile + ", " + prevState + " -> " + state);
@@ -953,45 +932,33 @@
         }
     }
 
-    private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
-            int state, int prevState) {
-        if(!mDevicesConnectionState.containsKey(device)) {
-            Log.e(TAG, "Can't find device connection record, adding new one: " + device);
-            mDevicesConnectionState.put(device, new ProfilesConnectionState(0, 0, 0));
-        }
-        ProfilesConnectionState connstate = mDevicesConnectionState.get(device);
-
-        Log.e(TAG, "prevState=" + prevState + " -> State=" + state +
-            " mProfilesConnecting=" + connstate.mProfilesConnecting +
-            " mProfilesConnected=" + connstate.mProfilesConnected +
-            " mProfilesDisconnecting=" + connstate.mProfilesDisconnecting);
-
+    private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
         switch (prevState) {
             case BluetoothProfile.STATE_CONNECTING:
-                if (connstate.mProfilesConnecting > 0) {
-                    connstate.mProfilesConnecting--;
+                if (mProfilesConnecting > 0) {
+                    mProfilesConnecting--;
                 } else {
-                    Log.e(TAG, "mProfilesConnecting " + connstate.mProfilesConnecting);
+                    Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
                 break;
 
             case BluetoothProfile.STATE_CONNECTED:
-                if (connstate.mProfilesConnected > 0) {
-                    connstate.mProfilesConnected--;
+                if (mProfilesConnected > 0) {
+                    mProfilesConnected--;
                 } else {
-                    Log.e(TAG, "mProfilesConnected " + connstate.mProfilesConnected);
+                    Log.e(TAG, "mProfilesConnected " + mProfilesConnected);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
                 break;
 
             case BluetoothProfile.STATE_DISCONNECTING:
-                if (connstate.mProfilesDisconnecting > 0) {
-                    connstate.mProfilesDisconnecting--;
+                if (mProfilesDisconnecting > 0) {
+                    mProfilesDisconnecting--;
                 } else {
-                    Log.e(TAG, "mProfilesDisconnecting " + connstate.mProfilesDisconnecting);
+                    Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting);
                     throw new IllegalStateException(
                             "Invalid state transition, " + prevState + " -> " + state);
                 }
@@ -1000,19 +967,19 @@
 
         switch (state) {
             case BluetoothProfile.STATE_CONNECTING:
-                connstate.mProfilesConnecting++;
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 1);
+                mProfilesConnecting++;
+                return (mProfilesConnected == 0 && mProfilesConnecting == 1);
 
             case BluetoothProfile.STATE_CONNECTED:
-                connstate.mProfilesConnected++;
-                return (connstate.mProfilesConnected == 1);
+                mProfilesConnected++;
+                return (mProfilesConnected == 1);
 
             case BluetoothProfile.STATE_DISCONNECTING:
-                connstate.mProfilesDisconnecting++;
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesDisconnecting == 1);
+                mProfilesDisconnecting++;
+                return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
 
             case BluetoothProfile.STATE_DISCONNECTED:
-                return (connstate.mProfilesConnected == 0 && connstate.mProfilesConnecting == 0);
+                return (mProfilesConnected == 0 && mProfilesConnecting == 0);
 
             default:
                 return true;
@@ -1273,8 +1240,9 @@
             // Reset adapter and profile connection states
             setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
             mProfileConnectionState.clear();
-            mDevicesConnectionState.clear();
-
+            mProfilesConnected = 0;
+            mProfilesConnecting = 0;
+            mProfilesDisconnecting = 0;
             // adapterPropertyChangedCallback has already been received.  Set the scan mode.
             setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
             // This keeps NV up-to date on first-boot after flash.
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 277eb8c..5bec2d0 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -2620,6 +2620,12 @@
             return false;
         }
 
+        if (pinCode.length != len) {
+            android.util.EventLog.writeEvent(0x534e4554, "139287605", -1,
+                    "PIN code length mismatch");
+            return false;
+        }
+
         StatsLog.write(StatsLog.BLUETOOTH_BOND_STATE_CHANGED,
                 obfuscateAddress(device), 0, device.getType(),
                 BluetoothDevice.BOND_BONDING,
@@ -2636,6 +2642,12 @@
             return false;
         }
 
+        if (passkey.length != len) {
+            android.util.EventLog.writeEvent(0x534e4554, "139287605", -1,
+                    "Passkey length mismatch");
+            return false;
+        }
+
         StatsLog.write(StatsLog.BLUETOOTH_BOND_STATE_CHANGED,
                 obfuscateAddress(device), 0, device.getType(),
                 BluetoothDevice.BOND_BONDING,
diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java
index afcd9b3..c906317 100755
--- a/src/com/android/bluetooth/btservice/Config.java
+++ b/src/com/android/bluetooth/btservice/Config.java
@@ -121,12 +121,10 @@
         for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
             boolean supported = resources.getBoolean(config.mSupported);
 
-            if (config.mClass == HearingAidService.class &&
-                    FeatureFlagUtils.isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) {
-                if (!isHearingAidSupported()) {
-                    supported = false;
-                }
-                if (supported) Log.v(TAG, "HearingAidService is enabled");
+            if (!supported && (config.mClass == HearingAidService.class) && FeatureFlagUtils
+                                .isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) {
+                Log.v(TAG, "Feature Flag enables support for HearingAidService");
+                supported = true;
             }
 
             if (supported && !isProfileDisabled(ctx, config.mMask)) {
@@ -213,15 +211,4 @@
         // always return true for other profiles
         return true;
     }
-
-    private static boolean isHearingAidSupported() {
-        AdapterService adapterService = AdapterService.getAdapterService();
-        boolean isHearingAidSupported = false;
-        if (adapterService != null) {
-          isHearingAidSupported = adapterService.getProfileInfo(
-              AbstractionLayer.HEARING_AID, AbstractionLayer.HEARING_AID_SUPPORT);
-          Log.d(TAG, "isHearingAidSupported: " + isHearingAidSupported);
-        }
-        return isHearingAidSupported;
-    }
 }
diff --git a/src/com/android/bluetooth/btservice/PhonePolicy.java b/src/com/android/bluetooth/btservice/PhonePolicy.java
index f3097fe..529decf 100644
--- a/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -326,6 +326,9 @@
                 + prevState + " -> " + nextState);
         if ((profileId == BluetoothProfile.A2DP) || (profileId == BluetoothProfile.HEADSET)
                 || profileId == BluetoothProfile.A2DP_SINK) {
+            BluetoothDevice peerTwsDevice =
+                    (mAdapterService != null && mAdapterService.isTwsPlusDevice(device)) ?
+                    mAdapterService.getTwsPlusPeerDevice(device):null;
             if (nextState == BluetoothProfile.STATE_CONNECTED) {
                 debugLog("processProfileStateChanged: isTwsDevice: " + mAdapterService.isTwsPlusDevice(device));
                 switch (profileId) {
@@ -333,12 +336,16 @@
                         mA2dpRetrySet.remove(device);
                         if (mAdapterService.isTwsPlusDevice(device)) {
                              setAutoConnectForA2dpSink(device);
+                             if (peerTwsDevice != null)
+                                 setAutoConnectForA2dpSink(peerTwsDevice);
                         }
                         break;
                     case BluetoothProfile.HEADSET:
                         mHeadsetRetrySet.remove(device);
                         if (mAdapterService.isTwsPlusDevice(device)) {
                              setAutoConnectForHeadset(device);
+                             if (peerTwsDevice != null)
+                                 setAutoConnectForHeadset(peerTwsDevice);
                         }
                         break;
                     case BluetoothProfile.A2DP_SINK:
@@ -348,24 +355,45 @@
                 connectOtherProfile(device);
             }
             if (nextState == BluetoothProfile.STATE_DISCONNECTED) {
+                HeadsetService hsService = mFactory.getHeadsetService();
+                A2dpService a2dpService = mFactory.getA2dpService();
                 handleAllProfilesDisconnected(device);
                 if (prevState == BluetoothProfile.STATE_CONNECTING) {
-                    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;
+                    boolean isAnyTwsPairConnected = (peerTwsDevice != null) && ((a2dpService != null) &&
+                            (a2dpService.getConnectionState(peerTwsDevice) == BluetoothProfile.STATE_CONNECTED))
+                            || ((hsService != null) &&
+                            (hsService.getConnectionState(peerTwsDevice) == BluetoothProfile.STATE_CONNECTED));
                     debugLog("processProfileStateChanged, device=" + device + ", a2dpDisconnected="
-                            + a2dpDisconnected + ", hsDisconnected=" + hsDisconnected);
+                            + a2dpDisconnected + ", hsDisconnected=" + hsDisconnected
+                            + ", TwsPairConnected=" + isAnyTwsPairConnected);
                     if (hsDisconnected && a2dpDisconnected) {
                         //remove a2dp and headset retry set.
                         mA2dpRetrySet.remove(device);
                         mHeadsetRetrySet.remove(device);
-                        removeAutoConnectFromA2dpSink(device);
-                        removeAutoConnectFromHeadset(device);
+                        if (!isAnyTwsPairConnected) {
+                            removeAutoConnectFromA2dpSink(device);
+                            removeAutoConnectFromHeadset(device);
+                        }
+                    }
+                } else if (prevState == BluetoothProfile.STATE_DISCONNECTING) {
+                    if (peerTwsDevice != null) {
+                        int autoConnect = BluetoothProfile.PRIORITY_AUTO_CONNECT;
+                        if (((a2dpService.getPriority(peerTwsDevice) == autoConnect) &&
+                                (hsService.getPriority(peerTwsDevice) == autoConnect)) ||
+                                ((a2dpService.getPriority(device) == autoConnect) &&
+                                (hsService.getPriority(device) == autoConnect))) {
+                            debugLog("User triggered disconnect reset priority ON to both EBs");
+                            removeAutoConnectFromA2dpSink(device);
+                            removeAutoConnectFromHeadset(device);
+                            removeAutoConnectFromA2dpSink(peerTwsDevice);
+                            removeAutoConnectFromHeadset(peerTwsDevice);
+                        }
                     }
                 }
             }
@@ -394,21 +422,13 @@
 
                 setAutoConnectForA2dpSink(activeDevice);
                 setAutoConnectForHeadset(activeDevice);
-                if (mAdapterService.isTwsPlusDevice(activeDevice)) {
-                    BluetoothDevice peerTwsDevice = hsService.getTwsPlusConnectedPeer(activeDevice);
+                if (mAdapterService != null && mAdapterService.isTwsPlusDevice(activeDevice)) {
+                    BluetoothDevice peerTwsDevice = mAdapterService.getTwsPlusPeerDevice(activeDevice);
                     if (peerTwsDevice != null) {
-                        if (a2dpService != null &&
-                            a2dpService.getConnectionState(peerTwsDevice) !=
-                            BluetoothProfile.STATE_DISCONNECTED) {
-                            debugLog("A2DP: Set Autoconnect for Peer TWS+ as well");
-                            setAutoConnectForA2dpSink(peerTwsDevice);
-                        }
-                        if (hsService != null &&
-                            hsService.getConnectionState(peerTwsDevice) !=
-                            BluetoothProfile.STATE_DISCONNECTED) {
-                            debugLog("HFP: Set Autoconnect for Peer TWS+ as well");
-                            setAutoConnectForHeadset(peerTwsDevice);
-                        }
+                        debugLog("A2DP: Set Autoconnect for Peer TWS+ as well");
+                        setAutoConnectForA2dpSink(peerTwsDevice);
+                        debugLog("HFP: Set Autoconnect for Peer TWS+ as well");
+                        setAutoConnectForHeadset(peerTwsDevice);
                     }
                 }
                 break;
diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java
index d585cb4..7374f7a 100644
--- a/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -438,6 +438,7 @@
                     without waiting for the ACTION_UUID intent.
                     This was resulting in multiple calls to connect().*/
                     mUuids = null;
+                    mAlias = null;
                 }
             }
         }
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 4c0e6dc..8fefdba 100755
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -988,13 +988,13 @@
             Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString());
             return false;
         }
-        ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
-        if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
-            Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
-                    + Utils.getUidPidString());
-            return false;
-        }
         synchronized (mStateMachines) {
+            ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
+            if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) {
+                Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, "
+                    + Utils.getUidPidString());
+                return false;
+            }
             Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString());
             HeadsetStateMachine stateMachine = mStateMachines.get(device);
             if (stateMachine == null) {
@@ -1106,12 +1106,12 @@
             Log.e(TAG, "->States is null");
             return devices;
         }
-        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
-        if (bondedDevices == null) {
-            Log.e(TAG, "->Bonded device is null");
-            return devices;
-        }
         synchronized (mStateMachines) {
+            final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
+            if (bondedDevices == null) {
+                Log.e(TAG, "->Bonded device is null");
+                return devices;
+            }
             for (BluetoothDevice device : bondedDevices) {
 
                 int connectionState = getConnectionState(device);
@@ -1175,8 +1175,12 @@
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
         Log.i(TAG, "setPriority: device=" + device + ", priority=" + priority + ", "
                 + Utils.getUidPidString());
-        mAdapterService.getDatabase()
+        AdapterService adapterService = AdapterService.getAdapterService();
+        if (adapterService != null)
+            adapterService.getDatabase()
                 .setProfilePriority(device, BluetoothProfile.HEADSET, priority);
+        else
+            Log.i(TAG, "adapter service is null");
         return true;
     }
 
@@ -2249,7 +2253,7 @@
         synchronized (mStateMachines) {
             if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                 //Transfer SCO is not needed for TWS+ devices
-                if (mAdapterService.isTwsPlusDevice(device) &&
+                if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                    mActiveDevice != null &&
                    mAdapterService.isTwsPlusDevice(mActiveDevice) &&
                    isAudioOn()) {
@@ -2260,7 +2264,7 @@
                     Log.w(TAG, "onAudioStateChangedFromStateMachine:"
                             + "shouldPersistAudio() returns"
                             + shouldPersistAudio());
-                    if (mAdapterService.isTwsPlusDevice(device) &&
+                    if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                                    isAudioOn()) {
                         Log.w(TAG, "TWS: Don't stop VR or VOIP");
                     } else {
@@ -2293,11 +2297,11 @@
                     if (mActiveDevice != null &&
                                  !mActiveDevice.equals(device) &&
                                  shouldPersistAudio()) {
-                       if (mAdapterService.isTwsPlusDevice(device)
+                       if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device)
                                                    && isAudioOn()) {
                            Log.d(TAG, "TWS: Wait for both eSCO closed");
                        } else {
-                           if (mAdapterService.isTwsPlusDevice(device) &&
+                           if (mAdapterService != null && mAdapterService.isTwsPlusDevice(device) &&
                                isTwsPlusActive(mActiveDevice)) {
                                /* If the device for which SCO got disconnected
                                   is a TwsPlus device and TWS+ set is active
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 784dead..491a0bb 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -784,8 +784,16 @@
                     processIntentUpdateCallType((Intent) message.obj);
                     break;
                 case RESUME_A2DP: {
-                     stateLogD("RESUME_A2DP evt, resuming A2DP");
-                     mHeadsetService.getHfpA2DPSyncInterface().releaseA2DP(mDevice);
+                     /* If the call started/ended by the time A2DP suspend ack
+                      * is received, send the call indicators before resuming
+                      * A2DP.
+                      */
+                     if (mPendingCallStates.size() == 0) {
+                         stateLogD("RESUME_A2DP evt, resuming A2DP");
+                         mHeadsetService.getHfpA2DPSyncInterface().releaseA2DP(mDevice);
+                     } else {
+                         stateLogW("RESUME_A2DP evt, pending call states to be sent, not resuming");
+                     }
                      break;
                 }
                 case STACK_EVENT:
@@ -1436,8 +1444,16 @@
                     // ignore
                     break;
                 case RESUME_A2DP: {
-                     stateLogD("RESUME_A2DP evt, resuming A2DP");
-                     mHeadsetService.getHfpA2DPSyncInterface().releaseA2DP(mDevice);
+                     /* If the call started/ended by the time A2DP suspend ack
+                      * is received, send the call indicators before resuming
+                      * A2DP.
+                      */
+                     if (mPendingCallStates.size() == 0) {
+                         stateLogD("RESUME_A2DP evt, resuming A2DP");
+                         mHeadsetService.getHfpA2DPSyncInterface().releaseA2DP(mDevice);
+                     } else {
+                         stateLogW("RESUME_A2DP evt, pending call states to be sent, not resuming");
+                     }
                      break;
                 }
                 default:
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
index 349951c..ada8ab4 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -32,6 +32,7 @@
 
 package com.android.bluetooth.opp;
 
+import android.bluetooth.BluetoothAdapter;
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
@@ -80,12 +81,15 @@
 
     private int mNumFilesAttemptedToSend;
 
+    private BluetoothAdapter mAdapter;
+
     public BluetoothOppObexClientSession(Context context, ObexTransport transport) {
         if (transport == null) {
             throw new NullPointerException("transport is null");
         }
         mContext = context;
         mTransport = transport;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Override
@@ -99,7 +103,7 @@
     }
 
     @Override
-    public void stop() {
+    public synchronized void stop() {
         if (D) {
             Log.d(TAG, "Stop!");
         }
@@ -110,7 +114,23 @@
                 if (V) {
                     Log.v(TAG, "waiting for thread to terminate");
                 }
-                mThread.join();
+                if (mAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) {
+                    Log.d(TAG, "stop, bt is turning off");
+                    mThread.join(1500);
+                    if (mThread.isAlive()) {
+                        Log.d(TAG, "stop, close the transport");
+                        mThread.interrupt();
+                        try {
+                            mTransport.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "mTransport.close error");
+                        }
+                        Log.d(TAG, "stop, close the transport, done");
+                        mThread.join();
+                    }
+                } else {
+                    mThread.join();
+                }
                 mThread = null;
             } catch (InterruptedException e) {
                 if (V) {
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
index be19f10..60274a7 100644
--- a/src/com/android/bluetooth/pan/PanService.java
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -27,7 +27,6 @@
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -294,13 +293,14 @@
         }
 
         @Override
-        public void setBluetoothTethering(boolean value) {
+        public void setBluetoothTethering(boolean value, String pkgName) {
             PanService service = getService();
             if (service == null) {
                 return;
             }
-            Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + service.mTetherOn);
-            service.setBluetoothTethering(value);
+            Log.d(TAG, "setBluetoothTethering: " + value + ", pkgName: " + pkgName
+                    + ", mTetherOn: " + service.mTetherOn);
+            service.setBluetoothTethering(value, pkgName);
         }
 
         @Override
@@ -374,22 +374,14 @@
         return mTetherOn;
     }
 
-    void setBluetoothTethering(boolean value) {
+    void setBluetoothTethering(boolean value, final String pkgName) {
         if (DBG) {
             Log.d(TAG, "setBluetoothTethering: " + value + ", mTetherOn: " + mTetherOn);
         }
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
         final Context context = getBaseContext();
-        String pkgName = context.getOpPackageName();
 
-        // Clear caller identity temporarily so enforceTetherChangePermission UID checks work
-        // correctly
-        final long identityToken = Binder.clearCallingIdentity();
-        try {
-            ConnectivityManager.enforceTetherChangePermission(context, pkgName);
-        } finally {
-            Binder.restoreCallingIdentity(identityToken);
-        }
+        ConnectivityManager.enforceTetherChangePermission(context, pkgName);
 
         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING) && value) {
diff --git a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
index 53e8419..c759a8a 100644
--- a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
@@ -195,6 +195,24 @@
     }
 
     @Test
+    public void testFocusGainTransient() {
+        // Focus was lost then regained.
+        testSnkPlay();
+        mStreamHandler.handleMessage(
+                mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
+                        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT));
+        mStreamHandler.handleMessage(
+                mStreamHandler.obtainMessage(A2dpSinkStreamHandler.DELAYED_PAUSE));
+        mStreamHandler.handleMessage(
+                mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
+                        AudioManager.AUDIOFOCUS_GAIN));
+        verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
+        verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(0);
+        verify(mMockA2dpSink, times(1)).informAudioTrackGainNative(0);
+        verify(mMockA2dpSink, times(2)).informAudioTrackGainNative(1.0f);
+    }
+
+    @Test
     public void testFocusLost() {
         // Focus was lost permanently, expect streaming to stop.
         testSnkPlay();