Merge "Store volume only when Hearing Aid devices are not active" into qt-dev
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 6b4867f..0ac85f4 100644
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -683,7 +683,8 @@
   }
 }
 
-static bool initNative(JNIEnv* env, jobject obj) {
+static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
+                       jboolean isSingleUserMode) {
   ALOGV("%s", __func__);
 
   android_bluetooth_UidTraffic.clazz =
@@ -697,7 +698,9 @@
     return JNI_FALSE;
   }
 
-  int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
+  int ret = sBluetoothInterface->init(&sBluetoothCallbacks,
+                                      isGuest == JNI_TRUE ? 1 : 0,
+                                      isSingleUserMode == JNI_TRUE ? 1 : 0);
   if (ret != BT_STATUS_SUCCESS) {
     ALOGE("Error while setting the callbacks: %d\n", ret);
     sBluetoothInterface = NULL;
@@ -750,11 +753,11 @@
   return JNI_TRUE;
 }
 
-static jboolean enableNative(JNIEnv* env, jobject obj, jboolean isGuest) {
+static jboolean enableNative(JNIEnv* env, jobject obj) {
   ALOGV("%s", __func__);
 
   if (!sBluetoothInterface) return JNI_FALSE;
-  int ret = sBluetoothInterface->enable(isGuest == JNI_TRUE ? 1 : 0);
+  int ret = sBluetoothInterface->enable();
   return (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE
                                                              : JNI_FALSE;
 }
@@ -1237,9 +1240,9 @@
 static JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()Z", (void*)initNative},
+    {"initNative", "(ZZ)Z", (void*)initNative},
     {"cleanupNative", "()V", (void*)cleanupNative},
-    {"enableNative", "(Z)Z", (void*)enableNative},
+    {"enableNative", "()Z", (void*)enableNative},
     {"disableNative", "()Z", (void*)disableNative},
     {"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative},
     {"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative},
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index cf2b0d3..3d30f17 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -43,7 +43,7 @@
     <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>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% पूरा"</string>
     <string name="notification_sent_fail" msgid="6696082233774569445">"ब्लूटूथ शेयर: फ़ाइल <xliff:g id="FILE">%1$s</xliff:g> भेजी नहीं गई"</string>
     <string name="download_title" msgid="3353228219772092586">"फ़ाइल ट्रांसफ़र करें"</string>
     <string name="download_line1" msgid="4926604799202134144">"प्रेषक: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
@@ -102,8 +102,8 @@
     <string name="status_unknown_error" msgid="8156660554237824912">"अज्ञात गड़बड़ी‍."</string>
     <string name="btopp_live_folder" msgid="7967791481444474554">"ब्लूटूथ से मिली फ़ाइलें"</string>
     <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="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="outbound_history_title" msgid="4279418703178140526">"आउटबाउंड स्थानांतरण"</string>
     <string name="no_transfers" msgid="3482965619151865672">"कुछ भी ट्रांसफ़र नहीं किया गया."</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 4465490..d05c7b3 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -100,7 +100,7 @@
     <string name="status_connection_error" msgid="947681831523219891">"Neuspješno povezivanje."</string>
     <string name="status_protocol_error" msgid="3245444473429269539">"Zahtjev nije moguće ispravno obraditi."</string>
     <string name="status_unknown_error" msgid="8156660554237824912">"Nepoznata pogreška."</string>
-    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth primljen"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Primljeno Bluetoothom"</string>
     <string name="opp_notification_group" msgid="3486303082135789982">"Dijeljenje Bluetoothom"</string>
     <string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> primljeno u cijelosti."</string>
     <string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> Poslano u potpunosti."</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index c838d42..f5eeca9 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -100,7 +100,7 @@
     <string name="status_connection_error" msgid="947681831523219891">"연결하지 못했습니다."</string>
     <string name="status_protocol_error" msgid="3245444473429269539">"요청을 제대로 처리할 수 없습니다."</string>
     <string name="status_unknown_error" msgid="8156660554237824912">"알 수 없는 오류입니다."</string>
-    <string name="btopp_live_folder" msgid="7967791481444474554">"블루투스 수신함"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"블루투스로 받은 파일"</string>
     <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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 91c96c5..0ed4aa9 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -97,7 +97,7 @@
     <string name="status_file_error" msgid="3671917770630165299">"Сактагычта маселе бар."</string>
     <string name="status_no_sd_card_nosdcard" msgid="573631036356922221">"USB эстутуму жок."</string>
     <string name="status_no_sd_card_default" msgid="396564893716701954">"SD-карта жок. Өткөрүлгөн файлдарды сактоо үчүн SD-картаны салыңыз."</string>
-    <string name="status_connection_error" msgid="947681831523219891">"Туташуу ийгиликсиз."</string>
+    <string name="status_connection_error" msgid="947681831523219891">"Туташкан жок."</string>
     <string name="status_protocol_error" msgid="3245444473429269539">"Сурамды туура иштетүү мүмкүн эмес."</string>
     <string name="status_unknown_error" msgid="8156660554237824912">"Белгисиз ката."</string>
     <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth аркылуу алынгандар"</string>
@@ -127,7 +127,7 @@
     <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"Жокко чыгаруу"</string>
     <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"Bluetooth аркылуу бөлүшө турган каттоо эсептерин тандаңыз. Туташкан сайын каттоо эсептерине кирүү мүмкүнчүлүгүн ырастап турушуңуз керек."</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">"Bluetooth билдирүү бөлүшүү жөндөөлөрү"</string>
     <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Аккаунт тандалбай жатат: 0 орун калды"</string>
     <string name="bluetooth_connected" msgid="6718623220072656906">"Bluetooth аудио туташты"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 1613376..7e7763a 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -22,7 +22,7 @@
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"အက်ပ်အား ဘလူးတုသ်စက်ကို ယာယီခွင့်ပြုစာရင်းထဲ ထည့်ရန်ခွင့်ပြုကာ အသုံးပြုသူ၏ အတည်ပြုချက်မရယူပဲ ဖိုင်များကို စက်ထဲသို့ ပို့ခွင့်ပြုမည်"</string>
     <string name="bt_share_picker_label" msgid="6268100924487046932">"ဘလူးတုသ်"</string>
     <string name="unknown_device" msgid="9221903979877041009">"မသိသော စက်"</string>
-    <string name="unknownNumber" msgid="4994750948072751566">"မသိပါ"</string>
+    <string name="unknownNumber" msgid="4994750948072751566">"မသိ"</string>
     <string name="airplane_error_title" msgid="2683839635115739939">"လေယာဉ်ပျံမုဒ်"</string>
     <string name="airplane_error_msg" msgid="8698965595254137230">"လေယာဥ်ပျံပေါ်သုံးစနစ်တွင် ဘလူးတုသ်အသုံးပြုမရပါ"</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 2c4410a..8819773 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -106,7 +106,7 @@
     <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="outbound_history_title" msgid="4279418703178140526">"ଆଉଟ୍‌ବାଉଣ୍ଡ ଟ୍ରାନ୍ସଫର୍‌"</string>
-    <string name="no_transfers" msgid="3482965619151865672">"ଟ୍ରାନ୍ସଫର୍‌ ହିଷ୍ଟୋରୀ ଖାଲି ଅଛି।"</string>
+    <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>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index ea25889..07cab6a 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -17,7 +17,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="permdesc_bluetoothShareManager" msgid="8930572979123190223">"ਐਪ ਨੂੰ ਬਲੂਟੁੱਥShare ਪ੍ਰਬੰਧਕ ਤੱਕ ਪਹੁੰਚ ਅਤੇ ਫ਼ਾਈਲਾਂ ਟ੍ਰਾਂਸਫਰ ਕਰਨ ਲਈ ਇਸਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"ਐਪ ਨੂੰ BluetoothShare ਪ੍ਰਬੰਧਕ ਤੱਕ ਪਹੁੰਚ ਅਤੇ ਫ਼ਾਈਲਾਂ ਟ੍ਰਾਂਸਫਰ ਕਰਨ ਲਈ ਇਸਦੀ ਵਰਤੋਂ ਕਰਨ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"ਵਾਈਟਲਿਸਟ ਬਲੂਟੱਥ ਡੀਵਾਈਸ ਪਹੁੰਚ।"</string>
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"ਐਪ ਨੂੰ ਇਹ ਆਗਿਆ ਦਿੰਦੇ ਹੋਏ ਕਿ ਡੀਵਾਈਸ ਵਰਤੋਂਕਾਰ ਦੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਫ਼ਾਈਲਾਂ ਭੇਜੇ, ਇੱਕ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸ ਨੂੰ ਅਸਥਾਈ ਤੌਰ ਤੇ ਵਾਈਟਲਿਸਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="bt_share_picker_label" msgid="6268100924487046932">"ਬਲੂਟੁੱਥ"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index f6a51a2..8cfec4f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -23,7 +23,7 @@
     <string name="bt_share_picker_label" msgid="6268100924487046932">"บลูทูธ"</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">"คุณไม่สามารถใช้บลูทูธในโหมดใช้งานบนเครื่องบินได้"</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">" เมื่อต้องการใช้บริการบลูทูธ คุณต้องเปิดบลูทูธก่อน"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index d6bed0f..dfb352a 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -106,7 +106,7 @@
     <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="outbound_history_title" msgid="4279418703178140526">"向外傳輸"</string>
-    <string name="no_transfers" msgid="3482965619151865672">"傳輸記錄是空的。"</string>
+    <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>
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index be0ac0c..f9be61a 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -571,7 +571,7 @@
                 if (previousActiveDevice != null) {
                     if (!mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)) {
                         mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
-                                                         AudioManager.ADJUST_MUTE, 0);
+                                AudioManager.ADJUST_MUTE, AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
                         wasMuted = true;
                     }
                     mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@@ -595,7 +595,7 @@
                 mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice);
                 if (wasMuted) {
                     mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
-                                                     AudioManager.ADJUST_UNMUTE, 0);
+                                AudioManager.ADJUST_MUTE, AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
                 }
             }
         }
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index e951ed5..e41def0 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -224,6 +224,10 @@
             Log.d(TAG, " connect device: " + device
                     + ", InstanceMap start state: " + sb.toString());
         }
+        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
+            Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
+            return false;
+        }
         A2dpSinkStateMachine stateMachine = getOrCreateStateMachine(device);
         if (stateMachine != null) {
             stateMachine.connect();
@@ -233,7 +237,6 @@
             Log.e(TAG, "Maxed out on the number of allowed MAP connections. "
                     + "Connect request rejected on " + device);
             return false;
-
         }
     }
 
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index c1beb59..0bf34de 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -221,6 +221,7 @@
         PlaybackState.Builder pbb = new PlaybackState.Builder();
         pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
                 1.0f).setActions(0);
+        pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected));
         BluetoothMediaBrowserService.notifyChanged(pbb.build());
         mService.sBrowseTree.mRootNode.removeChild(
                 mBrowseTree.mRootNode);
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 7a7eb63..3504cd4 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -71,6 +71,11 @@
                 | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
         mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name));
         mSession.setQueue(mMediaQueue);
+        PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder();
+        playbackStateBuilder.setState(PlaybackState.STATE_ERROR,
+                PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
+        playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected));
+        mSession.setPlaybackState(playbackStateBuilder.build());
         sBluetoothMediaBrowserService = this;
     }
 
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 955a664..508eacf 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -286,7 +286,7 @@
                     }
                     mRunningProfiles.add(profile);
                     if (GattService.class.getSimpleName().equals(profile.getName())) {
-                        enableNativeWithGuestFlag();
+                        enableNative();
                     } else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                             && mRegisteredProfiles.size() == mRunningProfiles.size()) {
                         mAdapterProperties.onBluetoothReady();
@@ -393,7 +393,7 @@
         mAdapterProperties = new AdapterProperties(this);
         mAdapterStateMachine = AdapterState.make(this);
         mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
-        initNative();
+        initNative(isGuest(), isSingleUserMode());
         mNativeAvailable = true;
         mCallbacks = new RemoteCallbackList<IBluetoothCallback>();
         mAppOps = getSystemService(AppOpsManager.class);
@@ -2853,11 +2853,12 @@
         }
     };
 
-    private void enableNativeWithGuestFlag() {
-        boolean isGuest = UserManager.get(this).isGuestUser();
-        if (!enableNative(isGuest)) {
-            Log.e(TAG, "enableNative() returned false");
-        }
+    private boolean isGuest() {
+        return UserManager.get(this).isGuestUser();
+    }
+
+    private boolean isSingleUserMode() {
+        return UserManager.get(this).hasUserRestriction(UserManager.DISALLOW_ADD_USER);
     }
 
     /**
@@ -2876,12 +2877,12 @@
 
     static native void classInitNative();
 
-    native boolean initNative();
+    native boolean initNative(boolean startRestricted, boolean isSingleUserMode);
 
     native void cleanupNative();
 
     /*package*/
-    native boolean enableNative(boolean startRestricted);
+    native boolean enableNative();
 
     /*package*/
     native boolean disableNative();
diff --git a/src/com/android/bluetooth/btservice/SilenceDeviceManager.java b/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
index ec01130..e8ec235 100644
--- a/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
+++ b/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
@@ -64,8 +64,6 @@
     private final ServiceFactory mFactory;
     private Handler mHandler = null;
     private Looper mLooper = null;
-    private A2dpService mA2dpService = null;
-    private HeadsetService mHeadsetService = null;
 
     private final Map<BluetoothDevice, Boolean> mSilenceDevices = new HashMap<>();
     private final List<BluetoothDevice> mA2dpConnectedDevices = new ArrayList<>();
@@ -225,8 +223,6 @@
         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
         mAdapterService.registerReceiver(mReceiver, filter);
-        mA2dpService = mFactory.getA2dpService();
-        mHeadsetService = mFactory.getHeadsetService();
     }
 
     void cleanup() {
@@ -234,8 +230,6 @@
             Log.v(TAG, "cleanup()");
         }
         mSilenceDevices.clear();
-        mA2dpService = null;
-        mHeadsetService = null;
         mAdapterService.unregisterReceiver(mReceiver);
     }
 
@@ -268,16 +262,14 @@
         }
         mSilenceDevices.replace(device, state);
 
-        if (mA2dpService == null) {
-            Log.d(TAG, "A2dpService is null!");
-            return;
+        A2dpService a2dpService = mFactory.getA2dpService();
+        if (a2dpService != null) {
+            a2dpService.setSilenceMode(device, state);
         }
-        if (mHeadsetService == null) {
-            Log.d(TAG, "HeadsetService is null!");
-            return;
+        HeadsetService headsetService = mFactory.getHeadsetService();
+        if (headsetService != null) {
+            headsetService.setSilenceMode(device, state);
         }
-        mA2dpService.setSilenceMode(device, state);
-        mHeadsetService.setSilenceMode(device, state);
         Log.i(TAG, "Silence mode change " + device.getAddress() + ": " + oldState + " -> "
                 + state);
         broadcastSilenceStateChange(device, state);
diff --git a/src/com/android/bluetooth/mapclient/MapClientService.java b/src/com/android/bluetooth/mapclient/MapClientService.java
index 3f432ca..9989a98 100644
--- a/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -101,6 +101,10 @@
             Log.d(TAG, "MAP connect device: " + device
                     + ", InstanceMap start state: " + sb.toString());
         }
+        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
+            Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF");
+            return false;
+        }
         MceStateMachine mapStateMachine = mMapInstanceMap.get(device);
         if (mapStateMachine == null) {
             // a map state machine instance doesn't exist yet, create a new one if we can.
diff --git a/src/com/android/bluetooth/mapclient/MceStateMachine.java b/src/com/android/bluetooth/mapclient/MceStateMachine.java
index 36c1ee0..11b634c 100644
--- a/src/com/android/bluetooth/mapclient/MceStateMachine.java
+++ b/src/com/android/bluetooth/mapclient/MceStateMachine.java
@@ -51,6 +51,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Message;
+import android.provider.Telephony;
 import android.telecom.PhoneAccount;
 import android.telephony.SmsManager;
 import android.util.Log;
@@ -68,8 +69,10 @@
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 /* The MceStateMachine is responsible for setting up and maintaining a connection to a single
  * specific Messaging Server Equipment endpoint.  Upon connect command an SDP record is retrieved,
@@ -122,6 +125,48 @@
             new HashMap<>(MAX_MESSAGES);
     private Bmessage.Type mDefaultMessageType = Bmessage.Type.SMS_CDMA;
 
+    /**
+     * An object to hold the necessary meta-data for each message so we can broadcast it alongside
+     * the message content.
+     *
+     * This is necessary because the metadata is inferred or received separately from the actual
+     * message content.
+     *
+     * Note: In the future it may be best to use the entries from the MessageListing in full instead
+     * of this small subset.
+     */
+    private class MessageMetadata {
+        private final String mHandle;
+        private final Long mTimestamp;
+        private boolean mRead;
+
+        MessageMetadata(String handle, Long timestamp, boolean read) {
+            mHandle = handle;
+            mTimestamp = timestamp;
+            mRead = read;
+        }
+
+        public String getHandle() {
+            return mHandle;
+        }
+
+        public Long getTimestamp() {
+            return mTimestamp;
+        }
+
+        public synchronized boolean getRead() {
+            return mRead;
+        }
+
+        public synchronized void setRead(boolean read) {
+            mRead = read;
+        }
+    }
+
+    // Map each message to its metadata via the handle
+    private ConcurrentHashMap<String, MessageMetadata> mMessages =
+            new ConcurrentHashMap<String, MessageMetadata>();
+
     MceStateMachine(MapClientService service, BluetoothDevice device) {
         this(service, device, null);
     }
@@ -340,7 +385,6 @@
             }
             onConnectionStateChanged(mPreviousState, BluetoothProfile.STATE_CONNECTING);
 
-            BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
             // When commanded to connect begin SDP to find the MAS server.
             mDevice.sdpSearch(BluetoothUuid.MAS);
             sendMessageDelayed(MSG_CONNECTING_TIMEOUT, TIMEOUT);
@@ -504,6 +548,15 @@
             mPreviousState = BluetoothProfile.STATE_CONNECTED;
         }
 
+        /**
+         * Given a message notification event, will ensure message caching and updating and update
+         * interested applications.
+         *
+         * Message notifications arrive for both remote message reception and Message-Listing object
+         * updates that are triggered by the server side.
+         *
+         * @param msg - A Message object containing a EventReport object describing the remote event
+         */
         private void processNotification(Message msg) {
             if (DBG) {
                 Log.d(TAG, "Handler: msg: " + msg.what);
@@ -519,7 +572,14 @@
                     switch (ev.getType()) {
 
                         case NEW_MESSAGE:
-                            //mService.get().sendNewMessageNotification(ev);
+                            // Infer the timestamp for this message as 'now' and read status false
+                            // instead of getting the message listing data for it
+                            if (!mMessages.contains(ev.getHandle())) {
+                                Calendar calendar = Calendar.getInstance();
+                                MessageMetadata metadata = new MessageMetadata(ev.getHandle(),
+                                        calendar.getTime().getTime(), false);
+                                mMessages.put(ev.getHandle(), metadata);
+                            }
                             mMasClient.makeRequest(new RequestGetMessage(ev.getHandle(),
                                     MasClient.CharsetType.UTF_8, false));
                             break;
@@ -535,6 +595,8 @@
         // Sets the specified message status to "read" (from "unread" status, mostly)
         private void markMessageRead(RequestGetMessage request) {
             if (DBG) Log.d(TAG, "markMessageRead");
+            MessageMetadata metadata = mMessages.get(request.getHandle());
+            metadata.setRead(true);
             mMasClient.makeRequest(new RequestSetMessageStatus(
                     request.getHandle(), RequestSetMessageStatus.StatusIndicator.READ));
         }
@@ -546,21 +608,41 @@
                     request.getHandle(), RequestSetMessageStatus.StatusIndicator.DELETED));
         }
 
+        /**
+         * Given the result of a Message Listing request, will cache the contents of each Message in
+         * the Message Listing Object and kick off requests to retrieve message contents from the
+         * remote device.
+         *
+         * @param request - A request object that has been resolved and returned with a message list
+         */
         private void processMessageListing(RequestGetMessagesListing request) {
             if (DBG) {
                 Log.d(TAG, "processMessageListing");
             }
-            ArrayList<com.android.bluetooth.mapclient.Message> messageHandles = request.getList();
-            if (messageHandles != null) {
-                for (com.android.bluetooth.mapclient.Message handle : messageHandles) {
+            ArrayList<com.android.bluetooth.mapclient.Message> messageListing = request.getList();
+            if (messageListing != null) {
+                for (com.android.bluetooth.mapclient.Message msg : messageListing) {
                     if (DBG) {
                         Log.d(TAG, "getting message ");
                     }
-                    getMessage(handle.getHandle());
+                    // A message listing coming from the server should always have up to date data
+                    mMessages.put(msg.getHandle(), new MessageMetadata(msg.getHandle(),
+                            msg.getDateTime().getTime(), msg.isRead()));
+                    getMessage(msg.getHandle());
                 }
             }
         }
 
+        /**
+         * Given the response of a GetMessage request, will broadcast the bMessage contents on to
+         * all registered applications.
+         *
+         * Inbound messages arrive as bMessage objects following a GetMessage request. GetMessage
+         * uses a message handle that can arrive from both a GetMessageListing request or a Message
+         * Notification event.
+         *
+         * @param request - A request object that has been resolved and returned with message data
+         */
         private void processInboundMessage(RequestGetMessage request) {
             Bmessage message = request.getMessage();
             if (DBG) {
@@ -589,10 +671,18 @@
                         Log.d(TAG, "Recipients" + message.getRecipients().toString());
                     }
 
+                    // Grab the message metadata and update the cached read status from the bMessage
+                    MessageMetadata metadata = mMessages.get(request.getHandle());
+                    metadata.setRead(request.getMessage().getStatus() == Bmessage.Status.READ);
+
                     Intent intent = new Intent();
                     intent.setAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED);
                     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
                     intent.putExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE, request.getHandle());
+                    intent.putExtra(BluetoothMapClient.EXTRA_MESSAGE_TIMESTAMP,
+                            metadata.getTimestamp());
+                    intent.putExtra(BluetoothMapClient.EXTRA_MESSAGE_READ_STATUS,
+                            metadata.getRead());
                     intent.putExtra(android.content.Intent.EXTRA_TEXT, message.getBodyContent());
                     VCardEntry originator = message.getOriginator();
                     if (originator != null) {
@@ -611,7 +701,12 @@
                         intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME,
                                 originator.getDisplayName());
                     }
-                    mService.sendBroadcast(intent);
+                    // Only send to the current default SMS app if one exists
+                    String defaultMessagingPackage = Telephony.Sms.getDefaultSmsPackage(mService);
+                    if (defaultMessagingPackage != null) {
+                        intent.setPackage(defaultMessagingPackage);
+                    }
+                    mService.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
                     break;
 
                 case MMS:
diff --git a/src/com/android/bluetooth/opp/BluetoothOppService.java b/src/com/android/bluetooth/opp/BluetoothOppService.java
index 545f1ec..914b9b6 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -243,6 +243,10 @@
 
     @Override
     public boolean stop() {
+        if (sBluetoothOppService == null) {
+            Log.w(TAG, "stop() called before start()");
+            return true;
+        }
         setBluetoothOppService(null);
         mHandler.sendMessage(mHandler.obtainMessage(STOP_LISTENER));
         return true;
diff --git a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
index 39eda7b..d82d28b 100644
--- a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
@@ -15,7 +15,11 @@
  */
 package com.android.bluetooth.a2dpsink;
 
+import static org.mockito.Mockito.*;
+
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
 import androidx.test.InstrumentationRegistry;
@@ -26,6 +30,7 @@
 import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -47,6 +52,7 @@
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
     @Mock private AdapterService mAdapterService;
+    @Mock private DatabaseManager mDatabaseManager;
 
     @Before
     public void setUp() throws Exception {
@@ -61,6 +67,7 @@
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         Assert.assertNotNull(mAdapter);
+        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
     }
 
     @After
@@ -74,8 +81,43 @@
         TestUtils.clearAdapterService(mAdapterService);
     }
 
+    private BluetoothDevice makeBluetoothDevice(String address) {
+        return mAdapter.getRemoteDevice(address);
+    }
+
+    /**
+     * Mock the priority of a bluetooth device
+     *
+     * @param device - The bluetooth device you wish to mock the priority of
+     * @param priority - The priority value you want the device to have
+     */
+    private void mockDevicePriority(BluetoothDevice device, int priority) {
+        when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.A2DP_SINK))
+                .thenReturn(priority);
+    }
+
     @Test
     public void testInitialize() {
         Assert.assertNotNull(A2dpSinkService.getA2dpSinkService());
     }
+
+    /**
+     * Test that a PRIORITY_ON device is connected to
+     */
+    @Test
+    public void testConnect() {
+        BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
+        mockDevicePriority(device, BluetoothProfile.PRIORITY_ON);
+        Assert.assertTrue(mService.connect(device));
+    }
+
+    /**
+     * Test that a PRIORITY_OFF device is not connected to
+     */
+    @Test
+    public void testConnectPriorityOffDevice() {
+        BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
+        mockDevicePriority(device, BluetoothProfile.PRIORITY_OFF);
+        Assert.assertFalse(mService.connect(device));
+    }
 }
diff --git a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index 58f29ae..14adf89 100644
--- a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -89,6 +89,7 @@
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         TestUtils.startService(mServiceRule, AvrcpControllerService.class);
+        doReturn(mTargetContext.getResources()).when(mAvrcpControllerService).getResources();
 
         // This line must be called to make sure relevant objects are initialized properly
         mAdapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 897863e..82fa163 100644
--- a/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -101,7 +101,7 @@
         }
         Assert.assertNotNull(Looper.myLooper());
         AdapterService adapterService = new AdapterService();
-        adapterService.initNative();
+        adapterService.initNative(false /* is_restricted */, false /* is_single_user_mode */);
         adapterService.cleanupNative();
         HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
         Assert.assertNotNull(adapterConfig);
diff --git a/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java b/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
index 999ccd1..6567398 100644
--- a/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
@@ -96,7 +96,7 @@
 
         mProfiles = Config.getSupportedProfiles();
 
-        mMockAdapterService.initNative();
+        mMockAdapterService.initNative(false /* is_restricted */, false /* is_single_user_mode */);
 
         TestUtils.setAdapterService(mMockAdapterService);
 
diff --git a/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java b/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
index b80e75e..bc25a11 100644
--- a/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
+++ b/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
@@ -16,8 +16,11 @@
 
 package com.android.bluetooth.mapclient;
 
+import static org.mockito.Mockito.*;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
 import androidx.test.InstrumentationRegistry;
@@ -28,6 +31,7 @@
 import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -53,6 +57,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private MnsService mMockMnsService;
+    @Mock private DatabaseManager mDatabaseManager;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -69,6 +74,7 @@
         Assert.assertNotNull(mService);
         cleanUpInstanceMap();
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
     }
 
     @After
@@ -92,6 +98,17 @@
         Assert.assertTrue(mService.getInstanceMap().isEmpty());
     }
 
+    /**
+     * Mock the priority of a bluetooth device
+     *
+     * @param device - The bluetooth device you wish to mock the priority of
+     * @param priority - The priority value you want the device to have
+     */
+    private void mockDevicePriority(BluetoothDevice device, int priority) {
+        when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.MAP_CLIENT))
+                .thenReturn(priority);
+    }
+
     @Test
     public void testInitialize() {
         Assert.assertNotNull(MapClientService.getMapClientService());
@@ -107,6 +124,7 @@
         Assert.assertNull(mService.getInstanceMap().get(device));
 
         // connect a bluetooth device
+        mockDevicePriority(device, BluetoothProfile.PRIORITY_ON);
         Assert.assertTrue(mService.connect(device));
 
         // is the statemachine created
@@ -116,6 +134,25 @@
     }
 
     /**
+     * Test that a PRIORITY_OFF device is not connected to
+     */
+    @Test
+    public void testConnectPriorityOffDevice() {
+        // make sure there is no statemachine already defined for this device
+        BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
+        Assert.assertNull(mService.getInstanceMap().get(device));
+
+        // connect a bluetooth device
+        mockDevicePriority(device, BluetoothProfile.PRIORITY_OFF);
+        Assert.assertFalse(mService.connect(device));
+
+        // is the statemachine created
+        Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap();
+        Assert.assertEquals(0, map.size());
+        Assert.assertNull(map.get(device));
+    }
+
+    /**
      * Test connecting MAXIMUM_CONNECTED_DEVICES devices.
      */
     @Test
@@ -132,8 +169,9 @@
             Assert.assertNull(mService.getInstanceMap().get(d));
         }
 
-        // run the test - connect all devices
+        // run the test - connect all devices, set their priorities to on
         for (BluetoothDevice d : list) {
+            mockDevicePriority(d, BluetoothProfile.PRIORITY_ON);
             Assert.assertTrue(mService.connect(d));
         }