summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--AndroidManifest.xml10
-rw-r--r--res/values-af/strings.xml1
-rw-r--r--res/values-am/strings.xml1
-rw-r--r--res/values-ar/strings.xml9
-rw-r--r--res/values-as/strings.xml1
-rw-r--r--res/values-az/strings.xml1
-rw-r--r--res/values-b+sr+Latn/strings.xml1
-rw-r--r--res/values-be/strings.xml1
-rw-r--r--res/values-bg/strings.xml1
-rw-r--r--res/values-bn/strings.xml1
-rw-r--r--res/values-bs/strings.xml1
-rw-r--r--res/values-ca/strings.xml1
-rw-r--r--res/values-cs/strings.xml1
-rw-r--r--res/values-da/strings.xml1
-rw-r--r--res/values-de/strings.xml1
-rw-r--r--res/values-el/strings.xml1
-rw-r--r--res/values-en-rAU/strings.xml1
-rw-r--r--res/values-en-rCA/strings.xml1
-rw-r--r--res/values-en-rGB/strings.xml1
-rw-r--r--res/values-en-rIN/strings.xml1
-rw-r--r--res/values-en-rXC/strings.xml1
-rw-r--r--res/values-es-rUS/strings.xml1
-rw-r--r--res/values-es/strings.xml3
-rw-r--r--res/values-et/strings.xml1
-rw-r--r--res/values-eu/strings.xml1
-rw-r--r--res/values-fa/strings.xml1
-rw-r--r--res/values-fi/strings.xml1
-rw-r--r--res/values-fr-rCA/strings.xml1
-rw-r--r--res/values-fr/strings.xml1
-rw-r--r--res/values-gl/strings.xml1
-rw-r--r--res/values-gu/strings.xml1
-rw-r--r--res/values-hi/strings.xml1
-rw-r--r--res/values-hr/strings.xml1
-rw-r--r--res/values-hu/strings.xml1
-rw-r--r--res/values-hy/strings.xml1
-rw-r--r--res/values-in/strings.xml1
-rw-r--r--res/values-is/strings.xml1
-rw-r--r--res/values-it/strings.xml1
-rw-r--r--res/values-iw/strings.xml1
-rw-r--r--res/values-ja/strings.xml1
-rw-r--r--res/values-ka/strings.xml1
-rw-r--r--res/values-kk/strings.xml1
-rw-r--r--res/values-kk/strings_pbap.xml4
-rw-r--r--res/values-km/strings.xml3
-rw-r--r--res/values-kn/strings.xml1
-rw-r--r--res/values-ko/strings.xml1
-rw-r--r--res/values-ky/strings.xml5
-rw-r--r--res/values-lo/strings.xml1
-rw-r--r--res/values-lt/strings.xml1
-rw-r--r--res/values-lv/strings.xml1
-rw-r--r--res/values-mk/strings.xml1
-rw-r--r--res/values-ml/strings.xml3
-rw-r--r--res/values-mn/strings.xml1
-rw-r--r--res/values-mr/strings.xml1
-rw-r--r--res/values-ms/strings.xml1
-rw-r--r--res/values-my/strings.xml1
-rw-r--r--res/values-nb/strings.xml1
-rw-r--r--res/values-ne/strings.xml1
-rw-r--r--res/values-nl/strings.xml1
-rw-r--r--res/values-or/strings.xml1
-rw-r--r--res/values-pa/strings.xml1
-rw-r--r--res/values-pl/strings.xml1
-rw-r--r--res/values-pt-rPT/strings.xml1
-rw-r--r--res/values-pt/strings.xml1
-rw-r--r--res/values-ro/strings.xml1
-rw-r--r--res/values-ru/strings.xml1
-rw-r--r--res/values-si/strings.xml1
-rw-r--r--res/values-sk/strings.xml1
-rw-r--r--res/values-sl/strings.xml1
-rw-r--r--res/values-sq/strings.xml1
-rw-r--r--res/values-sr/strings.xml1
-rw-r--r--res/values-sv/strings.xml1
-rw-r--r--res/values-sw/strings.xml1
-rw-r--r--res/values-ta/strings.xml1
-rw-r--r--res/values-te/strings.xml1
-rw-r--r--res/values-th/strings.xml1
-rw-r--r--res/values-tl/strings.xml1
-rw-r--r--res/values-tr/strings.xml1
-rw-r--r--res/values-uk/strings.xml1
-rw-r--r--res/values-ur/strings.xml1
-rw-r--r--res/values-uz/strings.xml1
-rw-r--r--res/values-vi/strings.xml3
-rw-r--r--res/values-zh-rCN/strings.xml1
-rw-r--r--res/values-zh-rHK/strings.xml1
-rw-r--r--res/values-zh-rTW/strings.xml1
-rw-r--r--res/values-zu/strings.xml1
-rw-r--r--res/values/strings.xml1
-rw-r--r--res/xml/authenticator.xml3
-rw-r--r--src/com/android/bluetooth/BluetoothPrefs.java40
-rw-r--r--src/com/android/bluetooth/a2dpsink/A2dpSinkService.java3
-rw-r--r--src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java63
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java37
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java138
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java104
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java106
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/BrowseTree.java9
-rw-r--r--src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java201
-rw-r--r--src/com/android/bluetooth/btservice/AdapterService.java1
-rw-r--r--src/com/android/bluetooth/hfp/HeadsetStateMachine.java26
-rw-r--r--src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java2
-rw-r--r--src/com/android/bluetooth/mapclient/MceStateMachine.java28
-rw-r--r--src/com/android/bluetooth/mapclient/MnsObexServer.java4
-rw-r--r--src/com/android/bluetooth/mapclient/MnsService.java6
-rw-r--r--src/com/android/bluetooth/mapclient/obex/BmessageParser.java6
-rw-r--r--src/com/android/bluetooth/mapclient/obex/ObexTime.java43
-rw-r--r--src/com/android/bluetooth/pan/PanService.java13
-rw-r--r--src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java2
-rw-r--r--src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java66
-rw-r--r--src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java124
-rw-r--r--src/com/android/bluetooth/pbapclient/PbapClientService.java36
-rw-r--r--tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java15
-rw-r--r--tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java165
-rw-r--r--tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java96
-rw-r--r--tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java30
-rw-r--r--tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java114
-rw-r--r--tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java13
117 files changed, 1236 insertions, 378 deletions
diff --git a/Android.mk b/Android.mk
index a34d2a34f..1f73091ad 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.core_core \
+ androidx.legacy_legacy-support-v4 \
androidx.lifecycle_lifecycle-livedata \
androidx.room_room-runtime \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b524f7b54..99272a426 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -316,6 +316,16 @@
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
+
+ <activity
+ android:name=".BluetoothPrefs"
+ android:exported="@bool/profile_supported_a2dp_sink"
+ android:enabled="@bool/profile_supported_a2dp_sink">
+ <intent-filter>
+ <action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
+ </intent-filter>
+ </activity>
+
<service
android:process="@string/process"
android:name = ".avrcp.AvrcpTargetService"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 060fd9013..f011684cf 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-oudio is ontkoppel"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-oudio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Lêers groter as 4 GB kan nie oorgedra word nie"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Koppel aan Bluetooth"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index dbf4aadd6..0d2f87cc5 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"የብሉቱዝ ኦዲዮ ግንኙነት ተቋርጧል"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"የብሉቱዝ ኦዲዮ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"ከ4 ጊባ በላይ የሆኑ ፋይሎች ሊዛወሩ አይችሉም"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ከብሉቱዝ ጋር ተገናኝ"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 1525a71f5..2d4e70d29 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -26,10 +26,10 @@
<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>
- <string name="bt_enable_line2" msgid="4341936569415937994">"هل تريد تشغيل البلوتوث الآن؟"</string>
+ <string name="bt_enable_line1" msgid="7203551583048149">"لإستخدام خدمات البلوتوث، يجب تفعيل البلوتوث أولاً."</string>
+ <string name="bt_enable_line2" msgid="4341936569415937994">"هل تريد تفعيل البلوتوث الآن؟"</string>
<string name="bt_enable_cancel" msgid="1988832367505151727">"إلغاء"</string>
- <string name="bt_enable_ok" msgid="3432462749994538265">"تشغيل"</string>
+ <string name="bt_enable_ok" msgid="3432462749994538265">"تفعيل"</string>
<string name="incoming_file_confirm_title" msgid="8139874248612182627">"نقل الملف"</string>
<string name="incoming_file_confirm_content" msgid="2752605552743148036">"هل تقبل الملف الوارد؟"</string>
<string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"رفض"</string>
@@ -77,7 +77,7 @@
<string name="not_exist_file" msgid="3489434189599716133">"ليست هناك أي ملفات"</string>
<string name="not_exist_file_desc" msgid="4059531573790529229">"الملف غير موجود. \n"</string>
<string name="enabling_progress_title" msgid="436157952334723406">"يرجى الانتظار…"</string>
- <string name="enabling_progress_content" msgid="4601542238119927904">"جارٍ تشغيل البلوتوث..."</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>
@@ -142,4 +142,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"انقطع الاتصال بالبث الصوتي عبر البلوتوث."</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"بث صوتي عبر البلوتوث"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"يتعذّر نقل الملفات التي يزيد حجمها عن 4 غيغابايت"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"الاتصال ببلوتوث"</string>
</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index fd6ab371a..a5f682ce6 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ব্লুটুথ অডিঅ\'ৰ সৈতে সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ব্লুটুথ অডিঅ\'"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"৪ জি. বি. তকৈ ডাঙৰ ফাইল স্থানান্তৰ কৰিব নোৱাৰি"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ব্লুটুথৰ সৈতে সংযোগ কৰক"</string>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 4abf91f83..ea55d1771 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio ilə bağlantı kəsildi"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB-dən böyük olan faylları köçürmək mümkün deyil"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth\'a qoşulun"</string>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 4d999f36a..9ad26e1e8 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Veza sa Bluetooth audijom je prekinuta"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Ne mogu da se prenose datoteke veće od 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Poveži sa Bluetooth-om"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0026ea6d8..3c9dbd6cd 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-аўдыя адключана"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-аўдыя"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Немагчыма перадаць файлы, большыя за 4 ГБ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Падключыцца да Bluetooth"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 1566a37dc..4b57342d2 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудиовръзката през Bluetooth е прекратена"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Аудио през Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Файловете с размер над 4 ГБ не могат да бъдат прехвърлени"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Свързване с Bluetooth"</string>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 352f359c0..be016492a 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ব্লুটুথ অডিওর সংযোগ বিচ্ছিন্ন হয়েছে"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ব্লুটুথ অডিও"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"৪GB থেকে বড় ফটো ট্রান্সফার করা যাবে না"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ব্লুটুথের সাথে কানেক্ট করুন"</string>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 7f6eca1e4..3c7dec44d 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio je isključen"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Nije moguće prenijeti fajlove veće od 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Poveži se na Bluetooth"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index e24b808d3..bddf09672 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Àudio per Bluetooth desconnectat"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Àudio per Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"No es poden transferir fitxers més grans de 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connecta el Bluetooth"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 3e76d2bb4..9adb80c78 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth Audio – odpojeno"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Soubory větší než 4 GB nelze přenést"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Připojit k Bluetooth"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 25819a183..25cf3d7be 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-lyden blev afbrudt"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-lyd"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"File, der er større end 4 GB, kan ikke overføres"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Opret forbindelse til Bluetooth"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c9e655490..4ffe47022 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-Audio-Verbindung aufgehoben"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Dateien mit mehr als 4 GB können nicht übertragen werden"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Mit Bluetooth verbinden"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 1506bd2e7..e249a3889 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Ο ήχος Bluetooth αποσυνδέθηκε"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Ήχος Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Δεν είναι δυνατή η μεταφορά αρχείων που ξεπερνούν τα 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Σύνδεση σε Bluetooth"</string>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index ddca4979a..8dcbab466 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Files bigger than 4 GB cannot be transferred"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connect to Bluetooth"</string>
</resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index e549cb6d4..07202ef21 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Files bigger than 4 GB cannot be transferred"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connect to Bluetooth"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index ddca4979a..8dcbab466 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Files bigger than 4 GB cannot be transferred"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connect to Bluetooth"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index ddca4979a..8dcbab466 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio disconnected"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Files bigger than 4 GB cannot be transferred"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connect to Bluetooth"</string>
</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 02178d43d..8bbc66c7c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎Bluetooth audio disconnected‎‏‎‎‏‎"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎Bluetooth Audio‎‏‎‎‏‎"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎Files bigger than 4GB cannot be transferred‎‏‎‎‏‎"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎Connect to Bluetooth‎‏‎‎‏‎"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 4a4eaaaa3..eb95310b4 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth desconectado"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"No se pueden transferir los archivos de más de 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Conectarse a Bluetooth"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index a9e448c41..a67ba6b4f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -122,7 +122,7 @@
<string name="transfer_menu_open" msgid="3368984869083107200">"Abrir"</string>
<string name="transfer_menu_clear" msgid="5854038118831427492">"Borrar de la lista"</string>
<string name="transfer_clear_dlg_title" msgid="2953444575556460386">"Borrar"</string>
- <string name="bluetooth_a2dp_sink_queue_name" msgid="6864149958708669766">"Está Sonando"</string>
+ <string name="bluetooth_a2dp_sink_queue_name" msgid="6864149958708669766">"Reproduciendo"</string>
<string name="bluetooth_map_settings_save" msgid="7635491847388074606">"Guardar"</string>
<string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"Cancelar"</string>
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"Selecciona las cuentas que quieras compartir por Bluetooth. Tendrás que aceptar cualquier acceso a las cuentas al establecer conexión."</string>
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio por Bluetooth desconectado"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio por Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"No se pueden transferir archivos de más de 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Conectarse a un dispositivo Bluetooth"</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index dbc425adb..334174fbc 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetoothi heli ühendus on katkestatud"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetoothi heli"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Faile, mis on üle 4 GB, ei saa üle kanda"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Ühenda Bluetoothiga"</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index cc6e417f0..72e609650 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Deskonektatu da Bluetooth bidezko audioa"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth bidezko audioa"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Ezin dira transferitu 4 GB baino gehiagoko fitxategiak"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Konektatu Bluetooth-era"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c95558eac..e2c1b75c9 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ارتباط بلوتوث صوتی قطع شد"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"بلوتوث‌ صوتی"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"فایل‌های بزرگ‌تر از ۴ گیگابایت نمی‌توانند منتقل شوند"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"اتصال به بلوتوث"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 0fbc55c5a..f1792c634 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-ääni katkaistu"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-ääni"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Yli 4 Gt:n kokoisia tiedostoja ei voi siirtää."</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Muodosta Bluetooth-yhteys"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index b881cfcbd..4a62f98eb 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth déconnecté"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Les fichiers dépassant 4 Go ne peuvent pas être transférés"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connexion au Bluetooth"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 06fe2093c..e0caaee4f 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth déconnecté"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Impossible de transférer les fichiers supérieurs à 4 Go"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Se connecter au Bluetooth"</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 9dedefde2..1f2b1d0ca 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Desconectouse o audio por Bluetooth"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio por Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Non se poden transferir ficheiros de máis de 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Conectar ao Bluetooth"</string>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index d6e10005a..4195292df 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"બ્લૂટૂથ ઑડિઓ ડિસ્કનેક્ટ થયું"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"બ્લૂટૂથ ઑડિઓ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB કરતા મોટી ફાઇલ ટ્રાન્સફર કરી શકાતી નથી"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"બ્લૂટૂથ સાથે કનેક્ટ કરો"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index d01d1377a..cb0bb5f7d 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लूटूथ ऑडियो डिसकनेक्ट किया गया"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ब्लूटूथ ऑडियो"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 जीबी से बड़ी फ़ाइलें ट्रांसफ़र नहीं की जा सकतीं"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ब्लूटूथ से कनेक्ट करें"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d05c7b338..581e1a875 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Veza Bluetooth Audija prekinuta"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Datoteke veće od 4 GB ne mogu se prenijeti"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Povezivanje s Bluetoothom"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 4fa442f44..ff93e444d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audió leválasztva"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth audió"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"A 4 GB-nál nagyobb fájlokat nem lehet átvinni"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Csatlakozás Bluetooth-eszközhöz"</string>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index bf6fbf3b8..1fb6d74fb 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth աուդիոն անջատված է"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth աուդիո"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 ԳԲ-ից մեծ ֆայլերը հնարավոր չէ փոխանցել"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Միանալ Bluetooth-ին"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 15722dab8..01c4fe10d 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth audio terputus"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"File yang berukuran lebih dari 4GB tidak dapat ditransfer"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Hubungkan ke Bluetooth"</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 624638903..65db6e918 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-hljóð aftengt"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-hljóð"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Ekki er hægt að flytja skrár sem eru stærri en 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Tengjast við Bluetooth"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e4a486f33..27cefda39 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth disconnesso"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Impossibile trasferire file con dimensioni superiori a 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Connettiti a Bluetooth"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 415bd1077..11446ff3b 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"‏אודיו Bluetooth מנותק"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"‏אודיו Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"‏לא ניתן להעביר קבצים שגדולים מ-GB‏ 4"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"‏התחברות באמצעות Bluetooth"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 6171772a0..7688d485a 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth オーディオは接続を解除されています"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth オーディオ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 GB を超えるファイルは転送できません"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth に接続する"</string>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 4983eaaf6..92b632919 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth აუდიო გათიშულია"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth აუდიო"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 გბაიტზე დიდი მოცულობის ფაილების გადატანა ვერ მოხერხდება"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth-თან დაკავშირება"</string>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 82de8a6a4..811187fa3 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth дыбысы ажыратылды"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth aудиосы"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Көлемі 4 ГБ-тан асатын файлдар тасымалданбайды"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth-ге қосылу"</string>
</resources>
diff --git a/res/values-kk/strings_pbap.xml b/res/values-kk/strings_pbap.xml
index 29ff2212d..11893b9ab 100644
--- a/res/values-kk/strings_pbap.xml
+++ b/res/values-kk/strings_pbap.xml
@@ -4,9 +4,7 @@
<string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"%1$s үшін сессия кілтін теру"</string>
<string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth сессия кілті қажет"</string>
<string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"%1$s арқылы байланыс қабылдау уақыты өтіп кетті"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for pbap_authentication_timeout_message (4166979525521902687) -->
- <skip />
+ <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$s арқылы сессия кілтін енгізу уақыты өтіп кетті"</string>
<string name="auth_notif_ticker" msgid="1575825798053163744">"Obex растау талабы"</string>
<string name="auth_notif_title" msgid="7599854855681573258">"Сессия кілті"</string>
<string name="auth_notif_message" msgid="6667218116427605038">"%1$s үшін сессия кілтін теру"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index f4b7e7a5c..1db467c4d 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -122,7 +122,7 @@
<string name="transfer_menu_open" msgid="3368984869083107200">"បើក"</string>
<string name="transfer_menu_clear" msgid="5854038118831427492">"សម្អាត​ពី​បញ្ជី"</string>
<string name="transfer_clear_dlg_title" msgid="2953444575556460386">"សម្អាត"</string>
- <string name="bluetooth_a2dp_sink_queue_name" msgid="6864149958708669766">"Now Playing"</string>
+ <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>
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"សំឡេងប៊្លូធូសត្រូវបានផ្តាច់"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"សំឡេងប៊្លូធូស"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"ឯកសារ​ដែល​មាន​ទំហំ​ធំ​ជាង 4 GB មិន​អាចផ្ទេរ​បាន​ទេ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ភ្ជាប់​ប៊្លូធូស"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 0f0e06f92..1d87823c1 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB ಗಿಂತ ದೊಡ್ಡದಾದ ಫೈಲ್‌ಗಳನ್ನು ವರ್ಗಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ಬ್ಲೂಟೂತ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index f5eeca948..3977271cc 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"블루투스 오디오가 연결 해제됨"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"블루투스 오디오"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB보다 큰 파일은 전송할 수 없습니다"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"블루투스에 연결"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 0ed4aa9bd..f02c8748a 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -78,7 +78,7 @@
<string name="not_exist_file_desc" msgid="4059531573790529229">"Мындай файл жок. \n"</string>
<string name="enabling_progress_title" msgid="436157952334723406">"Күтө туруңуз…"</string>
<string name="enabling_progress_content" msgid="4601542238119927904">"Bluetooth жандырылууда…"</string>
- <string name="bt_toast_1" msgid="972182708034353383">"Файл алынат. Эскертмелер тактасынан жүрүшүн байкап турсаңыз болот."</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_4" msgid="4678812947604395649">"Кийинкиге файл жөнөтүлүүдө: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
@@ -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">"Bluetooth аркылуу бөлүшө турган каттоо эсептерин тандаңыз. Туташкан сайын каттоо эсептерине кирүү мүмкүнчүлүгүн ырастап турушуңуз керек."</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_title" msgid="7420332483392851321">"Bluetooth билдирүү бөлүшүү жөндөөлөрү"</string>
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth аудио ажыратылды"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth аудио"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4Гб чоң файлдарды өткөрүү мүмкүн эмес"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth\'га туташуу"</string>
</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index bebe622a8..3257841eb 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ຕັດການເຊື່ອມຕໍ່ສຽງ Bluetooth ແລ້ວ"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ສຽງ Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"ບໍ່ສາມາດໂອນຍ້າຍໄຟລ໌ທີ່ໃຫຍກວ່າ 4GB ໄດ້"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ເຊື່ອມຕໍ່ກັບ Bluetooth"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 5d0f75990..42562f4ad 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"„Bluetooth“ garsas atjungtas"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"„Bluetooth“ garsas"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Negalima perkelti didesnių nei 4 GB failų"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Prisijungti prie „Bluetooth“"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 062a0195e..6edd98470 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Pārtraukts savienojums ar Bluetooth audio"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Nevar pārsūtīt failus, kas lielāki par 4 GB."</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Izveidot savienojumu ar Bluetooth"</string>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 011c39dfb..5045b56db 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудиото преку Bluetooth е исклучено"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Аудио преку Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Не може да се пренесуваат датотеки поголеми од 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Поврзи се со Bluetooth"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 08e907f4f..716bf1dd5 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -20,7 +20,7 @@
<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>
+ <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>
@@ -132,4 +132,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth ഓഡിയോ വിച്ഛേദിച്ചു"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth ഓഡിയോ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB-യിൽ കൂടുതലുള്ള ഫയലുകൾ കൈമാറാനാവില്ല"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth-ലേക്ക് കണക്‌റ്റ് ചെയ്യുക"</string>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index aec448c75..60f238a72 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth аудиог салгасан"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Аудио"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4ГБ-с дээш хэмжээтэй файлыг шилжүүлэх боломжгүй"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth-тэй холбогдох"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 4db5d0aa8..43e0a545c 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लूटूथ ऑडिओ डिस्कनेक्ट केला"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ब्लूटूथ ऑडिओ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 GB हून मोठ्या फायली ट्रान्सफर करता येणार नाहीत"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ब्लूटूथशी कनेक्ट करा"</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index ab9c7a934..a0071ca53 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio Bluetooth diputuskan sambungannya"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Fail lebih besar daripada 4GB tidak boleh dipindahkan"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Sambung ke Bluetooth"</string>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 7e7763a23..eb35b0f85 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ဘလူးတုသ်အသံ မချိတ်ဆက်ထားပါ"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ဘလူးတုသ် အသံ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB ထက်ပိုကြီးသည့် ဖိုင်များကို လွှဲပြောင်းမရနိုင်ပါ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ဘလူးတုသ်သို့ ချိတ်ဆက်ရန်"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e0418b1b4..d6f9e7705 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-lyd er frakoblet"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-lyd"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Filer som er større enn 4 GB, kan ikke overføres"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Koble til Bluetooth"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 639df71d3..6f9ab5888 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ब्लुटुथ सम्बन्धी अडियो यन्त्रलाई विच्छेद गरियो"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ब्लुटुथको अडियो"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"४ जि.बि. भन्दा ठूला फाइलहरूलाई स्थानान्तरण गर्न सकिँदैन"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ब्लुटुथमा जोड्नुहोस्"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 920737897..8f496201b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-audio ontkoppeld"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Bestanden groter dan 4 GB kunnen niet worden overgedragen"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Verbinding maken met bluetooth"</string>
</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 8819773e2..94ad0410e 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ବ୍ଲୁଟୂଥ୍‍‌ ଅଡିଓ ବିଚ୍ଛିନ୍ନ କରାଗଲା"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ବ୍ଲୁଟୂଥ୍‍‌ ଅଡିଓ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GBରୁ ବଡ଼ ଫାଇଲ୍‌ଗୁଡ଼ିକୁ ଟ୍ରାନ୍ସଫର୍‌ କରାଯାଇପାରିବ ନାହିଁ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ବ୍ଲୁଟୁଥ୍ ସହ ସଂଯୋଗ କରନ୍ତୁ"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 07cab6a78..71c771b2a 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ਬਲੂਟੁੱਥ ਆਡੀਓ ਡਿਸਕਨੈਕਟ ਕੀਤੀ ਗਈ"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"ਬਲੂਟੁੱਥ ਆਡੀਓ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB ਤੋਂ ਵਧੇਰੇ ਵੱਡੀਆਂ ਫ਼ਾਈਲਾਂ ਨੂੰ ਟ੍ਰਾਂਸਫ਼ਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"ਬਲੂਟੁੱਥ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 8c4869f4c..3cfbe0d5f 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Dźwięk Bluetooth odłączony"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Dźwięk Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Nie można przenieść plików przekraczających 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Nawiązywanie połączeń przez Bluetooth"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 09e6781d3..9eae5c975 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Áudio Bluetooth desligado"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Áudio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Não é possível transferir os ficheiros com mais de 4 GB."</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Ligar ao Bluetooth"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 8472432ec..b4a775e33 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Áudio Bluetooth desconectado"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Áudio Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Não é possível transferir arquivos maiores que 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Conectar ao Bluetooth"</string>
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 67f117d40..fadc4a6d5 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audio prin Bluetooth deconectat"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audio prin Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Fișierele mai mari de 4 GB nu pot fi transferate"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Conectați-vă la Bluetooth"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 87c1492aa..6625b8346 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Звук через Bluetooth отключен"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Звук через Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Можно перенести только файлы размером до 4 ГБ."</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Подключиться по Bluetooth"</string>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index ed6e3551a..bbe1e0053 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"බ්ලූටූත් ශ්‍රව්‍යය විසන්ධි කරන ලදී"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"බ්ලූටූත් ශ්‍රව්‍යය"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GBට වඩා විශාල ගොනු මාරු කළ නොහැකිය"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"බ්ලූටූත් වෙත සබඳින්න"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index d189d0da2..e28a7920d 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Rozhranie Bluetooth Audio je odpojené"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Súbory väčšie ako 4 GB sa nedajú preniesť"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Pripojiť k zariadeniu Bluetooth"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 912ba37e9..d681fb27e 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Zvok prek Bluetootha ni povezan"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Zvok prek Bluetootha"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Datotek, večjih od 4 GB, ni mogoče prenesti"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Povezovanje z Bluetoothom"</string>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 1a95dcd6a..5d1aff31b 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Audioja e \"bluetooth-it\" e shkëputur"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Audioja e \"bluetooth-it\""</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Skedarët më të mëdhenj se 4 GB nuk mund të transferohen"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Lidhu me Bluetooth"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6bdf67a61..25fc24fba 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -136,4 +136,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Веза са Bluetooth аудијом је прекинута"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth аудио"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Не могу да се преносе датотеке веће од 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Повежи са Bluetooth-ом"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7c1609fbb..f243475b0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth-ljud är frånkopplat"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth-ljud"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Det går inte att överföra filer som är större än 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Anslut till Bluetooth"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 39f803ef6..edc03d96d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Imeondoa sauti ya Bluetooth"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Sauti ya Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Haiwezi kutuma faili zinazozidi GB 4"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Unganisha kwenye Bluetooth"</string>
</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index e584f9ff7..67d872f9e 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"புளூடூத் ஆடியோ துண்டிக்கப்பட்டது"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"புளூடூத் ஆடியோ"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4ஜி.பை.க்கு மேலிருக்கும் ஃபைல்களை இடமாற்ற முடியாது"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"புளூடூத் உடன் இணை"</string>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 7aaeaa459..e7f777546 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"బ్లూటూత్ ఆడియో డిస్‌కనెక్ట్ చేయబడింది"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"బ్లూటూత్ ఆడియో"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4GB కన్నా పెద్ద ఫైళ్లు బదిలీ చేయబడవు"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"బ్లూటూత్‌కు కనెక్ట్ చేయి"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 8cfec4fe8..48023cdf5 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"ยกเลิกการเชื่อมต่อ Bluetooth Audio แล้ว"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"โอนไฟล์ที่มีขนาดใหญ่กว่า 4 GB ไม่ได้"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"เชื่อมต่อบลูทูธ"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 1f50cebbe..3820800f6 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Nadiskonekta ang Bluetooth audio"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Hindi maililipat ang mga file na mas malaki sa 4GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Kumonekta sa Bluetooth"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 71ce9a4a5..e1a0bfb88 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth ses bağlantısı kesildi"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Ses"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 GB\'tan büyük dosyalar aktarılamaz"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetooth\'a bağlan"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index e357d1357..f7d6fbdd8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -138,4 +138,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Аудіо Bluetooth від’єднано"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth Audio"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Не можна перенести файли, більші за 4 ГБ"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Підключитися до Bluetooth"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index cc13746bf..e944fb8c2 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"بلوٹوتھ آڈیو غیر منسلک ہے"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"بلوٹوتھ آڈیو"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"‏4GB سے بڑی فائلیں منتقل نہیں کی جا سکتیں"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"بلوٹوتھ سے منسلک کریں"</string>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index dac670b6b..cadb8be49 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Bluetooth orqali ovoz o‘chirildi"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Bluetooth orqali ovoz"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"4 GBdan katta hajmli videolar o‘tkazilmaydi"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Bluetoothga ulanish"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 4a40dfe12..e0653c096 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -128,10 +128,11 @@
<string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"Chọn tài khoản mà bạn muốn chia sẻ qua Bluetooth. Bạn vẫn phải chấp nhận mọi quyền truy cập vào tài khoản khi kết nối."</string>
<string name="bluetooth_map_settings_count" msgid="4557473074937024833">"Số khe cắm còn lại:"</string>
<string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"Biểu tượng ứng dụng"</string>
- <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Cài đặt chia sẻ thư qua Bluetooth"</string>
+ <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Cài đặt cách chia sẻ thư qua Bluetooth"</string>
<string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"Không chọn được tài khoản. Còn lại 0 khe cắm"</string>
<string name="bluetooth_connected" msgid="6718623220072656906">"Đã kết nối âm thanh Bluetooth"</string>
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Đã ngắt kết nối âm thanh Bluetooth"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Âm thanh Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Không thể chuyển những tệp lớn hơn 4 GB"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Kết nối với Bluetooth"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 489853755..a5f6d2dad 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"蓝牙音频已断开连接"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"蓝牙音频"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"无法传输 4GB 以上的文件"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"连接到蓝牙"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index dfb352a71..2881ffe3c 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"已與藍牙音訊解除連接"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"藍牙音訊"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"無法轉移 4 GB 以上的檔案"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"連接藍牙"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 046dfc682..c8ece0167 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"已中斷與藍牙音訊的連線"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"藍牙音訊"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"無法轉移大於 4GB 的檔案"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"使用藍牙連線"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 1bc0fe622..da08ed2a9 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -134,4 +134,5 @@
<string name="bluetooth_disconnected" msgid="3318303728981478873">"Umsindo we-Bluetooth unqanyuliwe"</string>
<string name="a2dp_sink_mbs_label" msgid="7566075853395412558">"Umsindo we-Bluetooth"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="8894450394309084519">"Amafayela amakhulu kuno-4GB awakwazi ukudluliselwa"</string>
+ <string name="bluetooth_connect_action" msgid="4009848433321657090">"Xhumeka ku-Bluetooth"</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 016bb615b..b7dcaf2a9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -249,4 +249,5 @@
<string name="bluetooth_disconnected">Bluetooth audio disconnected"</string>
<string name="a2dp_sink_mbs_label">Bluetooth Audio</string>
<string name="bluetooth_opp_file_limit_exceeded">Files bigger than 4GB cannot be transferred</string>
+ <string name="bluetooth_connect_action">Connect to Bluetooth</string>
</resources>
diff --git a/res/xml/authenticator.xml b/res/xml/authenticator.xml
index b719fec4f..ab08a6103 100644
--- a/res/xml/authenticator.xml
+++ b/res/xml/authenticator.xml
@@ -17,5 +17,4 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:icon="@mipmap/bt_share"
android:smallIcon="@mipmap/bt_share"
- android:accountType="@string/pbap_account_type"
- android:label="@string/pbap_account_type" />
+ android:accountType="@string/pbap_account_type" />
diff --git a/src/com/android/bluetooth/BluetoothPrefs.java b/src/com/android/bluetooth/BluetoothPrefs.java
new file mode 100644
index 000000000..2c7c87aaa
--- /dev/null
+++ b/src/com/android/bluetooth/BluetoothPrefs.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Activity that routes to Bluetooth settings when launched
+ */
+public class BluetoothPrefs extends Activity {
+
+ public static final String BLUETOOTH_SETTING_ACTION = "android.settings.BLUETOOTH_SETTINGS";
+ public static final String BLUETOOTH_SETTING_CATEGORY = "android.intent.category.DEFAULT";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent launchIntent = new Intent();
+ launchIntent.setAction(BLUETOOTH_SETTING_ACTION);
+ launchIntent.addCategory(BLUETOOTH_SETTING_CATEGORY);
+ startActivity(launchIntent);
+ finish();
+ }
+}
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index e41def075..5271cc791 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -413,7 +413,8 @@ public class A2dpSinkService extends ProfileService {
if (state == StackEvent.AUDIO_STATE_STARTED) {
mA2dpSinkStreamHandler.obtainMessage(
A2dpSinkStreamHandler.SRC_STR_START).sendToTarget();
- } else if (state == StackEvent.AUDIO_STATE_STOPPED) {
+ } else if (state == StackEvent.AUDIO_STATE_STOPPED
+ || state == StackEvent.AUDIO_STATE_REMOTE_SUSPEND) {
mA2dpSinkStreamHandler.obtainMessage(
A2dpSinkStreamHandler.SRC_STR_STOP).sendToTarget();
}
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
index 5e3b3567c..5aa3cbbd5 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.a2dpsink;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClientCall;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
@@ -24,10 +25,9 @@ import android.media.AudioFocusRequest;
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.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import com.android.bluetooth.R;
@@ -183,8 +183,9 @@ public class A2dpSinkStreamHandler extends Handler {
break;
case AUDIO_FOCUS_CHANGE:
+ mAudioFocus = (int) message.obj;
// message.obj is the newly granted audio focus.
- switch ((int) message.obj) {
+ switch (mAudioFocus) {
case AudioManager.AUDIOFOCUS_GAIN:
removeMessages(DELAYED_PAUSE);
// Begin playing audio, if we paused the remote, send a play now.
@@ -228,7 +229,7 @@ public class A2dpSinkStreamHandler extends Handler {
case DELAYED_PAUSE:
if (BluetoothMediaBrowserService.getPlaybackState()
- == PlaybackState.STATE_PLAYING && !inCallFromStreamingDevice()) {
+ == PlaybackStateCompat.STATE_PLAYING && !inCallFromStreamingDevice()) {
sendAvrcpPause();
mSentPause = true;
mStreamAvailable = false;
@@ -245,12 +246,9 @@ public class A2dpSinkStreamHandler extends Handler {
*/
private void requestAudioFocusIfNone() {
if (DBG) Log.d(TAG, "requestAudioFocusIfNone()");
- if (mAudioFocus == AudioManager.AUDIOFOCUS_NONE) {
+ if (mAudioFocus != AudioManager.AUDIOFOCUS_GAIN) {
requestAudioFocus();
}
- // On the off change mMediaPlayer errors out and dies, we want to make sure we retry this.
- // This function immediately exits if we have a MediaPlayer object.
- requestMediaKeyFocus();
}
private synchronized int requestAudioFocus() {
@@ -277,8 +275,11 @@ public class A2dpSinkStreamHandler extends Handler {
}
/**
- * Creates a MediaPlayer that plays a silent audio sample so that MediaSessionService will be
- * aware of the fact that Bluetooth is playing audio.
+ * Plays a silent audio sample so that MediaSessionService will be aware of the fact that
+ * Bluetooth is playing audio.
+ *
+ * Creates a new MediaPlayer if one does not already exist. Repeat calls to this function are
+ * safe and will result in the silent audio sample again.
*
* This allows the MediaSession in AVRCP Controller to be routed media key events, if we've
* chosen to use it.
@@ -286,25 +287,25 @@ public class A2dpSinkStreamHandler extends Handler {
private synchronized void requestMediaKeyFocus() {
if (DBG) Log.d(TAG, "requestMediaKeyFocus()");
- if (mMediaPlayer != null) return;
-
- AudioAttributes attrs = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .build();
-
- mMediaPlayer = MediaPlayer.create(mContext, R.raw.silent, attrs,
- mAudioManager.generateAudioSessionId());
if (mMediaPlayer == null) {
- Log.e(TAG, "Failed to initialize media player. You may not get media key events");
- return;
- }
+ AudioAttributes attrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+
+ mMediaPlayer = MediaPlayer.create(mContext, R.raw.silent, attrs,
+ mAudioManager.generateAudioSessionId());
+ if (mMediaPlayer == null) {
+ Log.e(TAG, "Failed to initialize media player. You may not get media key events");
+ return;
+ }
- mMediaPlayer.setLooping(false);
- mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
- Log.e(TAG, "Silent media player error: " + what + ", " + extra);
- releaseMediaKeyFocus();
- return false;
- });
+ mMediaPlayer.setLooping(false);
+ mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
+ Log.e(TAG, "Silent media player error: " + what + ", " + extra);
+ releaseMediaKeyFocus();
+ return false;
+ });
+ }
mMediaPlayer.start();
BluetoothMediaBrowserService.setActive(true);
@@ -313,7 +314,6 @@ public class A2dpSinkStreamHandler extends Handler {
private synchronized void abandonAudioFocus() {
if (DBG) Log.d(TAG, "abandonAudioFocus()");
stopFluorideStreaming();
- releaseMediaKeyFocus();
mAudioManager.abandonAudioFocus(mAudioFocusListener);
mAudioFocus = AudioManager.AUDIOFOCUS_NONE;
}
@@ -336,9 +336,11 @@ public class A2dpSinkStreamHandler extends Handler {
private void startFluorideStreaming() {
mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_GRANTED);
mA2dpSinkService.informAudioTrackGainNative(1.0f);
+ requestMediaKeyFocus();
}
private void stopFluorideStreaming() {
+ releaseMediaKeyFocus();
mA2dpSinkService.informAudioFocusStateNative(STATE_FOCUS_LOST);
}
@@ -362,7 +364,10 @@ public class A2dpSinkStreamHandler extends Handler {
}
HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService();
if (targetDevice != null && headsetClientService != null) {
- return headsetClientService.getCurrentCalls(targetDevice).size() > 0;
+ List<BluetoothHeadsetClientCall> currentCalls =
+ headsetClientService.getCurrentCalls(targetDevice);
+ if (currentCalls == null) return false;
+ return currentCalls.size() > 0;
}
return false;
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 64e63df00..56684cd7c 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -172,9 +172,11 @@ public class AvrcpControllerService extends ProfileService {
}
}
+ // If we don't find a node in the tree then do not have any way to browse for the contents.
+ // Return an empty list instead.
if (requestedNode == null) {
if (DBG) Log.d(TAG, "Didn't find a node");
- return null;
+ return new ArrayList(0);
} else {
if (!requestedNode.isCached()) {
if (DBG) Log.d(TAG, "node is not cached");
@@ -412,9 +414,10 @@ public class AvrcpControllerService extends ProfileService {
if (stateMachine != null) {
PlayerApplicationSettings supportedSettings =
PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp);
+ stateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS,
+ supportedSettings);
}
- /* Do nothing */
-
}
private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
@@ -426,10 +429,12 @@ public class AvrcpControllerService extends ProfileService {
AvrcpControllerStateMachine stateMachine = getStateMachine(device);
if (stateMachine != null) {
- PlayerApplicationSettings desiredSettings =
+ PlayerApplicationSettings currentSettings =
PlayerApplicationSettings.makeSettings(playerAttribRsp);
+ stateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS,
+ currentSettings);
}
- /* Do nothing */
}
// Browsing related JNI callbacks.
@@ -711,7 +716,7 @@ public class AvrcpControllerService extends ProfileService {
/**
* Send button press commands to addressed device
*
- * @param keyCode key code as defined in AVRCP specification
+ * @param keyCode key code as defined in AVRCP specification
* @param keyState 0 = key pressed, 1 = key released
* @return command was sent
*/
@@ -720,7 +725,7 @@ public class AvrcpControllerService extends ProfileService {
/**
* Send group navigation commands
*
- * @param keyCode next/previous
+ * @param keyCode next/previous
* @param keyState state
* @return command was sent
*/
@@ -741,7 +746,7 @@ public class AvrcpControllerService extends ProfileService {
* Send response to set absolute volume
*
* @param absVol new volume
- * @param label label
+ * @param label label
*/
public native void sendAbsVolRspNative(byte[] address, int absVol, int label);
@@ -749,8 +754,8 @@ public class AvrcpControllerService extends ProfileService {
* Register for any volume level changes
*
* @param rspType type of response
- * @param absVol current volume
- * @param label label
+ * @param absVol current volume
+ * @param label label
*/
public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
int label);
@@ -764,7 +769,7 @@ public class AvrcpControllerService extends ProfileService {
* Fetch the current now playing list
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getNowPlayingListNative(byte[] address, int start, int end);
@@ -772,7 +777,7 @@ public class AvrcpControllerService extends ProfileService {
* Fetch the current folder's listing
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getFolderListNative(byte[] address, int start, int end);
@@ -780,7 +785,7 @@ public class AvrcpControllerService extends ProfileService {
* Fetch the listing of players
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getPlayerListNative(byte[] address, int start, int end);
@@ -788,15 +793,15 @@ public class AvrcpControllerService extends ProfileService {
* Change the current browsed folder
*
* @param direction up/down
- * @param uid folder unique id
+ * @param uid folder unique id
*/
public native void changeFolderPathNative(byte[] address, byte direction, long uid);
/**
* Play item with provided uid
*
- * @param scope scope of item to played
- * @param uid song unique id
+ * @param scope scope of item to played
+ * @param uid song unique id
* @param uidCounter counter
*/
public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 66571c4e7..c319364c1 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -24,10 +24,10 @@ import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Message;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.util.SparseArray;
@@ -42,6 +42,7 @@ import com.android.internal.util.StateMachine;
import java.util.ArrayList;
import java.util.List;
+
/**
* Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections
* and interactions with a remote controlable device.
@@ -76,11 +77,15 @@ class AvrcpControllerStateMachine extends StateMachine {
static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214;
static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215;
static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216;
+ static final int MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS = 217;
+ static final int MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS = 218;
//300->399 Events for Browsing
static final int MESSAGE_GET_FOLDER_ITEMS = 300;
static final int MESSAGE_PLAY_ITEM = 301;
static final int MSG_AVRCP_PASSTHRU = 302;
+ static final int MSG_AVRCP_SET_SHUFFLE = 303;
+ static final int MSG_AVRCP_SET_REPEAT = 304;
static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404;
@@ -218,26 +223,19 @@ class AvrcpControllerStateMachine extends StateMachine {
mService.sBrowseTree.mRootNode.addChild(mBrowseTree.mRootNode);
BluetoothMediaBrowserService.notifyChanged(mService
.sBrowseTree.mRootNode);
- BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
mBrowsingConnected = true;
}
synchronized void onBrowsingDisconnected() {
if (!mBrowsingConnected) return;
- mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR);
+ mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR);
mAddressedPlayer.updateCurrentTrack(null);
mBrowseTree.mNowPlayingNode.setCached(false);
BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
- 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);
BluetoothMediaBrowserService.notifyChanged(mService
.sBrowseTree.mRootNode);
- BluetoothMediaBrowserService.trackChanged(null);
mBrowsingConnected = false;
}
@@ -298,8 +296,9 @@ class AvrcpControllerStateMachine extends StateMachine {
@Override
public void enter() {
if (mMostRecentState == BluetoothProfile.STATE_CONNECTING) {
- broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED);
BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
+ BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+ broadcastConnectionStateChanged(BluetoothProfile.STATE_CONNECTED);
} else {
logD("ReEnteringConnected");
}
@@ -315,14 +314,14 @@ class AvrcpControllerStateMachine extends StateMachine {
removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT);
sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT,
ABS_VOL_TIMEOUT_MILLIS);
- setAbsVolume(msg.arg1, msg.arg2);
+ handleAbsVolumeRequest(msg.arg1, msg.arg2);
return true;
case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
mVolumeNotificationLabel = msg.arg1;
mService.sendRegisterAbsVolRspNative(mDeviceAddress,
NOTIFICATION_RSP_TYPE_INTERIM,
- getAbsVolumeResponse(), mVolumeNotificationLabel);
+ getAbsVolume(), mVolumeNotificationLabel);
return true;
case MESSAGE_GET_FOLDER_ITEMS:
@@ -338,6 +337,14 @@ class AvrcpControllerStateMachine extends StateMachine {
passThru(msg.arg1);
return true;
+ case MSG_AVRCP_SET_REPEAT:
+ setRepeat(msg.arg1);
+ return true;
+
+ case MSG_AVRCP_SET_SHUFFLE:
+ setShuffle(msg.arg1);
+ return true;
+
case MESSAGE_PROCESS_TRACK_CHANGED:
mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj);
BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj);
@@ -347,11 +354,14 @@ class AvrcpControllerStateMachine extends StateMachine {
mAddressedPlayer.setPlayStatus(msg.arg1);
BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
if (mAddressedPlayer.getPlaybackState().getState()
- == PlaybackState.STATE_PLAYING
- && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE
- && !shouldRequestFocus()) {
+ == PlaybackStateCompat.STATE_PLAYING
+ && A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE) {
+ if (shouldRequestFocus()) {
+ mSessionCallbacks.onPrepare();
+ } else {
sendMessage(MSG_AVRCP_PASSTHRU,
AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);
+ }
}
return true;
@@ -378,6 +388,18 @@ class AvrcpControllerStateMachine extends StateMachine {
}
return true;
+ case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
+ mAddressedPlayer.setSupportedPlayerApplicationSettings(
+ (PlayerApplicationSettings) msg.obj);
+ BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+ return true;
+
+ case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS:
+ mAddressedPlayer.setCurrentPlayerApplicationSettings(
+ (PlayerApplicationSettings) msg.obj);
+ BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+ return true;
+
case DISCONNECT:
transitionTo(mDisconnecting);
return true;
@@ -435,6 +457,20 @@ class AvrcpControllerStateMachine extends StateMachine {
return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND)
|| (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF);
}
+
+ private void setRepeat(int repeatMode) {
+ mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+ new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{
+ PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+ PlayerApplicationSettings.REPEAT_STATUS, repeatMode)});
+ }
+
+ private void setShuffle(int shuffleMode) {
+ mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+ new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{
+ PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+ PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)});
+ }
}
// Handle the get folder listing action
@@ -554,7 +590,7 @@ class AvrcpControllerStateMachine extends StateMachine {
case MESSAGE_GET_FOLDER_ITEMS:
if (!mBrowseNode.equals(msg.obj)) {
if (shouldAbort(mBrowseNode.getScope(),
- ((BrowseTree.BrowseNode) msg.obj).getScope())) {
+ ((BrowseTree.BrowseNode) msg.obj).getScope())) {
mAbort = true;
}
deferMessage(msg);
@@ -576,8 +612,8 @@ class AvrcpControllerStateMachine extends StateMachine {
* necessary.
*
* @return true: a new folder in the same scope
- * a new player while fetching contents of a folder
- * false: other cases, specifically Now Playing while fetching a folder
+ * a new player while fetching contents of a folder
+ * false: other cases, specifically Now Playing while fetching a folder
*/
private boolean shouldAbort(int currentScope, int fetchScope) {
if ((currentScope == fetchScope)
@@ -674,31 +710,60 @@ class AvrcpControllerStateMachine extends StateMachine {
@Override
public void enter() {
onBrowsingDisconnected();
+ BluetoothMediaBrowserService.trackChanged(null);
+ BluetoothMediaBrowserService.addressedPlayerChanged(null);
broadcastConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING);
transitionTo(mDisconnected);
}
}
+ /**
+ * Handle a request to align our local volume with the volume of a remote device. If
+ * we're assuming the source volume is fixed then a response of ABS_VOL_MAX will always be
+ * sent and no volume adjustment action will be taken on the sink side.
+ *
+ * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX]
+ * @param label Volume notification label
+ */
+ private void handleAbsVolumeRequest(int absVol, int label) {
+ logD("handleAbsVolumeRequest: absVol = " + absVol + ", label = " + label);
+ if (mIsVolumeFixed) {
+ logD("Source volume is assumed to be fixed, responding with max volume");
+ absVol = ABS_VOL_BASE;
+ } else {
+ mVolumeChangedNotificationsToIgnore++;
+ removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT);
+ sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT,
+ ABS_VOL_TIMEOUT_MILLIS);
+ setAbsVolume(absVol);
+ }
+ mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
+ }
+
+ /**
+ * Align our volume with a requested absolute volume level
+ *
+ * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX]
+ */
+ private void setAbsVolume(int absVol) {
+ int maxLocalVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ int curLocalVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ int reqLocalVolume = (maxLocalVolume * absVol) / ABS_VOL_BASE;
+ logD("setAbsVolme: absVol = " + absVol + ", reqLocal = " + reqLocalVolume
+ + ", curLocal = " + curLocalVolume + ", maxLocal = " + maxLocalVolume);
- private void setAbsVolume(int absVol, int label) {
- int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
- int newIndex = (maxVolume * absVol) / ABS_VOL_BASE;
- logD(" setAbsVolume =" + absVol + " maxVol = " + maxVolume
- + " cur = " + currIndex + " new = " + newIndex);
/*
* In some cases change in percentage is not sufficient enough to warrant
* change in index values which are in range of 0-15. For such cases
* no action is required
*/
- if (newIndex != currIndex) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex,
+ if (reqLocalVolume != curLocalVolume) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, reqLocalVolume,
AudioManager.FLAG_SHOW_UI);
}
- mService.sendAbsVolRspNative(mDeviceAddress, getAbsVolumeResponse(), label);
}
- private int getAbsVolumeResponse() {
+ private int getAbsVolume() {
if (mIsVolumeFixed) {
return ABS_VOL_BASE;
}
@@ -708,7 +773,7 @@ class AvrcpControllerStateMachine extends StateMachine {
return newIndex;
}
- MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {
+ MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
logD("onPlay");
@@ -781,6 +846,19 @@ class AvrcpControllerStateMachine extends StateMachine {
BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId);
sendMessage(MESSAGE_PLAY_ITEM, node);
}
+
+ @Override
+ public void onSetRepeatMode(int repeatMode) {
+ logD("onSetRepeatMode");
+ sendMessage(MSG_AVRCP_SET_REPEAT, repeatMode);
+ }
+
+ @Override
+ public void onSetShuffleMode(int shuffleMode) {
+ logD("onSetShuffleMode");
+ sendMessage(MSG_AVRCP_SET_SHUFFLE, shuffleMode);
+
+ }
};
protected void broadcastConnectionStateChanged(int currentState) {
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
index bed38d905..4736acffa 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
@@ -17,8 +17,9 @@
package com.android.bluetooth.avrcpcontroller;
import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
import android.os.SystemClock;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import java.util.Arrays;
@@ -41,27 +42,31 @@ class AvrcpPlayer {
public static final int FEATURE_PREVIOUS = 48;
public static final int FEATURE_BROWSING = 59;
- private int mPlayStatus = PlaybackState.STATE_NONE;
- private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+ private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
+ private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
private long mPlayTimeUpdate = 0;
private float mPlaySpeed = 1;
private int mId;
private String mName = "";
private int mPlayerType;
- private byte[] mPlayerFeatures;
- private long mAvailableActions;
+ private byte[] mPlayerFeatures = new byte[16];
+ private long mAvailableActions = PlaybackStateCompat.ACTION_PREPARE;
private MediaMetadata mCurrentTrack;
- private PlaybackState mPlaybackState;
+ private PlaybackStateCompat mPlaybackStateCompat;
+ private PlayerApplicationSettings mSupportedPlayerApplicationSettings =
+ new PlayerApplicationSettings();
+ private PlayerApplicationSettings mCurrentPlayerApplicationSettings;
AvrcpPlayer() {
mId = INVALID_ID;
//Set Default Actions in case Player data isn't available.
- mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY
- | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
- | PlaybackState.ACTION_STOP;
- PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+ mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY
+ | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
+ | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+ | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PREPARE;
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(mAvailableActions);
- mPlaybackState = playbackStateBuilder.build();
+ mPlaybackStateCompat = playbackStateBuilder.build();
}
AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) {
@@ -70,10 +75,10 @@ class AvrcpPlayer {
mPlayStatus = playStatus;
mPlayerType = playerType;
mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
- updateAvailableActions();
- PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(mAvailableActions);
- mPlaybackState = playbackStateBuilder.build();
+ mPlaybackStateCompat = playbackStateBuilder.build();
+ updateAvailableActions();
}
public int getId() {
@@ -87,7 +92,8 @@ class AvrcpPlayer {
public void setPlayTime(int playTime) {
mPlayTime = playTime;
mPlayTimeUpdate = SystemClock.elapsedRealtime();
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+ mPlayStatus, mPlayTime,
mPlaySpeed).build();
}
@@ -97,30 +103,48 @@ class AvrcpPlayer {
public void setPlayStatus(int playStatus) {
mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
- - mPlaybackState.getLastPositionUpdateTime());
+ - mPlaybackStateCompat.getLastPositionUpdateTime());
mPlayStatus = playStatus;
switch (mPlayStatus) {
- case PlaybackState.STATE_STOPPED:
+ case PlaybackStateCompat.STATE_STOPPED:
mPlaySpeed = 0;
break;
- case PlaybackState.STATE_PLAYING:
+ case PlaybackStateCompat.STATE_PLAYING:
mPlaySpeed = 1;
break;
- case PlaybackState.STATE_PAUSED:
+ case PlaybackStateCompat.STATE_PAUSED:
mPlaySpeed = 0;
break;
- case PlaybackState.STATE_FAST_FORWARDING:
+ case PlaybackStateCompat.STATE_FAST_FORWARDING:
mPlaySpeed = 3;
break;
- case PlaybackState.STATE_REWINDING:
+ case PlaybackStateCompat.STATE_REWINDING:
mPlaySpeed = -3;
break;
}
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+ mPlayStatus, mPlayTime,
mPlaySpeed).build();
}
+ public void setSupportedPlayerApplicationSettings(
+ PlayerApplicationSettings playerApplicationSettings) {
+ mSupportedPlayerApplicationSettings = playerApplicationSettings;
+ updateAvailableActions();
+ }
+
+ public void setCurrentPlayerApplicationSettings(
+ PlayerApplicationSettings playerApplicationSettings) {
+ Log.d(TAG, "Settings changed");
+ mCurrentPlayerApplicationSettings = playerApplicationSettings;
+ MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
+ session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting(
+ PlayerApplicationSettings.REPEAT_STATUS));
+ session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting(
+ PlayerApplicationSettings.SHUFFLE_STATUS));
+ }
+
public int getPlayStatus() {
return mPlayStatus;
}
@@ -131,17 +155,22 @@ class AvrcpPlayer {
return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
}
- public PlaybackState getPlaybackState() {
+ public boolean supportsSetting(int settingType, int settingValue) {
+ return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue);
+ }
+
+ public PlaybackStateCompat getPlaybackState() {
if (DBG) {
Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
}
- return mPlaybackState;
+ return mPlaybackStateCompat;
}
public synchronized void updateCurrentTrack(MediaMetadata update) {
if (update != null) {
long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId(
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(
+ mPlaybackStateCompat).setActiveQueueItemId(
trackNumber - 1).build();
}
mCurrentTrack = update;
@@ -153,26 +182,37 @@ class AvrcpPlayer {
private void updateAvailableActions() {
if (supportsFeature(FEATURE_PLAY)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
}
if (supportsFeature(FEATURE_STOP)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP;
}
if (supportsFeature(FEATURE_PAUSE)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE;
}
if (supportsFeature(FEATURE_REWIND)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND;
}
if (supportsFeature(FEATURE_FAST_FORWARD)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD;
}
if (supportsFeature(FEATURE_FORWARD)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
}
if (supportsFeature(FEATURE_PREVIOUS)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
}
+ if (mSupportedPlayerApplicationSettings.supportsSetting(
+ PlayerApplicationSettings.REPEAT_STATUS)) {
+ mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
+ }
+ if (mSupportedPlayerApplicationSettings.supportsSetting(
+ PlayerApplicationSettings.SHUFFLE_STATUS)) {
+ mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
+ }
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat)
+ .setActions(mAvailableActions).build();
+
if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
}
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 304d5a2c9..a0b1224ee 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -16,15 +16,22 @@
package com.android.bluetooth.avrcpcontroller;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.service.media.MediaBrowserService;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
+import androidx.media.MediaBrowserServiceCompat;
+
+import com.android.bluetooth.BluetoothPrefs;
import com.android.bluetooth.R;
import java.util.ArrayList;
@@ -37,45 +44,48 @@ import java.util.List;
* The applications are expected to use MediaBrowser (see API) and all the music
* browsing/playback/metadata can be controlled via MediaBrowser and MediaController.
*
- * The current behavior of MediaSession exposed by this service is as follows:
- * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is
- * connected and first starts playing. Before it starts playing we do not active the session.
+ * The current behavior of MediaSessionCompat exposed by this service is as follows:
+ * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when
+ * device is connected and first starts playing. Before it starts playing we do not activate the
+ * session.
* 1.1 The session is active throughout the duration of connection.
* 2. The session is de-activated when the device disconnects. It will be connected again when (1)
* happens.
*/
-public class BluetoothMediaBrowserService extends MediaBrowserService {
+public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
private static final String TAG = "BluetoothMediaBrowserService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static BluetoothMediaBrowserService sBluetoothMediaBrowserService;
- private MediaSession mSession;
+ private MediaSessionCompat mSession;
// Browsing related structures.
- private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>();
+ private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>();
+
+ // Error messaging extras
+ public static final String ERROR_RESOLUTION_ACTION_INTENT =
+ "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
+ public static final String ERROR_RESOLUTION_ACTION_LABEL =
+ "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
/**
- * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and
- * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
+ * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer
+ * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
*/
@Override
public void onCreate() {
if (DBG) Log.d(TAG, "onCreate");
super.onCreate();
- // Create and configure the MediaSession
- mSession = new MediaSession(this, TAG);
+ // Create and configure the MediaSessionCompat
+ mSession = new MediaSessionCompat(this, TAG);
setSessionToken(mSession.getSessionToken());
- mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
- | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
+ | MediaSessionCompat.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());
+ setErrorPlaybackState();
sBluetoothMediaBrowserService = this;
}
@@ -89,11 +99,30 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
}
}
+ private void setErrorPlaybackState() {
+ Bundle extras = new Bundle();
+ extras.putString(ERROR_RESOLUTION_ACTION_LABEL,
+ getString(R.string.bluetooth_connect_action));
+ Intent launchIntent = new Intent();
+ launchIntent.setAction(BluetoothPrefs.BLUETOOTH_SETTING_ACTION);
+ launchIntent.addCategory(BluetoothPrefs.BLUETOOTH_SETTING_CATEGORY);
+ PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
+ launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ extras.putParcelable(ERROR_RESOLUTION_ACTION_INTENT, pendingIntent);
+ PlaybackStateCompat errorState = new PlaybackStateCompat.Builder()
+ .setErrorMessage(getString(R.string.bluetooth_disconnected))
+ .setExtras(extras)
+ .setState(PlaybackStateCompat.STATE_ERROR, 0, 0)
+ .build();
+ mSession.setPlaybackState(errorState);
+ }
+
@Override
public synchronized void onLoadChildren(final String parentMediaId,
- final Result<List<MediaItem>> result) {
+ final Result<List<MediaBrowserCompat.MediaItem>> result) {
if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
- List<MediaItem> contents = getContents(parentMediaId);
+ List<MediaBrowserCompat.MediaItem> contents =
+ MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId));
if (contents == null) {
result.detach();
} else {
@@ -112,7 +141,8 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
mMediaQueue.clear();
if (songList != null) {
for (MediaItem song : songList) {
- mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(),
+ mMediaQueue.add(new MediaSessionCompat.QueueItem(
+ MediaDescriptionCompat.fromMediaDescription(song.getDescription()),
mMediaQueue.size()));
}
}
@@ -129,8 +159,11 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
}
}
- static synchronized void addressedPlayerChanged(MediaSession.Callback callback) {
+ static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) {
if (sBluetoothMediaBrowserService != null) {
+ if (callback == null) {
+ sBluetoothMediaBrowserService.setErrorPlaybackState();
+ }
sBluetoothMediaBrowserService.mSession.setCallback(callback);
} else {
Log.w(TAG, "addressedPlayerChanged Unavailable");
@@ -139,13 +172,14 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
static synchronized void trackChanged(MediaMetadata mediaMetadata) {
if (sBluetoothMediaBrowserService != null) {
- sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata);
+ sBluetoothMediaBrowserService.mSession.setMetadata(
+ MediaMetadataCompat.fromMediaMetadata(mediaMetadata));
} else {
Log.w(TAG, "trackChanged Unavailable");
}
}
- static synchronized void notifyChanged(PlaybackState playbackState) {
+ static synchronized void notifyChanged(PlaybackStateCompat playbackState) {
Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
if (sBluetoothMediaBrowserService != null) {
sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
@@ -181,19 +215,19 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
*/
public static synchronized int getPlaybackState() {
if (sBluetoothMediaBrowserService != null) {
- PlaybackState currentPlaybackState =
+ PlaybackStateCompat currentPlaybackState =
sBluetoothMediaBrowserService.mSession.getController().getPlaybackState();
if (currentPlaybackState != null) {
return currentPlaybackState.getState();
}
}
- return PlaybackState.STATE_ERROR;
+ return PlaybackStateCompat.STATE_ERROR;
}
/**
* Get object for controlling playback
*/
- public static synchronized MediaController.TransportControls getTransportControls() {
+ public static synchronized MediaControllerCompat.TransportControls getTransportControls() {
if (sBluetoothMediaBrowserService != null) {
return sBluetoothMediaBrowserService.mSession.getController().getTransportControls();
} else {
@@ -212,4 +246,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserService {
Log.w(TAG, "setActive Unavailable");
}
}
+
+ /**
+ * Get Media session for updating state
+ */
+ public static synchronized MediaSessionCompat getSession() {
+ if (sBluetoothMediaBrowserService != null) {
+ return sBluetoothMediaBrowserService.mSession;
+ } else {
+ Log.w(TAG, "getSession Unavailable");
+ return null;
+ }
+ }
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
index accea2a70..923282d34 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
@@ -100,7 +100,7 @@ public class BrowseTree {
}
BrowseNode getTrackFromNowPlayingList(int trackNumber) {
- return mNowPlayingNode.mChildren.get(trackNumber);
+ return mNowPlayingNode.getChild(trackNumber);
}
// Each node of the tree is represented by Folder ID, Folder Name and the children.
@@ -218,6 +218,13 @@ public class BrowseTree {
return mChildren;
}
+ synchronized BrowseNode getChild(int index) {
+ if (index < 0 || index >= mChildren.size()) {
+ return null;
+ }
+ return mChildren.get(index);
+ }
+
synchronized BrowseNode getParent() {
return mParent;
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
index c34a2d7d0..362548e5f 100644
--- a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
+++ b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
@@ -16,12 +16,11 @@
package com.android.bluetooth.avrcpcontroller;
-import android.bluetooth.BluetoothAvrcpPlayerSettings;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
+import android.util.SparseArray;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
/*
* Contains information Player Application Setting extended from BluetootAvrcpPlayerSettings
@@ -32,10 +31,10 @@ class PlayerApplicationSettings {
/*
* Values for SetPlayerApplicationSettings from AVRCP Spec V1.6 Appendix F.
*/
- private static final byte JNI_ATTRIB_EQUALIZER_STATUS = 0x01;
- private static final byte JNI_ATTRIB_REPEAT_STATUS = 0x02;
- private static final byte JNI_ATTRIB_SHUFFLE_STATUS = 0x03;
- private static final byte JNI_ATTRIB_SCAN_STATUS = 0x04;
+ static final byte EQUALIZER_STATUS = 0x01;
+ static final byte REPEAT_STATUS = 0x02;
+ static final byte SHUFFLE_STATUS = 0x03;
+ static final byte SCAN_STATUS = 0x04;
private static final byte JNI_EQUALIZER_STATUS_OFF = 0x01;
private static final byte JNI_EQUALIZER_STATUS_ON = 0x02;
@@ -55,18 +54,17 @@ class PlayerApplicationSettings {
private static final byte JNI_STATUS_INVALID = -1;
-
/*
* Hash map of current settings.
*/
- private Map<Integer, Integer> mSettings = new HashMap<Integer, Integer>();
+ private SparseArray<Integer> mSettings = new SparseArray<>();
/*
* Hash map of supported values, a setting should be supported by the remote in order to enable
* in mSettings.
*/
- private Map<Integer, ArrayList<Integer>> mSupportedValues =
- new HashMap<Integer, ArrayList<Integer>>();
+ private SparseArray<ArrayList<Integer>> mSupportedValues =
+ new SparseArray<ArrayList<Integer>>();
/* Convert from JNI array to Java classes. */
static PlayerApplicationSettings makeSupportedSettings(byte[] btAvrcpAttributeList) {
@@ -82,8 +80,7 @@ class PlayerApplicationSettings {
supportedValues.add(
mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
}
- newObj.mSupportedValues.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
- supportedValues);
+ newObj.mSupportedValues.put(attrId, supportedValues);
}
} catch (ArrayIndexOutOfBoundsException exception) {
Log.e(TAG, "makeSupportedSettings attributeList index error.");
@@ -91,25 +88,13 @@ class PlayerApplicationSettings {
return newObj;
}
- public BluetoothAvrcpPlayerSettings getAvrcpSettings() {
- int supportedSettings = 0;
- for (Integer setting : mSettings.keySet()) {
- supportedSettings |= setting;
- }
- BluetoothAvrcpPlayerSettings result = new BluetoothAvrcpPlayerSettings(supportedSettings);
- for (Integer setting : mSettings.keySet()) {
- result.addSettingValue(setting, mSettings.get(setting));
- }
- return result;
- }
-
static PlayerApplicationSettings makeSettings(byte[] btAvrcpAttributeList) {
PlayerApplicationSettings newObj = new PlayerApplicationSettings();
try {
for (int i = 0; i < btAvrcpAttributeList.length; ) {
byte attrId = btAvrcpAttributeList[i++];
- newObj.mSettings.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
+ newObj.mSettings.put(attrId,
mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
}
} catch (ArrayIndexOutOfBoundsException exception) {
@@ -123,177 +108,69 @@ class PlayerApplicationSettings {
mSupportedValues = updates.mSupportedValues;
}
- public void setValues(BluetoothAvrcpPlayerSettings updates) {
- int supportedSettings = updates.getSettings();
- for (int i = 1; i <= BluetoothAvrcpPlayerSettings.SETTING_SCAN; i++) {
- if ((i & supportedSettings) > 0) {
- mSettings.put(i, updates.getSettingValue(i));
- }
- }
+ public boolean supportsSetting(int settingType, int settingValue) {
+ if (null == mSupportedValues.get(settingType)) return false;
+ return mSupportedValues.valueAt(settingType).contains(settingValue);
}
- /*
- * Check through all settings to ensure that they are all available to be set and then check
- * that the desired value is in fact supported by our remote player.
- */
- public boolean supportsSettings(BluetoothAvrcpPlayerSettings settingsToCheck) {
- int settingSubset = settingsToCheck.getSettings();
- int supportedSettings = 0;
- for (Integer setting : mSupportedValues.keySet()) {
- supportedSettings |= setting;
- }
- try {
- if ((supportedSettings & settingSubset) == settingSubset) {
- for (Integer settingId : mSettings.keySet()) {
- // The setting is in both settings to check and supported settings but the
- // value is not supported.
- if ((settingId & settingSubset) == settingId && (!mSupportedValues.get(
- settingId).contains(settingsToCheck.getSettingValue(settingId)))) {
- return false;
- }
- }
- return true;
- }
- } catch (NullPointerException e) {
- Log.e(TAG,
- "supportsSettings received a supported setting that has no supported values.");
- }
- return false;
+ public boolean supportsSetting(int settingType) {
+ return (null != mSupportedValues.get(settingType));
}
- // Convert currently desired settings into an attribute array to pass to the native layer to
- // enable them.
- public ArrayList<Byte> getNativeSettings() {
- int i = 0;
- ArrayList<Byte> attribArray = new ArrayList<Byte>();
- for (Integer settingId : mSettings.keySet()) {
- switch (settingId) {
- case BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER:
- attribArray.add(JNI_ATTRIB_EQUALIZER_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_REPEAT:
- attribArray.add(JNI_ATTRIB_REPEAT_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE:
- attribArray.add(JNI_ATTRIB_SHUFFLE_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_SCAN:
- attribArray.add(JNI_ATTRIB_SCAN_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- default:
- Log.w(TAG, "Unknown setting found in getNativeSettings: " + settingId);
- }
- }
- return attribArray;
+ public int getSetting(int settingType) {
+ if (null == mSettings.get(settingType)) return -1;
+ return mSettings.get(settingType);
}
// Convert a native Attribute Id/Value pair into the AVRCP equivalent value.
private static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
- if (attribId == JNI_ATTRIB_EQUALIZER_STATUS) {
- switch (attribVal) {
- case JNI_EQUALIZER_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
- case JNI_EQUALIZER_STATUS_ON:
- return BluetoothAvrcpPlayerSettings.STATE_ON;
- }
- } else if (attribId == JNI_ATTRIB_REPEAT_STATUS) {
+ if (attribId == REPEAT_STATUS) {
switch (attribVal) {
case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+ return PlaybackStateCompat.REPEAT_MODE_ALL;
case JNI_REPEAT_STATUS_GROUP_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+ return PlaybackStateCompat.REPEAT_MODE_GROUP;
case JNI_REPEAT_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
+ return PlaybackStateCompat.REPEAT_MODE_NONE;
case JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK;
- }
- } else if (attribId == JNI_ATTRIB_SCAN_STATUS) {
- switch (attribVal) {
- case JNI_SCAN_STATUS_ALL_TRACK_SCAN:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
- case JNI_SCAN_STATUS_GROUP_SCAN:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
- case JNI_SCAN_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
+ return PlaybackStateCompat.REPEAT_MODE_ONE;
}
- } else if (attribId == JNI_ATTRIB_SHUFFLE_STATUS) {
+ } else if (attribId == SHUFFLE_STATUS) {
switch (attribVal) {
case JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+ return PlaybackStateCompat.SHUFFLE_MODE_ALL;
case JNI_SHUFFLE_STATUS_GROUP_SHUFFLE:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+ return PlaybackStateCompat.SHUFFLE_MODE_GROUP;
case JNI_SHUFFLE_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
+ return PlaybackStateCompat.SHUFFLE_MODE_NONE;
}
}
- return BluetoothAvrcpPlayerSettings.STATE_INVALID;
+ return JNI_STATUS_INVALID;
}
// Convert an AVRCP Setting/Value pair into the native equivalent value;
- private static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
- if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_EQUALIZER_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ON:
- return JNI_EQUALIZER_STATUS_ON;
- }
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_REPEAT) {
+ static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
+ if (mSetting == REPEAT_STATUS) {
switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
+ case PlaybackStateCompat.REPEAT_MODE_NONE:
return JNI_REPEAT_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK:
+ case PlaybackStateCompat.REPEAT_MODE_ONE:
return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
+ case PlaybackStateCompat.REPEAT_MODE_ALL:
return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
+ case PlaybackStateCompat.REPEAT_MODE_GROUP:
return JNI_REPEAT_STATUS_GROUP_REPEAT;
}
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) {
+ } else if (mSetting == SHUFFLE_STATUS) {
switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
+ case PlaybackStateCompat.SHUFFLE_MODE_NONE:
return JNI_SHUFFLE_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
+ case PlaybackStateCompat.SHUFFLE_MODE_ALL:
return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
+ case PlaybackStateCompat.SHUFFLE_MODE_GROUP:
return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
}
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SCAN) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_SCAN_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
- return JNI_SCAN_STATUS_ALL_TRACK_SCAN;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
- return JNI_SCAN_STATUS_GROUP_SCAN;
- }
}
return JNI_STATUS_INVALID;
}
-
- // convert a native Attribute Id into the AVRCP Setting equivalent value;
- private static int mapBTAttribIdToAvrcpPlayerSettings(byte attribId) {
- switch (attribId) {
- case JNI_ATTRIB_EQUALIZER_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER;
- case JNI_ATTRIB_REPEAT_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_REPEAT;
- case JNI_ATTRIB_SHUFFLE_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE;
- case JNI_ATTRIB_SCAN_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_SCAN;
- default:
- return BluetoothAvrcpPlayerSettings.STATE_INVALID;
- }
- }
-
}
-
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 009e42cef..a65bcfdf9 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -1559,7 +1559,6 @@ public class AdapterService extends Service {
if (service == null) {
return false;
}
- service.disable();
return service.factoryReset();
}
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 989043a7d..e92688f8c 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -1196,6 +1196,21 @@ public class HeadsetStateMachine extends StateMachine {
}
}
+ class MyAudioServerStateCallback extends AudioManager.AudioServerStateCallback {
+ @Override
+ public void onAudioServerDown() {
+ logi("onAudioServerDown");
+ }
+
+ @Override
+ public void onAudioServerUp() {
+ logi("onAudioServerUp restoring audio parameters");
+ setAudioParameters();
+ }
+ }
+
+ MyAudioServerStateCallback mAudioServerStateCallback = new MyAudioServerStateCallback();
+
class AudioOn extends ConnectedBase {
@Override
int getAudioStateInt() {
@@ -1214,10 +1229,21 @@ public class HeadsetStateMachine extends StateMachine {
mHeadsetService.setActiveDevice(mDevice);
}
setAudioParameters();
+
+ mSystemInterface.getAudioManager().setAudioServerStateCallback(
+ mHeadsetService.getMainExecutor(), mAudioServerStateCallback);
+
broadcastStateTransitions();
}
@Override
+ public void exit() {
+ super.exit();
+
+ mSystemInterface.getAudioManager().clearAudioServerStateCallback();
+ }
+
+ @Override
public boolean processMessage(Message message) {
switch (message.what) {
case CONNECT: {
diff --git a/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java b/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java
index 05af73e08..b567371f1 100644
--- a/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java
+++ b/src/com/android/bluetooth/hfpclient/connserv/HfpClientDeviceBlock.java
@@ -224,7 +224,7 @@ public class HfpClientDeviceBlock {
if (DBG) {
Log.d(mTAG, "prevConn " + prevConn.isClosing() + " new call " + newCall.getState());
}
- if (prevConn.isClosing()
+ if (prevConn.isClosing() && prevConn.getCall().getState() != newCall.getState()
&& newCall.getState() != BluetoothHeadsetClientCall.CALL_STATE_TERMINATED) {
return true;
}
diff --git a/src/com/android/bluetooth/mapclient/MceStateMachine.java b/src/com/android/bluetooth/mapclient/MceStateMachine.java
index 0a428b418..9b86aaef1 100644
--- a/src/com/android/bluetooth/mapclient/MceStateMachine.java
+++ b/src/com/android/bluetooth/mapclient/MceStateMachine.java
@@ -42,7 +42,6 @@ package com.android.bluetooth.mapclient;
import android.app.Activity;
import android.app.PendingIntent;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
@@ -59,6 +58,7 @@ import android.util.Log;
import com.android.bluetooth.BluetoothMetricsProto;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.map.BluetoothMapbMessageMime;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IState;
import com.android.internal.util.State;
@@ -69,7 +69,6 @@ import com.android.vcard.VCardProperty;
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;
@@ -347,7 +346,9 @@ final class MceStateMachine extends StateMachine {
void setDefaultMessageType(SdpMasRecord sdpMasRecord) {
int supportedMessageTypes = sdpMasRecord.getSupportedMessageTypes();
synchronized (mDefaultMessageType) {
- if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_CDMA) > 0) {
+ if ((supportedMessageTypes & SdpMasRecord.MessageType.MMS) > 0) {
+ mDefaultMessageType = Bmessage.Type.MMS;
+ } else if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_CDMA) > 0) {
mDefaultMessageType = Bmessage.Type.SMS_CDMA;
} else if ((supportedMessageTypes & SdpMasRecord.MessageType.SMS_GSM) > 0) {
mDefaultMessageType = Bmessage.Type.SMS_GSM;
@@ -473,6 +474,11 @@ final class MceStateMachine extends StateMachine {
}
break;
+ case MSG_MAS_DISCONNECTED:
+ deferMessage(message);
+ transitionTo(mDisconnecting);
+ break;
+
case MSG_OUTBOUND_MESSAGE:
mMasClient.makeRequest(
new RequestPushMessage(FOLDER_OUTBOX, (Bmessage) message.obj, null,
@@ -625,9 +631,12 @@ final class MceStateMachine extends StateMachine {
}
ArrayList<com.android.bluetooth.mapclient.Message> messageListing = request.getList();
if (messageListing != null) {
- for (com.android.bluetooth.mapclient.Message msg : messageListing) {
+ // Message listings by spec arrive ordered newest first but we wish to broadcast as
+ // oldest first. Iterate in reverse order so we initiate requests oldest first.
+ for (int i = messageListing.size() - 1; i >= 0; i--) {
+ com.android.bluetooth.mapclient.Message msg = messageListing.get(i);
if (DBG) {
- Log.d(TAG, "getting message ");
+ Log.d(TAG, "getting message for handle " + msg.getHandle());
}
// A message listing coming from the server should always have up to date data
mMessages.put(msg.getHandle(), new MessageMetadata(msg.getHandle(),
@@ -665,6 +674,7 @@ final class MceStateMachine extends StateMachine {
switch (message.getType()) {
case SMS_CDMA:
case SMS_GSM:
+ case MMS:
if (DBG) {
Log.d(TAG, "Body: " + message.getBodyContent());
}
@@ -705,6 +715,12 @@ final class MceStateMachine extends StateMachine {
intent.putExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME,
originator.getDisplayName());
}
+ if (message.getType() == Bmessage.Type.MMS) {
+ BluetoothMapbMessageMime mmsBmessage = new BluetoothMapbMessageMime();
+ mmsBmessage.parseMsgPart(message.getBodyContent());
+ intent.putExtra(android.content.Intent.EXTRA_TEXT,
+ mmsBmessage.getMessageAsText());
+ }
// Only send to the current default SMS app if one exists
String defaultMessagingPackage = Telephony.Sms.getDefaultSmsPackage(mService);
if (defaultMessagingPackage != null) {
@@ -712,8 +728,6 @@ final class MceStateMachine extends StateMachine {
}
mService.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
break;
-
- case MMS:
case EMAIL:
default:
Log.e(TAG, "Received unhandled type" + message.getType().toString());
diff --git a/src/com/android/bluetooth/mapclient/MnsObexServer.java b/src/com/android/bluetooth/mapclient/MnsObexServer.java
index 53cd79bb2..33ba1ea74 100644
--- a/src/com/android/bluetooth/mapclient/MnsObexServer.java
+++ b/src/com/android/bluetooth/mapclient/MnsObexServer.java
@@ -90,6 +90,10 @@ class MnsObexServer extends ServerRequestHandler {
if (VDBG) {
Log.v(TAG, "onDisconnect");
}
+ MceStateMachine currentStateMachine = mStateMachineReference.get();
+ if (currentStateMachine != null) {
+ currentStateMachine.disconnect();
+ }
}
@Override
diff --git a/src/com/android/bluetooth/mapclient/MnsService.java b/src/com/android/bluetooth/mapclient/MnsService.java
index c1ab39e00..b3317df90 100644
--- a/src/com/android/bluetooth/mapclient/MnsService.java
+++ b/src/com/android/bluetooth/mapclient/MnsService.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.mapclient;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
@@ -129,6 +130,11 @@ public class MnsService {
Log.e(TAG, "Error: NO statemachine for device: " + device.getAddress()
+ " (name: " + device.getName());
return false;
+ } else if (stateMachine.getState() != BluetoothProfile.STATE_CONNECTED) {
+ Log.e(TAG, "Error: statemachine for device: " + device.getAddress()
+ + " (name: " + device.getName() + ") is not currently CONNECTED : "
+ + stateMachine.getCurrentState());
+ return false;
}
MnsObexServer srv = new MnsObexServer(stateMachine, sServerSockets);
BluetoothObexTransport transport = new BluetoothObexTransport(socket);
diff --git a/src/com/android/bluetooth/mapclient/obex/BmessageParser.java b/src/com/android/bluetooth/mapclient/obex/BmessageParser.java
index 2705e3429..5b844dce5 100644
--- a/src/com/android/bluetooth/mapclient/obex/BmessageParser.java
+++ b/src/com/android/bluetooth/mapclient/obex/BmessageParser.java
@@ -309,6 +309,12 @@ class BmessageParser {
String remng = mParser.remaining();
byte[] data = remng.getBytes();
+ if (offset < 0 || offset > data.length) {
+ /* Handle possible exception for incorrect LENGTH value
+ * from MSE while parsing end of props */
+ throw new ParseException("Invalid LENGTH value", mParser.pos());
+ }
+
/* restart parsing from after 'message'<CRLF> */
mParser = new BmsgTokenizer(new String(data, offset, data.length - offset), restartPos);
diff --git a/src/com/android/bluetooth/mapclient/obex/ObexTime.java b/src/com/android/bluetooth/mapclient/obex/ObexTime.java
index 42a32c10f..cc58a5144 100644
--- a/src/com/android/bluetooth/mapclient/obex/ObexTime.java
+++ b/src/com/android/bluetooth/mapclient/obex/ObexTime.java
@@ -29,8 +29,17 @@ public final class ObexTime {
public ObexTime(String time) {
/*
- * match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset
- * +/-hhmm
+ * Match OBEX time string: YYYYMMDDTHHMMSS with optional UTF offset +/-hhmm
+ *
+ * Matched groups are numberes as follows:
+ *
+ * YYYY MM DD T HH MM SS + hh mm
+ * ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^
+ * 1 2 3 4 5 6 8 9 10
+ * |---7---|
+ *
+ * All groups are guaranteed to be numeric so conversion will always succeed (except group 8
+ * which is either + or -)
*/
Pattern p = Pattern.compile(
"(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})(([+-])(\\d{2})(\\d{2})" + ")?");
@@ -39,20 +48,26 @@ public final class ObexTime {
if (m.matches()) {
/*
- * matched groups are numberes as follows: YYYY MM DD T HH MM SS +
- * hh mm ^^^^ ^^ ^^ ^^ ^^ ^^ ^ ^^ ^^ 1 2 3 4 5 6 8 9 10 all groups
- * are guaranteed to be numeric so conversion will always succeed
- * (except group 8 which is either + or -)
+ * MAP spec says to default to "Local Time basis" for a message listing timestamp. We'll
+ * use the system default timezone and assume it knows best what our local timezone is.
+ * The builder defaults to the default locale and timezone if none is provided.
*/
+ Calendar.Builder builder = new Calendar.Builder();
- Calendar cal = Calendar.getInstance();
- cal.set(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) - 1,
- Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)),
- Integer.parseInt(m.group(5)), Integer.parseInt(m.group(6)));
+ /* Note that Calendar months are zero-based */
+ builder.setDate(Integer.parseInt(m.group(1)), /* year */
+ Integer.parseInt(m.group(2)) - 1, /* month */
+ Integer.parseInt(m.group(3))); /* day of month */
+
+ /* Note the MAP timestamp doesn't have milliseconds and we're explicitly setting to 0 */
+ builder.setTimeOfDay(Integer.parseInt(m.group(4)), /* hours */
+ Integer.parseInt(m.group(5)), /* minutes */
+ Integer.parseInt(m.group(6)), /* seconds */
+ 0); /* milliseconds */
/*
- * if 7th group is matched then we have UTC offset information
- * included
+ * If 7th group is matched then we're no longer using "Local Time basis" and instead
+ * have a UTC based timestamp and offset information included
*/
if (m.group(7) != null) {
int ohh = Integer.parseInt(m.group(9));
@@ -68,10 +83,10 @@ public final class ObexTime {
TimeZone tz = TimeZone.getTimeZone("UTC");
tz.setRawOffset(offset);
- cal.setTimeZone(tz);
+ builder.setTimeZone(tz);
}
- mDate = cal.getTime();
+ mDate = builder.build().getTime();
}
}
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
index aa5a1fd31..92eab7787 100644
--- a/src/com/android/bluetooth/pan/PanService.java
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -40,6 +40,7 @@ import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
import java.net.InetAddress;
import java.util.ArrayList;
@@ -67,6 +68,9 @@ public class PanService extends ProfileService {
private String mNapIfaceAddr;
private boolean mNativeAvailable;
+ @VisibleForTesting
+ UserManager mUserManager;
+
private static final int MESSAGE_CONNECT = 1;
private static final int MESSAGE_DISCONNECT = 2;
private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
@@ -116,6 +120,8 @@ public class PanService extends ProfileService {
initializeNative();
mNativeAvailable = true;
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+
mNetworkFactory =
new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(), this);
setPanService(this);
@@ -137,6 +143,9 @@ public class PanService extends ProfileService {
cleanupNative();
mNativeAvailable = false;
}
+
+ mUserManager = null;
+
if (mPanDevices != null) {
int[] desiredStates = {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTING};
@@ -319,6 +328,10 @@ public class PanService extends ProfileService {
public boolean connect(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (mUserManager.isGuestUser()) {
+ Log.w(TAG, "Guest user does not have the permission to change the WiFi network");
+ return false;
+ }
if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
Log.e(TAG, "Pan Device not disconnected: " + device);
return false;
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
index 34ab7d52c..a5b3fbfea 100644
--- a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBook.java
@@ -73,7 +73,7 @@ final class BluetoothPbapRequestPullPhoneBook extends BluetoothPbapRequest {
oap.add(OAP_TAGID_FORMAT, format);
/*
- * maxListCount is a special case which is handled in
+ * maxListCount == 0 is a special case which is handled in
* BluetoothPbapRequestPullPhoneBookSize
*/
if (maxListCount > 0) {
diff --git a/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
new file mode 100644
index 000000000..f0706610f
--- /dev/null
+++ b/src/com/android/bluetooth/pbapclient/BluetoothPbapRequestPullPhoneBookSize.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pbapclient;
+
+import android.util.Log;
+
+import javax.obex.HeaderSet;
+
+final class BluetoothPbapRequestPullPhoneBookSize extends BluetoothPbapRequest {
+
+ private static final boolean VDBG = Utils.VDBG;
+
+ private static final String TAG = "BtPbapReqPullPhoneBookSize";
+
+ private static final String TYPE = "x-bt/phonebook";
+
+ private int mSize;
+
+ BluetoothPbapRequestPullPhoneBookSize(String pbName, long filter) {
+ mHeaderSet.setHeader(HeaderSet.NAME, pbName);
+
+ mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
+
+ ObexAppParameters oap = new ObexAppParameters();
+ // Set MaxListCount in the request to 0 to get PhonebookSize in the response.
+ // If a vCardSelector is present in the request, then the result shall
+ // contain the number of items that satisfy the selector’s criteria.
+ // See PBAP v1.2.3, Sec. 5.1.4.5.
+ oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0);
+ if (filter != 0) {
+ oap.add(OAP_TAGID_FILTER, filter);
+ }
+ oap.addToHeaderSet(mHeaderSet);
+ }
+
+ @Override
+ protected void readResponseHeaders(HeaderSet headerset) {
+ if (VDBG) {
+ Log.v(TAG, "readResponseHeaders");
+ }
+
+ ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
+
+ if (oap.exists(OAP_TAGID_PHONEBOOK_SIZE)) {
+ mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE);
+ }
+ }
+
+ public int getSize() {
+ return mSize;
+ }
+}
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
index 914b5b163..4e4a240f1 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
@@ -32,8 +32,10 @@ import android.util.Log;
import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.R;
+import com.android.vcard.VCardEntry;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
import javax.obex.ClientSession;
@@ -46,6 +48,15 @@ import javax.obex.ResponseCodes;
* controlling state machine.
*/
class PbapClientConnectionHandler extends Handler {
+ // Tradeoff: larger BATCH_SIZE leads to faster download rates, while smaller
+ // BATCH_SIZE is less prone to IO Exceptions if there is a download in
+ // progress when Bluetooth stack is torn down.
+ private static final int DEFAULT_BATCH_SIZE = 250;
+
+ // Upper limit on the indices of the vcf cards/entries, inclusive,
+ // i.e., valid indices are [0, 1, ... , UPPER_LIMIT]
+ private static final int UPPER_LIMIT = 65535;
+
static final String TAG = "PbapClientConnHandler";
static final boolean DBG = Utils.DBG;
static final boolean VDBG = Utils.VDBG;
@@ -88,16 +99,26 @@ class PbapClientConnectionHandler extends Handler {
private static final long PBAP_FILTER_NICKNAME = 1 << 23;
private static final int PBAP_SUPPORTED_FEATURE =
- PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_BROWSING | PBAP_FEATURE_DOWNLOADING;
+ PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_DOWNLOADING;
private static final long PBAP_REQUESTED_FIELDS =
PBAP_FILTER_VERSION | PBAP_FILTER_FN | PBAP_FILTER_N | PBAP_FILTER_PHOTO
| PBAP_FILTER_ADR | PBAP_FILTER_EMAIL | PBAP_FILTER_TEL | PBAP_FILTER_NICKNAME;
private static final int L2CAP_INVALID_PSM = -1;
public static final String PB_PATH = "telecom/pb.vcf";
+ public static final String FAV_PATH = "telecom/fav.vcf";
public static final String MCH_PATH = "telecom/mch.vcf";
public static final String ICH_PATH = "telecom/ich.vcf";
public static final String OCH_PATH = "telecom/och.vcf";
+ public static final String SIM_PB_PATH = "SIM1/telecom/pb.vcf";
+ public static final String SIM_MCH_PATH = "SIM1/telecom/mch.vcf";
+ public static final String SIM_ICH_PATH = "SIM1/telecom/ich.vcf";
+ public static final String SIM_OCH_PATH = "SIM1/telecom/och.vcf";
+
+ // PBAP v1.2.3 Sec. 7.1.2
+ private static final int SUPPORTED_REPOSITORIES_LOCALPHONEBOOK = 1 << 0;
+ private static final int SUPPORTED_REPOSITORIES_SIMCARD = 1 << 1;
+ private static final int SUPPORTED_REPOSITORIES_FAVORITES = 1 << 3;
public static final int PBAP_V1_2 = 0x0102;
public static final byte VCARD_TYPE_21 = 0;
@@ -239,29 +260,25 @@ class PbapClientConnectionHandler extends Handler {
break;
case MSG_DOWNLOAD:
- try {
- mAccountCreated = addAccount(mAccount);
- if (!mAccountCreated) {
- Log.e(TAG, "Account creation failed.");
- return;
- }
- // Start at contact 1 to exclued Owner Card PBAP 1.1 sec 3.1.5.2
- BluetoothPbapRequestPullPhoneBook request =
- new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount,
- PBAP_REQUESTED_FIELDS, VCARD_TYPE_30, 0, 1);
- request.execute(mObexSession);
- PhonebookPullRequest processor =
- new PhonebookPullRequest(mPbapClientStateMachine.getContext(),
- mAccount);
- processor.setResults(request.getList());
- processor.onPullComplete();
- HashMap<String, Integer> callCounter = new HashMap<>();
- downloadCallLog(MCH_PATH, callCounter);
- downloadCallLog(ICH_PATH, callCounter);
- downloadCallLog(OCH_PATH, callCounter);
- } catch (IOException e) {
- Log.w(TAG, "DOWNLOAD_CONTACTS Failure" + e.toString());
+ mAccountCreated = addAccount(mAccount);
+ if (!mAccountCreated) {
+ Log.e(TAG, "Account creation failed.");
+ return;
}
+ if (isRepositorySupported(SUPPORTED_REPOSITORIES_FAVORITES)) {
+ downloadContacts(FAV_PATH);
+ }
+ if (isRepositorySupported(SUPPORTED_REPOSITORIES_LOCALPHONEBOOK)) {
+ downloadContacts(PB_PATH);
+ }
+ if (isRepositorySupported(SUPPORTED_REPOSITORIES_SIMCARD)) {
+ downloadContacts(SIM_PB_PATH);
+ }
+
+ HashMap<String, Integer> callCounter = new HashMap<>();
+ downloadCallLog(MCH_PATH, callCounter);
+ downloadCallLog(ICH_PATH, callCounter);
+ downloadCallLog(OCH_PATH, callCounter);
break;
default:
@@ -369,6 +386,59 @@ class PbapClientConnectionHandler extends Handler {
}
}
+ void downloadContacts(String path) {
+ try {
+ PhonebookPullRequest processor =
+ new PhonebookPullRequest(mPbapClientStateMachine.getContext(),
+ mAccount);
+
+ // Download contacts in batches of size DEFAULT_BATCH_SIZE
+ BluetoothPbapRequestPullPhoneBookSize requestPbSize =
+ new BluetoothPbapRequestPullPhoneBookSize(path,
+ PBAP_REQUESTED_FIELDS);
+ requestPbSize.execute(mObexSession);
+
+ int numberOfContactsRemaining = requestPbSize.getSize();
+ int startOffset = 0;
+ if (PB_PATH.equals(path)) {
+ // PBAP v1.2.3, Sec 3.1.5. The first contact in pb is owner card 0.vcf, which we
+ // do not want to download. The other phonebook objects (e.g., fav) don't have an
+ // owner card, so they don't need an offset.
+ startOffset = 1;
+ // "-1" because Owner Card 0.vcf is also included in /pb, but not in /fav.
+ numberOfContactsRemaining -= 1;
+ }
+
+ while ((numberOfContactsRemaining > 0) && (startOffset <= UPPER_LIMIT)) {
+ int numberOfContactsToDownload =
+ Math.min(Math.min(DEFAULT_BATCH_SIZE, numberOfContactsRemaining),
+ UPPER_LIMIT - startOffset + 1);
+ BluetoothPbapRequestPullPhoneBook request =
+ new BluetoothPbapRequestPullPhoneBook(path, mAccount,
+ PBAP_REQUESTED_FIELDS, VCARD_TYPE_30,
+ numberOfContactsToDownload, startOffset);
+ request.execute(mObexSession);
+ ArrayList<VCardEntry> vcards = request.getList();
+ if (path == FAV_PATH) {
+ // mark each vcard as a favorite
+ for (VCardEntry v : vcards) {
+ v.setStarred(true);
+ }
+ }
+ processor.setResults(vcards);
+ processor.onPullComplete();
+
+ startOffset += numberOfContactsToDownload;
+ numberOfContactsRemaining -= numberOfContactsToDownload;
+ }
+ if ((startOffset > UPPER_LIMIT) && (numberOfContactsRemaining > 0)) {
+ Log.w(TAG, "Download contacts incomplete, index exceeded upper limit.");
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Download contacts failure" + e.toString());
+ }
+ }
+
void downloadCallLog(String path, HashMap<String, Integer> callCounter) {
try {
BluetoothPbapRequestPullPhoneBook request =
@@ -419,4 +489,12 @@ class PbapClientConnectionHandler extends Handler {
Log.d(TAG, "Call Logs could not be deleted, they may not exist yet.");
}
}
+
+ private boolean isRepositorySupported(int mask) {
+ if (mPseRec == null) {
+ if (VDBG) Log.v(TAG, "No PBAP Server SDP Record");
+ return false;
+ }
+ return (mask & mPseRec.getSupportedRepositories()) != 0;
+ }
}
diff --git a/src/com/android/bluetooth/pbapclient/PbapClientService.java b/src/com/android/bluetooth/pbapclient/PbapClientService.java
index f150cddec..02b1e7a51 100644
--- a/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -19,9 +19,11 @@ package com.android.bluetooth.pbapclient;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothPbapClient;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -31,6 +33,7 @@ import android.util.Log;
import com.android.bluetooth.R;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService;
import com.android.bluetooth.sdp.SdpManager;
import java.util.ArrayList;
@@ -71,6 +74,9 @@ public class PbapClientService extends ProfileService {
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
// delay initial download until after the user is unlocked to add an account.
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ // To remove call logs when PBAP was never connected while calls were made,
+ // we also listen for HFP to become disconnected.
+ filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
try {
registerReceiver(mPbapBroadcastReceiver, filter);
} catch (Exception e) {
@@ -128,6 +134,21 @@ public class PbapClientService extends ProfileService {
}
}
+ private void removeHfpCallLog(String accountName, Context context) {
+ if (DBG) Log.d(TAG, "Removing call logs from " + accountName);
+ // Delete call logs belonging to accountName==BD_ADDR that also match
+ // component name "hfpclient".
+ ComponentName componentName = new ComponentName(context, HfpClientConnectionService.class);
+ String selectionFilter = CallLog.Calls.PHONE_ACCOUNT_ID + "=? AND "
+ + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME + "=?";
+ String[] selectionArgs = new String[]{accountName, componentName.flattenToString()};
+ try {
+ getContentResolver().delete(CallLog.Calls.CONTENT_URI, selectionFilter, selectionArgs);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Call Logs could not be deleted, they may not exist yet.");
+ }
+ }
+
private void registerSdpRecord() {
SdpManager sdpManager = SdpManager.getDefaultManager();
if (sdpManager == null) {
@@ -171,6 +192,21 @@ public class PbapClientService extends ProfileService {
for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
stateMachine.resumeDownload();
}
+ } else if (action.equals(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED)) {
+ // PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects.
+ // However, if PBAP was never connected/enabled in the first place, and calls are
+ // made over HFP, these calllogs will not be removed when the device disconnects.
+ // This code ensures callogs are still removed in this case.
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+
+ if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ if (DBG) {
+ Log.d(TAG, "Received intent to disconnect HFP with " + device);
+ }
+ // HFP client stores entries in calllog.db by BD_ADDR and component name
+ removeHfpCallLog(device.getAddress(), context);
+ }
}
}
}
diff --git a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
index c759a8a1f..b8fbbb9ef 100644
--- a/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
+++ b/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
@@ -195,6 +195,21 @@ public class A2dpSinkStreamHandlerTest {
}
@Test
+ public void testFocusRerequest() {
+ // Focus was lost transiently, expect streaming to stop.
+ testSnkPlay();
+ mStreamHandler.handleMessage(
+ mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT));
+ verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
+ verify(mMockA2dpSink, times(0)).informAudioFocusStateNative(0);
+ verify(mMockA2dpSink, times(1)).informAudioTrackGainNative(0);
+ mStreamHandler.handleMessage(
+ mStreamHandler.obtainMessage(A2dpSinkStreamHandler.REQUEST_FOCUS, true));
+ verify(mMockAudioManager, times(2)).requestAudioFocus(any());
+ }
+
+ @Test
public void testFocusGainTransient() {
// Focus was lost then regained.
testSnkPlay();
diff --git a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index 4fedc889a..907f0dcdc 100644
--- a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -23,9 +23,11 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.media.AudioManager;
-import android.media.session.MediaController;
import android.os.Looper;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -34,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
@@ -66,15 +69,23 @@ public class AvrcpControllerStateMachineTest {
private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
private byte[] mTestAddress = new byte[]{00, 01, 02, 03, 04, 05};
- @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
+ @Rule public final ServiceTestRule mAvrcpServiceRule = new ServiceTestRule();
+ @Rule public final ServiceTestRule mA2dpServiceRule = new ServiceTestRule();
@Mock
- private AdapterService mAdapterService;
+ private AdapterService mAvrcpAdapterService;
+
+ @Mock
+ private AdapterService mA2dpAdapterService;
+
@Mock
private AudioManager mAudioManager;
@Mock
private AvrcpControllerService mAvrcpControllerService;
+ @Mock
+ private Resources mMockResources;
+
AvrcpControllerStateMachine mAvrcpStateMachine;
@Before
@@ -90,9 +101,14 @@ public class AvrcpControllerStateMachineTest {
// Setup mocks and test assets
MockitoAnnotations.initMocks(this);
- TestUtils.setAdapterService(mAdapterService);
- TestUtils.startService(mServiceRule, AvrcpControllerService.class);
- doReturn(mTargetContext.getResources()).when(mAvrcpControllerService).getResources();
+ TestUtils.setAdapterService(mAvrcpAdapterService);
+ TestUtils.startService(mAvrcpServiceRule, AvrcpControllerService.class);
+ TestUtils.clearAdapterService(mAvrcpAdapterService);
+ TestUtils.setAdapterService(mA2dpAdapterService);
+ TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class);
+ when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
+ .thenReturn(true);
+ doReturn(mMockResources).when(mAvrcpControllerService).getResources();
doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt());
doReturn(8).when(mAudioManager).getStreamVolume(anyInt());
doReturn(true).when(mAudioManager).isVolumeFixed();
@@ -113,7 +129,7 @@ public class AvrcpControllerStateMachineTest {
if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_avrcp_controller)) {
return;
}
- TestUtils.clearAdapterService(mAdapterService);
+ TestUtils.clearAdapterService(mA2dpAdapterService);
}
/**
@@ -141,6 +157,10 @@ public class AvrcpControllerStateMachineTest {
IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+ Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
+ BluetoothMediaBrowserService.getPlaybackState());
}
/**
@@ -149,6 +169,11 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testControlOnly() {
int numBroadcastsSent = setUpConnectedState(true, false);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+ Assert.assertNotNull(transportControls);
+ Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
+ BluetoothMediaBrowserService.getPlaybackState());
StackEvent event =
StackEvent.connectionStateChanged(false, false);
mAvrcpStateMachine.disconnect();
@@ -166,6 +191,8 @@ public class AvrcpControllerStateMachineTest {
IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
+ Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
+ BluetoothMediaBrowserService.getPlaybackState());
}
/**
@@ -176,6 +203,8 @@ public class AvrcpControllerStateMachineTest {
Assert.assertEquals(0, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
int numBroadcastsSent = setUpConnectedState(false, true);
Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
+ Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
+ BluetoothMediaBrowserService.getPlaybackState());
StackEvent event =
StackEvent.connectionStateChanged(false, false);
mAvrcpStateMachine.disconnect();
@@ -193,6 +222,10 @@ public class AvrcpControllerStateMachineTest {
IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+ Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
+ BluetoothMediaBrowserService.getPlaybackState());
}
/**
@@ -221,7 +254,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testPlay() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Play
@@ -240,7 +273,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testPause() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Pause
@@ -259,7 +292,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testStop() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Stop
@@ -278,7 +311,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testNext() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Next
@@ -298,7 +331,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testPrevious() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Previous
@@ -318,7 +351,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testFastForward() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//FastForward
@@ -339,7 +372,7 @@ public class AvrcpControllerStateMachineTest {
@Test
public void testRewind() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Rewind
@@ -355,6 +388,69 @@ public class AvrcpControllerStateMachineTest {
}
/**
+ * Test media browser skip to queue item
+ */
+ @Test
+ public void testSkipToQueueInvalid() throws Exception {
+ byte scope = 1;
+ int minSize = 0;
+ int maxSize = 255;
+ setUpConnectedState(true, true);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+
+ //Play an invalid item below start
+ transportControls.skipToQueueItem(minSize - 1);
+ verify(mAvrcpControllerService,
+ timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
+ eq(mTestAddress), eq(scope), anyLong(), anyInt());
+
+ //Play an invalid item beyond end
+ transportControls.skipToQueueItem(maxSize + 1);
+ verify(mAvrcpControllerService,
+ timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
+ eq(mTestAddress), eq(scope), anyLong(), anyInt());
+ }
+
+ /**
+ * Test media browser shuffle command
+ */
+ @Test
+ public void testShuffle() throws Exception {
+ byte[] shuffleSetting = new byte[]{3};
+ byte[] shuffleMode = new byte[]{2};
+
+ setUpConnectedState(true, true);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+
+ //Shuffle
+ transportControls.setShuffleMode(1);
+ verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+ .setPlayerApplicationSettingValuesNative(
+ eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
+ }
+
+ /**
+ * Test media browser repeat command
+ */
+ @Test
+ public void testRepeat() throws Exception {
+ byte[] repeatSetting = new byte[]{2};
+ byte[] repeatMode = new byte[]{3};
+
+ setUpConnectedState(true, true);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+
+ //Shuffle
+ transportControls.setRepeatMode(2);
+ verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+ .setPlayerApplicationSettingValuesNative(
+ eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
+ }
+
+ /**
* Test media browsing
* Verify that a browse tree is created with the proper root
* Verify that a player can be fetched and added to the browse tree
@@ -483,7 +579,7 @@ public class AvrcpControllerStateMachineTest {
BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
mAvrcpStateMachine.requestContents(results);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
transportControls.play();
verify(mAvrcpControllerService,
@@ -507,6 +603,45 @@ public class AvrcpControllerStateMachineTest {
}
/**
+ * Test playback does not request focus when another app is playing music.
+ */
+ @Test
+ public void testPlaybackWhileMusicPlaying() {
+ when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
+ .thenReturn(false);
+ Assert.assertEquals(AudioManager.AUDIOFOCUS_NONE, A2dpSinkService.getFocusState());
+ doReturn(true).when(mAudioManager).isMusicActive();
+ setUpConnectedState(true, true);
+ mAvrcpStateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
+ PlaybackStateCompat.STATE_PLAYING);
+ TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+ verify(mAvrcpControllerService,
+ timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
+ eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+ TestUtils.waitForLooperToFinishScheduledTask(
+ A2dpSinkService.getA2dpSinkService().getMainLooper());
+ Assert.assertEquals(AudioManager.AUDIOFOCUS_NONE, A2dpSinkService.getFocusState());
+ }
+
+ /**
+ * Test playback requests focus while nothing is playing music.
+ */
+ @Test
+ public void testPlaybackWhileIdle() {
+ Assert.assertEquals(AudioManager.AUDIOFOCUS_NONE, A2dpSinkService.getFocusState());
+ doReturn(false).when(mAudioManager).isMusicActive();
+ setUpConnectedState(true, true);
+ mAvrcpStateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
+ PlaybackStateCompat.STATE_PLAYING);
+ TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+ TestUtils.waitForLooperToFinishScheduledTask(
+ A2dpSinkService.getA2dpSinkService().getMainLooper());
+ Assert.assertEquals(AudioManager.AUDIOFOCUS_GAIN, A2dpSinkService.getFocusState());
+ }
+
+ /**
* Setup Connected State
*
* @return number of times mAvrcpControllerService.sendBroadcastAsUser() has been invoked
diff --git a/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java b/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java
new file mode 100644
index 000000000..acd05ed02
--- /dev/null
+++ b/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.mapclient;
+
+import static org.mockito.Mockito.*;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BmessageTest {
+ private static final String TAG = BmessageTest.class.getSimpleName();
+ private static final String SIMPLE_MMS_MESSAGE =
+ "BEGIN:BMSG\r\nVERSION:1.0\r\nSTATUS:READ\r\nTYPE:MMS\r\nFOLDER:null\r\nBEGIN:BENV\r\n"
+ + "BEGIN:VCARD\r\nVERSION:2.1\r\nN:null;;;;\r\nTEL:555-5555\r\nEND:VCARD\r\n"
+ + "BEGIN:BBODY\r\nLENGTH:39\r\nBEGIN:MSG\r\nThis is a new msg\r\nEND:MSG\r\n"
+ + "END:BBODY\r\nEND:BENV\r\nEND:BMSG\r\n";
+
+ private static final String NO_END_MESSAGE =
+ "BEGIN:BMSG\r\nVERSION:1.0\r\nSTATUS:READ\r\nTYPE:MMS\r\nFOLDER:null\r\nBEGIN:BENV\r\n"
+ + "BEGIN:VCARD\r\nVERSION:2.1\r\nN:null;;;;\r\nTEL:555-5555\r\nEND:VCARD\r\n"
+ + "BEGIN:BBODY\r\nLENGTH:39\r\nBEGIN:MSG\r\nThis is a new msg\r\n";
+
+ private static final String WRONG_LENGTH_MESSAGE =
+ "BEGIN:BMSG\r\nVERSION:1.0\r\nSTATUS:READ\r\nTYPE:MMS\r\nFOLDER:null\r\nBEGIN:BENV\r\n"
+ + "BEGIN:VCARD\r\nVERSION:2.1\r\nN:null;;;;\r\nTEL:555-5555\r\nEND:VCARD\r\n"
+ + "BEGIN:BBODY\r\nLENGTH:200\r\nBEGIN:MSG\r\nThis is a new msg\r\nEND:MSG\r\n"
+ + "END:BBODY\r\nEND:BENV\r\nEND:BMSG\r\n";
+
+ private static final String NO_BODY_MESSAGE =
+ "BEGIN:BMSG\r\nVERSION:1.0\r\nSTATUS:READ\r\nTYPE:MMS\r\nFOLDER:null\r\nBEGIN:BENV\r\n"
+ + "BEGIN:VCARD\r\nVERSION:2.1\r\nN:null;;;;\r\nTEL:555-5555\r\nEND:VCARD\r\n"
+ + "BEGIN:BBODY\r\nLENGTH:\r\n";
+
+ private static final String NEGATIVE_LENGTH_MESSAGE =
+ "BEGIN:BMSG\r\nVERSION:1.0\r\nSTATUS:READ\r\nTYPE:MMS\r\nFOLDER:null\r\nBEGIN:BENV\r\n"
+ + "BEGIN:VCARD\r\nVERSION:2.1\r\nN:null;;;;\r\nTEL:555-5555\r\nEND:VCARD\r\n"
+ + "BEGIN:BBODY\r\nLENGTH:-1\r\nBEGIN:MSG\r\nThis is a new msg\r\nEND:MSG\r\n"
+ + "END:BBODY\r\nEND:BENV\r\nEND:BMSG\r\n";
+
+ @Test
+ public void testNormalMessages() {
+ Bmessage message = BmessageParser.createBmessage(SIMPLE_MMS_MESSAGE);
+ Assert.assertNotNull(message);
+ }
+
+ @Test
+ public void testParseWrongLengthMessage() {
+ Bmessage message = BmessageParser.createBmessage(WRONG_LENGTH_MESSAGE);
+ Assert.assertNull(message);
+ }
+
+ @Test
+ public void testParseNoEndMessage() {
+ Bmessage message = BmessageParser.createBmessage(NO_END_MESSAGE);
+ Assert.assertNull(message);
+ }
+
+ @Test
+ public void testParseReallyLongMessage() {
+ String testMessage = new String(new char[68048]).replace('\0', 'A');
+ Bmessage message = BmessageParser.createBmessage(testMessage);
+ Assert.assertNull(message);
+ }
+
+ @Test
+ public void testNoBodyMessage() {
+ Bmessage message = BmessageParser.createBmessage(NO_BODY_MESSAGE);
+ Assert.assertNull(message);
+ }
+
+ @Test
+ public void testNegativeLengthMessage() {
+ Bmessage message = BmessageParser.createBmessage(NEGATIVE_LENGTH_MESSAGE);
+ Assert.assertNull(message);
+ }
+}
diff --git a/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java b/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
index 70854d8d1..ccfd9f810 100644
--- a/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
+++ b/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
@@ -150,6 +150,36 @@ public class MapClientStateMachineTest {
Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState());
}
+ /**
+ * Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED
+ * --> (receive MSG_MAS_DISCONNECTED) --> STATE_DISCONNECTED
+ */
+ @Test
+ public void testStateTransitionFromConnectedWithMasDisconnected() {
+ Log.i(TAG, "in testStateTransitionFromConnectedWithMasDisconnected");
+
+ setupSdpRecordReceipt();
+ Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED);
+ mMceStateMachine.sendMessage(msg);
+
+ // Wait until the message is processed and a broadcast request is sent to
+ // to MapClientService to change
+ // state from STATE_CONNECTING to STATE_CONNECTED
+ verify(mMockMapClientService,
+ timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast(
+ mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM));
+ Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState());
+
+ msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED);
+ mMceStateMachine.sendMessage(msg);
+ verify(mMockMapClientService,
+ timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcast(
+ mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM));
+
+ Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState());
+ }
+
+
/**
* Test receiving an empty event report
*/
diff --git a/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java b/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java
new file mode 100644
index 000000000..5ef2d455b
--- /dev/null
+++ b/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.mapclient;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+@RunWith(AndroidJUnit4.class)
+public class ObexTimeTest {
+ private static final String TAG = ObexTimeTest.class.getSimpleName();
+
+ private static final String VALID_TIME_STRING = "20190101T121314";
+ private static final String VALID_TIME_STRING_WITH_OFFSET_POS = "20190101T121314+0130";
+ private static final String VALID_TIME_STRING_WITH_OFFSET_NEG = "20190101T121314-0130";
+
+ private static final String INVALID_TIME_STRING_OFFSET_EXTRA_DIGITS = "20190101T121314-99999";
+ private static final String INVALID_TIME_STRING_BAD_DELIMITER = "20190101Q121314";
+
+ // MAP message listing times, per spec, use "local time basis" if UTC offset isn't given. The
+ // ObexTime class parses using the current default timezone (assumed to be the "local timezone")
+ // in the case that UTC isn't provided. However, the Date class assumes UTC ALWAYS when
+ // initializing off of a long value. We have to take that into account when determining our
+ // expected results for time strings that don't have an offset.
+ private static final long LOCAL_TIMEZONE_OFFSET = TimeZone.getDefault().getRawOffset();
+
+ // If you are a positive offset from GMT then GMT is in the "past" and you need to subtract that
+ // offset from the time. If you are negative then GMT is in the future and you need to add that
+ // offset to the time.
+ private static final long VALID_TS = 1546344794000L; // Jan 01, 2019 at 12:13:14 GMT
+ private static final long TS_OFFSET = 5400000L; // 1 Hour, 30 minutes -> milliseconds
+ private static final long VALID_TS_LOCAL_TZ = VALID_TS - LOCAL_TIMEZONE_OFFSET;
+ private static final long VALID_TS_OFFSET_POS = VALID_TS - TS_OFFSET;
+ private static final long VALID_TS_OFFSET_NEG = VALID_TS + TS_OFFSET;
+
+ private static final Date VALID_DATE_LOCAL_TZ = new Date(VALID_TS_LOCAL_TZ);
+ private static final Date VALID_DATE_WITH_OFFSET_POS = new Date(VALID_TS_OFFSET_POS);
+ private static final Date VALID_DATE_WITH_OFFSET_NEG = new Date(VALID_TS_OFFSET_NEG);
+
+ @Test
+ public void createWithValidDateTimeString_TimestampCorrect() {
+ ObexTime timestamp = new ObexTime(VALID_TIME_STRING);
+ Assert.assertEquals("Parsed timestamp must match expected", VALID_DATE_LOCAL_TZ,
+ timestamp.getTime());
+ }
+
+ @Test
+ public void createWithValidDateTimeStringWithPosOffset_TimestampCorrect() {
+ ObexTime timestamp = new ObexTime(VALID_TIME_STRING_WITH_OFFSET_POS);
+ Assert.assertEquals("Parsed timestamp must match expected", VALID_DATE_WITH_OFFSET_POS,
+ timestamp.getTime());
+ }
+
+ @Test
+ public void createWithValidDateTimeStringWithNegOffset_TimestampCorrect() {
+ ObexTime timestamp = new ObexTime(VALID_TIME_STRING_WITH_OFFSET_NEG);
+ Assert.assertEquals("Parsed timestamp must match expected", VALID_DATE_WITH_OFFSET_NEG,
+ timestamp.getTime());
+ }
+
+ @Test
+ public void createWithValidDate_TimestampCorrect() {
+ ObexTime timestamp = new ObexTime(VALID_DATE_LOCAL_TZ);
+ Assert.assertEquals("ObexTime created with a date must return the same date",
+ VALID_DATE_LOCAL_TZ, timestamp.getTime());
+ }
+
+ @Test
+ public void printValidTime_TimestampMatchesInput() {
+ ObexTime timestamp = new ObexTime(VALID_TIME_STRING);
+ Assert.assertEquals("Timestamp as a string must match the input string", VALID_TIME_STRING,
+ timestamp.toString());
+ }
+
+ @Test
+ public void createWithInvalidDelimiterString_TimestampIsNull() {
+ ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_BAD_DELIMITER);
+ Assert.assertEquals("Parsed timestamp was invalid and must result in a null object", null,
+ timestamp.getTime());
+ }
+
+ @Test
+ public void createWithInvalidOffsetString_TimestampIsNull() {
+ ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_OFFSET_EXTRA_DIGITS);
+ Assert.assertEquals("Parsed timestamp was invalid and must result in a null object", null,
+ timestamp.getTime());
+ }
+
+ @Test
+ public void printInvalidTime_ReturnsNull() {
+ ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_BAD_DELIMITER);
+ Assert.assertEquals("Invalid timestamps must return null for toString()", null,
+ timestamp.toString());
+ }
+}
diff --git a/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java b/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
index 6d4574f11..3573f847a 100644
--- a/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
+++ b/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
@@ -15,8 +15,12 @@
*/
package com.android.bluetooth.pan;
+import static org.mockito.Mockito.when;
+
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.os.UserManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -47,6 +51,7 @@ public class PanServiceTest {
@Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
@Mock private AdapterService mAdapterService;
+ @Mock private UserManager mMockUserManager;
@Before
public void setUp() throws Exception {
@@ -61,6 +66,7 @@ public class PanServiceTest {
// Try getting the Bluetooth adapter
mAdapter = BluetoothAdapter.getDefaultAdapter();
Assert.assertNotNull(mAdapter);
+ mService.mUserManager = mMockUserManager;
}
@After
@@ -78,4 +84,11 @@ public class PanServiceTest {
public void testInitialize() {
Assert.assertNotNull(PanService.getPanService());
}
+
+ @Test
+ public void testGuestUserConnect() {
+ BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
+ when(mMockUserManager.isGuestUser()).thenReturn(true);
+ Assert.assertFalse(mService.connect(device));
+ }
}