Fix an OOB bug in BqrVseSubEvt::ParseBqrLinkQualityEvt am: ecd5a3e85b
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/20684834
Change-Id: I39d48d6b0da8de78150a901a5f1ed20e3e326ad4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index 2d3b648..02b536d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,7 @@
# Project owners
sattiraju@google.com
siyuanh@google.com
+sungsoo@google.com
zachoverflow@google.com
# Per-file ownership
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 1c86ca8..ba69e74 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -71,6 +71,7 @@
static jmethodID method_sspRequestCallback;
static jmethodID method_bondStateChangeCallback;
static jmethodID method_addressConsolidateCallback;
+static jmethodID method_leAddressAssociateCallback;
static jmethodID method_aclStateChangeCallback;
static jmethodID method_discoveryStateChangeCallback;
static jmethodID method_linkQualityReportCallback;
@@ -336,6 +337,34 @@
main_addr.get(), secondary_addr.get());
}
+static void le_address_associate_callback(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+
+ ScopedLocalRef<jbyteArray> main_addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!main_addr.get()) {
+ ALOGE("Address allocation failed in %s", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(main_addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)main_bd_addr);
+
+ ScopedLocalRef<jbyteArray> secondary_addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!secondary_addr.get()) {
+ ALOGE("Address allocation failed in %s", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(secondary_addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)secondary_bd_addr);
+
+ sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
+ method_leAddressAssociateCallback,
+ main_addr.get(), secondary_addr.get());
+}
+
static void acl_state_changed_callback(bt_status_t status, RawAddress* bd_addr,
bt_acl_state_t state,
int transport_link_type,
@@ -696,6 +725,7 @@
ssp_request_callback,
bond_state_changed_callback,
address_consolidate_callback,
+ le_address_associate_callback,
acl_state_changed_callback,
callback_thread_event,
dut_mode_recv_callback,
@@ -922,6 +952,9 @@
method_addressConsolidateCallback = env->GetMethodID(
jniCallbackClass, "addressConsolidateCallback", "([B[B)V");
+ method_leAddressAssociateCallback = env->GetMethodID(
+ jniCallbackClass, "leAddressAssociateCallback", "([B[B)V");
+
method_aclStateChangeCallback =
env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BIII)V");
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index b97385f..3980d1c 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -25,7 +25,6 @@
#include "com_android_bluetooth.h"
#include "hardware/bt_le_audio.h"
-using bluetooth::le_audio::BroadcastAudioProfile;
using bluetooth::le_audio::BroadcastId;
using bluetooth::le_audio::BroadcastState;
using bluetooth::le_audio::btle_audio_codec_config_t;
@@ -1125,7 +1124,7 @@
}
static void CreateBroadcastNative(JNIEnv* env, jobject object,
- jbyteArray metadata, jint audio_profile,
+ jbyteArray metadata,
jbyteArray broadcast_code) {
LOG(INFO) << __func__;
std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
@@ -1138,7 +1137,6 @@
jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
sLeAudioBroadcasterInterface->CreateBroadcast(
std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)),
- static_cast<BroadcastAudioProfile>(audio_profile),
broadcast_code ? std::optional<std::array<uint8_t, 16>>(code_array)
: std::nullopt);
env->ReleaseByteArrayElements(metadata, meta, 0);
@@ -1198,7 +1196,7 @@
{"initNative", "()V", (void*)BroadcasterInitNative},
{"stopNative", "()V", (void*)BroadcasterStopNative},
{"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
- {"createBroadcastNative", "([BI[B)V", (void*)CreateBroadcastNative},
+ {"createBroadcastNative", "([B[B)V", (void*)CreateBroadcastNative},
{"updateMetadataNative", "(I[B)V", (void*)UpdateMetadataNative},
{"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
{"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
diff --git a/android/app/jni/com_android_bluetooth_vc.cpp b/android/app/jni/com_android_bluetooth_vc.cpp
index ad31918..4c4103e 100644
--- a/android/app/jni/com_android_bluetooth_vc.cpp
+++ b/android/app/jni/com_android_bluetooth_vc.cpp
@@ -346,7 +346,7 @@
env->ReleaseByteArrayElements(address, addr, 0);
}
-static void setVolumeGroupNative(JNIEnv* env, jobject object, jint group_id,
+static void setGroupVolumeNative(JNIEnv* env, jobject object, jint group_id,
jint volume) {
if (!sVolumeControlInterface) {
LOG(ERROR) << __func__
@@ -547,7 +547,7 @@
{"disconnectVolumeControlNative", "([B)Z",
(void*)disconnectVolumeControlNative},
{"setVolumeNative", "([BI)V", (void*)setVolumeNative},
- {"setVolumeGroupNative", "(II)V", (void*)setVolumeGroupNative},
+ {"setGroupVolumeNative", "(II)V", (void*)setGroupVolumeNative},
{"muteNative", "([B)V", (void*)muteNative},
{"muteGroupNative", "(I)V", (void*)muteGroupNative},
{"unmuteNative", "([B)V", (void*)unmuteNative},
diff --git a/android/app/res/values-ar/strings.xml b/android/app/res/values-ar/strings.xml
index 87b7ca6..423bd41 100644
--- a/android/app/res/values-ar/strings.xml
+++ b/android/app/res/values-ar/strings.xml
@@ -115,7 +115,7 @@
<string name="transfer_menu_open" msgid="5193344638774400131">"فتح"</string>
<string name="transfer_menu_clear" msgid="7213491281898188730">"محو من القائمة"</string>
<string name="transfer_clear_dlg_title" msgid="128904516163257225">"محو"</string>
- <string name="bluetooth_a2dp_sink_queue_name" msgid="7521243473328258997">"التعرّف التلقائي على الموسيقى"</string>
+ <string name="bluetooth_a2dp_sink_queue_name" msgid="7521243473328258997">"قيد التشغيل الآن"</string>
<string name="bluetooth_map_settings_save" msgid="8309113239113961550">"حفظ"</string>
<string name="bluetooth_map_settings_cancel" msgid="3374494364625947793">"إلغاء"</string>
<string name="bluetooth_map_settings_intro" msgid="4748160773998753325">"حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال."</string>
diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml
index 4ecc5fb..847be12 100644
--- a/android/app/res/values-as/strings.xml
+++ b/android/app/res/values-as/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="permlab_bluetoothShareManager" msgid="5297865456717871041">"ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।"</string>
+ <string name="permlab_bluetoothShareManager" msgid="5297865456717871041">"ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।"</string>
<string name="permdesc_bluetoothShareManager" msgid="1588034776955941477">"এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_bluetoothAcceptlist" msgid="5785922051395856524">"ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।"</string>
<string name="permdesc_bluetoothAcceptlist" msgid="259308920158011885">"এপ্টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।"</string>
@@ -118,7 +118,7 @@
<string name="bluetooth_a2dp_sink_queue_name" msgid="7521243473328258997">"এতিয়া প্লে’ হৈ আছে"</string>
<string name="bluetooth_map_settings_save" msgid="8309113239113961550">"ছেভ কৰক"</string>
<string name="bluetooth_map_settings_cancel" msgid="3374494364625947793">"বাতিল কৰক"</string>
- <string name="bluetooth_map_settings_intro" msgid="4748160773998753325">"ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।"</string>
+ <string name="bluetooth_map_settings_intro" msgid="4748160773998753325">"ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ এক্সেছ দিবই লাগিব।"</string>
<string name="bluetooth_map_settings_count" msgid="183013143617807702">"বাকী থকা শ্লটবোৰ:"</string>
<string name="bluetooth_map_settings_app_icon" msgid="3501432663809664982">"এপ্লিকেশ্বন আইকন"</string>
<string name="bluetooth_map_settings_title" msgid="4226030082708590023">"ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিং"</string>
diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml
index f886e47..f18138b 100644
--- a/android/app/res/values-as/strings_sap.xml
+++ b/android/app/res/values-as/strings_sap.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="bluetooth_sap_notif_title" msgid="7854456947435963346">"ব্লুটুথৰ ছিম ব্যৱহাৰ"</string>
- <string name="bluetooth_sap_notif_ticker" msgid="7295825445933648498">"ব্লুটুথৰ ছিম ব্যৱহাৰ"</string>
+ <string name="bluetooth_sap_notif_title" msgid="7854456947435963346">"ব্লুটুথৰ ছিম এক্সেছ"</string>
+ <string name="bluetooth_sap_notif_ticker" msgid="7295825445933648498">"ব্লুটুথৰ ছিম এক্সেছ"</string>
<string name="bluetooth_sap_notif_message" msgid="1004269289836361678">"সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?"</string>
<string name="bluetooth_sap_notif_disconnecting" msgid="6041257463440623400">"সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে"</string>
<string name="bluetooth_sap_notif_disconnect_button" msgid="3059012556387692616">"বিচ্ছিন্ন কৰক"</string>
diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml
index 719dd95..f432967 100644
--- a/android/app/res/values-de/strings.xml
+++ b/android/app/res/values-de/strings.xml
@@ -80,9 +80,9 @@
<string name="bt_toast_1" msgid="8791691594887576215">"Die Datei wird empfangen. Überprüfe den Fortschritt in der Benachrichtigungskonsole."</string>
<string name="bt_toast_2" msgid="2041575937953174042">"Die Datei kann nicht empfangen werden."</string>
<string name="bt_toast_3" msgid="3053157171297761920">"Der Empfang der Datei von \"<xliff:g id="SENDER">%1$s</xliff:g>\" wurde angehalten."</string>
- <string name="bt_toast_4" msgid="480365991944956695">"Datei wird an \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" gesendet..."</string>
+ <string name="bt_toast_4" msgid="480365991944956695">"Datei wird an „<xliff:g id="RECIPIENT">%1$s</xliff:g>“ gesendet..."</string>
<string name="bt_toast_5" msgid="4818264207982268297">"<xliff:g id="NUMBER">%1$s</xliff:g> Dateien werden an \"<xliff:g id="RECIPIENT">%2$s</xliff:g>\" gesendet."</string>
- <string name="bt_toast_6" msgid="8814166471030694787">"Die Übertragung der Datei an \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" wurde abgebrochen"</string>
+ <string name="bt_toast_6" msgid="8814166471030694787">"Die Übertragung der Datei an „<xliff:g id="RECIPIENT">%1$s</xliff:g>“ wurde abgebrochen"</string>
<string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Auf dem USB-Speicher ist nicht genügend Platz, um die Datei zu speichern."</string>
<string name="bt_sm_2_1_default" msgid="5070195264206471656">"Auf der SD-Karte ist nicht genügend Platz, um die Datei zu speichern."</string>
<string name="bt_sm_2_2" msgid="6200119660562110560">"Erforderlicher Speicherplatz: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml
index d48e5a0..149917e 100644
--- a/android/app/res/values-eu/strings.xml
+++ b/android/app/res/values-eu/strings.xml
@@ -20,14 +20,14 @@
<string name="permdesc_bluetoothShareManager" msgid="1588034776955941477">"Bluetooth bidezko partekatzeen kudeatzailea atzitzea eta fitxategiak transferitzeko erabiltzeko baimena ematen die aplikazioei."</string>
<string name="permlab_bluetoothAcceptlist" msgid="5785922051395856524">"Ezarri Bluetooth bidezko gailuak onartutakoen zerrendan."</string>
<string name="permdesc_bluetoothAcceptlist" msgid="259308920158011885">"Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan ezartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe."</string>
- <string name="bt_share_picker_label" msgid="7464438494743777696">"Bluetooth-a"</string>
+ <string name="bt_share_picker_label" msgid="7464438494743777696">"Bluetootha"</string>
<string name="unknown_device" msgid="2317679521750821654">"Identifikatu ezin den gailua"</string>
<string name="unknownNumber" msgid="1245183329830158661">"Ezezaguna"</string>
<string name="airplane_error_title" msgid="2570111716678850860">"Hegaldi modua"</string>
- <string name="airplane_error_msg" msgid="4853111123699559578">"Ezin duzu erabili Bluetooth-a Hegaldi moduan."</string>
+ <string name="airplane_error_msg" msgid="4853111123699559578">"Ezin duzu erabili Bluetootha Hegaldi moduan."</string>
<string name="bt_enable_title" msgid="4484289159118416315"></string>
- <string name="bt_enable_line1" msgid="8429910585843481489">"Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu."</string>
- <string name="bt_enable_line2" msgid="1466367120348920892">"Bluetooth-a aktibatu nahi duzu?"</string>
+ <string name="bt_enable_line1" msgid="8429910585843481489">"Bluetooth-zerbitzuak erabiltzeko, Bluetootha aktibatu behar duzu."</string>
+ <string name="bt_enable_line2" msgid="1466367120348920892">"Bluetootha aktibatu nahi duzu?"</string>
<string name="bt_enable_cancel" msgid="6770180540581977614">"Utzi"</string>
<string name="bt_enable_ok" msgid="4224374055813566166">"Aktibatu"</string>
<string name="incoming_file_confirm_title" msgid="938251186275547290">"Fitxategi-transferentzia"</string>
@@ -76,7 +76,7 @@
<string name="not_exist_file" msgid="5097565588949092486">"Ez dago fitxategirik"</string>
<string name="not_exist_file_desc" msgid="250802392160941265">"Ez dago horrelako fitxategirik. \n"</string>
<string name="enabling_progress_title" msgid="5262637688863903594">"Itxaron…"</string>
- <string name="enabling_progress_content" msgid="685427201206684584">"Bluetooth-a aktibatzen…"</string>
+ <string name="enabling_progress_content" msgid="685427201206684584">"Bluetootha aktibatzen…"</string>
<string name="bt_toast_1" msgid="8791691594887576215">"Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera."</string>
<string name="bt_toast_2" msgid="2041575937953174042">"Ezin da fitxategia jaso."</string>
<string name="bt_toast_3" msgid="3053157171297761920">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" igorlearen fitxategia jasotzeari utzi zaio"</string>
@@ -127,5 +127,5 @@
<string name="bluetooth_disconnected" msgid="6841396291728343534">"Deskonektatu da Bluetooth bidezko audioa"</string>
<string name="a2dp_sink_mbs_label" msgid="6035366346569127155">"Bluetooth bidezko audioa"</string>
<string name="bluetooth_opp_file_limit_exceeded" msgid="6612109860149473930">"Ezin dira transferitu 4 GB baino gehiagoko fitxategiak"</string>
- <string name="bluetooth_connect_action" msgid="2319449093046720209">"Konektatu Bluetooth-era"</string>
+ <string name="bluetooth_connect_action" msgid="2319449093046720209">"Konektatu Bluetoothera"</string>
</resources>
diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml
index e601649..4c501ab 100644
--- a/android/app/res/values-eu/test_strings.xml
+++ b/android/app/res/values-eu/test_strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="7766152617107310582">"Bluetooth-a"</string>
+ <string name="app_name" msgid="7766152617107310582">"Bluetootha"</string>
<string name="insert_record" msgid="4024416351836939752">"Sartu erregistroa"</string>
<string name="update_record" msgid="7201772850942641237">"Berretsi erregistroa"</string>
<string name="ack_record" msgid="2404738476192250210">"ACK erregistroa"</string>
diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml
index 5e10989..1bc3d20 100644
--- a/android/app/res/values-fr-rCA/strings.xml
+++ b/android/app/res/values-fr-rCA/strings.xml
@@ -80,9 +80,9 @@
<string name="bt_toast_1" msgid="8791691594887576215">"La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification."</string>
<string name="bt_toast_2" msgid="2041575937953174042">"Impossible de recevoir le fichier."</string>
<string name="bt_toast_3" msgid="3053157171297761920">"Réception du fichier de \"<xliff:g id="SENDER">%1$s</xliff:g>\" interrompue"</string>
- <string name="bt_toast_4" msgid="480365991944956695">"Envoi du fichier à \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+ <string name="bt_toast_4" msgid="480365991944956695">"Envoi du fichier à « <xliff:g id="RECIPIENT">%1$s</xliff:g> »"</string>
<string name="bt_toast_5" msgid="4818264207982268297">"Envoi de <xliff:g id="NUMBER">%1$s</xliff:g> fichiers à \"<xliff:g id="RECIPIENT">%2$s</xliff:g>\""</string>
- <string name="bt_toast_6" msgid="8814166471030694787">"Envoi du fichier à \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" interrompu"</string>
+ <string name="bt_toast_6" msgid="8814166471030694787">"Envoi du fichier à « <xliff:g id="RECIPIENT">%1$s</xliff:g> » interrompu"</string>
<string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Espace insuffisant sur la mémoire de stockage USB pour l\'enregistrement du fichier."</string>
<string name="bt_sm_2_1_default" msgid="5070195264206471656">"Espace insuffisant sur la carte SD pour l\'enregistrement du fichier."</string>
<string name="bt_sm_2_2" msgid="6200119660562110560">"Espace requis : <xliff:g id="SIZE">%1$s</xliff:g>"</string>
diff --git a/android/app/res/values-pt-rPT/strings.xml b/android/app/res/values-pt-rPT/strings.xml
index c2c09c3..524a95b 100644
--- a/android/app/res/values-pt-rPT/strings.xml
+++ b/android/app/res/values-pt-rPT/strings.xml
@@ -86,7 +86,7 @@
<string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Não existe espaço suficiente na memória USB para guardar o ficheiro."</string>
<string name="bt_sm_2_1_default" msgid="5070195264206471656">"Não existe espaço suficiente no cartão SD para guardar o ficheiro."</string>
<string name="bt_sm_2_2" msgid="6200119660562110560">"Espaço necessário: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
- <string name="ErrorTooManyRequests" msgid="5049670841391761475">"Existem demasiados pedidos em processamento. Tente novamente mais tarde."</string>
+ <string name="ErrorTooManyRequests" msgid="5049670841391761475">"Existem demasiados pedidos em processamento. Tente mais tarde."</string>
<string name="status_pending" msgid="4781040740237733479">"Ainda não foi iniciada a transferência do ficheiro."</string>
<string name="status_running" msgid="7419075903776657351">"Transferência do ficheiro em curso."</string>
<string name="status_success" msgid="7963589000098719541">"Transferência de ficheiros concluída com êxito."</string>
diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml
index bdca35f..6e8024e 100644
--- a/android/app/res/values-te/strings.xml
+++ b/android/app/res/values-te/strings.xml
@@ -48,14 +48,14 @@
<string name="download_title" msgid="6449408649671518102">"ఫైల్ బదిలీ"</string>
<string name="download_line1" msgid="6449220145685308846">"వీరి నుండి: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
<string name="download_line2" msgid="7634316500490825390">"ఫైల్: <xliff:g id="FILE">%1$s</xliff:g>"</string>
- <string name="download_line3" msgid="6722284930665532816">"ఫైల్ పరిమాణం: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="download_line3" msgid="6722284930665532816">"ఫైల్ సైజ్: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
<string name="download_line4" msgid="5234701398884321314"></string>
<string name="download_line5" msgid="4124272066218470715">"ఫైల్ను స్వీకరిస్తోంది…"</string>
<string name="download_cancel" msgid="1705762428762702342">"ఆపివేయి"</string>
<string name="download_ok" msgid="2404442707314575833">"దాచు"</string>
<string name="incoming_line1" msgid="6342300988329482408">"దీని నుండి"</string>
<string name="incoming_line2" msgid="2199520895444457585">"ఫైల్ పేరు"</string>
- <string name="incoming_line3" msgid="8630078246326525633">"పరిమాణం"</string>
+ <string name="incoming_line3" msgid="8630078246326525633">"సైజ్"</string>
<string name="download_fail_line1" msgid="3149552664349685007">"ఫైల్ స్వీకరించబడలేదు"</string>
<string name="download_fail_line2" msgid="4289018531070750414">"ఫైల్: <xliff:g id="FILE">%1$s</xliff:g>"</string>
<string name="download_fail_line3" msgid="2214989413171231684">"కారణం: <xliff:g id="REASON">%1$s</xliff:g>"</string>
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java
index 7afa86f..43b4e76 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryService.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java
@@ -38,6 +38,7 @@
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -62,6 +63,7 @@
private static BatteryService sBatteryService;
private AdapterService mAdapterService;
+ private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private final Map<BluetoothDevice, BatteryStateMachine> mStateMachines = new HashMap<>();
@@ -94,6 +96,8 @@
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when BatteryService starts");
+ mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+ "DatabaseManager cannot be null when BatteryService starts");
mStateMachines.clear();
mStateMachinesThread = new HandlerThread("BatteryService.StateMachines");
@@ -395,8 +399,7 @@
if (DBG) {
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
- mAdapterService.getDatabase()
- .setProfileConnectionPolicy(device, BluetoothProfile.BATTERY,
+ mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.BATTERY,
connectionPolicy);
if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
@@ -413,8 +416,7 @@
public int getConnectionPolicy(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
"Need BLUETOOTH_PRIVILEGED permission");
- return mAdapterService.getDatabase()
- .getProfileConnectionPolicy(device, BluetoothProfile.BATTERY);
+ return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.BATTERY);
}
/**
* Called when the battery level of the device is notified.
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
index 8f8007a..878c71e 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
@@ -24,6 +24,7 @@
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.os.Looper;
@@ -51,9 +52,10 @@
static final UUID GATT_BATTERY_SERVICE_UUID =
UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
-
static final UUID GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID =
UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");
+ static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID =
+ UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
static final int CONNECT = 1;
static final int DISCONNECT = 2;
@@ -553,7 +555,8 @@
return;
}
- gatt.setCharacteristicNotification(batteryLevel, /*enable=*/true);
+ // This may not trigger onCharacteristicRead if CCCD is already set but then
+ // onCharacteristicChanged will be triggered soon.
gatt.readCharacteristic(batteryLevel);
}
@@ -575,6 +578,23 @@
if (GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
updateBatteryLevel(value);
+ BluetoothGattDescriptor cccd =
+ characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
+ if (cccd != null) {
+ gatt.setCharacteristicNotification(characteristic, /*enable=*/true);
+ gatt.writeDescriptor(cccd, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+ } else {
+ Log.w(TAG, "No CCCD for battery level characteristic, "
+ + "it won't be notified");
+ }
+ }
+ }
+
+ @Override
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ if (status != BluetoothGatt.GATT_SUCCESS) {
+ Log.w(TAG, "Failed to write descriptor " + descriptor.getUuid());
}
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index 881ef14..584d173 100755
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.bass_client;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import android.bluetooth.BluetoothAdapter;
@@ -47,6 +48,7 @@
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
import java.nio.ByteBuffer;
@@ -74,6 +76,7 @@
private HandlerThread mStateMachinesThread;
private HandlerThread mCallbackHandlerThread;
private AdapterService mAdapterService;
+ private DatabaseManager mDatabaseManager;
private BluetoothAdapter mBluetoothAdapter = null;
private BassUtils mBassUtils = null;
private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
@@ -218,6 +221,8 @@
}
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when BassClientService starts");
+ mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+ "DatabaseManager cannot be null when BassClientService starts");
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mStateMachines.clear();
mStateMachinesThread = new HandlerThread("BassClientService.StateMachines");
@@ -545,7 +550,7 @@
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
boolean setSuccessfully =
- mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+ mDatabaseManager.setProfileConnectionPolicy(device,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, connectionPolicy);
if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
@@ -568,8 +573,7 @@
* @return connection policy of the device
*/
public int getConnectionPolicy(BluetoothDevice device) {
- return mAdapterService
- .getDatabase()
+ return mDatabaseManager
.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
}
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index b6321a4..61fbb24 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -217,7 +217,7 @@
break; // The device is already connected
}
mA2dpConnectedDevices.add(device);
- if (mHearingAidActiveDevice == null) {
+ if (mHearingAidActiveDevice == null && mLeAudioActiveDevice == null) {
// New connected device: select it as active
setA2dpActiveDevice(device);
break;
@@ -277,7 +277,7 @@
break; // The device is already connected
}
mHfpConnectedDevices.add(device);
- if (mHearingAidActiveDevice == null) {
+ if (mHearingAidActiveDevice == null && mLeAudioActiveDevice == null) {
// New connected device: select it as active
setHfpActiveDevice(device);
break;
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 9e007aa..1f3268b 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -3905,7 +3905,7 @@
public byte[] getByteIdentityAddress(BluetoothDevice device) {
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
- if (deviceProp != null && deviceProp.isConsolidated()) {
+ if (deviceProp != null && deviceProp.getIdentityAddress() != null) {
return Utils.getBytesFromAddress(deviceProp.getIdentityAddress());
} else {
return Utils.getByteAddress(device);
@@ -3923,7 +3923,7 @@
public String getIdentityAddress(String address) {
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address.toUpperCase());
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
- if (deviceProp != null && deviceProp.isConsolidated()) {
+ if (deviceProp != null && deviceProp.getIdentityAddress() != null) {
return deviceProp.getIdentityAddress();
} else {
return address;
@@ -5079,7 +5079,8 @@
private static final String GD_L2CAP_FLAG = "INIT_gd_l2cap";
private static final String GD_RUST_FLAG = "INIT_gd_rust";
private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy";
- private static final String GATT_ROBUST_CACHING_FLAG = "INIT_gatt_robust_caching";
+ private static final String GATT_ROBUST_CACHING_CLIENT_FLAG = "INIT_gatt_robust_caching_client";
+ private static final String GATT_ROBUST_CACHING_SERVER_FLAG = "INIT_gatt_robust_caching_server";
private static final String IRK_ROTATION_FLAG = "INIT_irk_rotation";
/**
@@ -5134,8 +5135,12 @@
initFlags.add(String.format("%s=%s", GD_LINK_POLICY_FLAG, "true"));
}
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
- GATT_ROBUST_CACHING_FLAG, false)) {
- initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_FLAG, "true"));
+ GATT_ROBUST_CACHING_CLIENT_FLAG, false)) {
+ initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_CLIENT_FLAG, "true"));
+ }
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
+ GATT_ROBUST_CACHING_SERVER_FLAG, false)) {
+ initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_SERVER_FLAG, "true"));
}
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, IRK_ROTATION_FLAG, false)) {
initFlags.add(String.format("%s=%s", IRK_ROTATION_FLAG, "true"));
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index 5dad27b..1c02159 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.res.Resources;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.bluetooth.R;
@@ -61,6 +62,11 @@
private static final String FEATURE_BATTERY = "settings_bluetooth_battery";
private static long sSupportedMask = 0;
+ private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY =
+ "ro.bluetooth.leaudio_switcher.supported";
+ private static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY =
+ "persist.bluetooth.leaudio_switcher.enabled";
+
private static class ProfileConfig {
Class mClass;
boolean mSupported;
@@ -159,6 +165,19 @@
private static boolean sIsGdEnabledUptoScanningLayer = false;
static void init(Context ctx) {
+ final boolean leAudioDynamicSwitchSupported =
+ SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false);
+
+ if (leAudioDynamicSwitchSupported) {
+ final String leAudioDynamicEnabled = SystemProperties
+ .get(LE_AUDIO_DYNAMIC_ENABLED_PROPERTY, "none");
+ if (leAudioDynamicEnabled.equals("true")) {
+ setLeAudioProfileStatus(true);
+ } else if (leAudioDynamicEnabled.equals("false")) {
+ setLeAudioProfileStatus(false);
+ }
+ }
+
ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
Log.i(TAG, "init: profile=" + config.mClass.getSimpleName() + ", enabled="
@@ -179,6 +198,15 @@
sIsGdEnabledUptoScanningLayer = resources.getBoolean(R.bool.enable_gd_up_to_scanning_layer);
}
+ static void setLeAudioProfileStatus(Boolean enable) {
+ setProfileEnabled(CsipSetCoordinatorService.class, enable);
+ setProfileEnabled(HapClientService.class, enable);
+ setProfileEnabled(LeAudioService.class, enable);
+ setProfileEnabled(TbsService.class, enable);
+ setProfileEnabled(McpService.class, enable);
+ setProfileEnabled(VolumeControlService.class, enable);
+ }
+
/**
* Remove the input profiles from the supported list.
*/
diff --git a/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
index 47906ac..a5c9869 100644
--- a/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
+++ b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
@@ -71,6 +71,10 @@
mRemoteDevices.addressConsolidateCallback(mainAddress, secondaryAddress);
}
+ void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+ mRemoteDevices.leAddressAssociateCallback(mainAddress, secondaryAddress);
+ }
+
void aclStateChangeCallback(int status, byte[] address, int newState,
int transportLinkType, int hciReason) {
mRemoteDevices.aclStateChangeCallback(status, address, newState,
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index e04d0fd..c81a5fb 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -36,6 +36,7 @@
import android.os.Message;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.bluetooth.R;
@@ -95,6 +96,9 @@
private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5;
private static final int MESSAGE_DEVICE_CONNECTED = 6;
+ private static final String PREFER_LE_AUDIO_ONLY_MODE =
+ "persist.bluetooth.prefer_le_audio_only_mode";
+
// Timeouts
@VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
@@ -106,6 +110,8 @@
private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();
+ private Boolean mPreferLeAudioOnlyMode = false;
+
// Broadcast receiver for all changes to states of various profiles
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -284,6 +290,7 @@
"DatabaseManager cannot be null when PhonePolicy starts");
mFactory = factory;
mHandler = new PhonePolicyHandler(service.getMainLooper());
+ mPreferLeAudioOnlyMode = SystemProperties.getBoolean(PREFER_LE_AUDIO_ONLY_MODE, true);
}
// Policy implementation, all functions MUST be private
@@ -354,6 +361,24 @@
debugLog("setting le audio profile priority for device " + device);
mAdapterService.getDatabase().setProfileConnectionPolicy(device,
BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mPreferLeAudioOnlyMode) {
+ if (mAdapterService.getDatabase()
+ .getProfileConnectionPolicy(device, BluetoothProfile.A2DP)
+ > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+ debugLog("clear a2dp profile priority for the le audio dual mode device "
+ + device);
+ mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+ BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ }
+ if (mAdapterService.getDatabase()
+ .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET)
+ > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+ debugLog("clear hfp profile priority for the le audio dual mode device "
+ + device);
+ mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+ BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ }
+ }
}
if ((hearingAidService != null) && Utils.arrayContains(uuids,
@@ -430,15 +455,42 @@
/**
* Updates the last connection date in the connection order database for the newly active device
- * if connected to a2dp profile
+ * if connected to a2dp profile. If the device is LE audio dual mode device, and
+ * mPreferLeAudioOnlyMode be true, A2DP/HFP will be disconnected as LE audio become active one
+ * after pairing.
*
* @param device is the device we just made the active device
*/
private void processActiveDeviceChanged(BluetoothDevice device, int profileId) {
- debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId);
+ debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId
+ + " mPreferLeAudioOnlyMode: " + mPreferLeAudioOnlyMode);
if (device != null) {
mDatabaseManager.setConnection(device, profileId == BluetoothProfile.A2DP);
+
+ if (!mPreferLeAudioOnlyMode) return;
+ if (profileId == BluetoothProfile.LE_AUDIO) {
+ HeadsetService hsService = mFactory.getHeadsetService();
+ if (hsService != null) {
+ if ((hsService.getConnectionPolicy(device)
+ != BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+ && (hsService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED)) {
+ debugLog("Disconnect HFP for the LE audio dual mode device " + device);
+ hsService.disconnect(device);
+ }
+ }
+ A2dpService a2dpService = mFactory.getA2dpService();
+ if (a2dpService != null) {
+ if ((a2dpService.getConnectionPolicy(device)
+ != BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+ && (a2dpService.getConnectionState(device)
+ == BluetoothProfile.STATE_CONNECTED)) {
+ debugLog("Disconnect A2DP for the LE audio dual mode device " + device);
+ a2dpService.disconnect(device);
+ }
+ }
+ }
}
}
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index da78ec6..9c8c88b 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -805,6 +805,27 @@
mDualDevicesMap.put(deviceProperties.getIdentityAddress(), Utils.getAddressStringFromByte(mainAddress));
}
+ /**
+ * Callback to associate an LE-only device's RPA with its identity address
+ *
+ * @param mainAddress the device's RPA
+ * @param secondaryAddress the device's identity address
+ */
+ void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+ BluetoothDevice device = getDevice(mainAddress);
+ if (device == null) {
+ errorLog("leAddressAssociateCallback: device is NULL, address="
+ + Utils.getAddressStringFromByte(mainAddress) + ", secondaryAddress="
+ + Utils.getAddressStringFromByte(secondaryAddress));
+ return;
+ }
+ Log.d(TAG, "leAddressAssociateCallback device: " + device + ", secondaryAddress:"
+ + Utils.getAddressStringFromByte(secondaryAddress));
+
+ DeviceProperties deviceProperties = getDeviceProperties(device);
+ deviceProperties.mIdentityAddress = Utils.getAddressStringFromByte(secondaryAddress);
+ }
+
void aclStateChangeCallback(int status, byte[] address, int newState,
int transportLinkType, int hciReason) {
BluetoothDevice device = getDevice(address);
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index f094e33..08e7809 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -47,6 +47,7 @@
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -76,6 +77,7 @@
private static CsipSetCoordinatorService sCsipSetCoordinatorService;
private AdapterService mAdapterService;
+ private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private BluetoothDevice mPreviousAudioDevice;
@@ -121,10 +123,12 @@
throw new IllegalStateException("start() called twice");
}
- // Get AdapterService, CsipSetCoordinatorNativeInterface.
+ // Get AdapterService, DatabaseManager, CsipSetCoordinatorNativeInterface.
// None of them can be null.
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when CsipSetCoordinatorService starts");
+ mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+ "DatabaseManager cannot be null when CsipSetCoordinatorService starts");
mCsipSetCoordinatorNativeInterface = Objects.requireNonNull(
CsipSetCoordinatorNativeInterface.getInstance(),
"CsipSetCoordinatorNativeInterface cannot be null when"
@@ -461,7 +465,7 @@
if (DBG) {
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
- mAdapterService.getDatabase().setProfileConnectionPolicy(
+ mDatabaseManager.setProfileConnectionPolicy(
device, BluetoothProfile.CSIP_SET_COORDINATOR, connectionPolicy);
if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
@@ -479,7 +483,7 @@
*/
public int getConnectionPolicy(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
- return mAdapterService.getDatabase().getProfileConnectionPolicy(
+ return mDatabaseManager.getProfileConnectionPolicy(
device, BluetoothProfile.CSIP_SET_COORDINATOR);
}
@@ -494,6 +498,9 @@
public @Nullable UUID lockGroup(
int groupId, @NonNull IBluetoothCsipSetCoordinatorLockCallback callback) {
if (callback == null) {
+ if (DBG) {
+ Log.d(TAG, "lockGroup(): " + groupId + ", callback not provided ");
+ }
return null;
}
@@ -520,12 +527,18 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ if (DBG) {
+ Log.d(TAG, "lockGroup(): " + groupId + ", ERROR_CSIP_GROUP_LOCKED_BY_OTHER ");
+ }
return null;
}
mLocks.put(groupId, new Pair<>(uuid, callback));
}
+ if (DBG) {
+ Log.d(TAG, "lockGroup(): locking group: " + groupId);
+ }
mCsipSetCoordinatorNativeInterface.groupLockSet(groupId, true);
return uuid;
}
@@ -538,6 +551,9 @@
*/
public void unlockGroup(@NonNull UUID lockUuid) {
if (lockUuid == null) {
+ if (DBG) {
+ Log.d(TAG, "unlockGroup(): lockUuid is null");
+ }
return;
}
@@ -546,6 +562,9 @@
mLocks.entrySet()) {
Pair<UUID, IBluetoothCsipSetCoordinatorLockCallback> uuidCbPair = entry.getValue();
if (uuidCbPair.first.equals(lockUuid)) {
+ if (DBG) {
+ Log.d(TAG, "unlockGroup(): unlocking ... " + lockUuid);
+ }
mCsipSetCoordinatorNativeInterface.groupLockSet(entry.getKey(), false);
return;
}
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 0eb5033..756d72b 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -49,6 +49,7 @@
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -79,6 +80,7 @@
@VisibleForTesting
HapClientNativeInterface mHapClientNativeInterface;
private AdapterService mAdapterService;
+ private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private BroadcastReceiver mBondStateChangedReceiver;
private BroadcastReceiver mConnectionStateChangedReceiver;
@@ -151,10 +153,12 @@
throw new IllegalStateException("start() called twice");
}
- // Get AdapterService, HapClientNativeInterface, AudioManager.
+ // Get AdapterService, HapClientNativeInterface, DatabaseManager, AudioManager.
// None of them can be null.
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when HapClientService starts");
+ mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+ "DatabaseManager cannot be null when HapClientService starts");
mHapClientNativeInterface = Objects.requireNonNull(
HapClientNativeInterface.getInstance(),
"HapClientNativeInterface cannot be null when HapClientService starts");
@@ -368,8 +372,7 @@
if (DBG) {
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
- mAdapterService.getDatabase()
- .setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT,
+ mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT,
connectionPolicy);
if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
@@ -392,8 +395,7 @@
* @hide
*/
public int getConnectionPolicy(BluetoothDevice device) {
- return mAdapterService.getDatabase()
- .getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT);
+ return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT);
}
/**
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
index cbacc60..52ffab4 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
@@ -169,12 +169,11 @@
* Creates LeAudio Broadcast instance.
*
* @param metadata metadata buffer with TLVs
- * @param audioProfile broadcast audio profile
* @param broadcastCode optional code if broadcast should be encrypted
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void createBroadcast(byte[] metadata, int audioProfile, byte[] broadcastCode) {
- createBroadcastNative(metadata, audioProfile, broadcastCode);
+ public void createBroadcast(byte[] metadata, byte[] broadcastCode) {
+ createBroadcastNative(metadata, broadcastCode);
}
/**
@@ -241,7 +240,7 @@
private native void initNative();
private native void stopNative();
private native void cleanupNative();
- private native void createBroadcastNative(byte[] metadata, int profile, byte[] broadcastCode);
+ private native void createBroadcastNative(byte[] metadata, byte[] broadcastCode);
private native void updateMetadataNative(int broadcastId, byte[] metadata);
private native void startBroadcastNative(int broadcastId);
private native void stopBroadcastNative(int broadcastId);
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index 33fa96f..ffcf22e 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -105,12 +105,6 @@
*/
private static final int ACTIVE_CONTEXTS_NONE = 0;
- /*
- * Brodcast profile used by the lower layers
- */
- private static final int BROADCAST_PROFILE_SONIFICATION = 0;
- private static final int BROADCAST_PROFILE_MEDIA = 1;
-
private AdapterService mAdapterService;
private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
@@ -128,6 +122,9 @@
LeAudioTmapGattServer mTmapGattServer;
@VisibleForTesting
+ VolumeControlService mVolumeControlService;
+
+ @VisibleForTesting
RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks;
@VisibleForTesting
@@ -384,6 +381,7 @@
mAdapterService = null;
mAudioManager = null;
+ mVolumeControlService = null;
return true;
}
@@ -412,6 +410,18 @@
sLeAudioService = instance;
}
+ private int getGroupVolume(int groupId) {
+ if (mVolumeControlService == null) {
+ mVolumeControlService = mServiceFactory.getVolumeControlService();
+ }
+ if (mVolumeControlService == null) {
+ Log.e(TAG, "Volume control service is not available");
+ return -1;
+ }
+
+ return mVolumeControlService.getGroupVolume(groupId);
+ }
+
public boolean connect(BluetoothDevice device) {
if (DBG) {
Log.d(TAG, "connect(): " + device);
@@ -646,8 +656,6 @@
/**
* Creates LeAudio Broadcast instance.
* @param metadata metadata buffer with TLVs
- * @param audioProfile broadcast audio profile
- * @param broadcastCode optional code if broadcast should be encrypted
*/
public void createBroadcast(BluetoothLeAudioContentMetadata metadata, byte[] broadcastCode) {
if (mLeAudioBroadcasterNativeInterface == null) {
@@ -655,7 +663,7 @@
return;
}
mLeAudioBroadcasterNativeInterface.createBroadcast(metadata.getRawMetadata(),
- BROADCAST_PROFILE_MEDIA, broadcastCode);
+ broadcastCode);
}
/**
@@ -830,6 +838,7 @@
+ previousInDevice + ", mActiveAudioInDevice" + mActiveAudioInDevice
+ " isLeOutput: false");
}
+
mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice,previousInDevice,
BluetoothProfileConnectionInfo.createLeAudioInfo(false, false));
@@ -899,9 +908,14 @@
+ previousOutDevice + ", mActiveOutDevice: " + mActiveAudioOutDevice
+ " isLeOutput: true");
}
+ int volume = -1;
+ if (mActiveAudioOutDevice != null) {
+ volume = getGroupVolume(groupId);
+ }
+
mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice,
previousOutDevice,
- BluetoothProfileConnectionInfo.createLeAudioInfo(suppressNoisyIntent, true));
+ getLeAudioOutputProfile(suppressNoisyIntent, volume));
return true;
}
Log.d(TAG, "updateActiveOutDevice: Nothing to do.");
@@ -1054,6 +1068,22 @@
}
}
+ BluetoothProfileConnectionInfo getLeAudioOutputProfile(boolean suppressNoisyIntent,
+ int volume) {
+ /* TODO - b/236618595 */
+ Parcel parcel = Parcel.obtain();
+ parcel.writeInt(BluetoothProfile.LE_AUDIO);
+ parcel.writeBoolean(suppressNoisyIntent);
+ parcel.writeInt(volume);
+ parcel.writeBoolean(true /* isLeOutput */);
+ parcel.setDataPosition(0);
+
+ BluetoothProfileConnectionInfo profileInfo =
+ BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return profileInfo;
+ }
+
BluetoothProfileConnectionInfo getBroadcastProfile(boolean suppressNoisyIntent) {
Parcel parcel = Parcel.obtain();
parcel.writeInt(BluetoothProfile.LE_AUDIO_BROADCAST);
@@ -1062,7 +1092,10 @@
parcel.writeBoolean(true /* mIsLeOutput */);
parcel.setDataPosition(0);
- return BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+ BluetoothProfileConnectionInfo profileInfo =
+ BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return profileInfo;
}
private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) {
@@ -1766,9 +1799,11 @@
return;
}
- VolumeControlService service = mServiceFactory.getVolumeControlService();
- if (service != null) {
- service.setVolumeGroup(currentlyActiveGroupId, volume);
+ if (mVolumeControlService == null) {
+ mVolumeControlService = mServiceFactory.getVolumeControlService();
+ }
+ if (mVolumeControlService != null) {
+ mVolumeControlService.setGroupVolume(currentlyActiveGroupId, volume);
}
}
diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java
index 208997a..dca1c1d 100644
--- a/android/app/src/com/android/bluetooth/mcp/McpService.java
+++ b/android/app/src/com/android/bluetooth/mcp/McpService.java
@@ -176,6 +176,11 @@
}
public void onDeviceUnauthorized(BluetoothDevice device) {
+ if (Utils.isPtsTestMode()) {
+ Log.d(TAG, "PTS test: setDeviceAuthorized");
+ setDeviceAuthorized(device, true);
+ return;
+ }
Log.w(TAG, "onDeviceUnauthorized - authorization notification not implemented yet ");
}
@@ -194,9 +199,10 @@
}
public int getDeviceAuthorization(BluetoothDevice device) {
- // TODO: For now just reject authorization for other than LeAudio device already authorized.
- // Consider intent based authorization mechanism for non-LeAudio devices.
- return mDeviceAuthorizations.getOrDefault(device, BluetoothDevice.ACCESS_UNKNOWN);
+ // TODO: For now just reject authorization for other than LeAudio device already authorized
+ // except for PTS. Consider intent based authorization mechanism for non-LeAudio devices.
+ return mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode()
+ ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN);
}
@GuardedBy("mLock")
diff --git a/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java b/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
index ca0534b..164eadc 100644
--- a/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
+++ b/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
@@ -17,10 +17,9 @@
package com.android.bluetooth.tbs;
-import android.bluetooth.BluetoothLeCallControl;
import android.bluetooth.BluetoothLeCall;
+import android.bluetooth.BluetoothLeCallControl;
-import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -36,6 +35,16 @@
private BluetoothLeCallControl mBluetoothLeCallControl;
+ public static final int BEARER_TECHNOLOGY_3G = 0x01;
+ public static final int BEARER_TECHNOLOGY_4G = 0x02;
+ public static final int BEARER_TECHNOLOGY_LTE = 0x03;
+ public static final int BEARER_TECHNOLOGY_WIFI = 0x04;
+ public static final int BEARER_TECHNOLOGY_5G = 0x05;
+ public static final int BEARER_TECHNOLOGY_GSM = 0x06;
+ public static final int BEARER_TECHNOLOGY_CDMA = 0x07;
+ public static final int BEARER_TECHNOLOGY_2G = 0x08;
+ public static final int BEARER_TECHNOLOGY_WCDMA = 0x09;
+
public BluetoothLeCallControlProxy(BluetoothLeCallControl tbs) {
mBluetoothLeCallControl = tbs;
}
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index b8dbbff..d73ae5b 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -52,7 +52,11 @@
private static final String UCI = "GTBS";
private static final String DEFAULT_PROVIDER_NAME = "none";
- private static final int DEFAULT_BEARER_TECHNOLOGY = 0x00;
+ /* Use GSM as default technology value. It is used only
+ * when bearer is not registered. It will be updated on the phone call
+ */
+ private static final int DEFAULT_BEARER_TECHNOLOGY =
+ BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
private static final String UNKNOWN_FRIENDLY_NAME = "unknown";
/** Class representing the pending request sent to the application */
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index 2313ff0..85ebcab 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -417,6 +417,62 @@
}
}
+ /**
+ * Gets the brearer technology.
+ *
+ * @return bearer technology as defined in Bluetooth Assigned Numbers
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int getBearerTechnology() {
+ synchronized (LOCK) {
+ enforceModifyPermission();
+ Log.i(TAG, "getBearerTechnology");
+ // Get the network name from telephony.
+ int dataNetworkType = mTelephonyManager.getDataNetworkType();
+ switch (dataNetworkType) {
+ case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
+
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_2G;
+
+ case TelephonyManager.NETWORK_TYPE_EDGE :
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ case TelephonyManager.NETWORK_TYPE_IDEN:
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_3G;
+
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WCDMA;
+
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_LTE;
+
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_CDMA;
+
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_4G;
+
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WIFI;
+
+ case TelephonyManager.NETWORK_TYPE_NR:
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_5G;
+ }
+
+ return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
+ }
+ }
+
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public String getSubscriberNumber() {
synchronized (LOCK) {
@@ -1241,9 +1297,10 @@
mBluetoothLeCallControl = bluetoothTbs;
if ((mBluetoothLeCallControl) != null && (mTelecomManager != null)) {
- mBluetoothLeCallControl.registerBearer(TAG, new ArrayList<String>(Arrays.asList("tel")),
- BluetoothLeCallControl.CAPABILITY_HOLD_CALL, getNetworkOperator(), 0x01, mExecutor,
- mBluetoothLeCallControlCallback);
+ mBluetoothLeCallControl.registerBearer(TAG,
+ new ArrayList<String>(Arrays.asList("tel")),
+ BluetoothLeCallControl.CAPABILITY_HOLD_CALL, getNetworkOperator(),
+ getBearerTechnology(), mExecutor, mBluetoothLeCallControlCallback);
}
}
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
index 0a50c6d..5a6927c 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
@@ -113,8 +113,8 @@
* @param volume
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void setVolumeGroup(int groupId, int volume) {
- setVolumeGroupNative(groupId, volume);
+ public void setGroupVolume(int groupId, int volume) {
+ setGroupVolumeNative(groupId, volume);
}
/**
@@ -178,6 +178,10 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean setExtAudioOutVolumeOffset(BluetoothDevice device, int externalOutputId,
int offset) {
+ if (Utils.isPtsTestMode()) {
+ setVolumeNative(getByteAddress(device), offset);
+ return true;
+ }
return setExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId, offset);
}
@@ -370,7 +374,7 @@
private native boolean connectVolumeControlNative(byte[] address);
private native boolean disconnectVolumeControlNative(byte[] address);
private native void setVolumeNative(byte[] address, int volume);
- private native void setVolumeGroupNative(int groupId, int volume);
+ private native void setGroupVolumeNative(int groupId, int volume);
private native void muteNative(byte[] address);
private native void muteGroupNative(int groupId);
private native void unmuteNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index e04c2e6..c1b2587 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -47,6 +47,7 @@
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;
@@ -71,6 +72,7 @@
private static VolumeControlService sVolumeControlService;
private AdapterService mAdapterService;
+ private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private BluetoothDevice mPreviousAudioDevice;
@@ -179,6 +181,7 @@
private final Map<BluetoothDevice, VolumeControlStateMachine> mStateMachines = new HashMap<>();
private final Map<BluetoothDevice, VolumeControlOffsetDescriptor> mAudioOffsets =
new HashMap<>();
+ private final Map<Integer, Integer> mGroupVolumeCache = new HashMap<>();
private BroadcastReceiver mBondStateChangedReceiver;
private BroadcastReceiver mConnectionStateChangedReceiver;
@@ -210,10 +213,12 @@
throw new IllegalStateException("start() called twice");
}
- // Get AdapterService, VolumeControlNativeInterface, AudioManager.
+ // Get AdapterService, VolumeControlNativeInterface, DatabaseManager, AudioManager.
// None of them can be null.
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when VolumeControlService starts");
+ mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+ "DatabaseManager cannot be null when VolumeControlService starts");
mVolumeControlNativeInterface = Objects.requireNonNull(
VolumeControlNativeInterface.getInstance(),
"VolumeControlNativeInterface cannot be null when VolumeControlService starts");
@@ -242,6 +247,7 @@
registerReceiver(mConnectionStateChangedReceiver, filter);
mAudioOffsets.clear();
+ mGroupVolumeCache.clear();
mCallbacks = new RemoteCallbackList<IBluetoothVolumeControlCallback>();
// Mark service as started
@@ -296,6 +302,7 @@
mVolumeControlNativeInterface = null;
mAudioOffsets.clear();
+ mGroupVolumeCache.clear();
// Clear AdapterService, VolumeControlNativeInterface
mAudioManager = null;
@@ -529,8 +536,7 @@
if (DBG) {
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
- mAdapterService.getDatabase()
- .setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL,
+ mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL,
connectionPolicy);
if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
@@ -544,8 +550,7 @@
public int getConnectionPolicy(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
"Need BLUETOOTH_PRIVILEGED permission");
- return mAdapterService.getDatabase()
- .getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL);
+ return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL);
}
boolean isVolumeOffsetAvailable(BluetoothDevice device) {
@@ -578,8 +583,17 @@
/**
* {@hide}
*/
- public void setVolumeGroup(int groupId, int volume) {
- mVolumeControlNativeInterface.setVolumeGroup(groupId, volume);
+ public void setGroupVolume(int groupId, int volume) {
+ mGroupVolumeCache.put(groupId, volume);
+ mVolumeControlNativeInterface.setGroupVolume(groupId, volume);
+ }
+
+ /**
+ * {@hide}
+ * @param groupId
+ */
+ public int getGroupVolume(int groupId) {
+ return mGroupVolumeCache.getOrDefault(groupId, -1);
}
/**
@@ -618,6 +632,11 @@
}
// TODO: Handle the other arguments: device, groupId, mute.
+ /* We are interested only in the group volume as any LeAudio device is a part of group */
+ if (device == null) {
+ mGroupVolumeCache.put(groupId, volume);
+ }
+
int streamType = getBluetoothContextualVolumeStream();
mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume),
AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
@@ -1125,7 +1144,7 @@
}
@Override
- public void setVolumeGroup(int groupId, int volume, AttributionSource source,
+ public void setGroupVolume(int groupId, int volume, AttributionSource source,
SynchronousResultReceiver receiver) {
try {
Objects.requireNonNull(source, "source cannot be null");
@@ -1133,7 +1152,7 @@
VolumeControlService service = getService(source);
if (service != null) {
- service.setVolumeGroup(groupId, volume);
+ service.setGroupVolume(groupId, volume);
}
receiver.send(null);
} catch (RuntimeException e) {
@@ -1142,6 +1161,24 @@
}
@Override
+ public void getGroupVolume(int groupId, AttributionSource source,
+ SynchronousResultReceiver receiver) {
+ try {
+ Objects.requireNonNull(source, "source cannot be null");
+ Objects.requireNonNull(receiver, "receiver cannot be null");
+
+ VolumeControlService service = getService(source);
+ if (service != null) {
+ service.getGroupVolume(groupId);
+ }
+ receiver.send(null);
+ } catch (RuntimeException e) {
+ receiver.propagateException(e);
+ }
+ }
+
+
+ @Override
public void mute(BluetoothDevice device, AttributionSource source,
SynchronousResultReceiver receiver) {
try {
@@ -1271,5 +1308,9 @@
ProfileService.println(sb, " Volume offset cnt: " + descriptor.size());
descriptor.dump(sb);
}
+ for (Map.Entry<Integer, Integer> entry : mGroupVolumeCache.entrySet()) {
+ ProfileService.println(sb, " GroupId: " + entry.getKey() + " volume: "
+ + entry.getValue());
+ }
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
index 46fa2fa..ef909ac 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
@@ -130,7 +130,6 @@
*/
@Test
public void testGetSetPolicy() {
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
when(mDatabaseManager
.getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
@@ -138,7 +137,6 @@
BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
mService.getConnectionPolicy(mDevice));
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
when(mDatabaseManager
.getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
@@ -146,7 +144,6 @@
BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
mService.getConnectionPolicy(mDevice));
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
when(mDatabaseManager
.getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -156,6 +153,20 @@
}
/**
+ * Test if getProfileConnectionPolicy works after the service is stopped.
+ */
+ @Test
+ public void testGetPolicyAfterStopped() {
+ mService.stop();
+ when(mDatabaseManager
+ .getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+ Assert.assertEquals("Initial device policy",
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mService.getConnectionPolicy(mDevice));
+ }
+
+ /**
* Test okToConnect method using various test cases
*/
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index 95ed30b..55905a7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -47,6 +47,7 @@
import com.android.bluetooth.btservice.storage.DatabaseManager;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -125,6 +126,10 @@
@After
public void tearDown() throws Exception {
+ if (mBassClientService == null) {
+ return;
+ }
+
TestUtils.stopService(mServiceRule, BassClientService.class);
mBassClientService = BassClientService.getBassClientService();
assertThat(mBassClientService).isNull();
@@ -147,6 +152,21 @@
}
/**
+ * Test if getProfileConnectionPolicy works after the service is stopped.
+ */
+ @Test
+ public void testGetPolicyAfterStopped() {
+ mBassClientService.stop();
+ when(mDatabaseManager
+ .getProfileConnectionPolicy(mCurrentDevice,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+ Assert.assertEquals("Initial device policy",
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mBassClientService.getConnectionPolicy(mCurrentDevice));
+ }
+
+ /**
* Test connecting to a test device.
* - service.connect() should return false
* - bassClientStateMachine.sendMessage(CONNECT) should be called.
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
index bc4221e..f6e8015 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
@@ -23,21 +23,21 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
-import android.sysprop.BluetoothProperties;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.le_audio.LeAudioService;
import org.junit.After;
import org.junit.Assert;
@@ -57,6 +57,7 @@
private BluetoothDevice mHeadsetDevice;
private BluetoothDevice mA2dpHeadsetDevice;
private BluetoothDevice mHearingAidDevice;
+ private BluetoothDevice mLeAudioDevice;
private ActiveDeviceManager mActiveDeviceManager;
private static final int TIMEOUT_MS = 1000;
@@ -65,6 +66,7 @@
@Mock private A2dpService mA2dpService;
@Mock private HeadsetService mHeadsetService;
@Mock private HearingAidService mHearingAidService;
+ @Mock private LeAudioService mLeAudioService;
@Mock private AudioManager mAudioManager;
@Before
@@ -83,9 +85,11 @@
when(mServiceFactory.getA2dpService()).thenReturn(mA2dpService);
when(mServiceFactory.getHeadsetService()).thenReturn(mHeadsetService);
when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService);
+ when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService);
when(mA2dpService.setActiveDevice(any())).thenReturn(true);
when(mHeadsetService.setActiveDevice(any())).thenReturn(true);
when(mHearingAidService.setActiveDevice(any())).thenReturn(true);
+ when(mLeAudioService.setActiveDevice(any())).thenReturn(true);
mActiveDeviceManager = new ActiveDeviceManager(mAdapterService, mServiceFactory);
mActiveDeviceManager.start();
@@ -96,6 +100,7 @@
mHeadsetDevice = TestUtils.getTestDevice(mAdapter, 1);
mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2);
mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3);
+ mLeAudioDevice = TestUtils.getTestDevice(mAdapter, 4);
}
@After
@@ -212,6 +217,7 @@
Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
}
+
/**
* A combo (A2DP + Headset) device is connected. Then a Hearing Aid is connected.
*/
@@ -288,6 +294,81 @@
}
/**
+ * A combo (A2DP + Headset) device is connected. Then an LE Audio is connected.
+ */
+ @Test
+ public void leAudioActive_clearA2dpAndHeadsetActive() {
+ Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+ LeAudioService.isEnabled());
+
+ a2dpConnected(mA2dpHeadsetDevice);
+ headsetConnected(mA2dpHeadsetDevice);
+ verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);
+ verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);
+
+ leAudioActiveDeviceChanged(mLeAudioDevice);
+ verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull());
+ verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull());
+ }
+
+ /**
+ * An LE Audio is connected. Then a combo (A2DP + Headset) device is connected.
+ */
+ @Test
+ public void leAudioActive_dontSetA2dpAndHeadsetActive() {
+ Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+ LeAudioService.isEnabled());
+
+ leAudioActiveDeviceChanged(mLeAudioDevice);
+ a2dpConnected(mA2dpHeadsetDevice);
+ headsetConnected(mA2dpHeadsetDevice);
+
+ TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+ verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice);
+ verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice);
+ }
+
+ /**
+ * An LE Audio is connected. Then an A2DP active device is explicitly set.
+ */
+ @Test
+ public void leAudioActive_setA2dpActiveExplicitly() {
+ Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+ LeAudioService.isEnabled());
+
+ leAudioActiveDeviceChanged(mLeAudioDevice);
+ a2dpConnected(mA2dpHeadsetDevice);
+ a2dpActiveDeviceChanged(mA2dpHeadsetDevice);
+
+ TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+ verify(mLeAudioService).setActiveDevice(isNull());
+ // Don't call mA2dpService.setActiveDevice()
+ verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice);
+ Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice());
+ Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
+ }
+
+ /**
+ * An LE Audio is connected. Then a Headset active device is explicitly set.
+ */
+ @Test
+ public void leAudioActive_setHeadsetActiveExplicitly() {
+ Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+ LeAudioService.isEnabled());
+
+ leAudioActiveDeviceChanged(mLeAudioDevice);
+ headsetConnected(mA2dpHeadsetDevice);
+ headsetActiveDeviceChanged(mA2dpHeadsetDevice);
+
+ TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+ verify(mLeAudioService).setActiveDevice(isNull());
+ // Don't call mLeAudioService.setActiveDevice()
+ verify(mLeAudioService, never()).setActiveDevice(mA2dpHeadsetDevice);
+ Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
+ Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
+ }
+
+ /**
* A wired audio device is connected. Then all active devices are set to null.
*/
@Test
@@ -373,4 +454,14 @@
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
}
+
+ /**
+ * Helper to indicate LE Audio active device changed for a device.
+ */
+ private void leAudioActiveDeviceChanged(BluetoothDevice device) {
+ Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ }
+
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
index a49db33..caac745 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
@@ -132,6 +132,14 @@
@After
public void tearDown() throws Exception {
+ if (mService == null) {
+ return;
+ }
+
+ if (Looper.myLooper() == null) {
+ return;
+ }
+
stopService();
mTargetContext.unregisterReceiver(mCsipSetCoordinatorIntentReceiver);
TestUtils.clearAdapterService(mAdapterService);
@@ -205,6 +213,20 @@
}
/**
+ * Test if getProfileConnectionPolicy works after the service is stopped.
+ */
+ @Test
+ public void testGetPolicyAfterStopped() {
+ mService.stop();
+ when(mDatabaseManager
+ .getProfileConnectionPolicy(mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+ Assert.assertEquals("Initial device policy",
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mService.getConnectionPolicy(mTestDevice));
+ }
+
+ /**
* Test okToConnect method using various test cases
*/
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
index 0d83662..77eb6e9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
@@ -170,6 +170,10 @@
@After
public void tearDown() throws Exception {
+ if (mService == null) {
+ return;
+ }
+
mService.mCallbacks.unregister(mCallback);
stopService();
@@ -247,6 +251,20 @@
}
/**
+ * Test if getProfileConnectionPolicy works after the service is stopped.
+ */
+ @Test
+ public void testGetPolicyAfterStopped() {
+ mService.stop();
+ when(mDatabaseManager
+ .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+ Assert.assertEquals("Initial device policy",
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mService.getConnectionPolicy(mDevice));
+ }
+
+ /**
* Test okToConnect method using various test cases
*/
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
index a33824a..9568d4b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
@@ -193,6 +193,10 @@
@After
public void tearDown() throws Exception {
+ if (mService == null) {
+ return;
+ }
+
stopService();
mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
TestUtils.clearAdapterService(mAdapterService);
@@ -239,7 +243,7 @@
BluetoothLeAudioContentMetadata meta) {
mService.createBroadcast(meta, code);
- verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(1),
+ verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()),
eq(code));
// Check if broadcast is started automatically when created
@@ -321,8 +325,7 @@
BluetoothLeAudioContentMetadata meta = meta_builder.build();
mService.createBroadcast(meta, code);
- verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(1),
- eq(code));
+ verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(code));
LeAudioStackEvent create_event =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index ae41ce0..26cfebe 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -47,6 +47,7 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.BluetoothProfileConnectionInfo;
+import android.os.Parcel;
import android.os.ParcelUuid;
import androidx.test.InstrumentationRegistry;
@@ -58,6 +59,7 @@
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.bluetooth.vc.VolumeControlService;
import org.junit.After;
import org.junit.Assume;
@@ -65,6 +67,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -105,6 +108,7 @@
@Mock private DatabaseManager mDatabaseManager;
@Mock private LeAudioNativeInterface mNativeInterface;
@Mock private AudioManager mAudioManager;
+ @Mock private VolumeControlService mVolumeControlService;
@Mock private LeAudioTmapGattServer mTmapGattServer;
@Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
@@ -179,6 +183,7 @@
startService();
mService.mLeAudioNativeInterface = mNativeInterface;
mService.mAudioManager = mAudioManager;
+ mService.mVolumeControlService = mVolumeControlService;
LeAudioStackEvent stackEvent =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
@@ -216,6 +221,10 @@
@After
public void tearDown() throws Exception {
+ if ((mService == null) || (mAdapter == null)) {
+ return;
+ }
+
mBondedDevices.clear();
mGroupIntentQueue.clear();
stopService();
@@ -1381,4 +1390,53 @@
verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), eq(leadDevice),
any(BluetoothProfileConnectionInfo.class));
}
+
+ /**
+ * Test volume caching for the group
+ */
+ @Test
+ public void testVolumeCache() {
+ int groupId = 1;
+ int volume = 100;
+ int availableContexts = 4;
+
+ doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
+
+ ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo =
+ ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class);
+
+ //Add location support.
+ injectAudioConfChanged(groupId, availableContexts);
+
+ doReturn(-1).when(mVolumeControlService).getGroupVolume(groupId);
+ //Set group and device as active.
+ injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(null),
+ profileInfo.capture());
+ assertThat(profileInfo.getValue().getVolume()).isEqualTo(-1);
+
+ mService.setVolume(volume);
+ verify(mVolumeControlService, times(1)).setGroupVolume(groupId, volume);
+
+ // Set group to inactive.
+ injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
+
+ verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), any(),
+ any(BluetoothProfileConnectionInfo.class));
+
+ doReturn(100).when(mVolumeControlService).getGroupVolume(groupId);
+
+ //Set back to active and check if last volume is restored.
+ injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+ verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), eq(null),
+ profileInfo.capture());
+
+ assertThat(profileInfo.getValue().getVolume()).isEqualTo(volume);
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
index 00b4d92..3374607 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
@@ -81,6 +81,10 @@
@After
public void tearDown() throws Exception {
+ if (mMcpService == null) {
+ return;
+ }
+
doReturn(false).when(mAdapterService).isStartedProfile(anyString());
TestUtils.stopService(mServiceRule, McpService.class);
mMcpService = McpService.getMcpService();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
index 002c322..3ec632a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
@@ -146,6 +146,10 @@
public void tearDown() throws Exception {
TestUtils.clearAdapterService(mAdapterService);
+ if (mMediaControlProfile == null) {
+ return;
+ }
+
mMediaControlProfile.cleanup();
mMediaControlProfile = null;
reset(mMockMediaPlayerList);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
index f127ede..2fbe191 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
@@ -118,6 +118,10 @@
@After
public void tearDown() throws Exception {
+ if (mService == null) {
+ return;
+ }
+
stopService();
mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver);
mDeviceQueueMap.clear();
@@ -232,6 +236,20 @@
}
/**
+ * Test if getProfileConnectionPolicy works after the service is stopped.
+ */
+ @Test
+ public void testGetPolicyAfterStopped() {
+ mService.stop();
+ when(mDatabaseManager
+ .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
+ .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+ Assert.assertEquals("Initial device policy",
+ BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mService.getConnectionPolicy(mDevice));
+ }
+
+ /**
* Test okToConnect method using various test cases
*/
@Test
@@ -496,6 +514,33 @@
mService.messageFromNative(stackEvent);
}
+ /**
+ * Test Volume Control cache.
+ */
+ @Test
+ public void testVolumeCache() {
+ int groupId = 1;
+ int volume = 6;
+
+ Assert.assertEquals(-1, mService.getGroupVolume(groupId));
+ mService.setGroupVolume(groupId, volume);
+ Assert.assertEquals(volume, mService.getGroupVolume(groupId));
+
+ volume = 10;
+
+ // Send autonomus volume change.
+ VolumeControlStackEvent stackEvent = new VolumeControlStackEvent(
+ VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
+ stackEvent.device = null;
+ stackEvent.valueInt1 = groupId;
+ stackEvent.valueInt2 = volume;
+ stackEvent.valueBool1 = false;
+ stackEvent.valueBool2 = true; /* autonomus */
+ mService.messageFromNative(stackEvent);
+
+ Assert.assertEquals(volume, mService.getGroupVolume(groupId));
+ }
+
private void connectDevice(BluetoothDevice device) {
VolumeControlStackEvent connCompletedEvent;
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
index 4bd1dee..e4031a1 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
@@ -88,7 +88,7 @@
.equals(groupId))
.collect(Collectors.toList());
for (LeAudioDeviceStateWrapper dev : valid_devices) {
- dev.leAudioData.groupStatusMutable.setValue(
+ dev.leAudioData.groupStatusMutable.postValue(
new Pair<>(groupId, new Pair<>(groupStatus, 0)));
}
}
@@ -112,8 +112,8 @@
LeAudioDeviceStateWrapper valid_device = valid_device_opt.get();
LeAudioDeviceStateWrapper.LeAudioData svc_data = valid_device.leAudioData;
- svc_data.nodeStatusMutable.setValue(new Pair<>(groupId, GROUP_NODE_ADDED));
- svc_data.groupStatusMutable.setValue(new Pair<>(groupId, new Pair<>(-1, -1)));
+ svc_data.nodeStatusMutable.postValue(new Pair<>(groupId, GROUP_NODE_ADDED));
+ svc_data.groupStatusMutable.postValue(new Pair<>(groupId, new Pair<>(-1, -1)));
}
@Override
public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
@@ -141,8 +141,8 @@
LeAudioDeviceStateWrapper valid_device = valid_device_opt.get();
LeAudioDeviceStateWrapper.LeAudioData svc_data = valid_device.leAudioData;
- svc_data.nodeStatusMutable.setValue(new Pair<>(groupId, GROUP_NODE_REMOVED));
- svc_data.groupStatusMutable.setValue(new Pair<>(groupId, new Pair<>(-1, -1)));
+ svc_data.nodeStatusMutable.postValue(new Pair<>(groupId, GROUP_NODE_REMOVED));
+ svc_data.groupStatusMutable.postValue(new Pair<>(groupId, new Pair<>(-1, -1)));
}
};
@@ -155,9 +155,9 @@
int toState =
intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (toState == BluetoothAdapter.STATE_ON) {
- enabledBluetoothMutable.setValue(true);
+ enabledBluetoothMutable.postValue(true);
} else if (toState == BluetoothAdapter.STATE_OFF) {
- enabledBluetoothMutable.setValue(false);
+ enabledBluetoothMutable.postValue(false);
}
}
}
@@ -192,10 +192,10 @@
.postValue(toState == BluetoothLeAudio.STATE_CONNECTED);
group_id = bluetoothLeAudio.getGroupId(device);
- svc_data.nodeStatusMutable.setValue(
+ svc_data.nodeStatusMutable.postValue(
new Pair<>(group_id, GROUP_NODE_ADDED));
svc_data.groupStatusMutable
- .setValue(new Pair<>(group_id, new Pair<>(-1, -1)));
+ .postValue(new Pair<>(group_id, new Pair<>(-1, -1)));
break;
}
}
@@ -669,18 +669,30 @@
break;
case BluetoothProfile.HAP_CLIENT:
bluetoothHapClient = (BluetoothHapClient) bluetoothProfile;
- bluetoothHapClient.registerCallback(mExecutor, hapCallback);
+ try {
+ bluetoothHapClient.registerCallback(mExecutor, hapCallback);
+ } catch (IllegalArgumentException e) {
+ Log.e("HAP", "Application callback already registered.");
+ }
break;
case BluetoothProfile.LE_AUDIO_BROADCAST:
mBluetoothLeBroadcast = (BluetoothLeBroadcast) bluetoothProfile;
- mBluetoothLeBroadcast.registerCallback(mExecutor, mBroadcasterCallback);
+ try {
+ mBluetoothLeBroadcast.registerCallback(mExecutor, mBroadcasterCallback);
+ } catch (IllegalArgumentException e) {
+ Log.e("Broadcast", "Application callback already registered.");
+ }
break;
case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
Log.d("BluetoothProxy", "LE_AUDIO_BROADCAST_ASSISTANT Service connected");
mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant)
bluetoothProfile;
- mBluetoothLeBroadcastAssistant.registerCallback(mExecutor,
+ try {
+ mBluetoothLeBroadcastAssistant.registerCallback(mExecutor,
mBroadcastAssistantCallback);
+ } catch (IllegalArgumentException e) {
+ Log.e("BASS", "Application callback already registered.");
+ }
break;
}
queryLeAudioDevices();
@@ -973,7 +985,7 @@
try {
Method groupAddNodeMethod = BluetoothLeAudio.class.getDeclaredMethod("groupAddNode",
- Integer.class, BluetoothDevice.class);
+ int.class, BluetoothDevice.class);
groupAddNodeMethod.setAccessible(true);
groupAddNodeMethod.invoke(bluetoothLeAudio, group_id, device);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -986,7 +998,7 @@
try {
Method groupRemoveNodeMethod = BluetoothLeAudio.class
- .getDeclaredMethod("groupRemoveNode", Integer.class, BluetoothDevice.class);
+ .getDeclaredMethod("groupRemoveNode", int.class, BluetoothDevice.class);
groupRemoveNodeMethod.setAccessible(true);
groupRemoveNodeMethod.invoke(bluetoothLeAudio, group_id, device);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -999,7 +1011,10 @@
Log.d("Lock", "lock: " + lock);
if (lock) {
- if (mGroupLocks.containsKey(group_id)) return;
+ if (mGroupLocks.containsKey(group_id)) {
+ Log.e("Lock", "group" + group_id + " is already in locking process or locked: " + lock);
+ return;
+ }
UUID uuid = bluetoothCsis.lockGroup(group_id, mExecutor,
(int group, int op_status, boolean is_locked) -> {
@@ -1121,8 +1136,10 @@
}
public void setVolume(BluetoothDevice device, int volume) {
- if (bluetoothLeAudio != null) {
+ if (bluetoothLeAudio != null && !bluetoothLeAudio.getConnectedDevices().isEmpty()) {
bluetoothLeAudio.setVolume(volume);
+ } else if (bluetoothVolumeControl != null) {
+ bluetoothVolumeControl.setVolumeOffset(device, volume);
}
}
@@ -1155,7 +1172,7 @@
// Use hidden API
try {
Method getPresetInfoMethod = BluetoothHapClient.class.getDeclaredMethod("getPresetInfo",
- BluetoothDevice.class, Integer.class);
+ BluetoothDevice.class, int.class);
getPresetInfoMethod.setAccessible(true);
new_preset = (BluetoothHapPresetInfo) getPresetInfoMethod.invoke(bluetoothHapClient,
@@ -1222,7 +1239,7 @@
switchToPreviousPresetMethod.invoke(bluetoothHapClient, device);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- // Do nothing
+ return false;
}
return true;
}
@@ -1239,7 +1256,7 @@
switchToNextPresetMethod.invoke(bluetoothHapClient, device);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- // Do nothing
+ return false;
}
return true;
}
@@ -1251,12 +1268,12 @@
// Use hidden API
try {
Method switchToPreviousPresetForGroupMethod = BluetoothHapClient.class
- .getDeclaredMethod("switchToPreviousPresetForGroup", Integer.class);
+ .getDeclaredMethod("switchToPreviousPresetForGroup", int.class);
switchToPreviousPresetForGroupMethod.setAccessible(true);
switchToPreviousPresetForGroupMethod.invoke(bluetoothHapClient, group_id);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- // Do nothing
+ return false;
}
return true;
}
@@ -1268,12 +1285,12 @@
// Use hidden API
try {
Method switchToNextPresetForGroupMethod = BluetoothHapClient.class
- .getDeclaredMethod("switchToNextPresetForGroup", Integer.class);
+ .getDeclaredMethod("switchToNextPresetForGroup", int.class);
switchToNextPresetForGroupMethod.setAccessible(true);
switchToNextPresetForGroupMethod.invoke(bluetoothHapClient, group_id);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- // Do nothing
+ return false;
}
return true;
}
@@ -1335,16 +1352,10 @@
return mBroadcastStatusMutableLive;
}
- public boolean startBroadcast(String programInfo, byte[] code) {
+ public boolean startBroadcast(BluetoothLeAudioContentMetadata meta, byte[] code) {
if (mBluetoothLeBroadcast == null)
return false;
-
- BluetoothLeAudioContentMetadata.Builder contentBuilder =
- new BluetoothLeAudioContentMetadata.Builder();
- if (!programInfo.isEmpty()) {
- contentBuilder.setProgramInfo(programInfo);
- }
- mBluetoothLeBroadcast.startBroadcast(contentBuilder.build(), code);
+ mBluetoothLeBroadcast.startBroadcast(meta, code);
return true;
}
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
index a53bd46..14f77dd 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
@@ -66,14 +66,17 @@
if (isPlaying) {
holder.background
.setCardBackgroundColor(ColorStateList.valueOf(Color.parseColor("#92b141")));
- holder.mTextViewBroadcastId.setText("ID: " + broadcastId + " ▶️");
+ holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+ + "(" + String.format("0x%x", broadcastId) + ") ▶️");
} else {
holder.background.setCardBackgroundColor(ColorStateList.valueOf(Color.WHITE));
- holder.mTextViewBroadcastId.setText("ID: " + broadcastId + " ⏸");
+ holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+ + "(" + String.format("0x%x", broadcastId) + ") ⏸");
}
} else {
holder.background.setCardBackgroundColor(ColorStateList.valueOf(Color.WHITE));
- holder.mTextViewBroadcastId.setText("ID: " + broadcastId);
+ holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+ + "(" + String.format("0x%x", broadcastId) + ")");
}
// TODO: Add additional informations to the card
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
index d2eb1d1..a3a2a61 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
@@ -17,6 +17,7 @@
package com.android.bluetooth.leaudio;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Intent;
import android.os.Bundle;
@@ -24,6 +25,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
+import android.widget.NumberPicker;
import android.widget.TextView;
import android.widget.Toast;
@@ -37,6 +39,8 @@
import com.android.bluetooth.leaudio.R;
+import java.io.ByteArrayOutputStream;
+
public class BroadcasterActivity extends AppCompatActivity {
private BroadcasterViewModel mViewModel;
@@ -55,12 +59,41 @@
View alertView = inflater.inflate(R.layout.broadcaster_add_broadcast_dialog, null);
final EditText code_input_text = alertView.findViewById(R.id.broadcast_code_input);
- EditText metadata_input_text = alertView.findViewById(R.id.broadcast_meta_input);
+ final EditText program_info = alertView.findViewById(R.id.broadcast_program_info_input);
+ final NumberPicker contextPicker = alertView.findViewById(R.id.context_picker);
+
+ // Add context type selector
+ contextPicker.setMinValue(1);
+ contextPicker.setMaxValue(
+ alertView.getResources().getStringArray(R.array.content_types).length - 1);
+ contextPicker.setDisplayedValues(
+ alertView.getResources().getStringArray(R.array.content_types));
alert.setView(alertView).setNegativeButton("Cancel", (dialog, which) -> {
// Do nothing
}).setPositiveButton("Start", (dialog, which) -> {
- if (mViewModel.startBroadcast(metadata_input_text.getText().toString(),
+
+ final BluetoothLeAudioContentMetadata.Builder contentBuilder =
+ new BluetoothLeAudioContentMetadata.Builder();
+ final String programInfo = program_info.getText().toString();
+ if (!programInfo.isEmpty()) {
+ contentBuilder.setProgramInfo(programInfo);
+ }
+
+ // Extract raw metadata
+ byte[] metaBuffer = contentBuilder.build().getRawMetadata();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write(metaBuffer, 0 , metaBuffer.length);
+
+ // Extend raw metadata with context type
+ final int contextValue = 1 << (contextPicker.getValue() - 1);
+ stream.write((byte)0x03); // Length
+ stream.write((byte)0x02); // Type for the Streaming Audio Context
+ stream.write((byte)(contextValue & 0x00FF)); // Value LSB
+ stream.write((byte)((contextValue & 0xFF00) >> 8)); // Value MSB
+
+ if (mViewModel.startBroadcast(
+ BluetoothLeAudioContentMetadata.fromRawBytes(stream.toByteArray()),
code_input_text.getText() == null
|| code_input_text.getText().length() == 0 ? null
: code_input_text.getText().toString().getBytes()))
@@ -135,7 +168,7 @@
LayoutInflater inflater = getLayoutInflater();
View alertView = inflater.inflate(R.layout.broadcaster_add_broadcast_dialog, null);
- EditText metadata_input_text = alertView.findViewById(R.id.broadcast_meta_input);
+ EditText program_info_input_text = alertView.findViewById(R.id.broadcast_program_info_input);
// The Code cannot be changed, so just hide it
final EditText code_input_text = alertView.findViewById(R.id.broadcast_code_input);
@@ -146,7 +179,7 @@
// Do nothing
}).setPositiveButton("Update", (modifyDialog, modifyWhich) -> {
if (mViewModel.updateBroadcast(broadcastId,
- metadata_input_text.getText().toString()))
+ program_info_input_text.getText().toString()))
Toast.makeText(BroadcasterActivity.this, "Broadcast was updated.",
Toast.LENGTH_SHORT).show();
});
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
index bf22c8e..7469bac 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
@@ -19,6 +19,7 @@
import android.app.Application;
import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
@@ -39,8 +40,8 @@
mBluetooth.initProfiles();
}
- public boolean startBroadcast(String programInfo, byte[] code) {
- return mBluetooth.startBroadcast(programInfo, code);
+ public boolean startBroadcast(BluetoothLeAudioContentMetadata meta, byte[] code) {
+ return mBluetooth.startBroadcast(meta, code);
}
public boolean stopBroadcast(int broadcastId) {
diff --git a/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml b/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
index 5bc50b5..2b320e7 100644
--- a/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
+++ b/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
@@ -42,7 +42,7 @@
android:text="Program Info:" />
<EditText
- android:id="@+id/broadcast_meta_input"
+ android:id="@+id/broadcast_program_info_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
@@ -51,4 +51,25 @@
android:inputType="text|textAutoCorrect" />
</LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/textView26"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Context type:" />
+
+ <NumberPicker
+ android:id="@+id/context_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:orientation="horizontal"
+ max="11"
+ min="1" />
+ </LinearLayout>
+
</LinearLayout>
\ No newline at end of file
diff --git a/android/leaudio/app/src/main/res/values/donottranslate_strings.xml b/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
index 0b7a060..9665e48 100644
--- a/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
+++ b/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
@@ -32,14 +32,15 @@
<item>Unspecified</item>
<item>Conversational</item>
<item>Media</item>
+ <item>Game</item>
<item>Instructional</item>
- <item>AttentionSeeking</item>
- <item>ImmediateAlert</item>
- <item>ManMachine</item>
- <item>EmergencyAlert</item>
- <item>Ringtone</item>
- <item>TV</item>
+ <item>Voice Assistant</item>
<item>Live</item>
+ <item>Sound Effects</item>
+ <item>Notifications</item>
+ <item>Ringtone</item>
+ <item>Alerts</item>
+ <item>Emergency Alarm</item>
</string-array>
<string-array name="audio_locations">
<item>Front Left</item>
diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
index dda3f04..d3aec71 100644
--- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
@@ -95,6 +95,7 @@
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -218,6 +219,7 @@
private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
private boolean mBinding;
private boolean mUnbinding;
+ private List<Integer> mSupportedProfileList = new ArrayList<>();
private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
@@ -872,6 +874,25 @@
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
}
+ @GuardedBy("mBluetoothLock")
+ private List<Integer> synchronousGetSupportedProfiles(AttributionSource attributionSource)
+ throws RemoteException, TimeoutException {
+ final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
+ if (mBluetooth == null) return supportedProfiles;
+ final SynchronousResultReceiver<Long> recv = SynchronousResultReceiver.get();
+ mBluetooth.getSupportedProfiles(attributionSource, recv);
+ final long supportedProfilesBitMask =
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) 0);
+
+ for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
+ if ((supportedProfilesBitMask & (1 << i)) != 0) {
+ supportedProfiles.add(i);
+ }
+ }
+
+ return supportedProfiles;
+ }
+
/**
* Sends the current foreground user id to the Bluetooth process. This user id is used to
* determine if Binder calls are coming from the active user.
@@ -1449,10 +1470,10 @@
ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
Intent intent;
if (bluetoothProfile == BluetoothProfile.HEADSET
- && BluetoothProperties.isProfileHfpAgEnabled().orElse(false)) {
+ && mSupportedProfileList.contains(BluetoothProfile.HEADSET)) {
intent = new Intent(IBluetoothHeadset.class.getName());
} else if (bluetoothProfile == BluetoothProfile.LE_CALL_CONTROL
- && BluetoothProperties.isProfileCcpServerEnabled().orElse(false)) {
+ && mSupportedProfileList.contains(BluetoothProfile.LE_CALL_CONTROL)) {
intent = new Intent(IBluetoothLeCallControl.class.getName());
} else {
return false;
@@ -2241,6 +2262,14 @@
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
+ // Get the supported profiles list
+ try {
+ mSupportedProfileList = synchronousGetSupportedProfiles(
+ mContext.getAttributionSource());
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, "Unable to get the supported profiles list", e);
+ }
+
//Do enable request
try {
if (!synchronousEnable(mQuietEnable, mContext.getAttributionSource())) {
@@ -2319,6 +2348,7 @@
break;
}
mBluetooth = null;
+ mSupportedProfileList.clear();
} else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt = null;
break;
@@ -2905,16 +2935,21 @@
String launcherActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity";
- PackageManager packageManager = mContext.createContextAsUser(userHandle, 0)
+ PackageManager systemPackageManager = mContext.getPackageManager();
+ PackageManager userPackageManager = mContext.createContextAsUser(userHandle, 0)
.getPackageManager();
- var allPackages = packageManager.getPackagesForUid(Process.BLUETOOTH_UID);
+ var allPackages = systemPackageManager.getPackagesForUid(Process.BLUETOOTH_UID);
for (String candidatePackage : allPackages) {
+ Log.v(TAG, "Searching package " + candidatePackage);
PackageInfo packageInfo;
try {
- // note: we need the package manager for the SYSTEM user, not our userHandle
- packageInfo = mContext.getPackageManager().getPackageInfo(
+ packageInfo = systemPackageManager.getPackageInfo(
candidatePackage,
- PackageManager.PackageInfoFlags.of(PackageManager.GET_ACTIVITIES));
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.GET_ACTIVITIES
+ | PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS));
} catch (PackageManager.NameNotFoundException e) {
// ignore, try next package
Log.e(TAG, "Could not find package " + candidatePackage);
@@ -2927,11 +2962,12 @@
continue;
}
for (var activity : packageInfo.activities) {
+ Log.v(TAG, "Checking activity " + activity.name);
if (launcherActivity.equals(activity.name)) {
final ComponentName oppLauncherComponent = new ComponentName(
candidatePackage, launcherActivity
);
- packageManager.setComponentEnabledSetting(
+ userPackageManager.setComponentEnabledSetting(
oppLauncherComponent, newState, PackageManager.DONT_KILL_APP
);
return;
diff --git a/system/audio_hal_interface/aidl/client_interface_aidl.cc b/system/audio_hal_interface/aidl/client_interface_aidl.cc
index 814c6c7..6c7a687 100644
--- a/system/audio_hal_interface/aidl/client_interface_aidl.cc
+++ b/system/audio_hal_interface/aidl/client_interface_aidl.cc
@@ -46,6 +46,7 @@
BluetoothAudioClientInterface::BluetoothAudioClientInterface(
IBluetoothTransportInstance* instance)
: provider_(nullptr),
+ provider_factory_(nullptr),
session_started_(false),
data_mq_(nullptr),
transport_(instance) {
@@ -134,8 +135,12 @@
}
CHECK(provider_ != nullptr);
- AIBinder_linkToDeath(provider_factory->asBinder().get(),
- death_recipient_.get(), this);
+ binder_status_t binder_status = AIBinder_linkToDeath(
+ provider_factory->asBinder().get(), death_recipient_.get(), this);
+ if (binder_status != STATUS_OK) {
+ LOG(ERROR) << "Failed to linkToDeath " << static_cast<int>(binder_status);
+ }
+ provider_factory_ = std::move(provider_factory);
LOG(INFO) << "IBluetoothAudioProvidersFactory::openProvider() returned "
<< provider_.get()
@@ -150,8 +155,8 @@
}
BluetoothAudioSinkClientInterface::~BluetoothAudioSinkClientInterface() {
- if (provider_ != nullptr) {
- AIBinder_unlinkToDeath(provider_->asBinder().get(), death_recipient_.get(),
+ if (provider_factory_ != nullptr) {
+ AIBinder_unlinkToDeath(provider_factory_->asBinder().get(), death_recipient_.get(),
nullptr);
}
}
@@ -164,8 +169,8 @@
}
BluetoothAudioSourceClientInterface::~BluetoothAudioSourceClientInterface() {
- if (provider_ != nullptr) {
- AIBinder_unlinkToDeath(provider_->asBinder().get(), death_recipient_.get(),
+ if (provider_factory_ != nullptr) {
+ AIBinder_unlinkToDeath(provider_factory_->asBinder().get(), death_recipient_.get(),
nullptr);
}
}
diff --git a/system/audio_hal_interface/aidl/client_interface_aidl.h b/system/audio_hal_interface/aidl/client_interface_aidl.h
index 87dd450..17abefe 100644
--- a/system/audio_hal_interface/aidl/client_interface_aidl.h
+++ b/system/audio_hal_interface/aidl/client_interface_aidl.h
@@ -107,6 +107,8 @@
std::shared_ptr<IBluetoothAudioProvider> provider_;
+ std::shared_ptr<IBluetoothAudioProviderFactory> provider_factory_;
+
bool session_started_;
std::unique_ptr<DataMQ> data_mq_;
diff --git a/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl b/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
index eb6ddd1..d5f157c 100644
--- a/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
+++ b/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
@@ -50,7 +50,9 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void setVolumeOffset(in BluetoothDevice device, int volumeOffset, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
- void setVolumeGroup(int group_id, int volume, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ void setGroupVolume(int group_id, int volume, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+ void getGroupVolume(int group_id, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void mute(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
diff --git a/system/blueberry/tests/gd/cert/os_utils.py b/system/blueberry/tests/gd/cert/os_utils.py
index 0749145..31e4a0e 100644
--- a/system/blueberry/tests/gd/cert/os_utils.py
+++ b/system/blueberry/tests/gd/cert/os_utils.py
@@ -77,7 +77,6 @@
logging.warning("Freeing port %d used by %s" % (conn.laddr.port, str(conn)))
if not conn.pid:
logging.error("Failed to kill process occupying port %d due to lack of pid" % conn.laddr.port)
- success = False
continue
logging.warning("Killing pid %d that is using port port %d" % (conn.pid, conn.laddr.port))
if conn.pid in killed_pids:
diff --git a/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py
new file mode 100644
index 0000000..4e0f2bc
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+import binascii
+import io
+import logging
+import os
+import queue
+
+from blueberry.tests.gd.cert.context import get_current_context
+
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types
+
+
+class LeAdvertisingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass):
+
+ def setup_class(self):
+ super().setup_class()
+
+ def setup_test(self):
+ super().setup_test()
+
+ def teardown_test(self):
+ super().teardown_test()
+
+ def test_advertise_name(self):
+ rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+ self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+ self.dut_scanner_.stop_scanning()
+ self.cert_advertiser_.stop_advertising()
+
+ def test_advertise_name_stress(self):
+ for i in range(0, 10):
+ self.test_advertise_name()
+
+ def test_advertise_name_twice_no_stop(self):
+ rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+ self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+ self.dut_scanner_.stop_scanning()
+ rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+ self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+ self.dut_scanner_.stop_scanning()
+ self.cert_advertiser_.stop_advertising()
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py
new file mode 100644
index 0000000..b6d5cfe
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+import binascii
+import logging
+import queue
+
+from blueberry.facade import common_pb2 as common
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.closable import safeClose
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.ble_lib import generate_ble_advertise_objects
+from blueberry.tests.gd_sl4a.lib.bt_constants import adv_succ
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_advertise_settings_modes
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+
+
+class LeAdvertiser(Closable):
+
+ is_advertising = False
+ device = None
+ default_timeout = 10 # seconds
+ advertise_callback = None
+ advertise_data = None
+ advertise_settings = None
+
+ def __init__(self, device):
+ self.device = device
+
+ def __wait_for_event(self, expected_event_name):
+ try:
+ event_info = self.device.ed.pop_event(expected_event_name, self.default_timeout)
+ logging.info(event_info)
+ except queue.Empty as error:
+ logging.error("Failed to find event: %s", expected_event_name)
+ return False
+ return True
+
+ def advertise_rpa_public_extended_pdu(self, name="SL4A Device"):
+ if self.is_advertising:
+ logging.info("Already advertising!")
+ return
+ self.is_advertising = True
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.device.sl4a.bleSetAdvertiseSettingsIsConnectable(True)
+ self.device.sl4a.bleSetAdvertiseDataIncludeDeviceName(True)
+ self.device.sl4a.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency'])
+ self.device.sl4a.bleSetAdvertiseSettingsOwnAddressType(common.RANDOM_DEVICE_ADDRESS)
+ self.advertise_callback, self.advertise_data, self.advertise_settings = generate_ble_advertise_objects(
+ self.device.sl4a)
+ self.device.sl4a.bleStartBleAdvertising(self.advertise_callback, self.advertise_data, self.advertise_settings)
+
+ # Wait for SL4A cert to start advertising
+ assertThat(self.__wait_for_event(adv_succ.format(self.advertise_callback))).isTrue()
+ logging.info("Advertising started")
+
+ def get_local_advertising_name(self):
+ return self.device.sl4a.bluetoothGetLocalName()
+
+ def stop_advertising(self):
+ if self.is_advertising:
+ logging.info("Stopping advertisement")
+ self.device.sl4a.bleStopBleAdvertising(self.advertise_callback)
+ self.is_advertising = False
+
+ def close(self):
+ self.stop_advertising()
+ self.device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py
new file mode 100644
index 0000000..c7874e6
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+import logging
+import queue
+
+from blueberry.facade import common_pb2 as common
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.closable import safeClose
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.ble_lib import generate_ble_scan_objects
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_scan_settings_modes
+from blueberry.tests.gd_sl4a.lib.bt_constants import scan_result
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+
+
+class LeScanner(Closable):
+
+ is_scanning = False
+ device = None
+ filter_list = None
+ scan_settings = None
+ scan_callback = None
+
+ def __init__(self, device):
+ self.device = device
+
+ def __wait_for_scan_result_event(self, expected_event_name, timeout=60):
+ try:
+ event_info = self.device.ed.pop_event(expected_event_name, timeout)
+ except queue.Empty as error:
+ logging.error("Could not find scan result event: %s", expected_event_name)
+ return None
+ return event_info['data']['Result']['deviceInfo']['address']
+
+ def scan_for_address_expect_none(self, address, addr_type):
+ if self.is_scanning:
+ print("Already scanning!")
+ return
+ self.is_scanning = True
+ logging.info("Start scanning for identity address {} or type {}".format(address, addr_type))
+ self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+ expected_event_name = scan_result.format(self.scan_callback)
+
+ # Start scanning on SL4A DUT
+ self.device.sl4a.bleSetScanFilterDeviceAddressAndType(address, addr_type)
+ self.device.sl4a.bleBuildScanFilter(self.filter_list)
+ self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+ # Verify that scan result is received on SL4A DUT
+ advertising_address = self.__wait_for_scan_result_event(expected_event_name, 1)
+ assertThat(advertising_address).isNone()
+ logging.info("Filter advertisement with address {}".format(advertising_address))
+
+ def scan_for_address(self, address, addr_type):
+ if self.is_scanning:
+ print("Already scanning!")
+ return
+ self.is_scanning = True
+ logging.info("Start scanning for identity address {} or type {}".format(address, addr_type))
+ self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+ expected_event_name = scan_result.format(self.scan_callback)
+
+ # Start scanning on SL4A DUT
+ self.device.sl4a.bleSetScanFilterDeviceAddressAndType(address, addr_type)
+ self.device.sl4a.bleBuildScanFilter(self.filter_list)
+ self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+ # Verify that scan result is received on SL4A DUT
+ advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+ assertThat(advertising_address).isNotNone()
+ logging.info("Filter advertisement with address {}".format(advertising_address))
+
+ def scan_for_address_with_irk(self, address, addr_type, irk):
+ if self.is_scanning:
+ print("Already scanning!")
+ return
+ self.is_scanning = True
+ logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk))
+ self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+ expected_event_name = scan_result.format(self.scan_callback)
+
+ # Start scanning on SL4A DUT
+ self.device.sl4a.bleSetScanFilterDeviceAddressTypeAndIrkHexString(address, addr_type, irk)
+ self.device.sl4a.bleBuildScanFilter(self.filter_list)
+ self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+ # Verify that scan result is received on SL4A DUT
+ advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+ assertThat(advertising_address).isNotNone()
+ logging.info("Filter advertisement with address {}".format(advertising_address))
+
+ def scan_for_address_with_irk_pending_intent(self, address, addr_type, irk):
+ if self.is_scanning:
+ print("Already scanning!")
+ return
+ self.is_scanning = True
+ logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk))
+ self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+ # Hard code here since callback index iterates and will cause this to fail if ran
+ # Second as the impl in SL4A sends this since it's a single callback for broadcast.
+ expected_event_name = "BleScan1onScanResults"
+
+ # Start scanning on SL4A DUT
+ self.device.sl4a.bleSetScanFilterDeviceAddressTypeAndIrkHexString(address, addr_type, irk)
+ self.device.sl4a.bleBuildScanFilter(self.filter_list)
+ self.device.sl4a.bleStartBleScanPendingIntent(self.filter_list, self.scan_settings)
+
+ # Verify that scan result is received on SL4A DUT
+ advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+ assertThat(advertising_address).isNotNone()
+ logging.info("Filter advertisement with address {}".format(advertising_address))
+
+ def scan_for_name(self, name):
+ if self.is_scanning:
+ print("Already scanning!")
+ return
+ self.is_scanning = True
+ logging.info("Start scanning for name {}".format(name))
+ self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+ self.device.sl4a.bleSetScanSettingsLegacy(False)
+ self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+ expected_event_name = scan_result.format(1)
+
+ # Start scanning on SL4A DUT
+ self.device.sl4a.bleSetScanFilterDeviceName(name)
+ self.device.sl4a.bleBuildScanFilter(self.filter_list)
+ self.device.sl4a.bleStartBleScanPendingIntent(self.filter_list, self.scan_settings)
+
+ # Verify that scan result is received on SL4A DUT
+ advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+ assertThat(advertising_address).isNotNone()
+ logging.info("Filter advertisement with address {}".format(advertising_address))
+ return advertising_address
+
+ def stop_scanning(self):
+ """
+ Warning: no java callback registered for this
+ """
+ if self.is_scanning:
+ logging.info("Stopping scan")
+ self.device.sl4a.bleStopBleScan(self.scan_callback)
+ self.is_scanning = False
+
+ def close(self):
+ self.stop_scanning()
+ self.device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py
new file mode 100644
index 0000000..d37f3f4
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+
+class OobData:
+ """
+ This represents the data generated from the device
+ """
+
+ address = None
+ confirmation = None
+ randomizer = None
+
+ def __init__(self, address, confirmation, randomizer):
+ self.address = address
+ self.confirmation = confirmation
+ self.randomizer = randomizer
+
+ def to_sl4a_address(self):
+ oob_address = self.address.upper()
+ address_str_octets = []
+ i = 1
+ buf = ""
+ for c in oob_address:
+ buf += c
+ if i % 2 == 0:
+ address_str_octets.append(buf)
+ buf = ""
+ i += 1
+ address_str_octets = address_str_octets[:6]
+ address_str_octets.reverse()
+ return ":".join(address_str_octets)
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/security.py b/system/blueberry/tests/sl4a_sl4a/lib/security.py
new file mode 100644
index 0000000..05a6645
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/security.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+import queue
+import logging
+
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.sl4a_sl4a.lib.oob_data import OobData
+
+
+class Security:
+
+ # Events sent from SL4A
+ SL4A_EVENT_GENERATED = "GeneratedOobData"
+ SL4A_EVENT_ERROR = "ErrorOobData"
+ SL4A_EVENT_BONDED = "Bonded"
+ SL4A_EVENT_UNBONDED = "Unbonded"
+
+ # Matches tBT_TRANSPORT
+ # Used Strings because ints were causing gRPC problems
+ TRANSPORT_AUTO = "0"
+ TRANSPORT_BREDR = "1"
+ TRANSPORT_LE = "2"
+
+ __default_timeout = 10 # seconds
+ __default_bonding_timeout = 30 # seconds
+ __device = None
+
+ def __init__(self, device):
+ self.__device = device
+ self.__device.sl4a.bluetoothStartPairingHelper(True)
+
+ def generate_oob_data(self, transport):
+ self.__device.sl4a.bluetoothGenerateLocalOobData(transport)
+ try:
+ event_info = self.__device.ed.pop_event(self.SL4A_EVENT_GENERATED, self.__default_timeout)
+ except queue.Empty as error:
+ logging.error("Failed to generate OOB data!")
+ return None
+ return OobData(event_info["data"]["address_with_type"], event_info["data"]["confirmation"],
+ event_info["data"]["randomizer"])
+
+ def ensure_device_bonded(self):
+ bond_state = None
+ try:
+ bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_BONDED, self.__default_bonding_timeout)
+ except queue.Empty as error:
+ logging.error("Failed to get bond event!")
+
+ assertThat(bond_state).isNotNone()
+ logging.info("Bonded: %s", bond_state["data"]["bonded_state"])
+ assertThat(bond_state["data"]["bonded_state"]).isEqualTo(True)
+
+ def create_bond_out_of_band(self, oob_data):
+ assertThat(oob_data).isNotNone()
+ address = oob_data.to_sl4a_address()
+ self.__device.sl4a.bluetoothCreateBondOutOfBand(address, self.TRANSPORT_LE, oob_data.confirmation,
+ oob_data.randomizer)
+ self.ensure_device_bonded()
+
+ def create_bond_numeric_comparison(self, address, transport=TRANSPORT_LE):
+ assertThat(address).isNotNone()
+ if transport == self.TRANSPORT_LE:
+ self.__device.sl4a.bluetoothLeBond(address)
+ else:
+ self.__device.sl4a.bluetoothBond(address)
+ self.ensure_device_bonded()
+
+ def remove_all_bonded_devices(self):
+ bonded_devices = self.__device.sl4a.bluetoothGetBondedDevices()
+ for device in bonded_devices:
+ self.remove_bond(device["address"])
+
+ def remove_bond(self, address):
+ self.__device.sl4a.bluetoothUnbond(address)
+ bond_state = None
+ try:
+ bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_UNBONDED, self.__default_timeout)
+ except queue.Empty as error:
+ logging.error("Failed to get bond event!")
+
+ assertThat(bond_state).isNotNone()
+ assertThat(bond_state["data"]["bonded_state"]).isEqualTo(False)
+
+ def close(self):
+ self.remove_all_bonded_devices()
+ self.__device.sl4a.bluetoothStartPairingHelper(False)
+ self.__device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
index 86efbd2..fe37b00 100644
--- a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
@@ -19,10 +19,14 @@
import traceback
from functools import wraps
+from blueberry.tests.gd.cert.closable import safeClose
from blueberry.tests.gd.cert.context import get_current_context
from blueberry.tests.gd_sl4a.lib.ble_lib import BleLib
from blueberry.tests.gd_sl4a.lib.ble_lib import disable_bluetooth
from blueberry.tests.gd_sl4a.lib.ble_lib import enable_bluetooth
+from blueberry.tests.sl4a_sl4a.lib.le_advertiser import LeAdvertiser
+from blueberry.tests.sl4a_sl4a.lib.le_scanner import LeScanner
+from blueberry.tests.sl4a_sl4a.lib.security import Security
from blueberry.utils.mobly_sl4a_utils import setup_sl4a
from blueberry.utils.mobly_sl4a_utils import teardown_sl4a
from grpc import RpcError
@@ -35,6 +39,16 @@
class Sl4aSl4aBaseTestClass(BaseTestClass):
+ # DUT
+ dut_advertiser_ = None
+ dut_scanner_ = None
+ dut_security_ = None
+
+ # CERT
+ cert_advertiser_ = None
+ cert_scanner_ = None
+ cert_security_ = None
+
SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
def setup_class(self):
@@ -94,9 +108,29 @@
def setup_test(self):
self.setup_device_for_test(self.dut)
self.setup_device_for_test(self.cert)
+ self.dut_advertiser_ = LeAdvertiser(self.dut)
+ self.dut_scanner_ = LeScanner(self.dut)
+ self.dut_security_ = Security(self.dut)
+ self.cert_advertiser_ = LeAdvertiser(self.cert)
+ self.cert_scanner_ = LeScanner(self.cert)
+ self.cert_security_ = Security(self.cert)
return True
def teardown_test(self):
+ # Go ahead and remove everything before turning off the stack
+ safeClose(self.dut_advertiser_)
+ safeClose(self.dut_scanner_)
+ safeClose(self.dut_security_)
+ safeClose(self.cert_advertiser_)
+ safeClose(self.cert_scanner_)
+ safeClose(self.cert_security_)
+ self.dut_advertiser_ = None
+ self.dut_scanner_ = None
+ self.dut_security_ = None
+ self.cert_advertiser_ = None
+ self.cert_scanner_ = None
+ self.cert_security_ = None
+
# Make sure BLE is disabled and Bluetooth is disabled after test
self.dut.sl4a.bluetoothDisableBLE()
disable_bluetooth(self.dut.sl4a, self.dut.ed)
diff --git a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py
new file mode 100644
index 0000000..becdbc7
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 - 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.
+
+import binascii
+import io
+import logging
+import os
+import queue
+
+from blueberry.tests.gd.cert.context import get_current_context
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+from blueberry.tests.sl4a_sl4a.lib.security import Security
+
+
+class OobPairingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass):
+
+ def __get_cert_public_address_and_irk_from_bt_config(self):
+ # Pull IRK from SL4A cert side to pass in from SL4A DUT side when scanning
+ bt_config_file_path = os.path.join(get_current_context().get_full_output_path(),
+ "DUT_%s_bt_config.conf" % self.cert.serial)
+ try:
+ self.cert.adb.pull(["/data/misc/bluedroid/bt_config.conf", bt_config_file_path])
+ except AdbError as error:
+ logging.error("Failed to pull SL4A cert BT config")
+ return False
+ logging.debug("Reading SL4A cert BT config")
+ with io.open(bt_config_file_path) as f:
+ for line in f.readlines():
+ stripped_line = line.strip()
+ if (stripped_line.startswith("Address")):
+ address_fields = stripped_line.split(' ')
+ # API currently requires public address to be capitalized
+ address = address_fields[2].upper()
+ logging.debug("Found cert address: %s" % address)
+ continue
+ if (stripped_line.startswith("LE_LOCAL_KEY_IRK")):
+ irk_fields = stripped_line.split(' ')
+ irk = irk_fields[2]
+ logging.debug("Found cert IRK: %s" % irk)
+ continue
+
+ return address, irk
+
+ def setup_class(self):
+ super().setup_class()
+
+ def setup_test(self):
+ assertThat(super().setup_test()).isTrue()
+
+ def teardown_test(self):
+ super().teardown_test()
+
+ def __test_scan(self, address_type="public"):
+ cert_public_address, irk = self.__get_cert_public_address_and_irk_from_bt_config()
+ rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+ self.dut_scanner_.start_identity_address_scan(cert_public_address, ble_address_types[address_type])
+ self.dut_scanner_.stop_scanning()
+ self.cert_advertiser_.stop_advertising()
+
+ def test_classic_generate_local_oob_data(self):
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+ assertThat(oob_data).isNotNone()
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+ assertThat(oob_data).isNotNone()
+
+ def test_classic_generate_local_oob_data_stress(self):
+ for i in range(1, 20):
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+ assertThat(oob_data).isNotNone()
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+ assertThat(oob_data).isNotNone()
+
+ def test_le_generate_local_oob_data(self):
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE)
+ assertThat(oob_data).isNotNone()
+ oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+ assertThat(oob_data).isNotNone()
+
+ def test_le_generate_local_oob_data_stress(self):
+ for i in range(1, 20):
+ oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE)
+ assertThat(oob_data).isNotNone()
+ oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+ assertThat(oob_data).isNotNone()
+
+ def test_le_bond_oob(self):
+ oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+ assertThat(oob_data).isNotNone()
+ self.dut_security_.create_bond_out_of_band(oob_data)
+
+ def test_le_bond_oob_stress(self):
+ for i in range(0, 10):
+ logging.info("Stress #%d" % i)
+ self.test_le_bond_oob()
+ self.dut_security_.remove_all_bonded_devices()
+ self.cert_security_.remove_all_bonded_devices()
+
+ def test_le_generate_local_oob_data_after_le_bond_oob(self):
+ self.test_le_bond_oob()
+ self.test_le_generate_local_oob_data()
diff --git a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
index 92f5074..c08836d 100644
--- a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
+++ b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
@@ -14,9 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from blueberry.tests.sl4a_sl4a.advertising.le_advertising import LeAdvertisingTest
from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_test import GattConnectTest
from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_with_irk_test import GattConnectWithIrkTest
from blueberry.tests.sl4a_sl4a.gatt.gatt_notify_test import GattNotifyTest
+from blueberry.tests.sl4a_sl4a.security.oob_pairing_test import OobPairingTest
from mobly import suite_runner
import argparse
@@ -25,6 +27,8 @@
GattConnectTest,
GattConnectWithIrkTest,
GattNotifyTest,
+ LeAdvertisingTest,
+ OobPairingTest,
]
diff --git a/system/blueberry/utils/mobly_sl4a_utils.py b/system/blueberry/utils/mobly_sl4a_utils.py
index 0d06d16..25d86cf 100644
--- a/system/blueberry/utils/mobly_sl4a_utils.py
+++ b/system/blueberry/utils/mobly_sl4a_utils.py
@@ -78,7 +78,10 @@
# waiting for the future. However, mobly calls it and cause InvalidStateError when it
# tries to do that after the thread pool has stopped, overriding it here
# TODO: Resolve this issue in mobly
- device.sl4a.ed.poller = FakeFuture()
+ try:
+ device.sl4a.ed.poller = FakeFuture()
+ except Exception as e:
+ print(e)
try:
# Guarded by is_alive internally
device.sl4a.stop()
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 939f0be..b6eb470 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -93,6 +93,7 @@
"le_audio/broadcaster/state_machine.cc",
"le_audio/client.cc",
"le_audio/codec_manager.cc",
+ "le_audio/content_control_id_keeper.cc",
"le_audio/devices.cc",
"le_audio/hal_verifier.cc",
"le_audio/state_machine.cc",
@@ -656,6 +657,7 @@
"le_audio/client.cc",
"le_audio/client_audio.cc",
"le_audio/client_parser.cc",
+ "le_audio/content_control_id_keeper.cc",
"le_audio/devices.cc",
"le_audio/le_audio_client_test.cc",
"le_audio/le_audio_set_configuration_provider_json.cc",
@@ -795,6 +797,7 @@
"le_audio/broadcaster/broadcaster_types.cc",
"le_audio/broadcaster/mock_ble_advertising_manager.cc",
"le_audio/broadcaster/mock_state_machine.cc",
+ "le_audio/content_control_id_keeper.cc",
"le_audio/client_audio.cc",
"le_audio/le_audio_types.cc",
"le_audio/mock_iso_manager.cc",
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index a1199f6..9ae6450 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -138,10 +138,12 @@
std::shared_ptr<bluetooth::csis::CsisGroup> AssignCsisGroup(
const RawAddress& address, int group_id,
bool create_group_if_non_existing, const bluetooth::Uuid& uuid) {
+ LOG_DEBUG("Device: %s, group_id: %d", address.ToString().c_str(), group_id);
auto csis_group = FindCsisGroup(group_id);
if (!csis_group) {
if (create_group_if_non_existing) {
/* Let's create a group */
+ LOG(INFO) << __func__ << ": Create a new group";
auto g = std::make_shared<CsisGroup>(group_id, uuid);
csis_groups_.push_back(g);
csis_group = FindCsisGroup(group_id);
@@ -290,14 +292,14 @@
csis_group->SetTargetLockState(CsisLockState::CSIS_STATE_UNSET);
int group_id = csis_group->GetGroupId();
- CsisLockState current_lock_state = csis_group->GetCurrentLockState();
/* Send unlock to previous devices. It shall be done in reverse order. */
auto prev_dev = csis_group->GetPrevDevice(csis_device);
while (prev_dev) {
if (prev_dev->IsConnected()) {
auto prev_csis_instance = prev_dev->GetCsisInstanceByGroupId(group_id);
LOG_ASSERT(prev_csis_instance) << " prev_csis_instance does not exist!";
- SetLock(prev_dev, prev_csis_instance, current_lock_state);
+ SetLock(prev_dev, prev_csis_instance,
+ CsisLockState::CSIS_STATE_UNLOCKED);
}
prev_dev = csis_group->GetPrevDevice(prev_dev);
}
@@ -321,6 +323,10 @@
}
CsisLockState target_lock_state = csis_group->GetTargetLockState();
+
+ LOG_DEBUG("Device %s, target lock: %d, status: 0x%02x",
+ device->addr.ToString().c_str(), (int)target_lock_state,
+ (int)status);
if (target_lock_state == CsisLockState::CSIS_STATE_UNSET) return;
if (status != GATT_SUCCESS &&
@@ -370,14 +376,16 @@
if (next_dev) {
auto next_csis_inst = next_dev->GetCsisInstanceByGroupId(group_id);
LOG_ASSERT(csis_instance) << " csis_instance does not exist!";
+#if CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK == FALSE
if (next_csis_inst->GetLockState() ==
CsisLockState::CSIS_STATE_LOCKED) {
/* Somebody else managed to lock it.
* Unlock previous devices
*/
- HandleCsisLockProcedureError(csis_group, device);
+ HandleCsisLockProcedureError(csis_group, next_dev);
return;
}
+#endif
SetLock(next_dev, next_csis_inst, CsisLockState::CSIS_STATE_LOCKED);
}
}
@@ -473,6 +481,7 @@
return;
}
+#if CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK == FALSE
if (lock && !csis_group->IsAvailableForCsisLockOperation()) {
DLOG(INFO) << __func__ << " Group " << group_id << " locked by other";
NotifyGroupStatus(group_id, false,
@@ -480,6 +489,7 @@
std::move(cb));
return;
}
+#endif
csis_group->SetTargetLockState(new_lock_state, std::move(cb));
@@ -489,6 +499,10 @@
* can revert lock previously locked devices as per specification.
*/
auto csis_device = csis_group->GetFirstDevice();
+ while (!csis_device->IsConnected()) {
+ csis_device = csis_group->GetNextDevice(csis_device);
+ }
+
auto csis_instance = csis_device->GetCsisInstanceByGroupId(group_id);
LOG_ASSERT(csis_instance) << " csis_instance does not exist!";
SetLock(csis_device, csis_instance, new_lock_state);
@@ -675,7 +689,9 @@
if (!instance) {
stream << " No csis instance available\n";
} else {
- stream << " rank: " << instance->GetRank() << "\n";
+ stream << " service handle: "
+ << loghex(instance->svc_data.start_handle)
+ << " rank: " << +instance->GetRank() << "\n";
}
if (!device->IsConnected()) {
@@ -1340,11 +1356,6 @@
group_id =
dev_groups_->AddDevice(device->addr, csis_instance->GetUuid());
LOG_ASSERT(group_id != -1);
-
- /* Create new group */
- auto g =
- std::make_shared<CsisGroup>(group_id, csis_instance->GetUuid());
- csis_groups_.push_back(g);
} else {
dev_groups_->AddDevice(device->addr, csis_instance->GetUuid(),
group_id);
diff --git a/system/bta/csis/csis_types.h b/system/bta/csis/csis_types.h
index 0be02fd..466a1d4 100644
--- a/system/bta/csis/csis_types.h
+++ b/system/bta/csis/csis_types.h
@@ -27,6 +27,8 @@
#include "bta_gatt_api.h"
#include "bta_groups.h"
#include "gap_api.h"
+#include "gd/common/init_flags.h"
+#include "gd/common/strings.h"
#include "stack/crypto_toolbox/crypto_toolbox.h"
namespace bluetooth {
@@ -173,7 +175,7 @@
uint8_t GetRank(void) const { return rank_; }
void SetRank(uint8_t rank) {
DLOG(INFO) << __func__ << " current rank state: " << loghex(rank_)
- << " new lock state: " << loghex(rank);
+ << " new rank state: " << loghex(rank);
rank_ = rank;
}
@@ -363,15 +365,34 @@
bool IsAvailableForCsisLockOperation(void) {
int id = group_id_;
- auto iter = std::find_if(devices_.begin(), devices_.end(), [id](auto& d) {
- if (!d->IsConnected()) return false;
- auto inst = d->GetCsisInstanceByGroupId(id);
- LOG_ASSERT(inst);
- return inst->GetLockState() == CsisLockState::CSIS_STATE_LOCKED;
- });
+ int number_of_connected = 0;
+ auto iter = std::find_if(
+ devices_.begin(), devices_.end(), [id, &number_of_connected](auto& d) {
+ if (!d->IsConnected()) {
+ LOG_DEBUG("Device %s is not connected in group %d",
+ d->addr.ToString().c_str(), id);
+ return false;
+ }
+ auto inst = d->GetCsisInstanceByGroupId(id);
+ if (!inst) {
+ LOG_DEBUG("Instance not available for group %d", id);
+ return false;
+ }
+ number_of_connected++;
+ LOG_DEBUG("Device %s, lock state: %d", d->addr.ToString().c_str(),
+ (int)inst->GetLockState());
+ return inst->GetLockState() == CsisLockState::CSIS_STATE_LOCKED;
+ });
+ LOG_DEBUG("Locked set: %d, number of connected %d", iter != devices_.end(),
+ number_of_connected);
/* If there is no locked device, we are good to go */
- return iter == devices_.end();
+ if (iter != devices_.end()) {
+ LOG_WARN("Device %s is locked ", (*iter)->addr.ToString().c_str());
+ return false;
+ }
+
+ return (number_of_connected > 0);
}
void SortByCsisRank(void) {
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 55f7871..477702b 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -957,7 +957,7 @@
/* Remote name discovery is on going now so BTM cannot notify through
* "bta_dm_remname_cback" */
- /* adding callback to get notified that current reading remore name done */
+ /* adding callback to get notified that current reading remote name done */
if (bluetooth::shim::is_gd_security_enabled()) {
bluetooth::shim::BTM_SecAddRmtNameNotifyCallback(
@@ -1872,6 +1872,7 @@
/* if this is what we are looking for */
if (bta_dm_search_cb.peer_bdaddr == bd_addr) {
+ rem_name.bd_addr = bd_addr;
rem_name.length = strlcpy((char*)rem_name.remote_bd_name, (char*)bd_name,
BD_NAME_LEN + 1);
if (rem_name.length > BD_NAME_LEN) {
@@ -1893,6 +1894,9 @@
APPL_TRACE_WARNING("%s: BTM_ReadRemoteDeviceName returns 0x%02X",
__func__, btm_status);
+ // needed so our response is not ignored, since this corresponds to the
+ // actual peer_bdaddr
+ rem_name.bd_addr = bta_dm_search_cb.peer_bdaddr;
rem_name.length = 0;
rem_name.remote_bd_name[0] = 0;
rem_name.status = btm_status;
@@ -1915,11 +1919,6 @@
APPL_TRACE_DEBUG("bta_dm_remname_cback len = %d name=<%s>",
p_remote_name->length, p_remote_name->remote_bd_name);
- /* remote name discovery is done but it could be failed */
- bta_dm_search_cb.name_discover_done = true;
- strlcpy((char*)bta_dm_search_cb.peer_name,
- (char*)p_remote_name->remote_bd_name, BD_NAME_LEN + 1);
-
if (bta_dm_search_cb.peer_bdaddr == p_remote_name->bd_addr) {
if (bluetooth::shim::is_gd_security_enabled()) {
bluetooth::shim::BTM_SecDeleteRmtNameNotifyCallback(
@@ -1927,8 +1926,19 @@
} else {
BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
}
+ } else {
+ // if we got a different response, ignore it
+ // we will have made a request directly from BTM_ReadRemoteDeviceName so we
+ // expect a dedicated response for us
+ LOG_INFO("ignoring remote name response in DM callback since it's for the wrong bd_addr");
+ return;
}
+ /* remote name discovery is done but it could be failed */
+ bta_dm_search_cb.name_discover_done = true;
+ strlcpy((char*)bta_dm_search_cb.peer_name,
+ (char*)p_remote_name->remote_bd_name, BD_NAME_LEN + 1);
+
if (bta_dm_search_cb.transport == BT_TRANSPORT_LE) {
GAP_BleReadPeerPrefConnParams(bta_dm_search_cb.peer_bdaddr);
}
@@ -3627,6 +3637,12 @@
}
break;
+ case BTM_LE_ADDR_ASSOC_EVT:
+ sec_event.proc_id_addr.pairing_bda = bda;
+ sec_event.proc_id_addr.id_addr = p_data->id_addr;
+ bta_dm_cb.p_sec_cback(BTA_DM_LE_ADDR_ASSOC_EVT, &sec_event);
+ break;
+
default:
status = BTM_NOT_AUTHORIZED;
break;
diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc
index f49ae36..1412702 100644
--- a/system/bta/gatt/bta_gattc_utils.cc
+++ b/system/bta/gatt/bta_gattc_utils.cc
@@ -677,5 +677,5 @@
*
******************************************************************************/
bool bta_gattc_is_robust_caching_enabled() {
- return bluetooth::common::init_flags::gatt_robust_caching_is_enabled();
+ return bluetooth::common::init_flags::gatt_robust_caching_client_is_enabled();
}
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index 59d6d98..98182f4 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -222,7 +222,8 @@
BTA_DM_BLE_SC_OOB_REQ_EVT = 29, /* SMP SC OOB request event */
BTA_DM_BLE_CONSENT_REQ_EVT = 30, /* SMP consent request event */
BTA_DM_BLE_SC_CR_LOC_OOB_EVT = 31, /* SMP SC Create Local OOB request event */
- BTA_DM_REPORT_BONDING_EVT = 32 /*handle for pin or key missing*/
+ BTA_DM_REPORT_BONDING_EVT = 32, /*handle for pin or key missing*/
+ BTA_DM_LE_ADDR_ASSOC_EVT = 33, /* identity address association event */
} tBTA_DM_SEC_EVT;
/* Structure associated with BTA_DM_PIN_REQ_EVT */
@@ -382,6 +383,11 @@
Octet16 local_oob_r; /* Local OOB Data Randomizer */
} tBTA_DM_LOC_OOB_DATA;
+typedef struct {
+ RawAddress pairing_bda;
+ RawAddress id_addr;
+} tBTA_DM_PROC_ID_ADDR;
+
/* Union of all security callback structures */
typedef union {
tBTA_DM_PIN_REQ pin_req; /* PIN request. */
@@ -399,6 +405,7 @@
Octet16 ble_er; /* ER event data */
tBTA_DM_LOC_OOB_DATA local_oob_data; /* Local OOB data generated by us */
tBTA_DM_RC_UNPAIR delete_key_RC_to_unpair;
+ tBTA_DM_PROC_ID_ADDR proc_id_addr; /* Identity address event */
} tBTA_DM_SEC;
/* Security callback */
diff --git a/system/bta/include/bta_le_audio_broadcaster_api.h b/system/bta/include/bta_le_audio_broadcaster_api.h
index b918fbe..a8f1daa 100644
--- a/system/bta/include/bta_le_audio_broadcaster_api.h
+++ b/system/bta/include/bta_le_audio_broadcaster_api.h
@@ -28,13 +28,7 @@
/* Interface class */
class LeAudioBroadcaster {
public:
- enum class AudioProfile {
- SONIFICATION = 0,
- MEDIA = 1,
- };
-
static constexpr uint8_t kInstanceIdUndefined = 0xFF;
- static constexpr AudioProfile kDefaultAudioProfile = AudioProfile::MEDIA;
virtual ~LeAudioBroadcaster(void) = default;
@@ -50,7 +44,7 @@
static void DebugDump(int fd);
virtual void CreateAudioBroadcast(
- std::vector<uint8_t> metadata, AudioProfile profile,
+ std::vector<uint8_t> metadata,
std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code =
std::nullopt) = 0;
virtual void SuspendAudioBroadcast(uint32_t broadcast_id) = 0;
@@ -67,8 +61,6 @@
RawAddress /* addr */, bool /* is_valid */)>
cb) = 0;
- virtual void SetNumRetransmit(uint8_t count) = 0;
- virtual uint8_t GetNumRetransmit(void) const = 0;
virtual void SetStreamingPhy(uint8_t phy) = 0;
virtual uint8_t GetStreamingPhy(void) const = 0;
};
diff --git a/system/bta/le_audio/audio_set_configurations.json b/system/bta/le_audio/audio_set_configurations.json
index be49dc1..c3b9200 100644
--- a/system/bta/le_audio/audio_set_configurations.json
+++ b/system/bta/le_audio/audio_set_configurations.json
@@ -363,6 +363,26 @@
"qos_config_name": ["QoS_Config_16_1_1"]
},
{
+ "name": "DualDev_OneChanMonoSrc_16_2_Server_Preferred",
+ "codec_config_name": "DualDev_OneChanMonoSrc_16_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSrc_16_2_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanStereoSrc_16_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSrc_24_2_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanMonoSrc_24_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSrc_16_2_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanMonoSrc_16_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
"name": "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_2",
"codec_config_name": "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1",
"qos_config_name": ["QoS_Config_16_1_2"]
@@ -498,6 +518,36 @@
"qos_config_name": ["QoS_Config_48_4_2"]
},
{
+ "name": "DualDev_OneChanStereoSnk_48_3_Server_Preferred",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_3_2",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_48_3_2"]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_2_Server_Preferred",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_2_2",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_48_2_2"]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_1_Server_Preferred",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_1_2",
+ "codec_config_name": "DualDev_OneChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_48_1_2"]
+ },
+ {
"name": "SingleDev_OneChanStereoSnk_48_4_Server_Preferred",
"codec_config_name": "SingleDev_OneChanStereoSnk_48_4",
"qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -513,6 +563,36 @@
"qos_config_name": ["QoS_Config_48_4_2"]
},
{
+ "name": "SingleDev_OneChanStereoSnk_48_3_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_3_2",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_48_3_2"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_2_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_2_2",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_48_2_2"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_1_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_1_2",
+ "codec_config_name": "SingleDev_OneChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_48_1_2"]
+ },
+ {
"name": "SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
"codec_config_name": "SingleDev_TwoChanStereoSnk_48_4",
"qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -528,6 +608,36 @@
"qos_config_name": ["QoS_Config_48_4_2"]
},
{
+ "name": "SingleDev_TwoChanStereoSnk_48_3_Server_Preferred",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_3_2",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_3",
+ "qos_config_name": ["QoS_Config_48_3_2"]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_2_Server_Preferred",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_2_2",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_2",
+ "qos_config_name": ["QoS_Config_48_2_2"]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_1_Server_Preferred",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_1_2",
+ "codec_config_name": "SingleDev_TwoChanStereoSnk_48_1",
+ "qos_config_name": ["QoS_Config_48_1_2"]
+ },
+ {
"name": "SingleDev_OneChanMonoSnk_48_4_Server_Preferred",
"codec_config_name": "SingleDev_OneChanMonoSnk_48_4",
"qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -543,6 +653,36 @@
"qos_config_name": ["QoS_Config_48_4_2"]
},
{
+ "name": "SingleDev_OneChanMonoSnk_48_3_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_3",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_3_2",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_3",
+ "qos_config_name": ["QoS_Config_48_3_2"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_2_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_2",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_2_2",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_2",
+ "qos_config_name": ["QoS_Config_48_2_2"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_1_Server_Preferred",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_1",
+ "qos_config_name": ["QoS_Config_Server_Preferred"]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_1_2",
+ "codec_config_name": "SingleDev_OneChanMonoSnk_48_1",
+ "qos_config_name": ["QoS_Config_48_1_2"]
+ },
+ {
"name": "VND_SingleDev_TwoChanStereoSnk_OneChanStereoSrc_32khz_60octs_Server_Preferred_1",
"codec_config_name": "VND_SingleDev_TwoChanStereoSnk_OneChanStereoSrc_32khz_60octs_1",
"qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -1227,6 +1367,139 @@
]
},
{
+ "name": "SingleDev_OneChanStereoSrc_16_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 2,
+ "direction": "SOURCE",
+ "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 3
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 40,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSrc_24_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SOURCE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 5
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 60,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "SingleDev_OneChanMonoSnk_16_2",
"subconfigurations": [
{
@@ -3278,6 +3551,138 @@
]
},
{
+ "name": "DualDev_OneChanMonoSrc_16_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 2,
+ "ase_cnt": 2,
+ "direction": "SOURCE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 3
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 40,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSrc_16_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SOURCE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 3
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 40,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "DualDev_OneChanStereoSnk_48_4",
"subconfigurations": [
{
@@ -3345,6 +3750,207 @@
]
},
{
+ "name": "DualDev_OneChanStereoSnk_48_3",
+ "subconfigurations": [
+ {
+ "device_cnt": 2,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 90,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 2,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 100,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "DualDev_OneChanStereoSnk_48_1",
+ "subconfigurations": [
+ {
+ "device_cnt": 2,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 75,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "SingleDev_OneChanStereoSnk_48_4",
"subconfigurations": [
{
@@ -3412,6 +4018,207 @@
]
},
{
+ "name": "SingleDev_OneChanStereoSnk_48_3",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 90,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 100,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanStereoSnk_48_1",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 2,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 75,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "SingleDev_TwoChanStereoSnk_48_4",
"subconfigurations": [
{
@@ -3479,6 +4286,207 @@
]
},
{
+ "name": "SingleDev_TwoChanStereoSnk_48_3",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 3,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 90,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 3,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 100,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_TwoChanStereoSnk_48_1",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 3,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 75,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "SingleDev_OneChanMonoSnk_48_4",
"subconfigurations": [
{
@@ -3546,6 +4554,207 @@
]
},
{
+ "name": "SingleDev_OneChanMonoSnk_48_3",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 90,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_2",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 100,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SingleDev_OneChanMonoSnk_48_1",
+ "subconfigurations": [
+ {
+ "device_cnt": 1,
+ "ase_cnt": 1,
+ "direction": "SINK",
+ "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+ "codec_id": {
+ "coding_format": 6,
+ "vendor_company_id": 0,
+ "vendor_codec_id": 0
+ },
+ "codec_configuration": [
+ {
+ "name": "sampling_frequency",
+ "type": 1,
+ "compound_value": {
+ "value": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": "frame_duration",
+ "type": 2,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ },
+ {
+ "name": "audio_channel_allocation",
+ "type": 3,
+ "compound_value": {
+ "value": [
+ 1,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "octets_per_codec_frame",
+ "type": 4,
+ "compound_value": {
+ "value": [
+ 75,
+ 0
+ ]
+ }
+ },
+ {
+ "name": "codec_frame_blocks_per_sdu",
+ "type": 5,
+ "compound_value": {
+ "value": [
+ 1
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
"name": "VND_SingleDev_TwoChanStereoSnk_48khz_100octs_1",
"subconfigurations": [
{
@@ -7885,6 +9094,21 @@
"max_transport_latency": 10
},
{
+ "name": "QoS_Config_48_1_2",
+ "retransmission_number": 13,
+ "max_transport_latency": 75
+ },
+ {
+ "name": "QoS_Config_48_2_2",
+ "retransmission_number": 13,
+ "max_transport_latency": 95
+ },
+ {
+ "name": "QoS_Config_48_3_2",
+ "retransmission_number": 13,
+ "max_transport_latency": 75
+ },
+ {
"name": "QoS_Config_48_4_1",
"retransmission_number": 5,
"max_transport_latency": 20
diff --git a/system/bta/le_audio/audio_set_scenarios.json b/system/bta/le_audio/audio_set_scenarios.json
index c92fbd6..4013e7e 100644
--- a/system/bta/le_audio/audio_set_scenarios.json
+++ b/system/bta/le_audio/audio_set_scenarios.json
@@ -8,6 +8,20 @@
{
"name": "Ringtone",
"configurations": [
+ "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Server_Preferred",
+ "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
+ "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+ "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
+ "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+ "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
+ "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
+ "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
+ "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+ "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
+ "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+ "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
"DualDev_OneChanStereoSnk_16_2_Server_Preferred",
"DualDev_OneChanStereoSnk_16_2_1",
"DualDev_OneChanStereoSnk_16_1_Server_Preferred",
@@ -75,6 +89,10 @@
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Server_Preferred",
"SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
+ "DualDev_OneChanMonoSrc_16_2_Server_Preferred",
+ "SingleDev_OneChanStereoSrc_16_2_Server_Preferred",
+ "SingleDev_OneChanMonoSrc_24_2_Server_Preferred",
+ "SingleDev_OneChanMonoSrc_16_2_Server_Preferred",
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_Server_Prefered_1",
"VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_60oct_R3_L22_1"
]
@@ -84,6 +102,12 @@
"configurations": [
"DualDev_OneChanStereoSnk_48_4_Server_Preferred",
"DualDev_OneChanStereoSnk_48_4_2",
+ "DualDev_OneChanStereoSnk_48_3_Server_Preferred",
+ "DualDev_OneChanStereoSnk_48_3_2",
+ "DualDev_OneChanStereoSnk_48_2_Server_Preferred",
+ "DualDev_OneChanStereoSnk_48_2_2",
+ "DualDev_OneChanStereoSnk_48_1_Server_Preferred",
+ "DualDev_OneChanStereoSnk_48_1_2",
"DualDev_OneChanStereoSnk_24_2_Server_Preferred",
"DualDev_OneChanStereoSnk_24_2_2",
"DualDev_OneChanStereoSnk_16_2_Server_Preferred",
@@ -92,6 +116,12 @@
"DualDev_OneChanStereoSnk_16_1_2",
"SingleDev_OneChanStereoSnk_48_4_Server_Preferred",
"SingleDev_OneChanStereoSnk_48_4_2",
+ "SingleDev_OneChanStereoSnk_48_3_Server_Preferred",
+ "SingleDev_OneChanStereoSnk_48_3_2",
+ "SingleDev_OneChanStereoSnk_48_2_Server_Preferred",
+ "SingleDev_OneChanStereoSnk_48_2_2",
+ "SingleDev_OneChanStereoSnk_48_1_Server_Preferred",
+ "SingleDev_OneChanStereoSnk_48_1_2",
"SingleDev_OneChanStereoSnk_24_2_Server_Preferred",
"SingleDev_OneChanStereoSnk_24_2_2",
"SingleDev_OneChanStereoSnk_16_2_Server_Preferred",
@@ -100,6 +130,14 @@
"SingleDev_OneChanStereoSnk_16_1_2",
"SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
"SingleDev_TwoChanStereoSnk_48_4_2",
+ "SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_48_4_2",
+ "SingleDev_TwoChanStereoSnk_48_3_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_48_3_2",
+ "SingleDev_TwoChanStereoSnk_48_2_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_48_2_2",
+ "SingleDev_TwoChanStereoSnk_48_1_Server_Preferred",
+ "SingleDev_TwoChanStereoSnk_48_1_2",
"SingleDev_TwoChanStereoSnk_24_2_Server_Preferred",
"SingleDev_TwoChanStereoSnk_24_2_2",
"SingleDev_TwoChanStereoSnk_16_2_Server_Preferred",
@@ -108,6 +146,12 @@
"SingleDev_TwoChanStereoSnk_16_1_2",
"SingleDev_OneChanMonoSnk_48_4_Server_Preferred",
"SingleDev_OneChanMonoSnk_48_4_2",
+ "SingleDev_OneChanMonoSnk_48_3_Server_Preferred",
+ "SingleDev_OneChanMonoSnk_48_3_2",
+ "SingleDev_OneChanMonoSnk_48_2_Server_Preferred",
+ "SingleDev_OneChanMonoSnk_48_2_2",
+ "SingleDev_OneChanMonoSnk_48_1_Server_Preferred",
+ "SingleDev_OneChanMonoSnk_48_1_2",
"SingleDev_OneChanMonoSnk_24_2_Server_Preferred",
"SingleDev_OneChanMonoSnk_24_2_2",
"SingleDev_OneChanMonoSnk_16_2_Server_Preferred",
@@ -128,7 +172,8 @@
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_TwoChanStereoSrc_16khz_30octs_Server_Preferred_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_TwoChanStereoSrc_16khz_30octs_R3_L12_1",
"VND_SingleDev_TwoChanStereoSnk_48khz_75octs_Server_Preferred_1",
- "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1"
+ "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1",
+ "DualDev_OneChanStereoSnk_48_4_Server_Preferred"
]
},
{
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index e639c14..f152957 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -20,6 +20,7 @@
#include "bta/include/bta_le_audio_api.h"
#include "bta/include/bta_le_audio_broadcaster_api.h"
#include "bta/le_audio/broadcaster/state_machine.h"
+#include "bta/le_audio/content_control_id_keeper.h"
#include "bta/le_audio/le_audio_types.h"
#include "device/include/controller.h"
#include "embdrv/lc3/include/lc3.h"
@@ -36,12 +37,15 @@
using bluetooth::hci::iso_manager::BigCallbacks;
using bluetooth::le_audio::BasicAudioAnnouncementData;
using bluetooth::le_audio::BroadcastId;
+using le_audio::ContentControlIdKeeper;
using le_audio::broadcaster::BigConfig;
using le_audio::broadcaster::BroadcastCodecWrapper;
+using le_audio::broadcaster::BroadcastQosConfig;
using le_audio::broadcaster::BroadcastStateMachine;
using le_audio::broadcaster::BroadcastStateMachineConfig;
using le_audio::broadcaster::IBroadcastStateMachineCallbacks;
using le_audio::types::kLeAudioCodingFormatLC3;
+using le_audio::types::LeAudioContextType;
using le_audio::types::LeAudioLtvMap;
namespace {
@@ -69,7 +73,6 @@
bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_)
: callbacks_(callbacks_),
current_phy_(PHY_LE_2M),
- num_retransmit_(3),
audio_data_path_state_(AudioDataPathState::INACTIVE),
audio_instance_(nullptr) {
LOG_INFO();
@@ -188,17 +191,8 @@
}
void CreateAudioBroadcast(std::vector<uint8_t> metadata,
- LeAudioBroadcaster::AudioProfile profile,
std::optional<bluetooth::le_audio::BroadcastCode>
broadcast_code) override {
- LOG_INFO("Audio profile: %s",
- profile == LeAudioBroadcaster::AudioProfile::MEDIA
- ? "Media"
- : "Sonification");
-
- auto& codec_wrapper =
- BroadcastCodecWrapper::getCodecConfigForProfile(profile);
-
auto broadcast_id = available_broadcast_ids_.back();
available_broadcast_ids_.pop_back();
if (available_broadcast_ids_.size() == 0) GenerateBroadcastIds();
@@ -212,13 +206,38 @@
return;
}
+ uint16_t context_type =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::MEDIA);
+ auto stream_context_vec =
+ ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
+ if (stream_context_vec) {
+ auto pp = stream_context_vec.value().data();
+ STREAM_TO_UINT16(context_type, pp);
+ }
+
+ // Append the CCID list
+ // TODO: We currently support only one context (and CCID) at a time for both
+ // Unicast and broadcast.
+ auto ccid = ContentControlIdKeeper::GetInstance()->GetCcid(context_type);
+ if (ccid != -1) {
+ ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList,
+ {static_cast<uint8_t>(ccid)});
+ }
+
+ auto codec_qos_pair =
+ le_audio::broadcaster::getStreamConfigForContext(context_type);
BroadcastStateMachineConfig msg = {
.broadcast_id = broadcast_id,
.streaming_phy = GetStreamingPhy(),
- .codec_wrapper = codec_wrapper,
- .announcement = prepareAnnouncement(codec_wrapper, std::move(ltv)),
+ .codec_wrapper = codec_qos_pair.first,
+ .qos_config = codec_qos_pair.second,
+ .announcement =
+ prepareAnnouncement(codec_qos_pair.first, std::move(ltv)),
.broadcast_code = std::move(broadcast_code)};
+ LOG_INFO("CreateAudioBroadcast");
+
/* Create the broadcaster instance - we'll receive it's init state in the
* async callback
*/
@@ -372,10 +391,6 @@
broadcast_id, addr_type, addr, std::move(cb)));
}
- void SetNumRetransmit(uint8_t count) override { num_retransmit_ = count; }
-
- uint8_t GetNumRetransmit(void) const override { return num_retransmit_; }
-
void SetStreamingPhy(uint8_t phy) override { current_phy_ = phy; }
uint8_t GetStreamingPhy(void) const override { return current_phy_; }
@@ -443,25 +458,6 @@
}
private:
- uint8_t GetNumRetransmit(uint32_t broadcast_id) {
- /* TODO: Should be based on QOS settings */
- return GetNumRetransmit();
- }
-
- uint32_t GetSduItv(uint32_t broadcast_id) {
- /* TODO: Should be based on QOS settings
- * currently tuned for media profile (music band)
- */
- return 0x002710;
- }
-
- uint16_t GetMaxTransportLatency(uint32_t broadcast_id) {
- /* TODO: Should be based on QOS settings
- * currently tuned for media profile (music band)
- */
- return 0x3C;
- }
-
static class BroadcastStateMachineCallbacks
: public IBroadcastStateMachineCallbacks {
void OnStateMachineCreateStatus(uint32_t broadcast_id,
@@ -561,26 +557,17 @@
RawAddress addr) override {
/* Not used currently */
}
-
- uint8_t GetNumRetransmit(uint32_t broadcast_id) override {
- return instance->GetNumRetransmit(broadcast_id);
- }
-
- uint32_t GetSduItv(uint32_t broadcast_id) override {
- return instance->GetSduItv(broadcast_id);
- }
-
- uint16_t GetMaxTransportLatency(uint32_t broadcast_id) override {
- return instance->GetMaxTransportLatency(broadcast_id);
- }
} state_machine_callbacks_;
static class LeAudioClientAudioSinkReceiverImpl
: public LeAudioClientAudioSinkReceiver {
public:
LeAudioClientAudioSinkReceiverImpl()
- : codec_wrapper_(BroadcastCodecWrapper::getCodecConfigForProfile(
- LeAudioBroadcaster::AudioProfile::SONIFICATION)) {}
+ : codec_wrapper_(
+ le_audio::broadcaster::getStreamConfigForContext(
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ le_audio::types::LeAudioContextType::UNSPECIFIED))
+ .first) {}
void CheckAndReconfigureEncoders() {
auto const& codec_id = codec_wrapper_.GetLeAudioCodecId();
@@ -737,7 +724,6 @@
/* Some BIG params are set globally */
uint8_t current_phy_;
- uint8_t num_retransmit_;
AudioDataPathState audio_data_path_state_;
const void* audio_instance_;
std::vector<BroadcastId> available_broadcast_ids_;
diff --git a/system/bta/le_audio/broadcaster/broadcaster_test.cc b/system/bta/le_audio/broadcaster/broadcaster_test.cc
index 8c0174f..8aa9147 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_test.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_test.cc
@@ -23,11 +23,15 @@
#include "bta/include/bta_le_audio_api.h"
#include "bta/include/bta_le_audio_broadcaster_api.h"
#include "bta/le_audio/broadcaster/mock_state_machine.h"
+#include "bta/le_audio/content_control_id_keeper.h"
+#include "bta/le_audio/le_audio_types.h"
#include "bta/le_audio/mock_iso_manager.h"
#include "bta/test/common/mock_controller.h"
#include "device/include/controller.h"
#include "stack/include/btm_iso_api.h"
+using le_audio::types::LeAudioContextType;
+
using testing::_;
using testing::AtLeast;
using testing::DoAll;
@@ -104,12 +108,25 @@
namespace le_audio {
namespace broadcaster {
namespace {
-static constexpr LeAudioBroadcaster::AudioProfile default_profile =
- LeAudioBroadcaster::AudioProfile::SONIFICATION;
+static constexpr auto default_context =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::ALERTS);
static constexpr BroadcastCode default_code = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
-static const std::vector<uint8_t> default_metadata = {0x03, 0x02, 0x01, 0x00};
+static const std::vector<uint8_t> default_metadata = {
+ le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1,
+ le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
+ default_context & 0x00FF, (default_context & 0xFF00) >> 8};
+
+static constexpr uint8_t media_ccid = 0xC0;
+static constexpr auto media_context =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::MEDIA);
+static const std::vector<uint8_t> media_metadata = {
+ le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1,
+ le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
+ media_context & 0x00FF, (media_context & 0xFF00) >> 8};
class MockLeAudioBroadcasterCallbacks
: public bluetooth::le_audio::LeAudioBroadcasterCallbacks {
@@ -185,6 +202,8 @@
LeAudioBroadcaster::Initialize(&mock_broadcaster_callbacks_,
base::Bind([]() -> bool { return true; }));
+ ContentControlIdKeeper::GetInstance()->Start();
+
/* Simulate random generator */
uint8_t random[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
generator_cb.Run(random);
@@ -208,13 +227,12 @@
}
uint32_t InstantiateBroadcast(
- LeAudioBroadcaster::AudioProfile profile = default_profile,
std::vector<uint8_t> metadata = default_metadata,
BroadcastCode code = default_code) {
uint32_t broadcast_id = LeAudioBroadcaster::kInstanceIdUndefined;
EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
.WillOnce(SaveArg<0>(&broadcast_id));
- LeAudioBroadcaster::Get()->CreateAudioBroadcast(metadata, profile, code);
+ LeAudioBroadcaster::Get()->CreateAudioBroadcast(metadata, code);
return broadcast_id;
}
@@ -232,13 +250,6 @@
ASSERT_TRUE(LeAudioBroadcaster::IsLeAudioBroadcasterRunning());
}
-TEST_F(BroadcasterTest, GetNumRetransmit) {
- LeAudioBroadcaster::Get()->SetNumRetransmit(8);
- ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 8);
- LeAudioBroadcaster::Get()->SetNumRetransmit(12);
- ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 12);
-}
-
TEST_F(BroadcasterTest, GetStreamingPhy) {
LeAudioBroadcaster::Get()->SetStreamingPhy(1);
ASSERT_EQ(LeAudioBroadcaster::Get()->GetStreamingPhy(), 1);
@@ -306,8 +317,7 @@
}
TEST_F(BroadcasterTest, StartAudioBroadcastMedia) {
- auto broadcast_id =
- InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::MEDIA);
+ auto broadcast_id = InstantiateBroadcast(media_metadata);
LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id);
EXPECT_CALL(mock_broadcaster_callbacks_,
@@ -478,15 +488,6 @@
ASSERT_EQ(sm->GetAdvertisingSid(), metadata.adv_sid);
}
-TEST_F(BroadcasterTest, SetNumRetransmit) {
- auto broadcast_id = InstantiateBroadcast();
- LeAudioBroadcaster::Get()->SetNumRetransmit(9);
- ASSERT_EQ(MockBroadcastStateMachine::GetLastInstance()->cb->GetNumRetransmit(
- broadcast_id),
- 9);
- ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 9);
-}
-
TEST_F(BroadcasterTest, SetStreamingPhy) {
LeAudioBroadcaster::Get()->SetStreamingPhy(2);
// From now on new streams should be using Phy = 2.
@@ -500,10 +501,9 @@
ASSERT_EQ(LeAudioBroadcaster::Get()->GetStreamingPhy(), 1);
}
-TEST_F(BroadcasterTest, StreamParamsSonification) {
+TEST_F(BroadcasterTest, StreamParamsAlerts) {
uint8_t expected_channels = 1u;
-
- InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::SONIFICATION);
+ InstantiateBroadcast();
auto config = MockBroadcastStateMachine::GetLastInstance()->cfg;
// Check audio configuration
@@ -516,15 +516,24 @@
TEST_F(BroadcasterTest, StreamParamsMedia) {
uint8_t expected_channels = 2u;
-
- InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::MEDIA);
+ ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+ InstantiateBroadcast(media_metadata);
auto config = MockBroadcastStateMachine::GetLastInstance()->cfg;
// Check audio configuration
ASSERT_EQ(config.codec_wrapper.GetNumChannels(), expected_channels);
+
+ auto& subgroup = config.announcement.subgroup_configs[0];
+
// Matches number of bises in the announcement
- ASSERT_EQ(config.announcement.subgroup_configs[0].bis_configs.size(),
- expected_channels);
+ ASSERT_EQ(subgroup.bis_configs.size(), expected_channels);
+ // Verify CCID for Media
+ auto ccid_list_opt = types::LeAudioLtvMap(subgroup.metadata)
+ .Find(le_audio::types::kLeAudioMetadataTypeCcidList);
+ ASSERT_TRUE(ccid_list_opt.has_value());
+ auto ccid_list = ccid_list_opt.value();
+ ASSERT_EQ(1u, ccid_list.size());
+ ASSERT_EQ(media_ccid, ccid_list[0]);
// Note: Num of bises at IsoManager level is verified by state machine tests
}
diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.cc b/system/bta/le_audio/broadcaster/broadcaster_types.cc
index 996c167..66048a0 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_types.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_types.cc
@@ -23,11 +23,13 @@
#include "bta_le_audio_broadcaster_api.h"
#include "btm_ble_api_types.h"
#include "embdrv/lc3/include/lc3.h"
+#include "osi/include/properties.h"
using bluetooth::le_audio::BasicAudioAnnouncementBisConfig;
using bluetooth::le_audio::BasicAudioAnnouncementCodecConfig;
using bluetooth::le_audio::BasicAudioAnnouncementData;
using bluetooth::le_audio::BasicAudioAnnouncementSubgroup;
+using le_audio::types::LeAudioContextType;
namespace le_audio {
namespace broadcaster {
@@ -197,6 +199,18 @@
// Frame len.
40);
+static const BroadcastCodecWrapper lc3_stereo_16_2 = BroadcastCodecWrapper(
+ kLeAudioCodecIdLc3,
+ // LeAudioCodecConfiguration
+ {.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
+ .sample_rate = LeAudioCodecConfiguration::kSampleRate16000,
+ .bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
+ .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
+ // Bitrate
+ 32000,
+ // Frame len.
+ 40);
+
static const BroadcastCodecWrapper lc3_stereo_24_2 = BroadcastCodecWrapper(
kLeAudioCodecIdLc3,
// LeAudioCodecConfiguration
@@ -209,16 +223,6 @@
// Frame len.
60);
-const BroadcastCodecWrapper& BroadcastCodecWrapper::getCodecConfigForProfile(
- LeAudioBroadcaster::AudioProfile profile) {
- switch (profile) {
- case LeAudioBroadcaster::AudioProfile::SONIFICATION:
- return lc3_mono_16_2;
- case LeAudioBroadcaster::AudioProfile::MEDIA:
- return lc3_stereo_24_2;
- };
-}
-
const std::map<uint32_t, uint8_t> sample_rate_to_sampling_freq_map = {
{LeAudioCodecConfiguration::kSampleRate8000,
codec_spec_conf::kLeAudioSamplingFreq8000Hz},
@@ -312,6 +316,77 @@
return os;
}
+static const BroadcastQosConfig qos_config_2_10 = BroadcastQosConfig(2, 10);
+
+static const BroadcastQosConfig qos_config_4_60 = BroadcastQosConfig(4, 60);
+
+std::ostream& operator<<(
+ std::ostream& os, const le_audio::broadcaster::BroadcastQosConfig& config) {
+ os << " BroadcastQosConfig=[";
+ os << "RTN=" << +config.getRetransmissionNumber();
+ os << ", MaxTransportLatency=" << config.getMaxTransportLatency();
+ os << "]";
+ return os;
+}
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+ lc3_mono_16_2_1 = {lc3_mono_16_2, qos_config_2_10};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+ lc3_mono_16_2_2 = {lc3_mono_16_2, qos_config_4_60};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+ lc3_stereo_16_2_2 = {lc3_stereo_16_2, qos_config_4_60};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+ lc3_stereo_24_2_1 = {lc3_stereo_24_2, qos_config_2_10};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+ lc3_stereo_24_2_2 = {lc3_stereo_24_2, qos_config_4_60};
+
+std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+getStreamConfigForContext(uint16_t context) {
+ // High quality, Low Latency
+ auto contexts_stereo_24_2_1 =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::GAME) |
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::LIVE);
+ if (context & contexts_stereo_24_2_1) return lc3_stereo_24_2_1;
+
+ // Low quality, Low Latency
+ auto contexts_mono_16_2_1 =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::INSTRUCTIONAL);
+ if (context & contexts_mono_16_2_1) return lc3_mono_16_2_1;
+
+ // Low quality, High Reliability
+ auto contexts_stereo_16_2_2 =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::SOUNDEFFECTS) |
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::UNSPECIFIED);
+ if (context & contexts_stereo_16_2_2) return lc3_stereo_16_2_2;
+
+ auto contexts_mono_16_2_2 =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::ALERTS) |
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::NOTIFICATIONS) |
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::EMERGENCYALARM);
+ if (context & contexts_mono_16_2_2) return lc3_mono_16_2_2;
+
+ // High quality, High Reliability
+ auto contexts_stereo_24_2_2 =
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::MEDIA);
+ if (context & contexts_stereo_24_2_2) return lc3_stereo_24_2_2;
+
+ // Defaults: Low quality, High Reliability
+ return lc3_mono_16_2_2;
+}
+
} /* namespace broadcaster */
} /* namespace le_audio */
diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.h b/system/bta/le_audio/broadcaster/broadcaster_types.h
index 5995c32..9e97da8 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_types.h
+++ b/system/bta/le_audio/broadcaster/broadcaster_types.h
@@ -71,9 +71,6 @@
return *this;
};
- static const BroadcastCodecWrapper& getCodecConfigForProfile(
- LeAudioBroadcaster::AudioProfile profile);
-
types::LeAudioLtvMap GetSubgroupCodecSpecData() const;
types::LeAudioLtvMap GetBisCodecSpecData(uint8_t bis_idx) const;
@@ -127,6 +124,32 @@
std::ostream& os,
const le_audio::broadcaster::BroadcastCodecWrapper& config);
+struct BroadcastQosConfig {
+ BroadcastQosConfig(uint8_t retransmission_number,
+ uint16_t max_transport_latency)
+ : retransmission_number(retransmission_number),
+ max_transport_latency(max_transport_latency) {}
+
+ BroadcastQosConfig& operator=(const BroadcastQosConfig& other) {
+ retransmission_number = other.retransmission_number;
+ max_transport_latency = other.max_transport_latency;
+ return *this;
+ };
+
+ uint8_t getRetransmissionNumber() const { return retransmission_number; }
+ uint16_t getMaxTransportLatency() const { return max_transport_latency; }
+
+ private:
+ uint8_t retransmission_number;
+ uint16_t max_transport_latency;
+};
+
+std::ostream& operator<<(
+ std::ostream& os, const le_audio::broadcaster::BroadcastQosConfig& config);
+
+std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+getStreamConfigForContext(uint16_t context);
+
} // namespace broadcaster
} // namespace le_audio
diff --git a/system/bta/le_audio/broadcaster/state_machine.cc b/system/bta/le_audio/broadcaster/state_machine.cc
index 71ed8fc..1014f9a 100644
--- a/system/bta/le_audio/broadcaster/state_machine.cc
+++ b/system/bta/le_audio/broadcaster/state_machine.cc
@@ -29,6 +29,7 @@
#include "bta/le_audio/le_audio_types.h"
#include "gd/common/strings.h"
#include "osi/include/log.h"
+#include "osi/include/properties.h"
#include "service/common/bluetooth/low_energy_constants.h"
#include "stack/include/ble_advertiser.h"
#include "stack/include/btm_iso_api.h"
@@ -365,11 +366,10 @@
struct bluetooth::hci::iso_manager::big_create_params big_params = {
.adv_handle = GetAdvertisingSid(),
.num_bis = sm_config_.codec_wrapper.GetNumChannels(),
- .sdu_itv = callbacks_->GetSduItv(GetBroadcastId()),
+ .sdu_itv = sm_config_.codec_wrapper.GetDataIntervalUs(),
.max_sdu_size = sm_config_.codec_wrapper.GetMaxSduSize(),
- .max_transport_latency =
- callbacks_->GetMaxTransportLatency(GetBroadcastId()),
- .rtn = callbacks_->GetNumRetransmit(GetBroadcastId()),
+ .max_transport_latency = sm_config_.qos_config.getMaxTransportLatency(),
+ .rtn = sm_config_.qos_config.getRetransmissionNumber(),
.phy = sm_config_.streaming_phy,
.packing = 0x00, /* Sequencial */
.framing = 0x00, /* Unframed */
@@ -655,6 +655,7 @@
: PHYS[config.streaming_phy])
<< "\n";
os << " Codec Wrapper: " << config.codec_wrapper << "\n";
+ os << " Qos Config: " << config.qos_config << "\n";
if (config.broadcast_code) {
os << " Broadcast Code: [";
for (auto& el : *config.broadcast_code) {
diff --git a/system/bta/le_audio/broadcaster/state_machine.h b/system/bta/le_audio/broadcaster/state_machine.h
index 10d6a93..c1cca06 100644
--- a/system/bta/le_audio/broadcaster/state_machine.h
+++ b/system/bta/le_audio/broadcaster/state_machine.h
@@ -97,6 +97,7 @@
bluetooth::le_audio::BroadcastId broadcast_id;
uint8_t streaming_phy;
BroadcastCodecWrapper codec_wrapper;
+ BroadcastQosConfig qos_config;
bluetooth::le_audio::BasicAudioAnnouncementData announcement;
std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code;
};
@@ -190,9 +191,6 @@
const void* data = nullptr) = 0;
virtual void OnOwnAddressResponse(uint32_t broadcast_id, uint8_t addr_type,
RawAddress address) = 0;
- virtual uint8_t GetNumRetransmit(uint32_t broadcast_id) = 0;
- virtual uint32_t GetSduItv(uint32_t broadcast_id) = 0;
- virtual uint16_t GetMaxTransportLatency(uint32_t broadcast_id) = 0;
};
std::ostream& operator<<(
diff --git a/system/bta/le_audio/broadcaster/state_machine_test.cc b/system/bta/le_audio/broadcaster/state_machine_test.cc
index 9c44117..d2a84ad 100644
--- a/system/bta/le_audio/broadcaster/state_machine_test.cc
+++ b/system/bta/le_audio/broadcaster/state_machine_test.cc
@@ -72,10 +72,6 @@
MOCK_METHOD((void), OnOwnAddressResponse,
(uint32_t broadcast_id, uint8_t addr_type, RawAddress addr),
(override));
- MOCK_METHOD((uint8_t), GetNumRetransmit, (uint32_t broadcast_id), (override));
- MOCK_METHOD((uint32_t), GetSduItv, (uint32_t broadcast_id), (override));
- MOCK_METHOD((uint16_t), GetMaxTransportLatency, (uint32_t broadcast_id),
- (override));
};
class StateMachineTest : public Test {
@@ -236,8 +232,8 @@
}
uint32_t InstantiateStateMachine(
- LeAudioBroadcaster::AudioProfile profile =
- LeAudioBroadcaster::AudioProfile::SONIFICATION) {
+ le_audio::types::LeAudioContextType context =
+ le_audio::types::LeAudioContextType::UNSPECIFIED) {
// We will get the state machine create status update in an async callback
// so let's wait for it here.
instance_creation_promise_ = std::promise<uint32_t>();
@@ -246,13 +242,16 @@
static uint8_t broadcast_id_lsb = 1;
- auto codec_wrapper =
- BroadcastCodecWrapper::getCodecConfigForProfile(profile);
+ auto context_int = static_cast<
+ std::underlying_type<le_audio::types::LeAudioContextType>::type>(
+ context);
+ auto codec_qos_pair = getStreamConfigForContext(context_int);
auto broadcast_id = broadcast_id_lsb++;
pending_broadcasts_.push_back(BroadcastStateMachine::CreateInstance({
.broadcast_id = broadcast_id,
// .streaming_phy = ,
- .codec_wrapper = std::move(codec_wrapper),
+ .codec_wrapper = codec_qos_pair.first,
+ .qos_config = codec_qos_pair.second,
// .announcement = ,
// .broadcast_code = ,
}));
@@ -456,10 +455,10 @@
EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true))
.Times(1);
- auto sound_profile = LeAudioBroadcaster::AudioProfile::MEDIA;
+ auto sound_context = le_audio::types::LeAudioContextType::MEDIA;
uint8_t num_channels = 2;
- auto broadcast_id = InstantiateStateMachine(sound_profile);
+ auto broadcast_id = InstantiateStateMachine(sound_context);
ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
BroadcastStateMachine::State::CONFIGURED);
@@ -509,7 +508,7 @@
.Times(1);
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
BroadcastStateMachine::State::CONFIGURED);
@@ -535,7 +534,7 @@
.Times(1);
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
BroadcastStateMachine::State::CONFIGURED);
@@ -552,7 +551,7 @@
TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::START);
@@ -573,7 +572,7 @@
TEST_F(StateMachineTest, ProcessMessageStopWhenStreaming) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::START);
@@ -599,7 +598,7 @@
TEST_F(StateMachineTest, ProcessMessageSuspendWhenStreaming) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::START);
@@ -621,7 +620,7 @@
TEST_F(StateMachineTest, ProcessMessageStartWhenStopped) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::STOP);
@@ -647,7 +646,7 @@
TEST_F(StateMachineTest, ProcessMessageStopWhenStopped) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::STOP);
@@ -668,7 +667,7 @@
TEST_F(StateMachineTest, ProcessMessageSuspendWhenStopped) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::STOP);
@@ -692,7 +691,7 @@
.Times(1);
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
BroadcastStateMachine::State::CONFIGURED);
@@ -751,7 +750,7 @@
TEST_F(StateMachineTest, OnRemoveIsoDataPathError) {
auto broadcast_id =
- InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+ InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
broadcasts_[broadcast_id]->ProcessMessage(
BroadcastStateMachine::Message::START);
@@ -801,10 +800,10 @@
EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true))
.Times(1);
- auto sound_profile = LeAudioBroadcaster::AudioProfile::MEDIA;
+ auto sound_context = le_audio::types::LeAudioContextType::MEDIA;
uint8_t num_channels = 2;
- auto broadcast_id = InstantiateStateMachine(sound_profile);
+ auto broadcast_id = InstantiateStateMachine(sound_context);
ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
BroadcastStateMachine::State::CONFIGURED);
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 5206f7b..f12a078 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -32,6 +32,7 @@
#include "client_parser.h"
#include "codec_manager.h"
#include "common/time_util.h"
+#include "content_control_id_keeper.h"
#include "device/include/controller.h"
#include "devices.h"
#include "embdrv/lc3/include/lc3.h"
@@ -61,6 +62,7 @@
using bluetooth::le_audio::GroupStatus;
using bluetooth::le_audio::GroupStreamStatus;
using le_audio::CodecManager;
+using le_audio::ContentControlIdKeeper;
using le_audio::LeAudioDevice;
using le_audio::LeAudioDeviceGroup;
using le_audio::LeAudioDeviceGroups;
@@ -82,6 +84,12 @@
using le_audio::client_parser::ascs::kCtpResponseNoReason;
/* Enums */
+enum class AudioReconfigurationResult {
+ RECONFIGURATION_NEEDED = 0x00,
+ RECONFIGURATION_NOT_NEEDED,
+ RECONFIGURATION_NOT_POSSIBLE
+};
+
enum class AudioState {
IDLE = 0x00,
READY_TO_START,
@@ -90,6 +98,25 @@
RELEASING,
};
+std::ostream& operator<<(std::ostream& os,
+ const AudioReconfigurationResult& state) {
+ switch (state) {
+ case AudioReconfigurationResult::RECONFIGURATION_NEEDED:
+ os << "RECONFIGURATION_NEEDED";
+ break;
+ case AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED:
+ os << "RECONFIGURATION_NOT_NEEDED";
+ break;
+ case AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE:
+ os << "RECONFIGRATION_NOT_POSSIBLE";
+ break;
+ default:
+ os << "UNKNOWN";
+ break;
+ }
+ return os;
+}
+
std::ostream& operator<<(std::ostream& os, const AudioState& audio_state) {
switch (audio_state) {
case AudioState::IDLE:
@@ -308,12 +335,8 @@
group_remove_node(group, address);
}
- int GetCcid(le_audio::types::LeAudioContextType context_type) {
- if (ccids_.count(context_type) == 0) {
- return -1;
- }
-
- return ccids_[context_type];
+ int GetCcid(uint16_t context_type) {
+ return ContentControlIdKeeper::GetInstance()->GetCcid(context_type);
}
/* This callback happens if kLeAudioDeviceSetStateTimeoutMs timeout happens
@@ -364,7 +387,7 @@
callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
group->snk_audio_locations_.to_ulong(),
group->src_audio_locations_.to_ulong(),
- new_group_updated_contexts->to_ulong());
+ group->GetActiveContexts().to_ulong());
}
}
@@ -487,7 +510,11 @@
std::optional<AudioContexts> old_group_updated_contexts =
old_group->UpdateActiveContextsMap(old_group->GetActiveContexts());
- if (old_group_updated_contexts || old_group->ReloadAudioLocations()) {
+ bool group_conf_changed = old_group->ReloadAudioLocations();
+ group_conf_changed |= old_group->ReloadAudioDirections();
+ group_conf_changed |= old_group_updated_contexts.has_value();
+
+ if (group_conf_changed) {
callbacks_->OnAudioConf(old_group->audio_directions_, old_group_id,
old_group->snk_audio_locations_.to_ulong(),
old_group->src_audio_locations_.to_ulong(),
@@ -550,7 +577,11 @@
std::optional<AudioContexts> updated_contexts =
group->UpdateActiveContextsMap(group->GetActiveContexts());
- if (updated_contexts || group->ReloadAudioLocations())
+ bool group_conf_changed = group->ReloadAudioLocations();
+ group_conf_changed |= group->ReloadAudioDirections();
+ group_conf_changed |= updated_contexts.has_value();
+
+ if (group_conf_changed)
callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
group->snk_audio_locations_.to_ulong(),
group->src_audio_locations_.to_ulong(),
@@ -622,7 +653,7 @@
bool result = groupStateMachine_->StartStream(
group, static_cast<LeAudioContextType>(final_context_type),
- GetCcid(static_cast<LeAudioContextType>(final_context_type)));
+ GetCcid(final_context_type));
if (result)
stream_setup_start_timestamp_ =
bluetooth::common::time_get_os_boottime_us();
@@ -713,17 +744,7 @@
void SetCcidInformation(int ccid, int context_type) override {
LOG_DEBUG("Ccid: %d, context type %d", ccid, context_type);
-
- std::bitset<16> test{static_cast<uint16_t>(context_type)};
- auto ctx_type =
- static_cast<le_audio::types::LeAudioContextType>(context_type);
- if (test.count() > 1 ||
- ctx_type >= le_audio::types::LeAudioContextType::RFU) {
- LOG_ERROR("Unknownd context type %d", context_type);
- return;
- }
-
- ccids_[ctx_type] = ccid;
+ ContentControlIdKeeper::GetInstance()->SetCcid(context_type, ccid);
}
void StartAudioSession(LeAudioDeviceGroup* group,
@@ -827,11 +848,24 @@
}
}
- /* Configure audio HAL sessions with most frequent context.
- * If reconfiguration is not needed it means, context type is not supported
+ /* Try configure audio HAL sessions with most frequent context.
+ * If reconfiguration is not needed it means, context type is not supported.
+ * If most frequest scenario is not supported, try to find first supported.
*/
+ LeAudioContextType default_context_type = LeAudioContextType::UNSPECIFIED;
+ if (group->IsContextSupported(LeAudioContextType::MEDIA)) {
+ default_context_type = LeAudioContextType::MEDIA;
+ } else {
+ for (LeAudioContextType context_type :
+ le_audio::types::kLeAudioContextAllTypesArray) {
+ if (group->IsContextSupported(context_type)) {
+ default_context_type = context_type;
+ break;
+ }
+ }
+ }
UpdateConfigAndCheckIfReconfigurationIsNeeded(group_id,
- LeAudioContextType::MEDIA);
+ default_context_type);
if (current_source_codec_config.IsInvalid() &&
current_sink_codec_config.IsInvalid()) {
LOG(WARNING) << __func__ << ", unsupported device configurations";
@@ -1086,9 +1120,13 @@
* Read of available context during initial attribute discovery.
* Group would be assigned once service search is completed.
*/
- if (group)
- group->UpdateActiveContextsMap(leAudioDevice->GetAvailableContexts());
-
+ if (group && group->UpdateActiveContextsMap(
+ leAudioDevice->GetAvailableContexts())) {
+ callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
+ group->snk_audio_locations_.to_ulong(),
+ group->src_audio_locations_.to_ulong(),
+ group->GetActiveContexts().to_ulong());
+ }
return;
}
@@ -1113,9 +1151,13 @@
* Read of available context during initial attribute discovery.
* Group would be assigned once service search is completed.
*/
- if (group)
- group->UpdateActiveContextsMap(leAudioDevice->GetAvailableContexts());
-
+ if (group && group->UpdateActiveContextsMap(
+ leAudioDevice->GetAvailableContexts())) {
+ callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
+ group->snk_audio_locations_.to_ulong(),
+ group->src_audio_locations_.to_ulong(),
+ group->GetActiveContexts().to_ulong());
+ }
return;
}
@@ -1144,7 +1186,12 @@
/* Read of source audio locations during initial attribute discovery.
* Group would be assigned once service search is completed.
*/
- if (group && group->ReloadAudioLocations()) {
+ if (!group) return;
+
+ bool group_conf_changed = group->ReloadAudioLocations();
+ group_conf_changed |= group->ReloadAudioDirections();
+
+ if (group_conf_changed) {
callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
group->snk_audio_locations_.to_ulong(),
group->src_audio_locations_.to_ulong(),
@@ -1173,7 +1220,12 @@
/* Read of source audio locations during initial attribute discovery.
* Group would be assigned once service search is completed.
*/
- if (group && group->ReloadAudioLocations()) {
+ if (!group) return;
+
+ bool group_conf_changed = group->ReloadAudioLocations();
+ group_conf_changed |= group->ReloadAudioDirections();
+
+ if (group_conf_changed) {
callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
group->snk_audio_locations_.to_ulong(),
group->src_audio_locations_.to_ulong(),
@@ -1234,6 +1286,9 @@
if (ParseAseCtpNotification(*ntf, len, value))
ControlPointNotificationHandler(*ntf);
+ } else if (hdl == leAudioDevice->tmap_role_hdl_) {
+ le_audio::client_parser::tmap::ParseTmapRole(leAudioDevice->tmap_role_,
+ len, value);
} else {
LOG(ERROR) << __func__ << ", Unknown attribute read: " << loghex(hdl);
}
@@ -1279,10 +1334,6 @@
BtaGattQueue::ConfigureMtu(leAudioDevice->conn_id_, 240);
}
- /* If we know services, register for notifications */
- if (leAudioDevice->known_service_handles_)
- RegisterKnownNotifications(leAudioDevice);
-
if (BTM_SecIsSecurityPending(address)) {
/* if security collision happened, wait for encryption done
* (BTA_GATTC_ENC_CMPL_CB_EVT) */
@@ -1376,6 +1427,10 @@
return;
}
+ /* If we know services, register for notifications */
+ if (leAudioDevice->known_service_handles_)
+ RegisterKnownNotifications(leAudioDevice);
+
if (leAudioDevice->encrypted_) {
LOG(INFO) << __func__ << " link already encrypted, nothing to do";
return;
@@ -1529,6 +1584,7 @@
const gatt::Service* pac_svc = nullptr;
const gatt::Service* ase_svc = nullptr;
+ const gatt::Service* tmas_svc = nullptr;
std::vector<uint16_t> csis_primary_handles;
uint16_t cas_csis_included_handle = 0;
@@ -1559,6 +1615,10 @@
break;
}
}
+ } else if (tmp.uuid == le_audio::uuid::kTelephonyMediaAudioServiceUuid) {
+ LOG_INFO(", Found Telephony and Media Audio service, handle: %04x",
+ tmp.handle);
+ tmas_svc = &tmp;
}
}
@@ -1815,6 +1875,25 @@
}
}
+ if (tmas_svc) {
+ for (const gatt::Characteristic& charac : tmas_svc->characteristics) {
+ if (charac.uuid ==
+ le_audio::uuid::kTelephonyMediaAudioProfileRoleCharacteristicUuid) {
+ leAudioDevice->tmap_role_hdl_ = charac.value_handle;
+
+ /* Obtain initial state of TMAP role */
+ BtaGattQueue::ReadCharacteristic(conn_id,
+ leAudioDevice->tmap_role_hdl_,
+ OnGattReadRspStatic, NULL);
+
+ LOG_INFO(
+ ", Found Telephony and Media Profile characteristic, "
+ "handle: %04x",
+ leAudioDevice->tmap_role_hdl_);
+ }
+ }
+ }
+
leAudioDevice->known_service_handles_ = true;
leAudioDevice->notify_connected_after_read_ = true;
@@ -1993,8 +2072,8 @@
if (bytes_per_sample == 2) {
int16_t* out = (int16_t*)mono_out.data();
+ const int16_t* in = (int16_t*)(buf.data());
for (size_t i = 0; i < frames; ++i) {
- const int16_t* in = (int16_t*)(buf.data());
int accum = 0;
accum += *in++;
accum += *in++;
@@ -2003,8 +2082,8 @@
}
} else if (bytes_per_sample == 4) {
int32_t* out = (int32_t*)mono_out.data();
+ const int32_t* in = (int32_t*)(buf.data());
for (size_t i = 0; i < frames; ++i) {
- const int32_t* in = (int32_t*)(buf.data());
int accum = 0;
accum += *in++;
accum += *in++;
@@ -2017,7 +2096,7 @@
return mono_out;
}
- void PrepareAndSendToTwoDevices(
+ void PrepareAndSendToTwoCises(
const std::vector<uint8_t>& data,
struct le_audio::stream_configuration* stream_conf) {
uint16_t byte_count = stream_conf->sink_octets_per_codec_frame;
@@ -2087,7 +2166,7 @@
right_cis_handle, chan_right_enc.data(), chan_right_enc.size());
}
- void PrepareAndSendToSingleDevice(
+ void PrepareAndSendToSingleCis(
const std::vector<uint8_t>& data,
struct le_audio::stream_configuration* stream_conf) {
int num_channels = stream_conf->sink_num_of_channels;
@@ -2270,9 +2349,12 @@
}
if (stream_conf.sink_num_of_devices == 2) {
- PrepareAndSendToTwoDevices(data, &stream_conf);
+ PrepareAndSendToTwoCises(data, &stream_conf);
+ } else if (stream_conf.sink_streams.size() == 2 ) {
+ /* Streaming to one device but 2 CISes */
+ PrepareAndSendToTwoCises(data, &stream_conf);
} else {
- PrepareAndSendToSingleDevice(data, &stream_conf);
+ PrepareAndSendToSingleCis(data, &stream_conf);
}
}
@@ -2701,14 +2783,17 @@
std::move(cleanupCb).Run();
}
- bool UpdateConfigAndCheckIfReconfigurationIsNeeded(
+ AudioReconfigurationResult UpdateConfigAndCheckIfReconfigurationIsNeeded(
int group_id, LeAudioContextType context_type) {
bool reconfiguration_needed = false;
+ bool sink_cfg_available = true;
+ bool source_cfg_available = true;
+
auto group = aseGroups_.FindById(group_id);
if (!group) {
LOG(ERROR) << __func__
<< ", Invalid group: " << static_cast<int>(group_id);
- return reconfiguration_needed;
+ return AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED;
}
std::optional<LeAudioCodecConfiguration> source_configuration =
@@ -2728,11 +2813,7 @@
current_source_codec_config = {0, 0, 0, 0};
reconfiguration_needed = true;
}
-
- LOG(INFO) << __func__
- << ", group does not supports source direction for"
- " context: "
- << static_cast<int>(context_type);
+ source_cfg_available = false;
}
if (sink_configuration) {
@@ -2746,20 +2827,28 @@
reconfiguration_needed = true;
}
- LOG(INFO) << __func__
- << ", group does not supports sink direction for"
- " context: "
- << static_cast<int>(context_type);
+ sink_cfg_available = false;
}
- if (reconfiguration_needed) {
- LOG(INFO) << __func__
- << " Session reconfiguration needed group: " << group->group_id_
- << " for context type: " << static_cast<int>(context_type);
+ LOG_DEBUG(
+ " Context: %s Reconfigufation_needed = %d, sink_cfg_available = %d, "
+ "source_cfg_available = %d",
+ ToString(context_type).c_str(), reconfiguration_needed,
+ sink_cfg_available, source_cfg_available);
+
+ if (!reconfiguration_needed) {
+ return AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED;
}
+ if (!sink_cfg_available && !source_cfg_available) {
+ return AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE;
+ }
+
+ LOG_INFO(" Session reconfiguration needed group: %d for context type: %s",
+ group->group_id_, ToString(context_type).c_str());
+
current_context_type_ = context_type;
- return reconfiguration_needed;
+ return AudioReconfigurationResult::RECONFIGURATION_NEEDED;
}
bool OnAudioResume(LeAudioDeviceGroup* group) {
@@ -3088,16 +3177,19 @@
}
LeAudioContextType AudioContentToLeAudioContext(
- LeAudioContextType current_context_type,
audio_content_type_t content_type, audio_usage_t usage) {
/* Check audio attribute usage of stream */
switch (usage) {
case AUDIO_USAGE_MEDIA:
return LeAudioContextType::MEDIA;
case AUDIO_USAGE_VOICE_COMMUNICATION:
- case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
case AUDIO_USAGE_CALL_ASSISTANT:
return LeAudioContextType::CONVERSATIONAL;
+ case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ if (content_type == AUDIO_CONTENT_TYPE_SPEECH)
+ return LeAudioContextType::CONVERSATIONAL;
+ else
+ return LeAudioContextType::MEDIA;
case AUDIO_USAGE_GAME:
return LeAudioContextType::GAME;
case AUDIO_USAGE_NOTIFICATION:
@@ -3119,13 +3211,17 @@
LeAudioContextType ChooseContextType(
std::vector<LeAudioContextType>& available_contents) {
- /* Mini policy. Voice is prio 1, media is prio 2 */
+ /* Mini policy. Voice is prio 1, game prio 2, media is prio 3 */
auto iter = find(available_contents.begin(), available_contents.end(),
LeAudioContextType::CONVERSATIONAL);
if (iter != available_contents.end())
return LeAudioContextType::CONVERSATIONAL;
iter = find(available_contents.begin(), available_contents.end(),
+ LeAudioContextType::GAME);
+ if (iter != available_contents.end()) return LeAudioContextType::GAME;
+
+ iter = find(available_contents.begin(), available_contents.end(),
LeAudioContextType::MEDIA);
if (iter != available_contents.end()) return LeAudioContextType::MEDIA;
@@ -3135,10 +3231,19 @@
bool StopStreamIfNeeded(LeAudioDeviceGroup* group,
LeAudioContextType new_context_type) {
- DLOG(INFO) << __func__ << " context type " << int(new_context_type);
- if (!UpdateConfigAndCheckIfReconfigurationIsNeeded(group->group_id_,
- new_context_type)) {
- DLOG(INFO) << __func__ << " reconfiguration not needed";
+ auto reconfig_result = UpdateConfigAndCheckIfReconfigurationIsNeeded(
+ group->group_id_, new_context_type);
+
+ LOG_INFO("group_id %d, context type %s, reconfig_needed %s",
+ group->group_id_, ToString(new_context_type).c_str(),
+ ToString(reconfig_result).c_str());
+ if (reconfig_result ==
+ AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED) {
+ return false;
+ }
+
+ if (reconfig_result ==
+ AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE) {
return false;
}
@@ -3159,8 +3264,24 @@
auto tracks = source_metadata.tracks;
auto track_count = source_metadata.track_count;
+ if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
+ LOG(WARNING) << ", cannot start streaming if no active group set";
+ return;
+ }
+
+ auto group = aseGroups_.FindById(active_group_id_);
+ if (!group) {
+ LOG(ERROR) << __func__
+ << ", Invalid group: " << static_cast<int>(active_group_id_);
+ return;
+ }
+ bool is_group_streaming =
+ (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
std::vector<LeAudioContextType> contexts;
+ auto supported_context_type = group->GetActiveContexts();
+
while (track_count) {
if (tracks->content_type == 0 && tracks->usage == 0) {
--track_count;
@@ -3171,37 +3292,31 @@
LOG_INFO("%s: usage=%d, content_type=%d, gain=%f", __func__,
tracks->usage, tracks->content_type, tracks->gain);
- auto new_context = AudioContentToLeAudioContext(
- current_context_type_, tracks->content_type, tracks->usage);
- contexts.push_back(new_context);
+ auto new_context =
+ AudioContentToLeAudioContext(tracks->content_type, tracks->usage);
+
+ /* Check only supported context types.*/
+ if (static_cast<int>(new_context) & supported_context_type.to_ulong()) {
+ contexts.push_back(new_context);
+ } else {
+ LOG_WARN(" Context type %s not supported by remote device",
+ ToString(new_context).c_str());
+ }
--track_count;
++tracks;
}
if (contexts.empty()) {
- DLOG(INFO) << __func__ << " invalid metadata update";
+ LOG_WARN(" invalid/unknown metadata update");
return;
}
auto new_context = ChooseContextType(contexts);
- DLOG(INFO) << __func__
- << " new_context_type: " << static_cast<int>(new_context);
-
- auto group = aseGroups_.FindById(active_group_id_);
- if (!group) {
- LOG(ERROR) << __func__
- << ", Invalid group: " << static_cast<int>(active_group_id_);
- return;
- }
+ LOG_DEBUG("new_context_type: %s", ToString(new_context).c_str());
if (new_context == current_context_type_) {
- LOG(INFO) << __func__ << " Context did not changed.";
- return;
- }
-
- if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
- LOG(WARNING) << ", cannot start streaming if no active group set";
+ LOG_INFO("Context did not changed.");
return;
}
@@ -3210,7 +3325,7 @@
return;
}
- if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ if (is_group_streaming) {
/* Configuration is the same for new context, just will do update
* metadata of stream
*/
@@ -3430,7 +3545,7 @@
callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
group->snk_audio_locations_.to_ulong(),
group->src_audio_locations_.to_ulong(),
- updated_contexts->to_ulong());
+ group->GetActiveContexts().to_ulong());
}
group->SetPendingUpdateAvailableContexts(std::nullopt);
@@ -3499,7 +3614,9 @@
SuspendedForReconfiguration();
if (groupStateMachine_->ConfigureStream(
group, current_context_type_,
- GetCcid(current_context_type_))) {
+ GetCcid(static_cast<
+ std::underlying_type<LeAudioContextType>::type>(
+ current_context_type_)))) {
/* If configuration succeed wait for new status. */
return;
}
@@ -3541,10 +3658,6 @@
/* Speaker(s) */
AudioState audio_sender_state_;
- /* Ccid informations */
- std::map<le_audio::types::LeAudioContextType /* context */, int /*ccid */>
- ccids_;
-
/* Current stream configuration */
LeAudioCodecConfiguration current_source_codec_config;
LeAudioCodecConfiguration current_sink_codec_config;
@@ -3846,6 +3959,7 @@
IsoManager::GetInstance()->RegisterCigCallbacks(stateMachineHciCallbacks);
CodecManager::GetInstance()->Start(offloading_preference);
+ ContentControlIdKeeper::GetInstance()->Start();
callbacks_->OnInitialized();
}
@@ -3888,6 +4002,7 @@
}
CodecManager::GetInstance()->Stop();
+ ContentControlIdKeeper::GetInstance()->Stop();
LeAudioGroupStateMachine::Cleanup();
IsoManager::GetInstance()->Stop();
le_audio::MetricsCollector::Get()->Flush();
diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc
index e7d6461..ddd744f 100644
--- a/system/bta/le_audio/client_parser.cc
+++ b/system/bta/le_audio/client_parser.cc
@@ -657,5 +657,26 @@
}
} // namespace pacs
+namespace tmap {
+
+bool ParseTmapRole(std::bitset<16>& role, uint16_t len, const uint8_t* value) {
+ if (len != kTmapRoleLen) {
+ LOG_ERROR(
+ ", Wrong len of Telephony Media Audio Profile Role, "
+ "characteristic");
+ return false;
+ }
+
+ STREAM_TO_UINT16(role, value);
+
+ LOG_INFO(
+ ", Telephony Media Audio Profile Role:"
+ "\n\tRole: %s",
+ role.to_string().c_str());
+
+ return true;
+}
+} // namespace tmap
+
} // namespace client_parser
} // namespace le_audio
diff --git a/system/bta/le_audio/client_parser.h b/system/bta/le_audio/client_parser.h
index 159f452..8974989 100644
--- a/system/bta/le_audio/client_parser.h
+++ b/system/bta/le_audio/client_parser.h
@@ -240,5 +240,13 @@
bool ParseSupportedAudioContexts(struct acs_supported_audio_contexts& rsp,
uint16_t len, const uint8_t* value);
} // namespace pacs
+
+namespace tmap {
+
+constexpr uint16_t kTmapRoleLen = 2;
+
+bool ParseTmapRole(std::bitset<16>& role, uint16_t len, const uint8_t* value);
+
+} // namespace tmap
} // namespace client_parser
} // namespace le_audio
diff --git a/system/bta/le_audio/client_parser_test.cc b/system/bta/le_audio/client_parser_test.cc
index 0dd98c8..c54c237 100644
--- a/system/bta/le_audio/client_parser_test.cc
+++ b/system/bta/le_audio/client_parser_test.cc
@@ -1645,5 +1645,25 @@
} // namespace ascs
+namespace tmap {
+
+TEST(LeAudioClientParserTest, testParseTmapRoleValid) {
+ std::bitset<16> role;
+ const uint8_t value[] = {0x3F, 0x00};
+
+ ASSERT_TRUE(ParseTmapRole(role, 2, value));
+
+ ASSERT_EQ(role, 0x003F); // All possible TMAP roles
+}
+
+TEST(LeAudioClientParserTest, testParseTmapRoleInvalidLen) {
+ std::bitset<16> role;
+ const uint8_t value[] = {0x00, 0x3F};
+
+ ASSERT_FALSE(ParseTmapRole(role, 3, value));
+}
+
+} // namespace tmap
+
} // namespace client_parser
} // namespace le_audio
diff --git a/system/bta/le_audio/content_control_id_keeper.cc b/system/bta/le_audio/content_control_id_keeper.cc
new file mode 100644
index 0000000..73657d9
--- /dev/null
+++ b/system/bta/le_audio/content_control_id_keeper.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#include "content_control_id_keeper.h"
+
+#include <bitset>
+#include <map>
+
+#include "le_audio_types.h"
+#include "osi/include/log.h"
+
+namespace {
+
+using le_audio::types::LeAudioContextType;
+
+} // namespace
+
+namespace le_audio {
+
+struct ccid_keeper {
+ public:
+ ccid_keeper() {}
+
+ ~ccid_keeper() {}
+
+ void SetCcid(uint16_t context_type, int ccid) {
+ LOG_DEBUG("Ccid: %d, context type %d", ccid, context_type);
+
+ std::bitset<16> test{context_type};
+ if (test.count() > 1 ||
+ context_type >=
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::RFU)) {
+ LOG_ERROR("Unknownd context type %d", context_type);
+ return;
+ }
+
+ auto ctx_type = static_cast<LeAudioContextType>(context_type);
+ ccids_.insert_or_assign(ctx_type, ccid);
+ }
+
+ int GetCcid(uint16_t context_type) const {
+ std::bitset<16> test{context_type};
+ if (test.count() > 1 ||
+ context_type >=
+ static_cast<std::underlying_type<LeAudioContextType>::type>(
+ LeAudioContextType::RFU)) {
+ LOG_ERROR("Unknownd context type %d", context_type);
+ return -1;
+ }
+
+ auto ctx_type = static_cast<LeAudioContextType>(context_type);
+
+ if (ccids_.count(ctx_type) == 0) {
+ return -1;
+ }
+
+ return ccids_.at(ctx_type);
+ }
+
+ private:
+ /* Ccid informations */
+ std::map<LeAudioContextType /* context */, int /*ccid */> ccids_;
+};
+
+struct ContentControlIdKeeper::impl {
+ impl(const ContentControlIdKeeper& ccid_keeper) : ccid_keeper_(ccid_keeper) {}
+
+ void Start() {
+ LOG_ASSERT(!ccid_keeper_impl_);
+ ccid_keeper_impl_ = std::make_unique<ccid_keeper>();
+ }
+
+ void Stop() {
+ LOG_ASSERT(ccid_keeper_impl_);
+ ccid_keeper_impl_.reset();
+ }
+
+ bool IsRunning() { return ccid_keeper_impl_ ? true : false; }
+
+ const ContentControlIdKeeper& ccid_keeper_;
+ std::unique_ptr<ccid_keeper> ccid_keeper_impl_;
+};
+
+ContentControlIdKeeper::ContentControlIdKeeper()
+ : pimpl_(std::make_unique<impl>(*this)) {}
+
+void ContentControlIdKeeper::Start() {
+ if (!pimpl_->IsRunning()) pimpl_->Start();
+}
+
+void ContentControlIdKeeper::Stop() {
+ if (pimpl_->IsRunning()) pimpl_->Stop();
+}
+
+int ContentControlIdKeeper::GetCcid(uint16_t context_type) const {
+ if (!pimpl_->IsRunning()) {
+ return -1;
+ }
+
+ return pimpl_->ccid_keeper_impl_->GetCcid(context_type);
+}
+
+void ContentControlIdKeeper::SetCcid(uint16_t context_type, int ccid) {
+ if (pimpl_->IsRunning()) {
+ pimpl_->ccid_keeper_impl_->SetCcid(context_type, ccid);
+ }
+}
+
+} // namespace le_audio
diff --git a/system/bta/le_audio/content_control_id_keeper.h b/system/bta/le_audio/content_control_id_keeper.h
new file mode 100644
index 0000000..697c19c
--- /dev/null
+++ b/system/bta/le_audio/content_control_id_keeper.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace le_audio {
+
+class ContentControlIdKeeper {
+ public:
+ ContentControlIdKeeper();
+ virtual ~ContentControlIdKeeper() = default;
+ static ContentControlIdKeeper* GetInstance(void) {
+ static ContentControlIdKeeper* instance = new ContentControlIdKeeper();
+ return instance;
+ }
+ void Start(void);
+ void Stop(void);
+ virtual void SetCcid(uint16_t context_type, int ccid);
+ virtual int GetCcid(uint16_t context_type) const;
+
+ private:
+ struct impl;
+ std::unique_ptr<impl> pimpl_;
+};
+} // namespace le_audio
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 6943f3e..a74c027 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -152,11 +152,11 @@
}
}
-void LeAudioDeviceGroup::Activate(void) {
+void LeAudioDeviceGroup::Activate(LeAudioContextType context_type) {
for (auto leAudioDevice : leAudioDevices_) {
if (leAudioDevice.expired()) continue;
- leAudioDevice.lock()->ActivateConfiguredAses();
+ leAudioDevice.lock()->ActivateConfiguredAses(context_type);
}
}
@@ -551,8 +551,8 @@
/* This method returns AudioContext value if support for any type has changed */
std::optional<AudioContexts> LeAudioDeviceGroup::UpdateActiveContextsMap(void) {
- DLOG(INFO) << __func__ << " group id: " << group_id_ << " active contexts: "
- << loghex(active_contexts_mask_.to_ulong());
+ LOG_DEBUG(" group id: %d, active contexts: 0x%04lx", group_id_,
+ active_contexts_mask_.to_ulong());
return UpdateActiveContextsMap(active_contexts_mask_);
}
@@ -562,10 +562,19 @@
AudioContexts contexts = 0x0000;
bool active_contexts_has_been_modified = false;
+ if (update_contexts.none()) {
+ LOG_DEBUG("No context updated");
+ return contexts;
+ }
+
for (LeAudioContextType ctx_type : types::kLeAudioContextAllTypesArray) {
AudioContexts type_set = static_cast<uint16_t>(ctx_type);
-
+ LOG_DEBUG("Taking context: %s, 0x%04lx",
+ bluetooth::common::ToString(ctx_type).c_str(),
+ update_contexts.to_ulong());
if ((type_set & update_contexts).none()) {
+ LOG_INFO("Configuration not in updated context %s",
+ bluetooth::common::ToString(ctx_type).c_str());
/* Fill context bitset for possible returned value if updated */
if (active_context_to_configuration_map.count(ctx_type) > 0)
contexts |= type_set;
@@ -575,9 +584,11 @@
auto new_conf = FindFirstSupportedConfiguration(ctx_type);
+ bool ctx_previously_not_supported =
+ (active_context_to_configuration_map.count(ctx_type) == 0 ||
+ active_context_to_configuration_map[ctx_type] == nullptr);
/* Check if support for context type has changed */
- if (active_context_to_configuration_map.count(ctx_type) == 0 ||
- active_context_to_configuration_map[ctx_type] == nullptr) {
+ if (ctx_previously_not_supported) {
/* Current configuration for context type is empty */
if (new_conf == nullptr) {
/* Configuration remains empty */
@@ -604,12 +615,14 @@
}
}
- LOG(INFO) << __func__ << ", updated context: " << loghex(int(ctx_type))
- << ", "
- << (active_context_to_configuration_map[ctx_type] != nullptr
- ? active_context_to_configuration_map[ctx_type]->name
- : "empty")
- << " -> " << (new_conf != nullptr ? new_conf->name : "empty");
+ LOG_INFO(
+ "updated context: %s, %s -> %s",
+ bluetooth::common::ToString(ctx_type).c_str(),
+ (ctx_previously_not_supported
+ ? "empty"
+ : active_context_to_configuration_map[ctx_type]->name.c_str()),
+ (new_conf != nullptr ? new_conf->name.c_str() : "empty"));
+
active_context_to_configuration_map[ctx_type] = new_conf;
}
@@ -646,6 +659,22 @@
return true;
}
+bool LeAudioDeviceGroup::ReloadAudioDirections(void) {
+ uint8_t updated_audio_directions = 0x00;
+
+ for (const auto& device : leAudioDevices_) {
+ if (device.expired()) continue;
+ updated_audio_directions |= device.lock().get()->audio_directions_;
+ }
+
+ /* Nothing has changed */
+ if (updated_audio_directions == audio_directions_) return false;
+
+ audio_directions_ = updated_audio_directions;
+
+ return true;
+}
+
bool LeAudioDeviceGroup::IsInTransition(void) {
return target_state_ != current_state_;
}
@@ -744,10 +773,9 @@
types::LeAudioContextType context_type) {
if (!set_configurations::check_if_may_cover_scenario(
audio_set_conf, NumOfConnected(context_type))) {
- DLOG(INFO) << __func__ << " cannot cover scenario "
- << static_cast<int>(context_type)
- << " size of for context type: "
- << +NumOfConnected(context_type);
+ LOG_DEBUG(" cannot cover scenario %s: size of for context type %d",
+ bluetooth::common::ToString(context_type).c_str(),
+ +NumOfConnected(context_type));
return false;
}
@@ -759,11 +787,9 @@
* 3) ASEs should be filled according to performance profile.
*/
for (const auto& ent : (*audio_set_conf).confs) {
- DLOG(INFO) << __func__
- << " Looking for configuration: " << audio_set_conf->name
- << " - "
- << (ent.direction == types::kLeAudioDirectionSink ? "snk"
- : "src");
+ LOG_DEBUG(" Looking for configuration: %s - %s",
+ audio_set_conf->name.c_str(),
+ (ent.direction == types::kLeAudioDirectionSink ? "snk" : "src"));
uint8_t required_device_cnt = ent.device_cnt;
uint8_t max_required_ase_per_dev =
@@ -771,10 +797,11 @@
uint8_t active_ase_num = 0;
auto strategy = ent.strategy;
- DLOG(INFO) << __func__ << " Number of devices: " << +required_device_cnt
- << " number of ASEs: " << +ent.ase_cnt
- << " Max ASE per device: " << +max_required_ase_per_dev
- << " strategy: " << static_cast<int>(strategy);
+ LOG_DEBUG(
+ " Number of devices: %d, number of ASEs: %d, Max ASE per device: %d "
+ "strategy: %d",
+ +required_device_cnt, +ent.ase_cnt, +max_required_ase_per_dev,
+ static_cast<int>(strategy));
for (auto* device = GetFirstDeviceWithActiveContext(context_type);
device != nullptr && required_device_cnt > 0;
@@ -806,8 +833,8 @@
strategy, audio_locations,
std::get<LeAudioLc3Config>(ent.codec.config).GetChannelCount(),
device->GetLc3SupportedChannelCount(ent.direction))) {
- DLOG(INFO) << __func__ << " insufficient device audio allocation: "
- << audio_locations;
+ LOG_DEBUG(" insufficient device audio allocation: %lu",
+ audio_locations.to_ulong());
continue;
}
@@ -825,13 +852,13 @@
if (required_device_cnt > 0) {
/* Don't left any active devices if requirements are not met */
- DLOG(INFO) << __func__ << " could not configure all the devices";
+ LOG_DEBUG(" could not configure all the devices");
return false;
}
}
- DLOG(INFO) << "Choosed ASE Configuration for group: " << this->group_id_
- << " configuration: " << audio_set_conf->name;
+ LOG_DEBUG("Chosen ASE Configuration for group: %d, configuration: %s",
+ this->group_id_, audio_set_conf->name.c_str());
return true;
}
@@ -996,6 +1023,7 @@
for (; needed_ase && ase; needed_ase--) {
ase->active = true;
+ ase->configured_for_context_type = context_type;
active_ases++;
if (ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED)
@@ -1205,13 +1233,14 @@
const set_configurations::AudioSetConfigurations* confs =
AudioSetConfigurationProvider::Get()->GetConfigurations(context_type);
- DLOG(INFO) << __func__ << " context type: " << (int)context_type
- << " number of connected devices: " << NumOfConnected();
+ LOG_DEBUG("context type: %s, number of connected devices: %d",
+ bluetooth::common::ToString(context_type).c_str(),
+ +NumOfConnected());
/* Filter out device set for all scenarios */
if (!set_configurations::check_if_may_cover_scenario(confs,
NumOfConnected())) {
- LOG(ERROR) << __func__ << ", group is unable to cover scenario";
+ LOG_ERROR(", group is unable to cover scenario");
return nullptr;
}
@@ -1219,7 +1248,7 @@
for (const auto& conf : *confs) {
if (IsConfigurationSupported(conf, context_type)) {
- DLOG(INFO) << __func__ << " found: " << conf->name;
+ LOG_DEBUG("found: %s", conf->name.c_str());
return conf;
}
}
@@ -1656,7 +1685,7 @@
direction == types::kLeAudioDirectionSink ? snk_pacs_ : src_pacs_;
if (pacs.size() == 0) {
- LOG(ERROR) << __func__ << " missing PAC for direction " << +direction;
+ LOG_ERROR("missing PAC for direction %d", direction);
return nullptr;
}
@@ -1738,12 +1767,12 @@
updated_contexts = snk_contexts ^ avail_snk_contexts_;
updated_contexts |= src_contexts ^ avail_src_contexts_;
- DLOG(INFO) << __func__
- << "\n\t avail_snk_contexts_: " << avail_snk_contexts_.to_string()
- << "\n\t avail_src_contexts_: " << avail_src_contexts_.to_string()
- << "\n\t snk_contexts:" << snk_contexts.to_string()
- << "\n\t src_contexts: " << src_contexts.to_string()
- << "\n\t updated_contexts: " << updated_contexts.to_string();
+ LOG_DEBUG(
+ "\n\t avail_snk_contexts_: %s \n\t avail_src_contexts_: %s \n\t "
+ "snk_contexts: %s \n\t src_contexts: %s \n\t updated_contexts: %s",
+ avail_snk_contexts_.to_string().c_str(),
+ avail_src_contexts_.to_string().c_str(), snk_contexts.to_string().c_str(),
+ src_contexts.to_string().c_str(), updated_contexts.to_string().c_str());
avail_snk_contexts_ = snk_contexts;
avail_src_contexts_ = src_contexts;
@@ -1751,7 +1780,7 @@
return updated_contexts;
}
-void LeAudioDevice::ActivateConfiguredAses(void) {
+void LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) {
if (conn_id_ == GATT_INVALID_CONN_ID) {
LOG_DEBUG(" Device %s is not connected ", address_.ToString().c_str());
return;
@@ -1760,7 +1789,8 @@
LOG_DEBUG(" Configuring device %s", address_.ToString().c_str());
for (auto& ase : ases_) {
if (!ase.active &&
- ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
+ ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
+ ase.configured_for_context_type == context_type) {
LOG_DEBUG(" Ase id %d, cis id %d activated.", ase.id, ase.cis_id);
ase.active = true;
}
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 30e4bbc..60881d0 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -62,6 +62,7 @@
bool encrypted_;
int group_id_;
bool csis_member_;
+ std::bitset<16> tmap_role_;
uint8_t audio_directions_;
types::AudioLocations snk_audio_locations_;
@@ -76,6 +77,7 @@
struct types::hdl_pair audio_supp_cont_hdls_;
std::vector<struct types::ase> ases_;
struct types::hdl_pair ctp_hdls_;
+ uint16_t tmap_role_hdl_;
alarm_t* link_quality_timer;
uint16_t link_quality_timer_data;
@@ -141,7 +143,7 @@
types::AudioContexts SetAvailableContexts(types::AudioContexts snk_cont_val,
types::AudioContexts src_cont_val);
void DeactivateAllAses(void);
- void ActivateConfiguredAses(void);
+ void ActivateConfiguredAses(types::LeAudioContextType context_type);
void Dump(int fd);
void DisconnectAcl(void);
std::vector<uint8_t> GetMetadata(types::LeAudioContextType context_type,
@@ -215,7 +217,7 @@
int Size(void);
int NumOfConnected(
types::LeAudioContextType context_type = types::LeAudioContextType::RFU);
- void Activate(void);
+ void Activate(types::LeAudioContextType context_type);
void Deactivate(void);
void Cleanup(void);
LeAudioDevice* GetFirstDevice(void);
@@ -249,6 +251,7 @@
types::AudioContexts contexts);
std::optional<types::AudioContexts> UpdateActiveContextsMap(void);
bool ReloadAudioLocations(void);
+ bool ReloadAudioDirections(void);
const set_configurations::AudioSetConfiguration* GetActiveConfiguration(void);
types::LeAudioContextType GetCurrentContextType(void);
bool IsPendingConfiguration(void);
diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc
index 7628182..2cb62d6 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -199,14 +199,15 @@
/* Update those values, on any change of codec linked with content type */
switch (context_type) {
case LeAudioContextType::RINGTONE:
- if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2)
+ if (id == Lc3SettingId::LC3_16_2 || id == Lc3SettingId::LC3_16_1 ||
+ id == Lc3SettingId::LC3_32_2)
return true;
break;
case LeAudioContextType::CONVERSATIONAL:
if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2 ||
- id == Lc3SettingId::LC3_32_2)
+ id == Lc3SettingId::LC3_24_2 || id == Lc3SettingId::LC3_32_2)
return true;
break;
@@ -526,6 +527,13 @@
for (const auto& audio_set_conf : *configurations) {
// the configuration should fail if there are no active ases expected
bool success_expected = data_size > 0;
+ bool not_matching_scenario = false;
+ uint8_t snk_ases_cnt = 0;
+ uint8_t src_ases_cnt = 0;
+ PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
+ snk_pac_builder.Reset();
+ src_pac_builder.Reset();
+
for (int i = 0; i < data_size; i++) {
success_expected &= (data[i].active_channel_num_snk +
data[i].active_channel_num_src) > 0;
@@ -536,19 +544,34 @@
* DualDev). This is just how the test is created and this limitation
* should be removed b/230107540
*/
- PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
for (const auto& entry : (*audio_set_conf).confs) {
+ /* Configuration requires more devices than are supplied */
+ if (entry.device_cnt > data_size) {
+ not_matching_scenario = true;
+ break;
+ }
if (entry.direction == kLeAudioDirectionSink) {
+ snk_ases_cnt += entry.ase_cnt;
snk_pac_builder.Add(entry.codec, data[i].audio_channel_counts_snk);
} else {
src_pac_builder.Add(entry.codec, data[i].audio_channel_counts_src);
}
}
+ /* Scenario requires more ASEs than defined requirement */
+ if (snk_ases_cnt < data[i].audio_channel_counts_snk ||
+ src_ases_cnt < data[i].audio_channel_counts_src) {
+ not_matching_scenario = true;
+ }
+
+ if (not_matching_scenario) break;
+
data[i].device->snk_pacs_ = snk_pac_builder.Get();
data[i].device->src_pacs_ = src_pac_builder.Get();
}
+ if (not_matching_scenario) continue;
+
/* Stimulate update of active context map */
group_->UpdateActiveContextsMap(static_cast<uint16_t>(context_type));
ASSERT_EQ(success_expected, group_->Configure(context_type));
@@ -564,9 +587,15 @@
void TestAsesActive(LeAudioCodecId codec_id, uint8_t sampling_frequency,
uint8_t frame_duration, uint16_t octets_per_frame) {
+ bool active_ase = false;
+
for (const auto& device : devices_) {
for (const auto& ase : device->ases_) {
- ASSERT_TRUE(ase.active);
+ if (!ase.active) continue;
+
+ /* Configure may request only partial ases to be activated */
+ if (!active_ase && ase.active) active_ase = true;
+
ASSERT_EQ(ase.codec_id, codec_id);
/* FIXME: Validate other codec parameters than LC3 if any */
@@ -578,6 +607,8 @@
}
}
}
+
+ ASSERT_TRUE(active_ase);
}
void TestActiveAses(void) {
@@ -664,10 +695,10 @@
};
TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_ringtone) {
- LeAudioDevice* mono_speaker = AddTestDevice(1, 0);
- TestGroupAseConfigurationData data({mono_speaker,
- kLeAudioCodecLC3ChannelCountSingleChannel,
- kLeAudioCodecLC3ChannelCountNone, 1, 0});
+ LeAudioDevice* mono_speaker = AddTestDevice(1, 1);
+ TestGroupAseConfigurationData data(
+ {mono_speaker, kLeAudioCodecLC3ChannelCountSingleChannel,
+ kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0});
TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
}
@@ -691,10 +722,10 @@
}
TEST_F(LeAudioAseConfigurationTest, test_bounded_headphones_ringtone) {
- LeAudioDevice* bounded_headphones = AddTestDevice(2, 0);
- TestGroupAseConfigurationData data({bounded_headphones,
- kLeAudioCodecLC3ChannelCountTwoChannel,
- kLeAudioCodecLC3ChannelCountNone, 2, 0});
+ LeAudioDevice* bounded_headphones = AddTestDevice(2, 1);
+ TestGroupAseConfigurationData data(
+ {bounded_headphones, kLeAudioCodecLC3ChannelCountTwoChannel,
+ kLeAudioCodecLC3ChannelCountSingleChannel, 2, 0});
TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
}
@@ -827,7 +858,7 @@
}
TEST_F(LeAudioAseConfigurationTest, test_lc3_config_ringtone) {
- AddTestDevice(1, 0);
+ AddTestDevice(1, 1);
TestLc3CodecConfig(LeAudioContextType::RINGTONE);
}
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index f2983dc..2525d23 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -808,7 +808,6 @@
void SetUp() override {
init_message_loop_thread();
-
ON_CALL(controller_interface_, SupportsBleConnectedIsochronousStreamCentral)
.WillByDefault(Return(true));
ON_CALL(controller_interface_,
@@ -3161,5 +3160,47 @@
LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
}
+
+TEST_F(UnicastTest, StartNotSupportedContextType) {
+ const RawAddress test_address0 = GetTestAddress(0);
+ int group_id = bluetooth::groups::kGroupUnknown;
+
+ SetSampleDatabaseEarbudsValid(
+ 1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
+ codec_spec_conf::kLeAudioLocationStereo, 0x0004, false /*add_csis*/,
+ true /*add_cas*/, true /*add_pacs*/, true /*add_ascs*/, 1 /*set_size*/,
+ 0 /*rank*/);
+ EXPECT_CALL(mock_client_callbacks_,
+ OnConnectionState(ConnectionState::CONNECTED, test_address0))
+ .Times(1);
+ EXPECT_CALL(mock_client_callbacks_,
+ OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
+ .WillOnce(DoAll(SaveArg<1>(&group_id)));
+
+ ConnectLeAudio(test_address0);
+ ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
+
+ // Start streaming
+ uint8_t cis_count_out = 1;
+ uint8_t cis_count_in = 0;
+
+ // Audio sessions are started only when device gets active
+ EXPECT_CALL(*mock_unicast_audio_source_, Start(_, _)).Times(1);
+ EXPECT_CALL(*mock_audio_sink_, Start(_, _)).Times(1);
+ LeAudioClient::Get()->GroupSetActive(group_id);
+
+ StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
+
+ Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
+ SyncOnMainLoop();
+
+ // Verify Data transfer on one audio source cis
+ TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
+
+ EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
+ UpdateMetadata(AUDIO_USAGE_GAME, AUDIO_CONTENT_TYPE_UNKNOWN);
+}
} // namespace
} // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 44d581b..69e958b 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -104,27 +104,33 @@
auto req = reqs.Find(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq);
auto pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeSamplingFreq);
if (!req || !pac) {
- DLOG(ERROR) << __func__ << ", lack of sampling frequency fields";
+ LOG_DEBUG(", lack of sampling frequency fields");
return false;
}
u8_req_val = VEC_UINT8_TO_UINT8(req.value());
u16_pac_val = VEC_UINT8_TO_UINT16(pac.value());
- /*
- * Note: Requirements are in the codec configuration specification which
- * are values coming from BAP Appendix A1.2.1
- */
- DLOG(INFO) << __func__ << " Req:SamplFreq=" << loghex(u8_req_val);
- /* NOTE: Below is Codec specific cababilities comes form BAP Appendix A A1.1.1
- * Note this is a bitfield
- */
- DLOG(INFO) << __func__ << " Pac:SamplFreq=" << loghex(u16_pac_val);
-
/* TODO: Integrate with codec capabilities */
if (!(u16_pac_val &
codec_spec_caps::SamplingFreqConfig2Capability(u8_req_val))) {
- DLOG(ERROR) << __func__ << ", sampling frequency not supported";
+ /*
+ * Note: Requirements are in the codec configuration specification which
+ * are values coming from Assigned Numbers: Codec_Specific_Configuration
+ */
+ LOG_DEBUG(
+ " Req:SamplFreq= 0x%04x (Assigned Numbers: "
+ "Codec_Specific_Configuration)",
+ u8_req_val);
+ /* NOTE: Below is Codec specific cababilities comes from Assigned Numbers:
+ * Codec_Specific_Capabilities
+ */
+ LOG_DEBUG(
+ " Pac:SamplFreq= 0x%04x (Assigned numbers: "
+ "Codec_Specific_Capabilities - bitfield)",
+ u16_pac_val);
+
+ LOG_DEBUG(", sampling frequency not supported");
return false;
}
@@ -132,20 +138,20 @@
req = reqs.Find(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration);
pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeFrameDuration);
if (!req || !pac) {
- DLOG(ERROR) << __func__ << ", lack of frame duration fields";
+ LOG_DEBUG(", lack of frame duration fields");
return false;
}
u8_req_val = VEC_UINT8_TO_UINT8(req.value());
u8_pac_val = VEC_UINT8_TO_UINT8(pac.value());
- DLOG(INFO) << __func__ << " Req:FrameDur=" << loghex(u8_req_val);
- DLOG(INFO) << __func__ << " Pac:FrameDur=" << loghex(u8_pac_val);
if ((u8_req_val != codec_spec_conf::kLeAudioCodecLC3FrameDur7500us &&
u8_req_val != codec_spec_conf::kLeAudioCodecLC3FrameDur10000us) ||
!(u8_pac_val &
(codec_spec_caps::FrameDurationConfig2Capability(u8_req_val)))) {
- DLOG(ERROR) << __func__ << ", frame duration not supported";
+ LOG_DEBUG(" Req:FrameDur=0x%04x", u8_req_val);
+ LOG_DEBUG(" Pac:FrameDur=0x%04x", u8_pac_val);
+ LOG_DEBUG(", frame duration not supported");
return false;
}
@@ -162,15 +168,16 @@
* the Unicast Server supports mandatory one channel.
*/
if (!pac) {
- DLOG(WARNING) << __func__ << ", no Audio_Channel_Counts field in PAC";
+ LOG_DEBUG(", no Audio_Channel_Counts field in PAC, using default 0x01");
u8_pac_val = 0x01;
} else {
u8_pac_val = VEC_UINT8_TO_UINT8(pac.value());
}
- DLOG(INFO) << __func__ << " Pac:AudioChanCnt=" << loghex(u8_pac_val);
if (!((1 << (required_audio_chan_num - 1)) & u8_pac_val)) {
- DLOG(ERROR) << __func__ << ", channel count warning";
+ LOG_DEBUG(" Req:AudioChanCnt=0x%04x", 1 << (required_audio_chan_num - 1));
+ LOG_DEBUG(" Pac:AudioChanCnt=0x%04x", u8_pac_val);
+ LOG_DEBUG(", channel count warning");
return false;
}
@@ -179,26 +186,26 @@
pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeOctetPerFrame);
if (!req || !pac) {
- DLOG(ERROR) << __func__ << ", lack of octet per frame fields";
+ LOG_DEBUG(", lack of octet per frame fields");
return false;
}
u16_req_val = VEC_UINT8_TO_UINT16(req.value());
- DLOG(INFO) << __func__ << " Req:OctetsPerFrame=" << int(u16_req_val);
-
/* Minimal value 0-1 byte */
u16_pac_val = VEC_UINT8_TO_UINT16(pac.value());
- DLOG(INFO) << __func__ << " Pac:MinOctetsPerFrame=" << int(u16_pac_val);
if (u16_req_val < u16_pac_val) {
- DLOG(ERROR) << __func__ << ", octet per frame below minimum";
+ LOG_DEBUG(" Req:OctetsPerFrame=%d", int(u16_req_val));
+ LOG_DEBUG(" Pac:MinOctetsPerFrame=%d", int(u16_pac_val));
+ LOG_DEBUG(", octet per frame below minimum");
return false;
}
/* Maximal value 2-3 byte */
u16_pac_val = OFF_VEC_UINT8_TO_UINT16(pac.value(), 2);
- DLOG(INFO) << __func__ << " Pac:MaxOctetsPerFrame=" << int(u16_pac_val);
if (u16_req_val > u16_pac_val) {
- DLOG(ERROR) << __func__ << ", octet per frame above maximum";
+ LOG_DEBUG(" Req:MaxOctetsPerFrame=%d", int(u16_req_val));
+ LOG_DEBUG(" Pac:MaxOctetsPerFrame=%d", int(u16_pac_val));
+ LOG_DEBUG(", octet per frame above maximum");
return false;
}
@@ -212,7 +219,7 @@
if (codec_id != pac.codec_id) return false;
- DLOG(INFO) << __func__ << ": Settings for format " << +codec_id.coding_format;
+ LOG_DEBUG(": Settings for format: 0x%02x ", codec_id.coding_format);
switch (codec_id.coding_format) {
case kLeAudioCodingFormatLC3:
@@ -229,7 +236,7 @@
case kLeAudioCodingFormatLC3:
return std::get<types::LeAudioLc3Config>(config).GetSamplingFrequencyHz();
default:
- DLOG(WARNING) << __func__ << ", invalid codec id";
+ LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
return 0;
}
};
@@ -239,7 +246,7 @@
case kLeAudioCodingFormatLC3:
return std::get<types::LeAudioLc3Config>(config).GetFrameDurationUs();
default:
- DLOG(WARNING) << __func__ << ", invalid codec id";
+ LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
return 0;
}
};
@@ -250,7 +257,7 @@
/* XXX LC3 supports 16, 24, 32 */
return 16;
default:
- DLOG(WARNING) << __func__ << ", invalid codec id";
+ LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
return 0;
}
};
@@ -258,12 +265,12 @@
uint8_t CodecCapabilitySetting::GetConfigChannelCount() const {
switch (id.coding_format) {
case kLeAudioCodingFormatLC3:
- DLOG(INFO) << __func__ << ", count = "
- << static_cast<int>(std::get<types::LeAudioLc3Config>(config)
- .channel_count);
+ LOG_DEBUG("count = %d",
+ static_cast<int>(
+ std::get<types::LeAudioLc3Config>(config).channel_count));
return std::get<types::LeAudioLc3Config>(config).channel_count;
default:
- DLOG(WARNING) << __func__ << ", invalid codec id";
+ LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
return 0;
}
}
@@ -469,6 +476,49 @@
<< ", AudioChanLoc=" << loghex(*config.audio_channel_allocation) << ")";
return os;
}
+std::ostream& operator<<(std::ostream& os, const LeAudioContextType& context) {
+ switch (context) {
+ case LeAudioContextType::UNINITIALIZED:
+ os << "UNINITIALIZED";
+ break;
+ case LeAudioContextType::UNSPECIFIED:
+ os << "UNSPECIFIED";
+ break;
+ case LeAudioContextType::CONVERSATIONAL:
+ os << "CONVERSATIONAL";
+ break;
+ case LeAudioContextType::MEDIA:
+ os << "MEDIA";
+ break;
+ case LeAudioContextType::GAME:
+ os << "GAME";
+ break;
+ case LeAudioContextType::INSTRUCTIONAL:
+ os << "INSTRUCTIONAL";
+ break;
+ case LeAudioContextType::VOICEASSISTANTS:
+ os << "VOICEASSISTANTS";
+ break;
+ case LeAudioContextType::LIVE:
+ os << "LIVE";
+ break;
+ case LeAudioContextType::SOUNDEFFECTS:
+ os << "SOUNDEFFECTS";
+ break;
+ case LeAudioContextType::NOTIFICATIONS:
+ os << "NOTIFICATIONS";
+ break;
+ case LeAudioContextType::RINGTONE:
+ os << "RINGTONE";
+ break;
+ case LeAudioContextType::EMERGENCYALARM:
+ os << "EMERGENCYALARM";
+ break;
+ default:
+ os << "UNKNOWN";
+ break;
+ }
+ return os;
+}
} // namespace types
-
} // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 7960359..a41742c 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -68,6 +68,9 @@
static const bluetooth::Uuid kAudioStreamControlServiceUuid =
bluetooth::Uuid::From16Bit(0x184E);
+static const bluetooth::Uuid kTelephonyMediaAudioServiceUuid =
+ bluetooth::Uuid::From16Bit(0x1855);
+
/* Published Audio Capabilities Service Characteristics */
static const bluetooth::Uuid kSinkPublishedAudioCapabilityCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BC9);
@@ -92,6 +95,10 @@
static const bluetooth::Uuid
kAudioStreamEndpointControlPointCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BC6);
+
+/* Telephony and Media Audio Service Characteristics */
+static const bluetooth::Uuid kTelephonyMediaAudioProfileRoleCharacteristicUuid =
+ bluetooth::Uuid::From16Bit(0x2B51);
} // namespace uuid
namespace codec_spec_conf {
@@ -391,6 +398,9 @@
: values(std::move(values)) {}
std::optional<std::vector<uint8_t>> Find(uint8_t type) const;
+ void Add(uint8_t type, std::vector<uint8_t> value) {
+ values.insert_or_assign(type, std::move(value));
+ }
bool IsEmpty() const { return values.empty(); }
void Clear() { values.clear(); }
size_t Size() const { return values.size(); }
@@ -519,6 +529,7 @@
active(false),
reconfigure(false),
data_path_state(AudioStreamDataPathState::IDLE),
+ configured_for_context_type(LeAudioContextType::UNINITIALIZED),
preferred_phy(0),
state(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {}
@@ -532,6 +543,7 @@
bool active;
bool reconfigure;
AudioStreamDataPathState data_path_state;
+ LeAudioContextType configured_for_context_type;
/* Codec configuration */
LeAudioCodecId codec_id;
@@ -572,6 +584,7 @@
std::ostream& operator<<(std::ostream& os, const AseState& state);
std::ostream& operator<<(std::ostream& os, const CigState& state);
std::ostream& operator<<(std::ostream& os, const LeAudioLc3Config& config);
+std::ostream& operator<<(std::ostream& os, const LeAudioContextType& context);
} // namespace types
namespace set_configurations {
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 2265f42..686d9a0 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -153,7 +153,7 @@
switch (group->GetState()) {
case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
if (group->GetCurrentContextType() == context_type) {
- group->Activate();
+ group->Activate(context_type);
SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
CigCreate(group);
return true;
@@ -579,6 +579,7 @@
if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) &&
(group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
LOG(INFO) << __func__ << " group: " << group->group_id_ << " is in IDLE";
+ group->UpdateActiveContextsMap();
return;
}
@@ -609,10 +610,10 @@
/* mark ASEs as not used. */
leAudioDevice->DeactivateAllAses();
- DLOG(INFO) << __func__ << " device: " << leAudioDevice->address_
- << " group connected: " << group->IsAnyDeviceConnected()
- << " all active ase disconnected: "
- << group->HaveAllActiveDevicesCisDisc();
+ LOG_DEBUG(
+ " device: %s, group connected: %d, all active ase disconnected:: %d",
+ leAudioDevice->address_.ToString().c_str(),
+ group->IsAnyDeviceConnected(), group->HaveAllActiveDevicesCisDisc());
/* Group has changed. Lets update available contexts */
group->UpdateActiveContextsMap();
@@ -1115,6 +1116,8 @@
LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
switch (ase->state) {
case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
+ case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
+ case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
if (ase->id == 0x00) {
/* Initial state of Ase - update id */
LOG(INFO) << __func__
@@ -1126,6 +1129,8 @@
LeAudioDevice* leAudioDeviceNext;
ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
ase->active = false;
+ ase->configured_for_context_type =
+ le_audio::types::LeAudioContextType::UNINITIALIZED;
if (!leAudioDevice->HaveAllActiveAsesSameState(
AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index ff33fd3..62af3b1 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -27,6 +27,7 @@
#include "btm_api_mock.h"
#include "client_parser.h"
#include "fake_osi.h"
+#include "gd/common/init_flags.h"
#include "le_audio_set_configuration_provider.h"
#include "mock_codec_manager.h"
#include "mock_controller.h"
@@ -643,6 +644,7 @@
void MultipleTestDevicePrepare(int leaudio_group_id, uint16_t context_type,
uint16_t device_cnt,
+ uint16_t update_context_type,
bool insert_default_pac_records = true) {
// Prepare fake connected device group
bool first_connections = true;
@@ -679,8 +681,10 @@
uint16_t attr_handle = ATTR_HANDLE_PACS_POOL_START;
/* As per spec, unspecified shall be supported */
- types::AudioContexts snk_context_type = kContextTypeUnspecified;
- types::AudioContexts src_context_type = kContextTypeUnspecified;
+ types::AudioContexts snk_context_type =
+ kContextTypeUnspecified | update_context_type;
+ types::AudioContexts src_context_type =
+ kContextTypeUnspecified | update_context_type;
// Prepare Sink Published Audio Capability records
if ((context_type & kContextTypeRingtone) ||
@@ -742,17 +746,20 @@
}
/* Stimulate update of active context map */
- types::AudioContexts type_set = static_cast<uint16_t>(context_type);
+ types::AudioContexts type_set = static_cast<uint16_t>(
+ update_context_type == 0 ? context_type
+ : context_type | update_context_type);
group->UpdateActiveContextsMap(type_set);
ASSERT_NE(group, nullptr);
ASSERT_EQ(group->Size(), total_devices);
}
- LeAudioDeviceGroup* PrepareSingleTestDeviceGroup(int leaudio_group_id,
- uint16_t context_type,
- uint16_t device_cnt = 1) {
- MultipleTestDevicePrepare(leaudio_group_id, context_type, device_cnt);
+ LeAudioDeviceGroup* PrepareSingleTestDeviceGroup(
+ int leaudio_group_id, uint16_t context_type, uint16_t device_cnt = 1,
+ uint16_t update_context_type = 0) {
+ MultipleTestDevicePrepare(leaudio_group_id, context_type, device_cnt,
+ update_context_type);
return le_audio_device_groups_.count(leaudio_group_id)
? le_audio_device_groups_[leaudio_group_id].get()
: nullptr;
@@ -1895,6 +1902,92 @@
types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
}
+TEST_F(StateMachineTest, testActivateStreamCachingSingle) {
+ auto context_type = kContextTypeConversational;
+ const int leaudio_group_id = 4;
+
+ additional_ases = 2;
+ /* Prepare fake connected device group with update of Media and Conversational
+ * contexts
+ */
+ auto* group = PrepareSingleTestDeviceGroup(
+ leaudio_group_id, context_type, 1,
+ kContextTypeConversational | kContextTypeMedia);
+
+ /* Since we prepared device with Conversational context in mind, only one ASE
+ * should have been configured.
+ */
+ PrepareConfigureCodecHandler(group, 0, true);
+ PrepareConfigureQosHandler(group, 0, true);
+ PrepareEnableHandler(group);
+ PrepareReceiverStartReady(group);
+ PrepareReleaseHandler(group);
+
+ /* Ctp messages we expect:
+ * 1. Codec Config
+ * 2. QoS Config
+ * 3. Enable
+ * 4. Release
+ * 5. QoS Config (because device stays in Configured state)
+ * 6. Enable
+ */
+ auto* leAudioDevice = group->GetFirstDevice();
+ EXPECT_CALL(gatt_queue,
+ WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _,
+ GATT_WRITE_NO_RSP, _, _))
+ .Times(8);
+
+ EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(2);
+ EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(2);
+ EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(5);
+ EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2);
+ EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2);
+ EXPECT_CALL(*mock_iso_manager_, RemoveCig(_)).Times(1);
+
+ InjectInitialIdleNotification(group);
+
+ // Validate GroupStreamStatus
+ EXPECT_CALL(
+ mock_callbacks_,
+ StatusReportCb(leaudio_group_id,
+ bluetooth::le_audio::GroupStreamStatus::RELEASING));
+
+ EXPECT_CALL(
+ mock_callbacks_,
+ StatusReportCb(
+ leaudio_group_id,
+ bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS));
+
+ EXPECT_CALL(mock_callbacks_,
+ StatusReportCb(leaudio_group_id,
+ bluetooth::le_audio::GroupStreamStatus::STREAMING))
+ .Times(2);
+
+ // Start the configuration and stream Conversational content
+ LeAudioGroupStateMachine::Get()->StartStream(
+ group, static_cast<types::LeAudioContextType>(context_type));
+
+ // Check if group has transitioned to a proper state
+ ASSERT_EQ(group->GetState(),
+ types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+ // Stop the stream
+ LeAudioGroupStateMachine::Get()->StopStream(group);
+
+ // Check if group has transitioned to a proper state
+ ASSERT_EQ(group->GetState(),
+ types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
+
+ // Start the configuration and stream Media content
+ context_type = kContextTypeMedia;
+ LeAudioGroupStateMachine::Get()->StartStream(
+ group, static_cast<types::LeAudioContextType>(context_type));
+
+ // Check if group has transitioned to a proper state
+ ASSERT_EQ(group->GetState(),
+ types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+}
+
TEST_F(StateMachineTest, testReleaseMultiple) {
const auto context_type = kContextTypeMedia;
const auto leaudio_group_id = 6;
diff --git a/system/btif/avrcp/avrcp_service.cc b/system/btif/avrcp/avrcp_service.cc
index 01b0998..7f33f36 100644
--- a/system/btif/avrcp/avrcp_service.cc
+++ b/system/btif/avrcp/avrcp_service.cc
@@ -106,6 +106,12 @@
BT_HDR* p_pkt) override {
return AVRC_MsgReq(handle, label, ctype, p_pkt);
}
+
+ void SaveControllerVersion(const RawAddress& bdaddr,
+ uint16_t version) override {
+ AVRC_SaveControllerVersion(bdaddr, version);
+ }
+
} avrcp_interface_;
class SdpInterfaceImpl : public SdpInterface {
diff --git a/system/btif/include/btif_common.h b/system/btif/include/btif_common.h
index abd7964..a0ee3c1 100644
--- a/system/btif/include/btif_common.h
+++ b/system/btif/include/btif_common.h
@@ -220,6 +220,8 @@
bt_bond_state_t state, int fail_reason);
void invoke_address_consolidate_cb(RawAddress main_bd_addr,
RawAddress secondary_bd_addr);
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+ RawAddress secondary_bd_addr);
void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
bt_acl_state_t state, int transport_link_type,
bt_hci_error_code_t hci_reason);
diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h
index 3e154b1..0d3225f 100644
--- a/system/btif/include/btif_storage.h
+++ b/system/btif/include/btif_storage.h
@@ -171,15 +171,17 @@
/*******************************************************************************
*
- * Function btif_storage_load_consolidate_devices
+ * Function btif_storage_load_le_devices
*
- * Description BTIF storage API - Load the consolidate devices from NVRAM
- * Additionally, this API also invokes the adaper_properties_cb
- * and invoke_address_consolidate_cb for each of the
- * consolidate devices.
+ * Description BTIF storage API - Loads all LE-only and Dual Mode devices
+ * from NVRAM. This API invokes the adaper_properties_cb.
+ * It also invokes invoke_address_consolidate_cb
+ * to consolidate each Dual Mode device and
+ * invoke_le_address_associate_cb to associate each LE-only
+ * device between its RPA and identity address.
*
******************************************************************************/
-void btif_storage_load_consolidate_devices(void);
+void btif_storage_load_le_devices(void);
/*******************************************************************************
*
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 2be88b2..58d6da0 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -883,6 +883,16 @@
main_bd_addr, secondary_bd_addr));
}
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+ RawAddress secondary_bd_addr) {
+ do_in_jni_thread(
+ FROM_HERE, base::BindOnce(
+ [](RawAddress main_bd_addr, RawAddress secondary_bd_addr) {
+ HAL_CBACK(bt_hal_cbacks, le_address_associate_cb,
+ &main_bd_addr, &secondary_bd_addr);
+ },
+ main_bd_addr, secondary_bd_addr));
+}
void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
bt_acl_state_t state, int transport_link_type,
bt_hci_error_code_t hci_reason) {
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 4c7bd52..ee6c2f2 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -78,6 +78,7 @@
#include "common/metrics.h"
#include "device/include/controller.h"
#include "device/include/interop.h"
+#include "gd/common/lru_cache.h"
#include "internal_include/stack_config.h"
#include "main/shim/dumpsys.h"
#include "main/shim/shim.h"
@@ -164,9 +165,6 @@
bool is_le_nc; /* LE Numeric comparison */
btif_dm_ble_cb_t ble;
uint8_t fail_reason;
- Uuid::UUID128Bit eir_uuids[32];
- uint8_t num_eir_uuids;
- std::set<Uuid::UUID128Bit> uuids;
} btif_dm_pairing_cb_t;
// TODO(jpawlowski): unify ?
@@ -214,6 +212,11 @@
#define MAX_BTIF_BOND_EVENT_ENTRIES 15
+#define MAX_NUM_DEVICES_IN_EIR_UUID_CACHE 128
+
+static bluetooth::common::LruCache<RawAddress, std::set<Uuid>> eir_uuids_cache(
+ MAX_NUM_DEVICES_IN_EIR_UUID_CACHE);
+
static skip_sdp_entry_t sdp_rejectlist[] = {{76}}; // Apple Mouse and Keyboard
/* This flag will be true if HCI_Inquiry is in progress */
@@ -1302,13 +1305,16 @@
/* Cache EIR queried services */
if (num_uuids > 0) {
uint16_t* p_uuid16 = (uint16_t*)uuid_list;
- pairing_cb.num_eir_uuids = 0;
- LOG_INFO("EIR UUIDS:");
+ auto uuid_iter = eir_uuids_cache.find(bdaddr);
+ if (uuid_iter == eir_uuids_cache.end()) {
+ auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{});
+ uuid_iter = std::get<0>(triple);
+ }
+ LOG_INFO("EIR UUIDs for %s:", bdaddr.ToString().c_str());
for (int i = 0; i < num_uuids; ++i) {
Uuid uuid = Uuid::From16Bit(p_uuid16[i]);
LOG_INFO(" %s", uuid.ToString().c_str());
- pairing_cb.eir_uuids[i] = uuid.To128BitBE();
- pairing_cb.num_eir_uuids++;
+ uuid_iter->second.insert(uuid);
}
}
@@ -1438,6 +1444,8 @@
/* onUuidChanged requires getBondedDevices to be populated.
** bond_state_changed needs to be sent prior to remote_device_property
*/
+ auto num_eir_uuids = 0;
+ Uuid uuid = {};
if (pairing_cb.state == BT_BOND_STATE_BONDED && pairing_cb.sdp_attempts &&
(p_data->disc_res.bd_addr == pairing_cb.bd_addr ||
p_data->disc_res.bd_addr == pairing_cb.static_bdaddr)) {
@@ -1448,33 +1456,33 @@
// when SDP failed or no UUID is discovered
if (p_data->disc_res.result != BTA_SUCCESS ||
p_data->disc_res.num_uuids == 0) {
- LOG_INFO("SDP failed, send %d EIR UUIDs to unblock bonding %s",
- pairing_cb.num_eir_uuids, bd_addr.ToString().c_str());
- bt_property_t prop_uuids;
- Uuid uuid = {};
- prop_uuids.type = BT_PROPERTY_UUIDS;
- if (pairing_cb.num_eir_uuids > 0) {
- prop_uuids.val = pairing_cb.eir_uuids;
- prop_uuids.len = pairing_cb.num_eir_uuids * Uuid::kNumBytes128;
- } else {
- prop_uuids.val = &uuid;
- prop_uuids.len = Uuid::kNumBytes128;
+ auto uuids_iter = eir_uuids_cache.find(bd_addr);
+ if (uuids_iter != eir_uuids_cache.end()) {
+ num_eir_uuids = static_cast<int>(uuids_iter->second.size());
+ LOG_INFO("SDP failed, send %d EIR UUIDs to unblock bonding %s",
+ num_eir_uuids, bd_addr.ToString().c_str());
+ for (auto eir_uuid : uuids_iter->second) {
+ auto uuid_128bit = eir_uuid.To128BitBE();
+ property_value.insert(property_value.end(), uuid_128bit.begin(),
+ uuid_128bit.end());
+ }
+ eir_uuids_cache.erase(uuids_iter);
}
-
- /* Send the event to the BTIF
- * prop_uuids will be deep copied by this call
- */
- invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, 1,
- &prop_uuids);
- pairing_cb = {};
- break;
+ if (num_eir_uuids > 0) {
+ prop.val = (void*)property_value.data();
+ prop.len = num_eir_uuids * Uuid::kNumBytes128;
+ } else {
+ LOG_WARN("SDP failed and we have no EIR UUIDs to report either");
+ prop.val = &uuid;
+ prop.len = Uuid::kNumBytes128;
+ }
}
// Both SDP and bonding are done, clear pairing control block in case
// it is not already cleared
pairing_cb = {};
}
- if (p_data->disc_res.num_uuids != 0) {
+ if (p_data->disc_res.num_uuids != 0 || num_eir_uuids != 0) {
/* Also write this to the NVRAM */
ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
@@ -1634,7 +1642,7 @@
pairing_cb.bond_type = tBTM_SEC_DEV_REC::BOND_TYPE_PERSISTENT;
if (enable_address_consolidate) {
LOG_INFO("enable address consolidate");
- btif_storage_load_consolidate_devices();
+ btif_storage_load_le_devices();
}
/* This function will also trigger the adapter_properties_cb
@@ -1892,6 +1900,12 @@
pairing_cb.fail_reason);
btif_dm_remove_bond(bd_addr);
break;
+
+ case BTA_DM_LE_ADDR_ASSOC_EVT:
+ invoke_le_address_associate_cb(p_data->proc_id_addr.pairing_bda,
+ p_data->proc_id_addr.id_addr);
+ break;
+
default:
BTIF_TRACE_WARNING("%s: unhandled event (%d)", __func__, event);
break;
@@ -2605,11 +2619,13 @@
}
static bool waiting_on_oob_advertiser_start = false;
-static uint8_t oob_advertiser_id = 0;
+static std::unique_ptr<uint8_t> oob_advertiser_id_;
static void stop_oob_advertiser() {
+ // For chasing an advertising bug b/237023051
+ LOG_DEBUG("oob_advertiser_id: %s", oob_advertiser_id_.get());
auto advertiser = get_ble_advertiser_instance();
- advertiser->Unregister(oob_advertiser_id);
- oob_advertiser_id = 0;
+ advertiser->Unregister(*oob_advertiser_id_);
+ oob_advertiser_id_ = nullptr;
}
/*******************************************************************************
@@ -2630,7 +2646,9 @@
// the state machine lifecycle. Rather, lets create the data, then start
// advertising then request the address.
if (!waiting_on_oob_advertiser_start) {
- if (oob_advertiser_id != 0) {
+ // For chasing an advertising bug b/237023051
+ LOG_DEBUG("oob_advertiser_id: %s", oob_advertiser_id_.get());
+ if (oob_advertiser_id_ != nullptr) {
stop_oob_advertiser();
}
waiting_on_oob_advertiser_start = true;
@@ -2659,7 +2677,7 @@
invoke_oob_data_request_cb(transport, false, c, r, RawAddress{}, 0x00);
SMP_ClearLocScOobData();
waiting_on_oob_advertiser_start = false;
- oob_advertiser_id = 0;
+ oob_advertiser_id_ = nullptr;
return;
}
LOG_DEBUG("OOB advertiser with id %hhd", id);
@@ -2675,7 +2693,7 @@
advertiser->Unregister(id);
SMP_ClearLocScOobData();
waiting_on_oob_advertiser_start = false;
- oob_advertiser_id = 0;
+ oob_advertiser_id_ = nullptr;
}
// Step Two: CallBack from Step One, advertise and get address
@@ -2687,11 +2705,12 @@
invoke_oob_data_request_cb(transport, false, c, r, RawAddress{}, 0x00);
SMP_ClearLocScOobData();
waiting_on_oob_advertiser_start = false;
- oob_advertiser_id = 0;
+ oob_advertiser_id_ = nullptr;
return;
}
- oob_advertiser_id = id;
+ oob_advertiser_id_ = std::make_unique<uint8_t>(id);
+ LOG_ERROR("oob_advertiser_id: %s", oob_advertiser_id_.get());
auto advertiser = get_ble_advertiser_instance();
AdvertiseParameters parameters;
@@ -2711,7 +2730,7 @@
advertiser->StartAdvertising(
id,
base::Bind(&start_advertising_callback, id, transport, is_valid, c, r),
- parameters, advertisement, scan_data, 3600 /* timeout_s */,
+ parameters, advertisement, scan_data, 120 /* timeout_s */,
base::Bind(&timeout_cb, id));
}
@@ -2947,6 +2966,7 @@
bond_state_changed(status, bd_addr, BT_BOND_STATE_BONDING);
}
bond_state_changed(status, bd_addr, state);
+ // TODO(240451061): Calling `stop_oob_advertiser();` gets command disallowed...
}
void btif_dm_load_ble_local_keys(void) {
diff --git a/system/btif/src/btif_le_audio_broadcaster.cc b/system/btif/src/btif_le_audio_broadcaster.cc
index f4825cd..7b867a1 100644
--- a/system/btif/src/btif_le_audio_broadcaster.cc
+++ b/system/btif/src/btif_le_audio_broadcaster.cc
@@ -28,7 +28,6 @@
using base::Bind;
using base::Unretained;
-using bluetooth::le_audio::BroadcastAudioProfile;
using bluetooth::le_audio::BroadcastId;
using bluetooth::le_audio::BroadcastState;
using bluetooth::le_audio::LeAudioBroadcasterCallbacks;
@@ -52,15 +51,12 @@
}
void CreateBroadcast(
- std::vector<uint8_t> metadata, BroadcastAudioProfile profile,
+ std::vector<uint8_t> metadata,
std::optional<std::array<uint8_t, 16>> broadcast_code) override {
DVLOG(2) << __func__;
- do_in_main_thread(
- FROM_HERE,
- Bind(&LeAudioBroadcaster::CreateAudioBroadcast,
- Unretained(LeAudioBroadcaster::Get()), std::move(metadata),
- static_cast<LeAudioBroadcaster::AudioProfile>(profile),
- broadcast_code));
+ do_in_main_thread(FROM_HERE, Bind(&LeAudioBroadcaster::CreateAudioBroadcast,
+ Unretained(LeAudioBroadcaster::Get()),
+ std::move(metadata), broadcast_code));
}
void UpdateMetadata(uint32_t broadcast_id,
diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc
index 41fa4df..b3653c1 100644
--- a/system/btif/src/btif_storage.cc
+++ b/system/btif/src/btif_storage.cc
@@ -943,15 +943,17 @@
/*******************************************************************************
*
- * Function btif_storage_load_consolidate_devices
+ * Function btif_storage_load_le_devices
*
- * Description BTIF storage API - Load the consolidate devices from NVRAM
- * Additionally, this API also invokes the adaper_properties_cb
- * and invoke_address_consolidate_cb for each of the
- * consolidate devices.
+ * Description BTIF storage API - Loads all LE-only and Dual Mode devices
+ * from NVRAM. This API invokes the adaper_properties_cb.
+ * It also invokes invoke_address_consolidate_cb
+ * to consolidate each Dual Mode device and
+ * invoke_le_address_associate_cb to associate each LE-only
+ * device between its RPA and identity address.
*
******************************************************************************/
-void btif_storage_load_consolidate_devices(void) {
+void btif_storage_load_le_devices(void) {
btif_bonded_devices_t bonded_devices;
btif_in_fetch_bonded_devices(&bonded_devices, 1);
std::unordered_set<RawAddress> bonded_addresses;
@@ -966,10 +968,8 @@
if (btif_storage_get_ble_bonding_key(
bonded_devices.devices[i], BTM_LE_KEY_PID, (uint8_t*)&key,
sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) {
- if (bonded_devices.devices[i] != key.pid_key.identity_addr &&
- bonded_addresses.find(key.pid_key.identity_addr) !=
- bonded_addresses.end()) {
- LOG_INFO("found consolidated device %s %s",
+ if (bonded_devices.devices[i] != key.pid_key.identity_addr) {
+ LOG_INFO("found device with a known identity address %s %s",
bonded_devices.devices[i].ToString().c_str(),
key.pid_key.identity_addr.ToString().c_str());
@@ -1001,7 +1001,13 @@
}
for (const auto& device : consolidated_devices) {
- invoke_address_consolidate_cb(device.first, device.second);
+ if (bonded_addresses.find(device.second) != bonded_addresses.end()) {
+ // Invokes address consolidation for DuMo devices
+ invoke_address_consolidate_cb(device.first, device.second);
+ } else {
+ // Associates RPA & identity address for LE-only devices
+ invoke_le_address_associate_cb(device.first, device.second);
+ }
}
}
diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc
index 67356ab..82f86dc 100644
--- a/system/btif/test/btif_core_test.cc
+++ b/system/btif/test/btif_core_test.cc
@@ -65,6 +65,8 @@
bt_bond_state_t state, int fail_reason) {}
void address_consolidate_callback(RawAddress* main_bd_addr,
RawAddress* secondary_bd_addr) {}
+void le_address_associate_callback(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr) {}
void acl_state_changed_callback(bt_status_t status, RawAddress* remote_bd_addr,
bt_acl_state_t state, int transport_link_type,
bt_hci_error_code_t hci_reason) {}
@@ -94,6 +96,7 @@
.ssp_request_cb = ssp_request_callback,
.bond_state_changed_cb = bond_state_changed_callback,
.address_consolidate_cb = address_consolidate_callback,
+ .le_address_associate_cb = le_address_associate_callback,
.acl_state_changed_cb = acl_state_changed_callback,
.thread_evt_cb = callback_thread_event,
.dut_mode_recv_cb = dut_mode_recv_callback,
diff --git a/system/conf/bt_stack.conf b/system/conf/bt_stack.conf
index d1e7756..942dfdc 100644
--- a/system/conf/bt_stack.conf
+++ b/system/conf/bt_stack.conf
@@ -44,6 +44,18 @@
# Disable LE Connection updates
#PTS_DisableConnUpdates=true
+# Use EATT for the notifications
+#PTS_ForceEattForNotifications=true
+
+# Start EATT without validation Server Supported Features
+#PTS_ConnectEattUncondictionally=true
+
+# Start EATT on unecrypted link
+#PTS_ConnectEattUnencrypted=true
+
+# Force EATT implementation to connect EATT as a peripheral for collision test case
+#PTS_EattPeripheralCollionSupport=true
+
# Disable BR/EDR discovery after LE pairing to avoid cross key derivation errors
#PTS_DisableSDPOnLEPair=true
@@ -53,6 +65,9 @@
# PTS AVRCP Test mode
#PTS_AvrcpTest=true
+# Start broadcast with unecryption mode
+#PTS_BroadcastUnencrypted=true
+
# SMP Certification Failure Cases
# Set any of the following SMP error values (from smp_api_types.h)
# to induce pairing failues for various PTS SMP test cases.
@@ -63,6 +78,7 @@
# SMP_PAIR_AUTH_FAIL = 3
# SMP_CONFIRM_VALUE_ERR = 4
# SMP_PAIR_NOT_SUPPORT = 5
+# SMP_ENC_KEY_SIZE = 6
# SMP_PAIR_FAIL_UNKNOWN = 8
# SMP_REPEATED_ATTEMPTS = 9
# SMP_NUMERIC_COMPAR_FAIL = 12
diff --git a/system/gd/cert/run b/system/gd/cert/run
index 4482a2d..28aab89 100755
--- a/system/gd/cert/run
+++ b/system/gd/cert/run
@@ -268,13 +268,13 @@
function soong_build {
if [ "$CLEAN_VENV" == true ] ; then
- $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j20
+ $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc)
if [[ $? -ne 0 ]] ; then
echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}"
exit 1
fi
else
- $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" $BUILD_TARGET -j20
+ $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc)
if [[ $? -ne 0 ]] ; then
echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}"
exit 1
diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc
index da5986f..cd6fa1f 100644
--- a/system/gd/hci/controller.cc
+++ b/system/gd/hci/controller.cc
@@ -146,7 +146,7 @@
LOG_INFO("LE_READ_PERIODIC_ADVERTISING_LIST_SIZE not supported, defaulting to 0");
le_periodic_advertiser_list_size_ = 0;
}
- if (is_supported(OpCode::LE_SET_HOST_FEATURE)) {
+ if (is_supported(OpCode::LE_SET_HOST_FEATURE) && module_.SupportsBleConnectedIsochronousStreamCentral()) {
hci_->EnqueueCommand(
LeSetHostFeatureBuilder::Create(LeHostFeatureBits::CONNECTED_ISO_STREAM_HOST_SUPPORT, Enable::ENABLED),
handler->BindOnceOn(this, &Controller::impl::le_set_host_feature_handler));
diff --git a/system/gd/hci/le_scanning_manager.cc b/system/gd/hci/le_scanning_manager.cc
index bc80498..5df7c4a 100644
--- a/system/gd/hci/le_scanning_manager.cc
+++ b/system/gd/hci/le_scanning_manager.cc
@@ -28,6 +28,7 @@
#include "module.h"
#include "os/handler.h"
#include "os/log.h"
+#include "storage/storage_module.h"
namespace bluetooth {
namespace hci {
@@ -235,12 +236,14 @@
hci::HciLayer* hci_layer,
hci::Controller* controller,
hci::AclManager* acl_manager,
- hci::VendorSpecificEventManager* vendor_specific_event_manager) {
+ hci::VendorSpecificEventManager* vendor_specific_event_manager,
+ storage::StorageModule* storage_module) {
module_handler_ = handler;
hci_layer_ = hci_layer;
controller_ = controller;
acl_manager_ = acl_manager;
vendor_specific_event_manager_ = vendor_specific_event_manager;
+ storage_module_ = storage_module;
le_address_manager_ = acl_manager->GetLeAddressManager();
le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
module_handler_->BindOn(this, &LeScanningManager::impl::handle_scan_results));
@@ -501,6 +504,16 @@
return;
}
+ switch (address_type) {
+ case (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS:
+ case (uint8_t)AddressType::PUBLIC_IDENTITY_ADDRESS:
+ address_type = (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS;
+ break;
+ case (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS:
+ case (uint8_t)AddressType::RANDOM_IDENTITY_ADDRESS:
+ address_type = (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS;
+ break;
+ }
scanning_callbacks_->OnScanResult(
event_type,
address_type,
@@ -701,6 +714,17 @@
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
}
+ bool is_bonded(Address target_address) {
+ for (auto device : storage_module_->GetBondedDevices()) {
+ if (device.GetAddress() == target_address) {
+ LOG_DEBUG("Addresses match!");
+ return true;
+ }
+ }
+ LOG_DEBUG("Addresse DON'Ts match!");
+ return false;
+ }
+
void scan_filter_parameter_setup(
ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) {
if (!is_filter_support_) {
@@ -708,6 +732,7 @@
return;
}
+ auto entry = remove_me_later_map_.find(filter_index);
switch (action) {
case ApcfAction::ADD:
le_scanning_interface_->EnqueueCommand(
@@ -730,11 +755,33 @@
le_scanning_interface_->EnqueueCommand(
LeAdvFilterDeleteFilteringParametersBuilder::Create(filter_index),
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+
+ // IRK Scanning
+ if (entry != remove_me_later_map_.end()) {
+ // Don't want to remove for a bonded device
+ if (!is_bonded(entry->second.GetAddress())) {
+ le_address_manager_->RemoveDeviceFromResolvingList(
+ static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+ }
+ remove_me_later_map_.erase(filter_index);
+ }
+
break;
case ApcfAction::CLEAR:
le_scanning_interface_->EnqueueCommand(
LeAdvFilterClearFilteringParametersBuilder::Create(),
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+
+ // IRK Scanning
+ if (entry != remove_me_later_map_.end()) {
+ // Don't want to remove for a bonded device
+ if (!is_bonded(entry->second.GetAddress())) {
+ le_address_manager_->RemoveDeviceFromResolvingList(
+ static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+ }
+ remove_me_later_map_.erase(filter_index);
+ }
+
break;
default:
LOG_ERROR("Unknown action type: %d", (uint16_t)action);
@@ -786,6 +833,8 @@
}
}
+ std::unordered_map<uint8_t, AddressWithType> remove_me_later_map_;
+
void update_address_filter(
ApcfAction action,
uint8_t filter_index,
@@ -813,14 +862,35 @@
action, filter_index, address, ApcfApplicationAddressType::NOT_APPLICABLE),
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
if (!is_empty_128bit(irk)) {
+ // If an entry exists for this filter index, replace data because the filter has been
+ // updated.
+ auto entry = remove_me_later_map_.find(filter_index);
+ // IRK Scanning
+ if (entry != remove_me_later_map_.end()) {
+ // Don't want to remove for a bonded device
+ if (!is_bonded(entry->second.GetAddress())) {
+ le_address_manager_->RemoveDeviceFromResolvingList(
+ static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+ }
+ remove_me_later_map_.erase(filter_index);
+ }
+
+ // Now replace it with a new one
std::array<uint8_t, 16> empty_irk;
le_address_manager_->AddDeviceToResolvingList(
static_cast<PeerAddressType>(address_type), address, irk, empty_irk);
+ remove_me_later_map_.emplace(filter_index, AddressWithType(address, static_cast<AddressType>(address_type)));
}
} else {
le_scanning_interface_->EnqueueCommand(
LeAdvFilterClearBroadcasterAddressBuilder::Create(filter_index),
module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+ auto entry = remove_me_later_map_.find(filter_index);
+ if (entry != remove_me_later_map_.end()) {
+ // TODO(optedoblivion): If not bonded
+ le_address_manager_->RemoveDeviceFromResolvingList(static_cast<PeerAddressType>(address_type), address);
+ remove_me_later_map_.erase(filter_index);
+ }
}
}
@@ -1434,6 +1504,7 @@
hci::Controller* controller_;
hci::AclManager* acl_manager_;
hci::VendorSpecificEventManager* vendor_specific_event_manager_;
+ storage::StorageModule* storage_module_;
hci::LeScanningInterface* le_scanning_interface_;
hci::LeAddressManager* le_address_manager_;
bool address_manager_registered_ = false;
@@ -1492,6 +1563,7 @@
list->add<hci::VendorSpecificEventManager>();
list->add<hci::Controller>();
list->add<hci::AclManager>();
+ list->add<storage::StorageModule>();
}
void LeScanningManager::Start() {
@@ -1500,7 +1572,8 @@
GetDependency<hci::HciLayer>(),
GetDependency<hci::Controller>(),
GetDependency<AclManager>(),
- GetDependency<VendorSpecificEventManager>());
+ GetDependency<VendorSpecificEventManager>(),
+ GetDependency<storage::StorageModule>());
}
void LeScanningManager::Stop() {
diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs
index 52425a8..07e63f4 100644
--- a/system/gd/rust/common/src/init_flags.rs
+++ b/system/gd/rust/common/src/init_flags.rs
@@ -77,7 +77,8 @@
gd_core,
gd_security,
gd_l2cap,
- gatt_robust_caching,
+ gatt_robust_caching_client,
+ gatt_robust_caching_server,
btaa_hci,
gd_rust,
gd_link_policy,
diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs
index d1cbfbf..1a1cea1 100644
--- a/system/gd/rust/shim/src/init_flags.rs
+++ b/system/gd/rust/shim/src/init_flags.rs
@@ -7,7 +7,8 @@
fn gd_core_is_enabled() -> bool;
fn gd_security_is_enabled() -> bool;
fn gd_l2cap_is_enabled() -> bool;
- fn gatt_robust_caching_is_enabled() -> bool;
+ fn gatt_robust_caching_client_is_enabled() -> bool;
+ fn gatt_robust_caching_server_is_enabled() -> bool;
fn btaa_hci_is_enabled() -> bool;
fn gd_rust_is_enabled() -> bool;
fn gd_link_policy_is_enabled() -> bool;
diff --git a/system/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs
index 922024d..5e9b50e 100644
--- a/system/gd/rust/topshim/src/btif.rs
+++ b/system/gd/rust/topshim/src/btif.rs
@@ -702,6 +702,7 @@
SspRequest(RawAddress, String, u32, BtSspVariant, u32),
BondState(BtStatus, RawAddress, BtBondState, i32),
AddressConsolidate(RawAddress, RawAddress),
+ LeAddressAssociate(RawAddress, RawAddress),
AclState(BtStatus, RawAddress, BtAclState, BtTransport, BtHciErrorCode),
// Unimplemented so far:
// thread_evt_cb
@@ -757,6 +758,12 @@
let _1 = unsafe { *(_1 as *const RawAddress) };
});
+cb_variant!(BaseCb, le_address_associate_cb -> BaseCallbacks::LeAddressAssociate,
+*mut FfiAddress, *mut FfiAddress, {
+ let _0 = unsafe { *(_0 as *const RawAddress) };
+ let _1 = unsafe { *(_1 as *const RawAddress) };
+});
+
cb_variant!(BaseCb, acl_state_cb -> BaseCallbacks::AclState,
u32 -> BtStatus, *mut FfiAddress, bindings::bt_acl_state_t -> BtAclState, i32 -> BtTransport, bindings::bt_hci_error_code_t -> BtHciErrorCode, {
let _1 = unsafe { *(_1 as *const RawAddress) };
@@ -871,6 +878,7 @@
ssp_request_cb: Some(ssp_request_cb),
bond_state_changed_cb: Some(bond_state_cb),
address_consolidate_cb: Some(address_consolidate_cb),
+ le_address_associate_cb: Some(le_address_associate_cb),
acl_state_changed_cb: Some(acl_state_cb),
thread_evt_cb: None,
dut_mode_recv_cb: None,
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 96e425c..d8f80ab 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -458,6 +458,12 @@
typedef void (*address_consolidate_callback)(RawAddress* main_bd_addr,
RawAddress* secondary_bd_addr);
+/** Bluetooth LE Address association callback */
+/* Callback for the upper layer to associate the LE-only device's RPA to the
+ * identity address */
+typedef void (*le_address_associate_callback)(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr);
+
/** Bluetooth ACL connection state changed callback */
typedef void (*acl_state_changed_callback)(bt_status_t status,
RawAddress* remote_bd_addr,
@@ -527,6 +533,7 @@
ssp_request_callback ssp_request_cb;
bond_state_changed_callback bond_state_changed_cb;
address_consolidate_callback address_consolidate_cb;
+ le_address_associate_callback le_address_associate_cb;
acl_state_changed_callback acl_state_changed_cb;
callback_thread_event thread_evt_cb;
dut_mode_recv_callback dut_mode_recv_cb;
diff --git a/system/include/hardware/bt_le_audio.h b/system/include/hardware/bt_le_audio.h
index 3188aa9..87766a3 100644
--- a/system/include/hardware/bt_le_audio.h
+++ b/system/include/hardware/bt_le_audio.h
@@ -163,12 +163,6 @@
STREAMING,
};
-/* A general hint for the codec configuration process. */
-enum class BroadcastAudioProfile {
- SONIFICATION = 0,
- MEDIA,
-};
-
using BroadcastId = uint32_t;
static constexpr BroadcastId kBroadcastIdInvalid = 0x00000000;
using BroadcastCode = std::array<uint8_t, 16>;
@@ -259,7 +253,6 @@
virtual void Cleanup(void) = 0;
/* Create Broadcast instance */
virtual void CreateBroadcast(std::vector<uint8_t> metadata,
- BroadcastAudioProfile profile,
std::optional<BroadcastCode> broadcast_code) = 0;
/* Update the ongoing Broadcast metadata */
virtual void UpdateMetadata(uint32_t broadcast_id,
diff --git a/system/internal_include/bt_target.h b/system/internal_include/bt_target.h
index 8ff2b37..98787a3 100644
--- a/system/internal_include/bt_target.h
+++ b/system/internal_include/bt_target.h
@@ -551,6 +551,27 @@
#define GATT_CONFORMANCE_TESTING FALSE
#endif
+/* Used only for GATT Multiple Variable Length Notifications PTS tests */
+#ifndef GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF
+#define GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF FALSE
+#endif
+
+/* Used only for GATT Multiple Variable Length READ PTS tests */
+#ifndef GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ
+#define GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ FALSE
+#endif
+
+/******************************************************************************
+ *
+ * CSIP
+ *
+ *****************************************************************************/
+
+/* Used to trigger invalid behaviour of CSIP test case PTS */
+#ifndef CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK
+#define CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK FALSE
+#endif
+
/******************************************************************************
*
* SMP
diff --git a/system/internal_include/stack_config.h b/system/internal_include/stack_config.h
index 19968ca..53dd186 100644
--- a/system/internal_include/stack_config.h
+++ b/system/internal_include/stack_config.h
@@ -33,6 +33,11 @@
bool (*get_pts_crosskey_sdp_disable)(void);
const std::string* (*get_pts_smp_options)(void);
int (*get_pts_smp_failure_case)(void);
+ bool (*get_pts_force_eatt_for_notifications)(void);
+ bool (*get_pts_connect_eatt_unconditionally)(void);
+ bool (*get_pts_connect_eatt_before_encryption)(void);
+ bool (*get_pts_unencrypt_broadcast)(void);
+ bool (*get_pts_eatt_peripheral_collision_support)(void);
config_t* (*get_all)(void);
} stack_config_t;
diff --git a/system/main/stack_config.cc b/system/main/stack_config.cc
index 29387a4..fdfe7d6 100644
--- a/system/main/stack_config.cc
+++ b/system/main/stack_config.cc
@@ -33,6 +33,13 @@
const char* PTS_DISABLE_SDP_LE_PAIR = "PTS_DisableSDPOnLEPair";
const char* PTS_SMP_PAIRING_OPTIONS_KEY = "PTS_SmpOptions";
const char* PTS_SMP_FAILURE_CASE_KEY = "PTS_SmpFailureCase";
+const char* PTS_FORCE_EATT_FOR_NOTIFICATIONS = "PTS_ForceEattForNotifications";
+const char* PTS_CONNECT_EATT_UNCONDITIONALLY =
+ "PTS_ConnectEattUncondictionally";
+const char* PTS_CONNECT_EATT_UNENCRYPTED = "PTS_ConnectEattUnencrypted";
+const char* PTS_BROADCAST_UNENCRYPTED = "PTS_BroadcastUnencrypted";
+const char* PTS_EATT_PERIPHERAL_COLLISION_SUPPORT =
+ "PTS_EattPeripheralCollionSupport";
static std::unique_ptr<config_t> config;
} // namespace
@@ -110,12 +117,45 @@
PTS_SMP_FAILURE_CASE_KEY, 0);
}
+static bool get_pts_force_eatt_for_notifications(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_FORCE_EATT_FOR_NOTIFICATIONS, false);
+}
+
+static bool get_pts_connect_eatt_unconditionally(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_CONNECT_EATT_UNCONDITIONALLY, false);
+}
+
+static bool get_pts_connect_eatt_before_encryption(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_CONNECT_EATT_UNENCRYPTED, false);
+}
+
+static bool get_pts_unencrypt_broadcast(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_BROADCAST_UNENCRYPTED, false);
+}
+
+static bool get_pts_eatt_peripheral_collision_support(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_EATT_PERIPHERAL_COLLISION_SUPPORT, false);
+}
+
static config_t* get_all(void) { return config.get(); }
-const stack_config_t interface = {
- get_trace_config_enabled, get_pts_avrcp_test,
- get_pts_secure_only_mode, get_pts_conn_updates_disabled,
- get_pts_crosskey_sdp_disable, get_pts_smp_options,
- get_pts_smp_failure_case, get_all};
+const stack_config_t interface = {get_trace_config_enabled,
+ get_pts_avrcp_test,
+ get_pts_secure_only_mode,
+ get_pts_conn_updates_disabled,
+ get_pts_crosskey_sdp_disable,
+ get_pts_smp_options,
+ get_pts_smp_failure_case,
+ get_pts_force_eatt_for_notifications,
+ get_pts_connect_eatt_unconditionally,
+ get_pts_connect_eatt_before_encryption,
+ get_pts_unencrypt_broadcast,
+ get_pts_eatt_peripheral_collision_support,
+ get_all};
const stack_config_t* stack_config_get_interface(void) { return &interface; }
diff --git a/system/profile/avrcp/avrcp_internal.h b/system/profile/avrcp/avrcp_internal.h
index 89dfcb1..74620dc 100644
--- a/system/profile/avrcp/avrcp_internal.h
+++ b/system/profile/avrcp/avrcp_internal.h
@@ -64,6 +64,9 @@
virtual uint16_t MsgReq(uint8_t handle, uint8_t label, uint8_t ctype,
BT_HDR* p_pkt) = 0;
+ virtual void SaveControllerVersion(const RawAddress& bdaddr,
+ uint16_t version) = 0;
+
virtual ~AvrcpInterface() = default;
};
diff --git a/system/profile/avrcp/connection_handler.cc b/system/profile/avrcp/connection_handler.cc
index 680f1ac..db86cde 100644
--- a/system/profile/avrcp/connection_handler.cc
+++ b/system/profile/avrcp/connection_handler.cc
@@ -18,6 +18,7 @@
#include <base/bind.h>
#include <base/logging.h>
+
#include <map>
#include "avrc_defs.h"
@@ -488,6 +489,10 @@
}
}
}
+
+ if (osi_property_get_bool(AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY, true)) {
+ avrc_->SaveControllerVersion(bdaddr, peer_avrcp_version);
+ }
}
}
diff --git a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
index bc3dd6f..441bf5c 100644
--- a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
+++ b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
@@ -54,9 +54,13 @@
bool get_pts_avrcp_test(void) { return false; }
-const stack_config_t interface = {
- nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
- nullptr};
+const stack_config_t interface = {nullptr, get_pts_avrcp_test,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr};
void Callback(uint8_t, bool, std::unique_ptr<::bluetooth::PacketBuilder>) {}
diff --git a/system/profile/avrcp/tests/avrcp_device_test.cc b/system/profile/avrcp/tests/avrcp_device_test.cc
index 7aea88b..2bbc39a 100644
--- a/system/profile/avrcp/tests/avrcp_device_test.cc
+++ b/system/profile/avrcp/tests/avrcp_device_test.cc
@@ -50,9 +50,13 @@
bool get_pts_avrcp_test(void) { return false; }
-const stack_config_t interface = {
- nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
- nullptr};
+const stack_config_t interface = {nullptr, get_pts_avrcp_test,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ nullptr};
// TODO (apanicke): All the tests below are just basic positive unit tests.
// Add more tests to increase code coverage.
diff --git a/system/profile/avrcp/tests/avrcp_test_helper.h b/system/profile/avrcp/tests/avrcp_test_helper.h
index 0ed5911..eed3dbc 100644
--- a/system/profile/avrcp/tests/avrcp_test_helper.h
+++ b/system/profile/avrcp/tests/avrcp_test_helper.h
@@ -75,6 +75,7 @@
MOCK_METHOD1(Close, uint16_t(uint8_t));
MOCK_METHOD1(CloseBrowse, uint16_t(uint8_t));
MOCK_METHOD4(MsgReq, uint16_t(uint8_t, uint8_t, uint8_t, BT_HDR*));
+ MOCK_METHOD2(SaveControllerVersion, void(const RawAddress&, uint16_t));
};
class MockA2dpInterface : public A2dpInterface {
diff --git a/system/service/hal/bluetooth_interface.cc b/system/service/hal/bluetooth_interface.cc
index f959d61..8f01058 100644
--- a/system/service/hal/bluetooth_interface.cc
+++ b/system/service/hal/bluetooth_interface.cc
@@ -156,6 +156,11 @@
// Do nothing
}
+void LeAddressAssociateCallback(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr) {
+ // Do nothing
+}
+
void AclStateChangedCallback(bt_status_t status, RawAddress* remote_bd_addr,
bt_acl_state_t state, int transport_link_type,
bt_hci_error_code_t hci_reason) {
@@ -250,6 +255,7 @@
SSPRequestCallback,
BondStateChangedCallback,
AddressConsolidateCallback,
+ LeAddressAssociateCallback,
AclStateChangedCallback,
ThreadEventCallback,
nullptr, /* dut_mode_recv_cb */
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 85095ae..8d1b8e5 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -668,6 +668,7 @@
"packages/modules/Bluetooth/system/utils/include",
],
srcs: crypto_toolbox_srcs + [
+ ":TestCommonMainHandler",
":TestMockStackBtm",
"gatt/gatt_db.cc",
"gatt/gatt_sr_hash.cc",
@@ -719,6 +720,7 @@
"test/common/mock_controller.cc",
"test/common/mock_gatt_layer.cc",
"test/common/mock_hcic_layer.cc",
+ ":TestCommonStackConfig"
],
static_libs: [
"libbt-common",
@@ -762,6 +764,7 @@
include_dirs:[
"packages/modules/Bluetooth/system",
"packages/modules/Bluetooth/system/gd",
+ "packages/modules/Bluetooth/system/internal_include"
],
srcs: [
"eatt/eatt.cc",
@@ -772,6 +775,8 @@
"test/common/mock_l2cap_layer.cc",
"test/gatt/mock_gatt_utils_ref.cc",
"test/eatt/eatt_test.cc",
+ ":TestCommonMainHandler",
+ ":TestCommonStackConfig",
],
shared_libs: [
"libcutils",
@@ -862,6 +867,7 @@
"metrics/stack_metrics_logging.cc",
"test/btm/stack_btm_test.cc",
"test/btm/peer_packet_types_test.cc",
+ "test/common/mock_eatt.cc",
],
static_libs: [
"libbt-common",
@@ -1073,6 +1079,8 @@
],
srcs: [
":OsiCompatSources",
+ ":TestCommonMainHandler",
+ ":TestCommonStackConfig",
":TestMockBta",
":TestMockBtif",
":TestMockHci",
@@ -1265,3 +1273,36 @@
},
},
}
+
+// Bluetooth stack connection multiplexing
+cc_test {
+ name: "net_test_stack_sdp",
+ defaults: ["fluoride_defaults"],
+ local_include_dirs: [
+ "include",
+ "test/common",
+ ],
+ include_dirs: [
+ "packages/modules/Bluetooth/system",
+ "packages/modules/Bluetooth/system/device/include/",
+ "packages/modules/Bluetooth/system/gd",
+ "packages/modules/Bluetooth/system/internal_include",
+ "packages/modules/Bluetooth/system/utils/include",
+ ],
+ srcs: [
+ ":TestCommonMockFunctions",
+ "sdp/sdp_main.cc",
+ "sdp/sdp_utils.cc",
+ "test/common/mock_btif_config.cc",
+ "test/stack_sdp_utils_test.cc",
+ ],
+ shared_libs: [
+ "libcutils",
+ ],
+ static_libs: [
+ "libbt-common",
+ "libbluetooth-types",
+ "liblog",
+ "libgmock",
+ ],
+}
diff --git a/system/stack/avct/avct_l2c.cc b/system/stack/avct/avct_l2c.cc
index 57bc46e..7634a65 100644
--- a/system/stack/avct/avct_l2c.cc
+++ b/system/stack/avct/avct_l2c.cc
@@ -61,6 +61,7 @@
NULL,
NULL,
NULL,
+ NULL,
};
/*******************************************************************************
diff --git a/system/stack/avct/avct_l2c_br.cc b/system/stack/avct/avct_l2c_br.cc
index a3eef3c..57bc501 100644
--- a/system/stack/avct/avct_l2c_br.cc
+++ b/system/stack/avct/avct_l2c_br.cc
@@ -61,6 +61,7 @@
avct_br_on_l2cap_error,
NULL,
NULL,
+ NULL,
NULL};
/*******************************************************************************
diff --git a/system/stack/avdt/avdt_l2c.cc b/system/stack/avdt/avdt_l2c.cc
index 2607cc1..3e93d37 100644
--- a/system/stack/avdt/avdt_l2c.cc
+++ b/system/stack/avdt/avdt_l2c.cc
@@ -62,6 +62,7 @@
avdt_on_l2cap_error,
NULL,
NULL,
+ NULL,
NULL};
/*******************************************************************************
diff --git a/system/stack/avrc/avrc_api.cc b/system/stack/avrc/avrc_api.cc
index d9b9014..c8f2f1c 100644
--- a/system/stack/avrc/avrc_api.cc
+++ b/system/stack/avrc/avrc_api.cc
@@ -27,6 +27,7 @@
#include <string.h>
#include "avrc_int.h"
+#include "btif/include/btif_config.h"
#include "osi/include/allocator.h"
#include "osi/include/fixed_queue.h"
#include "osi/include/log.h"
@@ -1363,3 +1364,44 @@
if (p_buf) return AVCT_MsgReq(handle, label, AVCT_RSP, p_buf);
return AVRC_NO_RESOURCES;
}
+
+/******************************************************************************
+ *
+ * Function AVRC_SaveControllerVersion
+ *
+ * Description Save AVRC controller version of peer device into bt_config.
+ * This version is used to send same AVRC target version to
+ * peer device to avoid version mismatch IOP issue.
+ *
+ * Input Parameters:
+ * bdaddr: BD address of peer device.
+ *
+ * version: AVRC controller version of peer device.
+ *
+ * Output Parameters:
+ * None.
+ *
+ * Returns Nothing
+ *
+ *****************************************************************************/
+void AVRC_SaveControllerVersion(const RawAddress& bdaddr,
+ uint16_t new_version) {
+ // store AVRC controller version into BT config
+ uint16_t old_version = 0;
+ size_t version_value_size = sizeof(old_version);
+ if (btif_config_get_bin(bdaddr.ToString(),
+ AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+ (uint8_t*)&old_version, &version_value_size) &&
+ new_version == old_version) {
+ LOG_INFO("AVRC controller version same as cached config");
+ } else if (btif_config_set_bin(
+ bdaddr.ToString(), AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+ (const uint8_t*)&new_version, sizeof(new_version))) {
+ btif_config_save();
+ LOG_INFO("store AVRC controller version %x for %s into config.",
+ new_version, bdaddr.ToString().c_str());
+ } else {
+ LOG_WARN("Failed to store AVRC controller version for %s",
+ bdaddr.ToString().c_str());
+ }
+}
diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 3a18f25..57edb61 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -1880,6 +1880,15 @@
}
break;
+ case SMP_LE_ADDR_ASSOC_EVT:
+ if (btm_cb.api.p_le_callback) {
+ BTM_TRACE_DEBUG("btm_cb.api.p_le_callback=0x%x",
+ btm_cb.api.p_le_callback);
+ (*btm_cb.api.p_le_callback)(event, bd_addr,
+ (tBTM_LE_EVT_DATA*)p_data);
+ }
+ break;
+
default:
BTM_TRACE_DEBUG("unknown event = %d", event);
break;
diff --git a/system/stack/btm/btm_inq.cc b/system/stack/btm/btm_inq.cc
index b0c8be4..f42b160 100644
--- a/system/stack/btm/btm_inq.cc
+++ b/system/stack/btm/btm_inq.cc
@@ -1411,7 +1411,10 @@
uint16_t temp_evt_len;
if (bda) {
+ rem_name.bd_addr = *bda;
VLOG(2) << "BDA " << *bda;
+ } else {
+ rem_name.bd_addr = RawAddress::kEmpty;
}
VLOG(2) << "Inquire BDA " << p_inq->remname_bda;
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 661b5ca..245a4ef 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -29,6 +29,7 @@
#include "common/time_util.h"
#include "device/include/controller.h"
#include "hci/include/hci_layer.h"
+#include "internal_include/stack_config.h"
#include "osi/include/allocator.h"
#include "osi/include/log.h"
#include "stack/include/bt_hdr.h"
@@ -657,6 +658,12 @@
LOG_ASSERT(!IsBigKnown(big_id))
<< "Invalid big - already exists: " << +big_id;
+ if (stack_config_get_interface()->get_pts_unencrypt_broadcast()) {
+ LOG_INFO("Force create broadcst without encryption for PTS test");
+ big_params.enc = 0;
+ big_params.enc_code = {0};
+ }
+
last_big_create_req_sdu_itv_ = big_params.sdu_itv;
btsnd_hcic_create_big(
big_id, big_params.adv_handle, big_params.num_bis, big_params.sdu_itv,
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 4a83cde..513a3ad 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -46,6 +46,7 @@
#include "osi/include/osi.h"
#include "stack/btm/btm_dev.h"
#include "stack/btm/security_device_record.h"
+#include "stack/eatt/eatt.h"
#include "stack/include/acl_api.h"
#include "stack/include/acl_hci_link_interface.h"
#include "stack/include/btm_status.h"
@@ -4734,6 +4735,11 @@
}
btm_sec_check_pending_reqs();
+
+ if (btm_status == BTM_SUCCESS && is_le_transport) {
+ /* Link is encrypted, start EATT */
+ bluetooth::eatt::EattExtension::GetInstance()->Connect(p_dev_rec->bd_addr);
+ }
}
void btm_sec_cr_loc_oob_data_cback_event(const RawAddress& address,
diff --git a/system/stack/eatt/eatt.cc b/system/stack/eatt/eatt.cc
index e01126b..a023026 100644
--- a/system/stack/eatt/eatt.cc
+++ b/system/stack/eatt/eatt.cc
@@ -45,6 +45,7 @@
reg_info_.pL2CA_DisconnectInd_Cb = eatt_disconnect_ind;
reg_info_.pL2CA_Error_Cb = eatt_error_cb;
reg_info_.pL2CA_DataInd_Cb = eatt_data_ind;
+ reg_info_.pL2CA_CreditBasedCollisionInd_Cb = eatt_collision_ind;
if (L2CA_RegisterLECoc(BT_PSM_EATT, reg_info_, BTM_SEC_NONE, {}) == 0) {
LOG(ERROR) << __func__ << " cannot register EATT";
@@ -94,6 +95,11 @@
p_cfg);
}
+ static void eatt_collision_ind(const RawAddress& bd_addr) {
+ auto p_eatt_impl = GetImplInstance();
+ if (p_eatt_impl) p_eatt_impl->eatt_l2cap_collision_ind(bd_addr);
+ }
+
static void eatt_error_cb(uint16_t lcid, uint16_t reason) {
auto p_eatt_impl = GetImplInstance();
if (p_eatt_impl) p_eatt_impl->eatt_l2cap_error_cb(lcid, reason);
@@ -129,8 +135,8 @@
pimpl_->eatt_impl_->connect(bd_addr);
}
-void EattExtension::Disconnect(const RawAddress& bd_addr) {
- pimpl_->eatt_impl_->disconnect(bd_addr);
+void EattExtension::Disconnect(const RawAddress& bd_addr, uint16_t cid) {
+ pimpl_->eatt_impl_->disconnect(bd_addr, cid);
}
void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid,
diff --git a/system/stack/eatt/eatt.h b/system/stack/eatt/eatt.h
index 2eac631..828d17a 100644
--- a/system/stack/eatt/eatt.h
+++ b/system/stack/eatt/eatt.h
@@ -24,6 +24,7 @@
#define EATT_MIN_MTU_MPS (64)
#define EATT_DEFAULT_MTU (256)
+#define EATT_ALL_CIDS (0xFFFF)
namespace bluetooth {
namespace eatt {
@@ -129,8 +130,10 @@
* Disconnect all EATT channels to peer device.
*
* @param bd_addr peer device address
+ * @param cid remote channel id (EATT_ALL_CIDS for all)
*/
- virtual void Disconnect(const RawAddress& bd_addr);
+ virtual void Disconnect(const RawAddress& bd_addr,
+ uint16_t cid = EATT_ALL_CIDS);
/**
* Reconfigure EATT channel for give CID
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 0324808..0a2c2af 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -24,12 +24,16 @@
#include "bind_helpers.h"
#include "device/include/controller.h"
#include "eatt.h"
+#include "gd/common/init_flags.h"
+#include "gd/common/strings.h"
+#include "internal_include/stack_config.h"
#include "l2c_api.h"
#include "osi/include/alarm.h"
#include "osi/include/allocator.h"
#include "stack/btm/btm_sec.h"
#include "stack/gatt/gatt_int.h"
#include "stack/include/bt_hdr.h"
+#include "stack/include/btu.h" // do_in_main_thread
#include "stack/l2cap/l2c_int.h"
#include "types/raw_address.h"
@@ -47,9 +51,9 @@
tGATT_TCB* eatt_tcb_;
std::map<uint16_t, std::shared_ptr<EattChannel>> eatt_channels;
-
+ bool collision;
eatt_device(const RawAddress& bd_addr, uint16_t mtu, uint16_t mps)
- : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr) {
+ : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr), collision(false) {
bda_ = bd_addr;
}
};
@@ -113,15 +117,33 @@
void eatt_l2cap_connect_ind(const RawAddress& bda,
std::vector<uint16_t>& lcids, uint16_t psm,
uint16_t peer_mtu, uint8_t identifier) {
+ if (!stack_config_get_interface()
+ ->get_pts_connect_eatt_before_encryption() &&
+ !BTM_IsEncrypted(bda, BT_TRANSPORT_LE)) {
+ /* If Link is not encrypted, we shall not accept EATT channel creation. */
+ std::vector<uint16_t> empty;
+ uint16_t result = L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION;
+ if (BTM_IsLinkKeyKnown(bda, BT_TRANSPORT_LE)) {
+ result = L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP;
+ }
+ LOG_ERROR("ACL to device %s is unencrypted.", bda.ToString().c_str());
+ L2CA_ConnectCreditBasedRsp(bda, identifier, empty, result, nullptr);
+ return;
+ }
+
/* The assumption is that L2CAP layer already check parameters etc.
* Get our capabilities and accept all the channels.
*/
eatt_device* eatt_dev = this->find_device_by_address(bda);
if (!eatt_dev) {
- LOG(ERROR) << __func__ << " unknown device: " << bda;
- L2CA_ConnectCreditBasedRsp(bda, identifier, lcids,
- L2CAP_CONN_NO_RESOURCES, NULL);
- return;
+ /* If there is no device it means, Android did not read yet Server
+ * supported features, but according to Core 5.3, Vol 3, Part G, 6.2.1,
+ * for LE case it is not necessary to read it before establish connection.
+ * Therefore assume, device supports EATT since we got request to create
+ * EATT channels. Just create device here. */
+ LOG(INFO) << __func__ << " Adding device: " << bda
+ << " on incoming EATT creation request";
+ eatt_dev = add_eatt_device(bda);
}
uint16_t max_mps = controller_get_interface()->get_acl_data_size_ble();
@@ -154,6 +176,49 @@
LOG(INFO) << __func__ << " Channel connected CID " << loghex(cid);
}
+
+ /* Android let Central to create EATT (PTS initiates EATT). Some PTS test
+ * cases wants Android to do it anyway (Android initiates EATT).
+ */
+ if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ connect_eatt_wrap(eatt_dev);
+ }
+ }
+
+ void eatt_retry_after_collision_if_needed(eatt_device* eatt_dev) {
+ if (!eatt_dev->collision) {
+ LOG_DEBUG("No collision.");
+ return;
+ }
+ /* We are here, because remote device wanted to create channels when
+ * Android proceed its own EATT creation. How to handle it is described
+ * here: BT Core 5.3, Volume 3, Part G, 5.4
+ */
+ LOG_INFO(
+ "EATT collision detected. If we are Central we will retry right "
+ "away");
+
+ eatt_dev->collision = false;
+ uint8_t role = L2CA_GetBleConnRole(eatt_dev->bda_);
+ if (role == HCI_ROLE_CENTRAL) {
+ LOG_INFO("Retrying EATT setup due to previous collision for device %s",
+ eatt_dev->bda_.ToString().c_str());
+ connect_eatt_wrap(eatt_dev);
+ } else if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ /* This is only for the PTS. Android does not setup EATT when is a
+ * peripheral.
+ */
+ bt_status_t status = do_in_main_thread_delayed(
+ FROM_HERE,
+ base::BindOnce(&eatt_impl::connect_eatt_wrap, base::Unretained(this),
+ std::move(eatt_dev)),
+ base::TimeDelta::FromMilliseconds(500));
+
+ LOG_INFO("Scheduled peripheral connect eatt for device with status: %d",
+ (int)status);
+ }
}
void eatt_l2cap_connect_cfm(const RawAddress& bda, uint16_t lcid,
@@ -188,6 +253,7 @@
eatt_dev->eatt_tcb_->eatt++;
LOG(INFO) << __func__ << " Channel connected CID " << loghex(lcid);
+ eatt_retry_after_collision_if_needed(eatt_dev);
}
void eatt_l2cap_reconfig_completed(const RawAddress& bda, uint16_t lcid,
@@ -216,6 +282,17 @@
channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED);
}
+ void eatt_l2cap_collision_ind(const RawAddress& bda) {
+ eatt_device* eatt_dev = find_device_by_address(bda);
+ if (!eatt_dev) {
+ LOG_ERROR("Device %s not available anymore:", bda.ToString().c_str());
+ return;
+ }
+ /* Remote wanted to setup channels as well. Let's retry remote's request
+ * when we are done with ours.*/
+ eatt_dev->collision = true;
+ }
+
void eatt_l2cap_error_cb(uint16_t lcid, uint16_t reason) {
LOG(INFO) << __func__ << " cid: " << loghex(lcid) << " reason "
<< loghex(reason);
@@ -245,6 +322,8 @@
<< static_cast<uint8_t>(channel->state_);
break;
}
+
+ eatt_retry_after_collision_if_needed(eatt_dev);
}
void eatt_l2cap_disconnect_ind(uint16_t lcid, bool please_confirm) {
@@ -296,7 +375,22 @@
return eatt_dev;
}
- void connect_eatt(eatt_device* eatt_dev) {
+ void connect_eatt_wrap(eatt_device* eatt_dev) {
+ if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ /* For PTS case, lets assume we support only 5 channels */
+ LOG_INFO("Number of existing channels %d",
+ (int)eatt_dev->eatt_channels.size());
+ connect_eatt(eatt_dev, L2CAP_CREDIT_BASED_MAX_CIDS -
+ (int)eatt_dev->eatt_channels.size());
+ return;
+ }
+
+ connect_eatt(eatt_dev);
+ }
+
+ void connect_eatt(eatt_device* eatt_dev,
+ uint8_t num_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS) {
/* Let us use maximum possible mps */
if (eatt_dev->rx_mps_ == EATT_MIN_MTU_MPS)
eatt_dev->rx_mps_ = controller_get_interface()->get_acl_data_size_ble();
@@ -305,8 +399,12 @@
.mtu = eatt_dev->rx_mtu_,
.mps = eatt_dev->rx_mps_,
.credits = L2CAP_LE_CREDIT_DEFAULT,
+ .number_of_channels = num_of_channels,
};
+ LOG_INFO("Connecting device %s, cnt count %d",
+ eatt_dev->bda_.ToString().c_str(), num_of_channels);
+
/* Warning! CIDs in Android are unique across the ACL connections */
std::vector<uint16_t> connecting_cids =
L2CA_ConnectCreditBasedReq(psm_, eatt_dev->bda_, &local_coc_cfg);
@@ -593,13 +691,13 @@
return;
}
- connect_eatt(eatt_dev);
+ connect_eatt_wrap(eatt_dev);
}
void disconnect_channel(uint16_t cid) { L2CA_DisconnectReq(cid); }
- void disconnect(const RawAddress& bd_addr) {
- LOG(INFO) << __func__ << " " << bd_addr;
+ void disconnect(const RawAddress& bd_addr, uint16_t cid) {
+ LOG_INFO(" Device: %s, cid: 0x%04x", bd_addr.ToString().c_str(), cid);
eatt_device* eatt_dev = find_device_by_address(bd_addr);
if (!eatt_dev) {
@@ -613,6 +711,19 @@
return;
}
+ if (cid != EATT_ALL_CIDS) {
+ auto chan = find_channel_by_cid(cid);
+ if (!chan) {
+ LOG_WARN("Cid %d not found for device %s", cid,
+ bd_addr.ToString().c_str());
+ return;
+ }
+ LOG_INFO("Disconnecting cid %d", cid);
+ disconnect_channel(cid);
+ remove_channel_by_cid(cid);
+ return;
+ }
+
auto iter = eatt_dev->eatt_channels.begin();
while (iter != eatt_dev->eatt_channels.end()) {
uint16_t cid = iter->first;
@@ -624,6 +735,7 @@
}
eatt_dev->eatt_tcb_->eatt = 0;
eatt_dev->eatt_tcb_ = nullptr;
+ eatt_dev->collision = false;
}
void connect(const RawAddress& bd_addr) {
@@ -635,8 +747,8 @@
return;
}
- LOG(INFO) << __func__ << " device " << bd_addr << " role"
- << (role == HCI_ROLE_CENTRAL ? "central" : "peripheral");
+ LOG_INFO("Device %s, role %s", bd_addr.ToString().c_str(),
+ (role == HCI_ROLE_CENTRAL ? "central" : "peripheral"));
if (eatt_dev) {
/* We are reconnecting device we know that support EATT.
@@ -650,16 +762,30 @@
return;
}
- connect_eatt(eatt_dev);
+ connect_eatt_wrap(eatt_dev);
return;
}
- /* For new device, first read GATT server supported features. */
+ /* This is needed for L2CAP test cases */
+ if (stack_config_get_interface()->get_pts_connect_eatt_unconditionally()) {
+ /* For PTS just start connecting EATT right away */
+ eatt_device* eatt_dev = add_eatt_device(bd_addr);
+ connect_eatt_wrap(eatt_dev);
+ return;
+ }
+
+ if (gatt_profile_get_eatt_support(bd_addr)) {
+ LOG_DEBUG("Eatt is supported for device %s", bd_addr.ToString().c_str());
+ supported_features_cb(role, bd_addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+ return;
+ }
+
+ /* If we don't know yet, read GATT server supported features. */
if (gatt_cl_read_sr_supp_feat_req(
bd_addr, base::BindOnce(&eatt_impl::supported_features_cb,
base::Unretained(this), role)) == false) {
- LOG(INFO) << __func__ << "Eatt is not supported. Checked for device "
- << bd_addr;
+ LOG_INFO("Read server supported features failed for device %s",
+ bd_addr.ToString().c_str());
}
}
diff --git a/system/stack/gatt/att_protocol.cc b/system/stack/gatt/att_protocol.cc
index 63847db..3683d40 100644
--- a/system/stack/gatt/att_protocol.cc
+++ b/system/stack/gatt/att_protocol.cc
@@ -353,8 +353,8 @@
}
/** Build ATT Server PDUs */
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
- tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+ uint16_t payload_size) {
uint16_t offset = 0;
switch (op_code) {
@@ -370,7 +370,7 @@
case GATT_HANDLE_VALUE_NOTIF:
case GATT_HANDLE_VALUE_IND:
return attp_build_value_cmd(
- tcb.payload_size, op_code, p_msg->attr_value.handle, offset,
+ payload_size, op_code, p_msg->attr_value.handle, offset,
p_msg->attr_value.len, p_msg->attr_value.value);
case GATT_RSP_WRITE:
diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc
index 303223f..3158f74 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -32,6 +32,7 @@
#include "bt_target.h"
#include "device/include/controller.h"
#include "gatt_int.h"
+#include "internal_include/stack_config.h"
#include "l2c_api.h"
#include "main/shim/dumpsys.h"
#include "osi/include/allocator.h"
@@ -468,8 +469,10 @@
tGATT_SR_MSG gatt_sr_msg;
gatt_sr_msg.attr_value = indication;
- BT_HDR* p_msg =
- attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_IND, &gatt_sr_msg);
+
+ uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+ BT_HDR* p_msg = attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_IND, &gatt_sr_msg,
+ payload_size);
if (!p_msg) return GATT_NO_RESOURCES;
tGATT_STATUS cmd_status = attp_send_sr_msg(*p_tcb, cid, p_msg);
@@ -480,6 +483,39 @@
return cmd_status;
}
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+static tGATT_STATUS GATTS_HandleMultileValueNotification(
+ tGATT_TCB* p_tcb, std::vector<tGATT_VALUE> gatt_notif_vector) {
+ LOG(INFO) << __func__;
+
+ uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, true /* eatt support */);
+ uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+
+ /* TODO Handle too big packet size here. Not needed now for testing. */
+ /* Just build the message. */
+ BT_HDR* p_buf =
+ (BT_HDR*)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET);
+
+ uint8_t* p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
+ UINT8_TO_STREAM(p, GATT_HANDLE_MULTI_VALUE_NOTIF);
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_buf->len = 1;
+ for (auto notif : gatt_notif_vector) {
+ LOG(INFO) << __func__ << "Adding handle: " << loghex(notif.handle)
+ << "val len: " << +notif.len;
+ UINT16_TO_STREAM(p, notif.handle);
+ p_buf->len += 2;
+ UINT16_TO_STREAM(p, notif.len);
+ p_buf->len += 2;
+ ARRAY_TO_STREAM(p, notif.value, notif.len);
+ p_buf->len += notif.len;
+ }
+
+ LOG(INFO) << __func__ << "Total len: " << +p_buf->len;
+
+ return attp_send_sr_msg(*p_tcb, cid, p_buf);
+}
+#endif
/*******************************************************************************
*
* Function GATTS_HandleValueNotification
@@ -503,6 +539,11 @@
uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx);
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+ static uint8_t cached_tcb_idx = 0xFF;
+ static std::vector<tGATT_VALUE> gatt_notif_vector(2);
+ tGATT_VALUE* p_gatt_notif;
+#endif
VLOG(1) << __func__;
@@ -515,6 +556,43 @@
return GATT_ILLEGAL_PARAMETER;
}
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+ /* Upper tester for Multiple Value length notifications */
+ if (stack_config_get_interface()->get_pts_force_eatt_for_notifications() &&
+ gatt_sr_is_cl_multi_variable_len_notif_supported(*p_tcb)) {
+ if (cached_tcb_idx == 0xFF) {
+ LOG(INFO) << __func__ << " Storing first notification";
+ p_gatt_notif = &gatt_notif_vector[0];
+
+ p_gatt_notif->handle = attr_handle;
+ p_gatt_notif->len = val_len;
+ std::copy(p_val, p_val + val_len, p_gatt_notif->value);
+
+ notif.auth_req = GATT_AUTH_REQ_NONE;
+
+ cached_tcb_idx = tcb_idx;
+ return GATT_SUCCESS;
+ }
+
+ if (cached_tcb_idx == tcb_idx) {
+ LOG(INFO) << __func__ << " Storing second notification";
+ cached_tcb_idx = 0xFF;
+ p_gatt_notif = &gatt_notif_vector[1];
+
+ p_gatt_notif->handle = attr_handle;
+ p_gatt_notif->len = val_len;
+ std::copy(p_val, p_val + val_len, p_gatt_notif->value);
+
+ notif.auth_req = GATT_AUTH_REQ_NONE;
+
+ return GATTS_HandleMultileValueNotification(p_tcb, gatt_notif_vector);
+ }
+
+ LOG(ERROR) << __func__ << "PTS Mode: Invalid tcb_idx: " << tcb_idx
+ << " cached_tcb_idx: " << cached_tcb_idx;
+ }
+#endif
+
memset(¬if, 0, sizeof(notif));
notif.handle = attr_handle;
notif.len = val_len;
@@ -526,13 +604,15 @@
gatt_sr_msg.attr_value = notif;
uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, p_reg->eatt_support);
+ uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+ BT_HDR* p_buf = attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF,
+ &gatt_sr_msg, payload_size);
- BT_HDR* p_buf =
- attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF, &gatt_sr_msg);
if (p_buf != NULL) {
cmd_sent = attp_send_sr_msg(*p_tcb, cid, p_buf);
- } else
+ } else {
cmd_sent = GATT_NO_RESOURCES;
+ }
return cmd_sent;
}
@@ -740,6 +820,10 @@
uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx);
tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
+ static uint16_t cached_read_handle;
+ static int cached_tcb_idx = -1;
+#endif
VLOG(1) << __func__ << ": conn_id=" << loghex(conn_id)
<< ", type=" << loghex(type);
@@ -783,6 +867,34 @@
break;
}
case GATT_READ_BY_HANDLE:
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
+ LOG_INFO("Upper tester: Handle read 0x%04x", p_read->by_handle.handle);
+ /* This is upper tester for the Multi Read stuff as this is mandatory for
+ * EATT, even Android is not making use of this operation :/ */
+ if (cached_tcb_idx < 0) {
+ cached_tcb_idx = tcb_idx;
+ LOG_INFO("Upper tester: Read multiple - first read");
+ cached_read_handle = p_read->by_handle.handle;
+ } else if (cached_tcb_idx == tcb_idx) {
+ LOG_INFO("Upper tester: Read multiple - second read");
+ cached_tcb_idx = -1;
+ tGATT_READ_MULTI* p_read_multi =
+ (tGATT_READ_MULTI*)osi_malloc(sizeof(tGATT_READ_MULTI));
+ p_read_multi->num_handles = 2;
+ p_read_multi->handles[0] = cached_read_handle;
+ p_read_multi->handles[1] = p_read->by_handle.handle;
+ p_read_multi->variable_len = true;
+
+ p_clcb->s_handle = 0;
+ p_clcb->op_subtype = GATT_READ_MULTIPLE_VAR_LEN;
+ p_clcb->p_attr_buf = (uint8_t*)p_read_multi;
+ p_clcb->cid = gatt_tcb_get_att_cid(*p_tcb, true /* eatt support */);
+
+ break;
+ }
+
+ FALLTHROUGH_INTENDED;
+#endif
case GATT_READ_PARTIAL:
p_clcb->uuid = Uuid::kEmpty;
p_clcb->s_handle = p_read->by_handle.handle;
diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc
index ccb9645..e67d890 100644
--- a/system/stack/gatt/gatt_attr.cc
+++ b/system/stack/gatt/gatt_attr.cc
@@ -81,6 +81,9 @@
static bool gatt_sr_is_robust_caching_enabled();
+static bool read_sr_supported_feat_req(
+ uint16_t conn_id, base::OnceCallback<void(const RawAddress&, uint8_t)> cb);
+
static tGATT_STATUS gatt_sr_read_db_hash(uint16_t conn_id,
tGATT_VALUE* p_value);
static tGATT_STATUS gatt_sr_read_cl_supp_feat(uint16_t conn_id,
@@ -567,10 +570,15 @@
<< " status: " << status
<< " conn id: " << loghex(static_cast<uint8_t>(conn_id));
- if (op != GATTC_OPTYPE_READ) return;
+ if (op != GATTC_OPTYPE_READ && op != GATTC_OPTYPE_WRITE) {
+ LOG_DEBUG("Not interested in opcode %d", op);
+ return;
+ }
if (iter == OngoingOps.end()) {
- LOG(ERROR) << __func__ << " Unexpected read complete";
+ /* If OngoingOps is empty it means we are not interested in the result here.
+ */
+ LOG_DEBUG("Unexpected read complete");
return;
}
@@ -578,6 +586,20 @@
uint16_t cl_op_uuid = operation_callback_data->op_uuid;
operation_callback_data->op_uuid = 0;
+ if (op == GATTC_OPTYPE_WRITE) {
+ if (cl_op_uuid == GATT_UUID_GATT_SRV_CHGD) {
+ LOG_DEBUG("Write response from Service Changed CCC");
+ OngoingOps.erase(iter);
+ /* Read server supported features here supported */
+ read_sr_supported_feat_req(
+ conn_id, base::BindOnce([](const RawAddress& bdaddr,
+ uint8_t support) { return; }));
+ } else {
+ LOG_DEBUG("Not interested in that write response");
+ }
+ return;
+ }
+
uint8_t* pp = p_data->att_value.value;
VLOG(1) << __func__ << " cl_op_uuid " << loghex(cl_op_uuid);
@@ -666,6 +688,13 @@
ccc_value.len = 2;
ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
GATTC_Write(p_clcb->conn_id, GATT_WRITE, &ccc_value);
+
+ gatt_op_cb_data cb_data;
+ cb_data.cb = base::BindOnce(
+ [](const RawAddress& bdaddr, uint8_t support) { return; });
+ cb_data.op_uuid = GATT_UUID_GATT_SRV_CHGD;
+ OngoingOps[p_clcb->conn_id] = std::move(cb_data);
+
break;
}
}
@@ -723,6 +752,30 @@
bluetooth::eatt::EattExtension::AddFromStorage(tcb.peer_bda);
}
+static bool read_sr_supported_feat_req(
+ uint16_t conn_id, base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
+ tGATT_READ_PARAM param = {};
+
+ param.service.s_handle = 1;
+ param.service.e_handle = 0xFFFF;
+ param.service.auth_req = 0;
+
+ param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
+
+ if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) != GATT_SUCCESS) {
+ LOG_ERROR("Read GATT Support features GATT_Read Failed");
+ return false;
+ }
+
+ gatt_op_cb_data cb_data;
+
+ cb_data.cb = std::move(cb);
+ cb_data.op_uuid = GATT_UUID_SERVER_SUP_FEAT;
+ OngoingOps[conn_id] = std::move(cb_data);
+
+ return true;
+}
+
/*******************************************************************************
*
* Function gatt_cl_read_sr_supp_feat_req
@@ -736,7 +789,6 @@
const RawAddress& peer_bda,
base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
tGATT_PROFILE_CLCB* p_clcb;
- tGATT_READ_PARAM param;
uint16_t conn_id;
if (!cb) return false;
@@ -765,25 +817,7 @@
return false;
}
- memset(¶m, 0, sizeof(tGATT_READ_PARAM));
-
- param.service.s_handle = 1;
- param.service.e_handle = 0xFFFF;
- param.service.auth_req = 0;
-
- param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
-
- if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) != GATT_SUCCESS) {
- LOG(ERROR) << __func__ << " Read GATT Support features GATT_Read Failed";
- return false;
- }
-
- gatt_op_cb_data cb_data;
- cb_data.cb = std::move(cb);
- cb_data.op_uuid = GATT_UUID_SERVER_SUP_FEAT;
- OngoingOps[conn_id] = std::move(cb_data);
-
- return true;
+ return read_sr_supported_feat_req(conn_id, std::move(cb));
}
/*******************************************************************************
@@ -822,7 +856,7 @@
*
******************************************************************************/
static bool gatt_sr_is_robust_caching_enabled() {
- return bluetooth::common::init_flags::gatt_robust_caching_is_enabled();
+ return bluetooth::common::init_flags::gatt_robust_caching_server_is_enabled();
}
/*******************************************************************************
@@ -842,6 +876,20 @@
/*******************************************************************************
*
+ * Function gatt_sr_is_cl_multi_variable_len_notif_supported
+ *
+ * Description Check if Multiple Variable Length Notifications
+ * supported for the connection
+ *
+ * Returns true if enabled by client side, otherwise false
+ *
+ ******************************************************************************/
+bool gatt_sr_is_cl_multi_variable_len_notif_supported(tGATT_TCB& tcb) {
+ return (tcb.cl_supp_feat & BLE_GATT_CL_SUP_FEAT_MULTI_NOTIF_BITMASK);
+}
+
+/*******************************************************************************
+ *
* Function gatt_sr_is_cl_change_aware
*
* Description Check if the connection is change-aware
diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc
index 89218e4..c5e5c76 100644
--- a/system/stack/gatt/gatt_cl.cc
+++ b/system/stack/gatt/gatt_cl.cc
@@ -200,6 +200,11 @@
memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
break;
+ case GATT_READ_MULTIPLE_VAR_LEN:
+ op_code = GATT_REQ_READ_MULTI_VAR;
+ memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
+ break;
+
case GATT_READ_INC_SRV_UUID128:
op_code = GATT_REQ_READ;
msg.handle = p_clcb->s_handle;
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 0641d4c..1e9e7e1 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -485,6 +485,7 @@
extern bool gatt_cl_read_sr_supp_feat_req(
const RawAddress& peer_bda,
base::OnceCallback<void(const RawAddress&, uint8_t)> cb);
+extern bool gatt_sr_is_cl_multi_variable_len_notif_supported(tGATT_TCB& tcb);
extern bool gatt_sr_is_cl_change_aware(tGATT_TCB& tcb);
extern void gatt_sr_init_cl_status(tGATT_TCB& tcb);
@@ -495,7 +496,7 @@
extern tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
uint8_t op_code, tGATT_CL_MSG* p_msg);
extern BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
- tGATT_SR_MSG* p_msg);
+ tGATT_SR_MSG* p_msg, uint16_t payload_size);
extern tGATT_STATUS attp_send_sr_msg(tGATT_TCB& tcb, uint16_t cid,
BT_HDR* p_msg);
extern tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB& tcb, uint16_t cid,
diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc
index df6a056..6fef443 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -27,6 +27,7 @@
#include "btif/include/btif_storage.h"
#include "connection_manager.h"
#include "device/include/interop.h"
+#include "internal_include/stack_config.h"
#include "l2c_api.h"
#include "osi/include/allocator.h"
#include "osi/include/osi.h"
@@ -82,6 +83,7 @@
gatt_on_l2cap_error,
NULL,
NULL,
+ NULL,
NULL};
tGATT_CB gatt_cb;
@@ -510,7 +512,10 @@
}
}
- EattExtension::GetInstance()->Connect(bd_addr);
+ if (stack_config_get_interface()->get_pts_connect_eatt_before_encryption()) {
+ LOG_INFO(" Start EATT before encryption ");
+ EattExtension::GetInstance()->Connect(bd_addr);
+ }
}
/** This function is called to process the congestion callback from lcb */
diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc
index 8e642d0..903cdaa 100644
--- a/system/stack/gatt/gatt_sr.cc
+++ b/system/stack/gatt/gatt_sr.cc
@@ -286,7 +286,7 @@
tGATTS_RSP* p_msg,
tGATT_SR_CMD* sr_res_p) {
tGATT_STATUS ret_code = GATT_SUCCESS;
- uint16_t payload_size = gatt_tcb_get_payload_size_rx(tcb, sr_res_p->cid);
+ uint16_t payload_size = gatt_tcb_get_payload_size_tx(tcb, sr_res_p->cid);
VLOG(1) << __func__ << " gatt_if=" << +gatt_if;
@@ -308,8 +308,8 @@
if (gatt_sr_is_cback_cnt_zero(tcb) && status == GATT_SUCCESS) {
if (sr_res_p->p_rsp_msg == NULL) {
- sr_res_p->p_rsp_msg = attp_build_sr_msg(tcb, (uint8_t)(op_code + 1),
- (tGATT_SR_MSG*)p_msg);
+ sr_res_p->p_rsp_msg = attp_build_sr_msg(
+ tcb, (uint8_t)(op_code + 1), (tGATT_SR_MSG*)p_msg, payload_size);
} else {
LOG(ERROR) << "Exception!!! already has respond message";
}
@@ -828,7 +828,8 @@
tGATT_SR_MSG gatt_sr_msg;
gatt_sr_msg.mtu = tcb.payload_size;
- BT_HDR* p_buf = attp_build_sr_msg(tcb, GATT_RSP_MTU, &gatt_sr_msg);
+ BT_HDR* p_buf =
+ attp_build_sr_msg(tcb, GATT_RSP_MTU, &gatt_sr_msg, tcb.payload_size);
attp_send_sr_msg(tcb, cid, p_buf);
tGATTS_DATA gatts_data;
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index 1311500..64b2f7a 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -664,8 +664,16 @@
}
}
- LOG(WARNING) << __func__ << " disconnecting...";
- gatt_disconnect(p_clcb->p_tcb);
+ auto eatt_channel = EattExtension::GetInstance()->FindEattChannelByCid(
+ p_clcb->p_tcb->peer_bda, p_clcb->cid);
+ if (eatt_channel) {
+ LOG_WARN("disconnecting EATT cid: %d", p_clcb->cid);
+ EattExtension::GetInstance()->Disconnect(p_clcb->p_tcb->peer_bda,
+ p_clcb->cid);
+ } else {
+ LOG_WARN("disconnecting GATT...");
+ gatt_disconnect(p_clcb->p_tcb);
+ }
}
extern void gatts_proc_srv_chg_ind_ack(tGATT_TCB tcb);
@@ -813,7 +821,8 @@
msg.error.reason = err_code;
msg.error.handle = handle;
- p_buf = attp_build_sr_msg(tcb, GATT_RSP_ERROR, &msg);
+ uint16_t payload_size = gatt_tcb_get_payload_size_tx(tcb, cid);
+ p_buf = attp_build_sr_msg(tcb, GATT_RSP_ERROR, &msg, payload_size);
if (p_buf != NULL) {
status = attp_send_sr_msg(tcb, cid, p_buf);
} else
diff --git a/system/stack/hid/hidd_conn.cc b/system/stack/hid/hidd_conn.cc
index 561c993..da78d40 100644
--- a/system/stack/hid/hidd_conn.cc
+++ b/system/stack/hid/hidd_conn.cc
@@ -58,6 +58,7 @@
hidd_on_l2cap_error,
NULL,
NULL,
+ NULL,
NULL};
/*******************************************************************************
diff --git a/system/stack/hid/hidh_conn.cc b/system/stack/hid/hidh_conn.cc
index ef0cc3b..1730a8e 100644
--- a/system/stack/hid/hidh_conn.cc
+++ b/system/stack/hid/hidh_conn.cc
@@ -78,6 +78,7 @@
.pL2CA_CreditBasedConnectInd_Cb = nullptr,
.pL2CA_CreditBasedConnectCfm_Cb = nullptr,
.pL2CA_CreditBasedReconfigCompleted_Cb = nullptr,
+ .pL2CA_CreditBasedCollisionInd_Cb = nullptr,
};
static void hidh_try_repage(uint8_t dhandle);
diff --git a/system/stack/include/avrc_api.h b/system/stack/include/avrc_api.h
index 134f463..877dafe 100644
--- a/system/stack/include/avrc_api.h
+++ b/system/stack/include/avrc_api.h
@@ -138,6 +138,17 @@
#define AVRC_DEFAULT_VERSION AVRC_1_5_STRING
#endif
+/* Configurable dynamic avrcp version enable key*/
+#ifndef AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY
+#define AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY \
+ "persist.bluetooth.dynamic_avrcp.enable"
+#endif
+
+/* Avrcp controller version key for bt_config.conf */
+#ifndef AVRCP_CONTROLLER_VERSION_CONFIG_KEY
+#define AVRCP_CONTROLLER_VERSION_CONFIG_KEY "AvrcpControllerVersion"
+#endif
+
/* Supported categories */
#define AVRC_SUPF_CT_CAT1 0x0001 /* Category 1 */
#define AVRC_SUPF_CT_CAT2 0x0002 /* Category 2 */
@@ -468,6 +479,28 @@
/******************************************************************************
*
+ * Function AVRC_SaveControllerVersion
+ *
+ * Description Save AVRC controller version of peer device into bt_config.
+ * This version is used to send same AVRC target version to
+ * peer device to avoid version mismatch IOP issue.
+ *
+ * Input Parameters:
+ * bdaddr: BD address of peer device.
+ *
+ * version: AVRC controller version of peer device.
+ *
+ * Output Parameters:
+ * None.
+ *
+ * Returns Nothing
+ *
+ *****************************************************************************/
+extern void AVRC_SaveControllerVersion(const RawAddress& bdaddr,
+ uint16_t new_version);
+
+/******************************************************************************
+ *
* Function AVRC_UnitCmd
*
* Description Send a UNIT INFO command to the peer device. This
diff --git a/system/stack/include/btm_api_types.h b/system/stack/include/btm_api_types.h
index 6fcfa6e..3e68f46 100644
--- a/system/stack/include/btm_api_types.h
+++ b/system/stack/include/btm_api_types.h
@@ -602,6 +602,8 @@
/* KEY update event */
#define BTM_LE_KEY_EVT (BTM_LE_LAST_FROM_SMP + 1)
#define BTM_LE_CONSENT_REQ_EVT SMP_CONSENT_REQ_EVT
+/* Identity address associate event */
+#define BTM_LE_ADDR_ASSOC_EVT SMP_LE_ADDR_ASSOC_EVT
typedef uint8_t tBTM_LE_EVT;
enum : uint8_t {
diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h
index afdb5e1..8de1dcc 100644
--- a/system/stack/include/btm_ble_api_types.h
+++ b/system/stack/include/btm_ble_api_types.h
@@ -565,6 +565,7 @@
tSMP_OOB_DATA_TYPE req_oob_type;
tBTM_LE_KEY key;
tSMP_LOC_OOB_DATA local_oob_data;
+ RawAddress id_addr;
} tBTM_LE_EVT_DATA;
/* Simple Pairing Events. Called by the stack when Simple Pairing related
diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h
index b38e943..1bb3d07 100644
--- a/system/stack/include/gatt_api.h
+++ b/system/stack/include/gatt_api.h
@@ -518,6 +518,7 @@
GATT_READ_BY_TYPE = 1,
GATT_READ_BY_HANDLE,
GATT_READ_MULTIPLE,
+ GATT_READ_MULTIPLE_VAR_LEN,
GATT_READ_CHAR_VALUE,
GATT_READ_PARTIAL,
GATT_READ_MAX
diff --git a/system/stack/include/l2c_api.h b/system/stack/include/l2c_api.h
index eb5ff83..92cf17f 100644
--- a/system/stack/include/l2c_api.h
+++ b/system/stack/include/l2c_api.h
@@ -181,6 +181,9 @@
static_assert(L2CAP_LE_CREDIT_THRESHOLD < L2CAP_LE_CREDIT_DEFAULT,
"Threshold must be smaller than default credits");
+// Max number of CIDs in the L2CAP CREDIT BASED CONNECTION REQUEST
+constexpr uint16_t L2CAP_CREDIT_BASED_MAX_CIDS = 5;
+
/* Define a structure to hold the configuration parameter for LE L2CAP
* connection oriented channels.
*/
@@ -189,6 +192,7 @@
uint16_t mtu = 100;
uint16_t mps = 100;
uint16_t credits = L2CAP_LE_CREDIT_DEFAULT;
+ uint8_t number_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS;
};
/*********************************
@@ -283,6 +287,14 @@
uint16_t psm, uint16_t peer_mtu,
uint8_t identifier);
+/* Collision Indication callback prototype. Used to notify upper layer that
+ * remote devices sent Credit Based Connection Request but it was rejected due
+ * to ongoing local request. Upper layer might want to sent another request when
+ * local request is completed. Parameters are:
+ * BD Address of remote
+ */
+typedef void(tL2CA_CREDIT_BASED_COLLISION_IND_CB)(const RawAddress& bdaddr);
+
/* Credit based connection confirmation callback prototype. Parameters are
* BD Address of remote
* Connected Local CIDs
@@ -324,6 +336,7 @@
tL2CA_CREDIT_BASED_CONNECT_CFM_CB* pL2CA_CreditBasedConnectCfm_Cb;
tL2CA_CREDIT_BASED_RECONFIG_COMPLETED_CB*
pL2CA_CreditBasedReconfigCompleted_Cb;
+ tL2CA_CREDIT_BASED_COLLISION_IND_CB* pL2CA_CreditBasedCollisionInd_Cb;
} tL2CAP_APPL_INFO;
/* Define the structure that applications use to create or accept
diff --git a/system/stack/include/smp_api_types.h b/system/stack/include/smp_api_types.h
index 42b61be..47e4078 100644
--- a/system/stack/include/smp_api_types.h
+++ b/system/stack/include/smp_api_types.h
@@ -98,7 +98,8 @@
SMP_UNUSED11 = 11,
SMP_BR_KEYS_REQ_EVT = 12, /* SMP over BR keys request event */
SMP_UNUSED13 = 13,
- SMP_CONSENT_REQ_EVT = 14, /* Consent request event */
+ SMP_CONSENT_REQ_EVT = 14, /* Consent request event */
+ SMP_LE_ADDR_ASSOC_EVT = 15, /* Identity address association event */
} tSMP_EVT;
/* pairing failure reason code */
@@ -297,6 +298,7 @@
tSMP_CMPL cmplt;
tSMP_OOB_DATA_TYPE req_oob_type;
tSMP_LOC_OOB_DATA loc_oob_data;
+ RawAddress id_addr;
} tSMP_EVT_DATA;
/* AES Encryption output */
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 48af5d1..6eedcbb 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -782,7 +782,13 @@
tL2C_CCB* p_ccb_primary;
- for (int i = 0; i < 5; i++) {
+ /* Make sure user set proper value for number of cids */
+ if (p_cfg->number_of_channels > L2CAP_CREDIT_BASED_MAX_CIDS ||
+ p_cfg->number_of_channels == 0) {
+ p_cfg->number_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS;
+ }
+
+ for (int i = 0; i < p_cfg->number_of_channels; i++) {
/* Allocate a channel control block */
tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);
if (p_ccb == NULL) {
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index 5ab208c..67ccd01 100755
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -541,15 +541,6 @@
"num_of_channels = %d",
mtu, mps, initial_credit, num_of_channels);
- if (p_lcb->pending_ecoc_conn_cnt > 0) {
- LOG_WARN("L2CAP - L2CAP_CMD_CREDIT_BASED_CONN_REQ collision:");
- l2cu_reject_credit_based_conn_req(p_lcb, id, num_of_channels,
- L2CAP_LE_RESULT_NO_RESOURCES);
- return;
- }
-
- p_lcb->pending_ecoc_conn_cnt = num_of_channels;
-
/* Check PSM Support */
p_rcb = l2cu_find_ble_rcb_by_psm(con_info.psm);
if (p_rcb == NULL) {
@@ -559,6 +550,19 @@
return;
}
+ if (p_lcb->pending_ecoc_conn_cnt > 0) {
+ LOG_WARN("L2CAP - L2CAP_CMD_CREDIT_BASED_CONN_REQ collision:");
+ if (p_rcb->api.pL2CA_CreditBasedCollisionInd_Cb &&
+ con_info.psm == BT_PSM_EATT) {
+ (*p_rcb->api.pL2CA_CreditBasedCollisionInd_Cb)(p_lcb->remote_bd_addr);
+ }
+ l2cu_reject_credit_based_conn_req(p_lcb, id, num_of_channels,
+ L2CAP_LE_RESULT_NO_RESOURCES);
+ return;
+ }
+
+ p_lcb->pending_ecoc_conn_cnt = num_of_channels;
+
if (!p_rcb->api.pL2CA_CreditBasedConnectInd_Cb) {
LOG_WARN("L2CAP - rcvd conn req for outgoing-only connection PSM: %d",
con_info.psm);
@@ -669,6 +673,7 @@
* all the channels has been rejected
*/
if (con_info.l2cap_result == L2CAP_LE_RESULT_NO_PSM ||
+ con_info.l2cap_result == L2CAP_LE_RESULT_NO_RESOURCES ||
con_info.l2cap_result ==
L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION ||
con_info.l2cap_result == L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP ||
@@ -715,22 +720,28 @@
for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
STREAM_TO_UINT16(rcid, p);
- /* if duplicated remote cid then disconnect original channel
- * and current channel by sending event to upper layer */
- temp_p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, rcid);
- if (temp_p_ccb != nullptr) {
- L2CAP_TRACE_ERROR(
- "Already Allocated Destination cid. "
- "rcid = %d "
- "send peer_disc_req", rcid);
- l2cu_send_peer_disc_req(temp_p_ccb);
+ if (rcid != 0) {
+ /* If remote cid is duplicated then disconnect original channel
+ * and current channel by sending event to upper layer
+ */
+ temp_p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, rcid);
+ if (temp_p_ccb != nullptr) {
+ L2CAP_TRACE_ERROR(
+ "Already Allocated Destination cid. "
+ "rcid = %d "
+ "send peer_disc_req",
+ rcid);
- temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
- con_info.l2cap_result = L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS;
- l2c_csm_execute(temp_p_ccb, L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG,
- &con_info);
- continue;
+ l2cu_send_peer_disc_req(temp_p_ccb);
+
+ temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
+ con_info.l2cap_result = L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS;
+ l2c_csm_execute(temp_p_ccb,
+ L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG,
+ &con_info);
+ continue;
+ }
}
temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
diff --git a/system/stack/l2cap/l2c_csm.cc b/system/stack/l2cap/l2c_csm.cc
index 3646964..79a35f6 100755
--- a/system/stack/l2cap/l2c_csm.cc
+++ b/system/stack/l2cap/l2c_csm.cc
@@ -801,6 +801,7 @@
static void l2c_csm_w4_l2ca_connect_rsp(tL2C_CCB* p_ccb, tL2CEVT event,
void* p_data) {
tL2C_CONN_INFO* p_ci;
+ tL2C_LCB* p_lcb = p_ccb->p_lcb;
tL2CA_DISCONNECT_IND_CB* disconnect_ind =
p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
uint16_t local_cid = p_ccb->local_cid;
@@ -818,7 +819,7 @@
case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP:
p_ci = (tL2C_CONN_INFO*)p_data;
- if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) {
+ if ((p_lcb == nullptr) || (p_lcb && p_lcb->transport != BT_TRANSPORT_LE)) {
LOG_WARN("LE link doesn't exist");
return;
}
@@ -826,14 +827,14 @@
p_ci->l2cap_result);
alarm_cancel(p_ccb->l2c_ccb_timer);
- for (int i = 0; i < p_ccb->p_lcb->pending_ecoc_conn_cnt; i++) {
- uint16_t cid = p_ccb->p_lcb->pending_ecoc_connection_cids[i];
+ for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
+ uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
if (cid == 0) {
LOG_WARN("pending_ecoc_connection_cids[%d] is %d", i, cid);
continue;
}
- tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_ccb->p_lcb, cid);
+ tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
if (temp_p_ccb) {
auto it = std::find(p_ci->lcids.begin(), p_ci->lcids.end(), cid);
if (it != p_ci->lcids.end()) {
@@ -846,8 +847,8 @@
LOG_WARN("temp_p_ccb is NULL, pending_ecoc_connection_cids[%d] is %d", i, cid);
}
}
- p_ccb->p_lcb->pending_ecoc_conn_cnt = 0;
- memset(p_ccb->p_lcb->pending_ecoc_connection_cids, 0,
+ p_lcb->pending_ecoc_conn_cnt = 0;
+ memset(p_lcb->pending_ecoc_connection_cids, 0,
L2CAP_CREDIT_BASED_MAX_CIDS);
break;
@@ -887,19 +888,19 @@
case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG:
p_ci = (tL2C_CONN_INFO*)p_data;
alarm_cancel(p_ccb->l2c_ccb_timer);
- if (p_ccb->p_lcb != nullptr) {
- if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) {
+ if (p_lcb != nullptr) {
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
l2cu_send_peer_credit_based_conn_res(p_ccb, p_ci->lcids,
p_ci->l2cap_result);
}
- for (int i = 0; i < p_ccb->p_lcb->pending_ecoc_conn_cnt; i++) {
- uint16_t cid = p_ccb->p_lcb->pending_ecoc_connection_cids[i];
- tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_ccb->p_lcb, cid);
+ for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
+ uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
+ tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
l2cu_release_ccb(temp_p_ccb);
}
- p_ccb->p_lcb->pending_ecoc_conn_cnt = 0;
- memset(p_ccb->p_lcb->pending_ecoc_connection_cids, 0,
+ p_lcb->pending_ecoc_conn_cnt = 0;
+ memset(p_lcb->pending_ecoc_connection_cids, 0,
L2CAP_CREDIT_BASED_MAX_CIDS);
}
break;
@@ -1619,6 +1620,10 @@
case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP: /* Upper layer credit based
connect response */
return ("SEND_CREDIT_BASED_CONNECT_RSP");
+ case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG: /* Upper layer credit based
+ connect response
+ (failed)*/
+ return ("SEND_CREDIT_BASED_CONNECT_RSP_NEG");
case L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ: /* Upper layer credit based
reconfig request */
return ("SEND_CREDIT_BASED_RECONFIG_REQ");
diff --git a/system/stack/l2cap/l2c_fcr.cc b/system/stack/l2cap/l2c_fcr.cc
index 1688e9b..4960858 100644
--- a/system/stack/l2cap/l2c_fcr.cc
+++ b/system/stack/l2cap/l2c_fcr.cc
@@ -681,8 +681,12 @@
/* Buffer length should not exceed local mps */
if (p_buf->len > p_ccb->local_conn_cfg.mps) {
- /* Discard the buffer */
+ LOG_ERROR("buffer length=%d exceeds local mps=%d. Drop and disconnect.",
+ p_buf->len, p_ccb->local_conn_cfg.mps);
+
+ /* Discard the buffer and disconnect*/
osi_free(p_buf);
+ l2cu_disconnect_chnl(p_ccb);
return;
}
@@ -699,8 +703,11 @@
/* Check the SDU Length with local MTU size */
if (sdu_length > p_ccb->local_conn_cfg.mtu) {
- /* Discard the buffer */
+ LOG_ERROR("sdu length=%d exceeds local mtu=%d. Drop and disconnect.",
+ sdu_length, p_ccb->local_conn_cfg.mtu);
+ /* Discard the buffer and disconnect*/
osi_free(p_buf);
+ l2cu_disconnect_chnl(p_ccb);
return;
}
diff --git a/system/stack/l2cap/l2c_int.h b/system/stack/l2cap/l2c_int.h
index 12b9992..39ba1ec 100644
--- a/system/stack/l2cap/l2c_int.h
+++ b/system/stack/l2cap/l2c_int.h
@@ -397,8 +397,6 @@
#define L2CAP_GET_PRIORITY_QUOTA(pri) \
((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT)
-#define L2CAP_CREDIT_BASED_MAX_CIDS 5
-
/* CCBs within the same LCB are served in round robin with priority It will make
* sure that low priority channel (for example, HF signaling on RFCOMM) can be
* sent to the headset even if higher priority channel (for example, AV media
diff --git a/system/stack/sdp/sdp_server.cc b/system/stack/sdp/sdp_server.cc
index 969d7c2..4842ecc 100644
--- a/system/stack/sdp/sdp_server.cc
+++ b/system/stack/sdp/sdp_server.cc
@@ -23,20 +23,21 @@
*
******************************************************************************/
+#include <base/logging.h>
#include <log/log.h>
#include <string.h> // memcpy
#include <cstdint>
+#include "btif/include/btif_config.h"
#include "device/include/interop.h"
#include "osi/include/allocator.h"
+#include "stack/include/avrc_api.h"
#include "stack/include/avrc_defs.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/sdp_api.h"
#include "stack/sdp/sdpint.h"
-#include <base/logging.h>
-
/* Maximum number of bytes to reserve out of SDP MTU for response data */
#define SDP_MAX_SERVICE_RSPHDR_LEN 12
#define SDP_MAX_SERVATTR_RSPHDR_LEN 10
@@ -410,12 +411,22 @@
p_ccb->cont_info.attr_offset = 0;
}
+ bool is_service_avrc_target = false;
+ const tSDP_ATTRIBUTE* p_attr_service_id;
+ p_attr_service_id = sdp_db_find_attr_in_rec(
+ p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_SERVICE_CLASS_ID_LIST);
+ if (p_attr_service_id) {
+ is_service_avrc_target = sdpu_is_service_id_avrc_target(p_attr_service_id);
+ }
/* Search for attributes that match the list given to us */
for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
attr_seq.attr_entry[xx].end);
if (p_attr) {
+ if (is_service_avrc_target) {
+ sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
+ }
/* Check if attribute fits. Assume 3-byte value type/length */
rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
@@ -564,7 +575,6 @@
const tSDP_RECORD* p_rec;
tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
const tSDP_ATTRIBUTE* p_attr;
- tSDP_ATTRIBUTE attr_sav;
bool maxxed_out = false, is_cont = false;
uint8_t* p_seq_start;
uint16_t seq_len, attr_len;
@@ -662,24 +672,22 @@
p_rsp += 3;
}
+ bool is_service_avrc_target = false;
+ const tSDP_ATTRIBUTE* p_attr_service_id;
+ p_attr_service_id = sdp_db_find_attr_in_rec(
+ p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_SERVICE_CLASS_ID_LIST);
+ if (p_attr_service_id) {
+ is_service_avrc_target =
+ sdpu_is_service_id_avrc_target(p_attr_service_id);
+ }
/* Get a list of handles that match the UUIDs given to us */
for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
attr_seq.attr_entry[xx].end);
if (p_attr) {
- // Check if the attribute contain AVRCP profile description list
- uint16_t avrcp_version = sdpu_is_avrcp_profile_description_list(p_attr);
- if (avrcp_version > AVRC_REV_1_4 &&
- interop_match_addr(INTEROP_AVRCP_1_4_ONLY,
- &(p_ccb->device_address))) {
- SDP_TRACE_DEBUG(
- "%s, device=%s is only accept AVRCP 1.4, reply AVRCP 1.4 "
- "instead.",
- __func__, p_ccb->device_address.ToString().c_str());
- memcpy(&attr_sav, p_attr, sizeof(tSDP_ATTRIBUTE));
- attr_sav.value_ptr[attr_sav.len - 1] = 0x04;
- p_attr = &attr_sav;
+ if (is_service_avrc_target) {
+ sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
}
/* Check if attribute fits. Assume 3-byte value type/length */
rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
diff --git a/system/stack/sdp/sdp_utils.cc b/system/stack/sdp/sdp_utils.cc
index 761883e..e919298 100644
--- a/system/stack/sdp/sdp_utils.cc
+++ b/system/stack/sdp/sdp_utils.cc
@@ -16,6 +16,8 @@
*
******************************************************************************/
+#define LOG_TAG "SDP_Utils"
+
/******************************************************************************
*
* This file contains SDP utility functions
@@ -28,12 +30,17 @@
#include <array>
#include <cstdint>
#include <cstring>
+#include <ostream>
#include <type_traits>
#include <utility>
#include <vector>
#include "btif/include/btif_config.h"
+#include "device/include/interop.h"
#include "osi/include/allocator.h"
+#include "osi/include/log.h"
+#include "osi/include/properties.h"
+#include "stack/include/avrc_api.h"
#include "stack/include/avrc_defs.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/sdp_api.h"
@@ -1202,3 +1209,136 @@
return 0;
}
}
+/*******************************************************************************
+ *
+ * Function sdpu_is_service_id_avrc_target
+ *
+ * Description This function is to check if attirbute is A/V Remote Control
+ * Target
+ *
+ * p_attr: attribute to be checked
+ *
+ * Returns true if service id of attirbute is A/V Remote Control
+ * Target, else false
+ *
+ ******************************************************************************/
+bool sdpu_is_service_id_avrc_target(const tSDP_ATTRIBUTE* p_attr) {
+ if (p_attr->id != ATTR_ID_SERVICE_CLASS_ID_LIST || p_attr->len != 3) {
+ return false;
+ }
+
+ uint8_t* p_uuid = p_attr->value_ptr + 1;
+ // check UUID of A/V Remote Control Target
+ if (p_uuid[0] != 0x11 || p_uuid[1] != 0xc) {
+ return false;
+ }
+
+ return true;
+}
+/*******************************************************************************
+ *
+ * Function spdu_is_avrcp_version_valid
+ *
+ * Description Check avrcp version is valid
+ *
+ * version: the avrcp version to check
+ *
+ * Returns true if avrcp version is valid, else false
+ *
+ ******************************************************************************/
+bool spdu_is_avrcp_version_valid(const uint16_t version) {
+ return version == AVRC_REV_1_0 || version == AVRC_REV_1_3 ||
+ version == AVRC_REV_1_4 || version == AVRC_REV_1_5 ||
+ version == AVRC_REV_1_6;
+}
+/*******************************************************************************
+ *
+ * Function sdpu_set_avrc_target_version
+ *
+ * Description This function is to set AVRCP version of A/V Remote Control
+ * Target according to IOP table and cached Bluetooth config
+ *
+ * p_attr: attribute to be modified
+ * bdaddr: for searching IOP table and BT config
+ *
+ *
+ * Returns true if service id of attirbute is A/V Remote Control
+ * Target, else false
+ *
+ ******************************************************************************/
+void sdpu_set_avrc_target_version(const tSDP_ATTRIBUTE* p_attr,
+ const RawAddress* bdaddr) {
+ // Check attribute is AVRCP profile description list and get AVRC Target
+ // version
+ uint16_t avrcp_version = sdpu_is_avrcp_profile_description_list(p_attr);
+ if (avrcp_version == 0) {
+ LOG_INFO("Not AVRCP version attribute or version not valid for device %s",
+ bdaddr->ToString().c_str());
+ return;
+ }
+
+ // Some remote devices will have interoperation issue when receive AVRCP
+ // version 1.5. If those devices are in IOP database and our version high than
+ // 1.4, we reply version 1.4 to them.
+ if (avrcp_version > AVRC_REV_1_4 &&
+ interop_match_addr(INTEROP_AVRCP_1_4_ONLY, bdaddr)) {
+ LOG_INFO(
+ "device=%s is in IOP database. "
+ "Reply AVRC Target version 1.4 instead of %x.",
+ bdaddr->ToString().c_str(), avrcp_version);
+ uint8_t* p_version = p_attr->value_ptr + 6;
+ UINT16_TO_BE_FIELD(p_version, AVRC_REV_1_4);
+ return;
+ }
+
+ // Dynamic ACRCP version. If our version high than remote device's version,
+ // reply version same as its. Otherwise, reply default version.
+ if (!osi_property_get_bool(AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY, true)) {
+ LOG_INFO(
+ "Dynamic AVRCP version feature is not enabled, skipping this method");
+ return;
+ }
+
+ // Read the remote device's AVRC Controller version from local storage
+ uint16_t cached_version = 0;
+ size_t version_value_size = btif_config_get_bin_length(
+ bdaddr->ToString(), AVRCP_CONTROLLER_VERSION_CONFIG_KEY);
+ if (version_value_size != sizeof(cached_version)) {
+ LOG_ERROR(
+ "cached value len wrong, bdaddr=%s. Len is %zu but should be %zu.",
+ bdaddr->ToString().c_str(), version_value_size, sizeof(cached_version));
+ return;
+ }
+
+ if (!btif_config_get_bin(bdaddr->ToString(),
+ AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+ (uint8_t*)&cached_version, &version_value_size)) {
+ LOG_INFO(
+ "no cached AVRC Controller version for %s. "
+ "Reply default AVRC Target version %x.",
+ bdaddr->ToString().c_str(), avrcp_version);
+ return;
+ }
+
+ if (!spdu_is_avrcp_version_valid(cached_version)) {
+ LOG_ERROR(
+ "cached AVRC Controller version %x of %s is not valid. "
+ "Reply default AVRC Target version %x.",
+ cached_version, bdaddr->ToString().c_str(), avrcp_version);
+ return;
+ }
+
+ if (avrcp_version > cached_version) {
+ LOG_INFO(
+ "read cached AVRC Controller version %x of %s. "
+ "Reply AVRC Target version %x.",
+ cached_version, bdaddr->ToString().c_str(), cached_version);
+ uint8_t* p_version = p_attr->value_ptr + 6;
+ UINT16_TO_BE_FIELD(p_version, cached_version);
+ } else {
+ LOG_INFO(
+ "read cached AVRC Controller version %x of %s. "
+ "Reply default AVRC Target version %x.",
+ cached_version, bdaddr->ToString().c_str(), avrcp_version);
+ }
+}
diff --git a/system/stack/sdp/sdpint.h b/system/stack/sdp/sdpint.h
index 74816dd..7b25bbc 100644
--- a/system/stack/sdp/sdpint.h
+++ b/system/stack/sdp/sdpint.h
@@ -232,6 +232,10 @@
uint16_t len, uint16_t* offset);
extern uint16_t sdpu_is_avrcp_profile_description_list(
const tSDP_ATTRIBUTE* p_attr);
+extern bool sdpu_is_service_id_avrc_target(const tSDP_ATTRIBUTE* p_attr);
+extern bool spdu_is_avrcp_version_valid(const uint16_t version);
+extern void sdpu_set_avrc_target_version(const tSDP_ATTRIBUTE* p_attr,
+ const RawAddress* bdaddr);
/* Functions provided by sdp_db.cc
*/
diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc
index 6d8f007..9aa64cf 100644
--- a/system/stack/smp/smp_act.cc
+++ b/system/stack/smp/smp_act.cc
@@ -138,6 +138,10 @@
cb_data.io_req.resp_keys = SMP_BR_SEC_DEFAULT_KEY;
break;
+ case SMP_LE_ADDR_ASSOC_EVT:
+ cb_data.id_addr = p_cb->id_addr;
+ break;
+
default:
LOG_ERROR("Unexpected event:%hhu", p_cb->cb_evt);
break;
@@ -212,6 +216,7 @@
// Expected, but nothing to do
case SMP_SC_LOC_OOB_DATA_UP_EVT:
+ case SMP_LE_ADDR_ASSOC_EVT:
break;
default:
@@ -544,6 +549,14 @@
STREAM_TO_UINT8(p_cb->peer_i_key, p);
STREAM_TO_UINT8(p_cb->peer_r_key, p);
+ tSMP_STATUS reason = p_cb->cert_failure;
+ if (reason == SMP_ENC_KEY_SIZE) {
+ tSMP_INT_DATA smp_int_data;
+ smp_int_data.status = reason;
+ smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
+ return;
+ }
+
if (smp_command_has_invalid_parameters(p_cb)) {
tSMP_INT_DATA smp_int_data;
smp_int_data.status = SMP_INVALID_PARAMETERS;
@@ -1050,8 +1063,11 @@
/* store the ID key from peer device */
if ((p_cb->peer_auth_req & SMP_AUTH_BOND) &&
- (p_cb->loc_auth_req & SMP_AUTH_BOND))
+ (p_cb->loc_auth_req & SMP_AUTH_BOND)) {
btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, &pid_key, true);
+ p_cb->cb_evt = SMP_LE_ADDR_ASSOC_EVT;
+ smp_send_app_cback(p_cb, NULL);
+ }
smp_key_distribution_by_transport(p_cb, NULL);
}
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index 256b474..e278e44 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -70,6 +70,11 @@
bool get_pts_crosskey_sdp_disable(void) { return false; }
const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
config_t* get_all(void) { return nullptr; }
const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
@@ -81,6 +86,15 @@
.get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
.get_pts_smp_options = get_pts_smp_options,
.get_pts_smp_failure_case = get_pts_smp_failure_case,
+ .get_pts_force_eatt_for_notifications =
+ get_pts_force_eatt_for_notifications,
+ .get_pts_connect_eatt_unconditionally =
+ get_pts_connect_eatt_unconditionally,
+ .get_pts_connect_eatt_before_encryption =
+ get_pts_connect_eatt_before_encryption,
+ .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};
const stack_config_t* stack_config_get_interface(void) {
diff --git a/system/stack/test/common/mock_btif_config.cc b/system/stack/test/common/mock_btif_config.cc
new file mode 100644
index 0000000..08fc80d
--- /dev/null
+++ b/system/stack/test/common/mock_btif_config.cc
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright 2022 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.
+ *
+ ******************************************************************************/
+
+#include "mock_btif_config.h"
+
+static bluetooth::manager::MockBtifConfigInterface* btif_config_interface =
+ nullptr;
+
+void bluetooth::manager::SetMockBtifConfigInterface(
+ MockBtifConfigInterface* mock_btif_config_interface) {
+ btif_config_interface = mock_btif_config_interface;
+}
+
+bool btif_config_get_bin(const std::string& section, const std::string& key,
+ uint8_t* value, size_t* length) {
+ return btif_config_interface->GetBin(section, key, value, length);
+}
+
+size_t btif_config_get_bin_length(const std::string& section,
+ const std::string& key) {
+ return btif_config_interface->GetBinLength(section, key);
+}
diff --git a/system/stack/test/common/mock_btif_config.h b/system/stack/test/common/mock_btif_config.h
new file mode 100644
index 0000000..9f11c43
--- /dev/null
+++ b/system/stack/test/common/mock_btif_config.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Copyright 2022 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <cstddef>
+
+namespace bluetooth {
+namespace manager {
+
+class BtifConfigInterface {
+ public:
+ virtual bool GetBin(const std::string& section, const std::string& key,
+ uint8_t* value, size_t* length) = 0;
+ virtual size_t GetBinLength(const std::string& section,
+ const std::string& key) = 0;
+ virtual ~BtifConfigInterface() = default;
+};
+
+class MockBtifConfigInterface : public BtifConfigInterface {
+ public:
+ MOCK_METHOD4(GetBin, bool(const std::string& section, const std::string& key,
+ uint8_t* value, size_t* length));
+ MOCK_METHOD2(GetBinLength,
+ size_t(const std::string& section, const std::string& key));
+};
+
+/**
+ * Set the {@link MockBtifConfigInterface} for testing
+ *
+ * @param mock_btif_config_interface pointer to mock btif config interface,
+ * could be null
+ */
+void SetMockBtifConfigInterface(
+ MockBtifConfigInterface* mock_btif_config_interface);
+
+} // namespace manager
+} // namespace bluetooth
diff --git a/system/stack/test/common/mock_btm_api_layer.cc b/system/stack/test/common/mock_btm_api_layer.cc
index 0b5c76f..c1779c5 100644
--- a/system/stack/test/common/mock_btm_api_layer.cc
+++ b/system/stack/test/common/mock_btm_api_layer.cc
@@ -31,3 +31,13 @@
sec_level, psm, mx_proto_id,
mx_chan_id);
}
+
+bool BTM_IsEncrypted(const RawAddress& remote_bd_addr,
+ tBT_TRANSPORT transport) {
+ return btm_api_interface->IsEncrypted(remote_bd_addr, transport);
+}
+
+bool BTM_IsLinkKeyKnown(const RawAddress& remote_bd_addr,
+ tBT_TRANSPORT transport) {
+ return btm_api_interface->IsLinkKeyKnown(remote_bd_addr, transport);
+}
diff --git a/system/stack/test/common/mock_btm_api_layer.h b/system/stack/test/common/mock_btm_api_layer.h
index 052babb..8c92fb3 100644
--- a/system/stack/test/common/mock_btm_api_layer.h
+++ b/system/stack/test/common/mock_btm_api_layer.h
@@ -32,6 +32,10 @@
uint32_t mx_chan_id) = 0;
virtual uint8_t acl_link_role(const RawAddress& remote_bd_addr,
tBT_TRANSPORT transport) = 0;
+ virtual bool IsEncrypted(const RawAddress& remote_bd_addr,
+ tBT_TRANSPORT transport) = 0;
+ virtual bool IsLinkKeyKnown(const RawAddress& remote_bd_addr,
+ tBT_TRANSPORT transport) = 0;
virtual ~BtmApiInterface() = default;
};
@@ -43,6 +47,10 @@
uint32_t mx_chan_id));
MOCK_METHOD2(acl_link_role, uint8_t(const RawAddress& remote_bd_addr,
tBT_TRANSPORT transport));
+ MOCK_METHOD2(IsEncrypted,
+ bool(const RawAddress& remote_bd_addr, tBT_TRANSPORT transport));
+ MOCK_METHOD2(IsLinkKeyKnown,
+ bool(const RawAddress& remote_bd_addr, tBT_TRANSPORT transport));
};
/**
diff --git a/system/stack/test/common/mock_eatt.cc b/system/stack/test/common/mock_eatt.cc
index 560531a..6e07bf5 100644
--- a/system/stack/test/common/mock_eatt.cc
+++ b/system/stack/test/common/mock_eatt.cc
@@ -46,8 +46,8 @@
pimpl_->Connect(bd_addr);
}
-void EattExtension::Disconnect(const RawAddress& bd_addr) {
- pimpl_->Disconnect(bd_addr);
+void EattExtension::Disconnect(const RawAddress& bd_addr, uint16_t cid) {
+ pimpl_->Disconnect(bd_addr, cid);
}
void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid,
diff --git a/system/stack/test/common/mock_eatt.h b/system/stack/test/common/mock_eatt.h
index a7b6c5f..6d1682b 100644
--- a/system/stack/test/common/mock_eatt.h
+++ b/system/stack/test/common/mock_eatt.h
@@ -35,7 +35,7 @@
static MockEattExtension* GetInstance();
MOCK_METHOD((void), Connect, (const RawAddress& bd_addr));
- MOCK_METHOD((void), Disconnect, (const RawAddress& bd_addr));
+ MOCK_METHOD((void), Disconnect, (const RawAddress& bd_addr, uint16_t cid));
MOCK_METHOD((void), Reconfigure,
(const RawAddress& bd_addr, uint16_t cid, uint16_t mtu));
MOCK_METHOD((void), ReconfigureAll,
diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc
index 3eda92c..fbd060f 100644
--- a/system/stack/test/eatt/eatt_test.cc
+++ b/system/stack/test/eatt/eatt_test.cc
@@ -62,7 +62,8 @@
class EattTest : public testing::Test {
protected:
- void ConnectDeviceEattSupported(int num_of_accepted_connections) {
+ void ConnectDeviceEattSupported(int num_of_accepted_connections,
+ bool collision = false) {
ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
.WillByDefault(
[](const RawAddress& addr,
@@ -80,6 +81,14 @@
eatt_instance_->Connect(test_address);
+ if (collision) {
+ EXPECT_CALL(l2cap_interface_,
+ ConnectCreditBasedReq(BT_PSM_EATT, test_address, _))
+ .Times(1);
+
+ l2cap_app_info_.pL2CA_CreditBasedCollisionInd_Cb(test_address);
+ }
+
int i = 0;
for (uint16_t cid : test_local_cids) {
EattChannel* channel =
@@ -185,17 +194,25 @@
TEST_F(EattTest, IncomingEattConnectionByUnknownDevice) {
std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
- EXPECT_CALL(l2cap_interface_,
- ConnectCreditBasedRsp(test_address, 1, incoming_cids,
- L2CAP_CONN_NO_RESOURCES, _))
+ ON_CALL(btm_api_interface_, IsEncrypted)
+ .WillByDefault(
+ [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+ EXPECT_CALL(
+ l2cap_interface_,
+ ConnectCreditBasedRsp(test_address, 1, incoming_cids, L2CAP_CONN_OK, _))
.WillOnce(Return(true));
l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+
+ DisconnectEattDevice(incoming_cids);
}
TEST_F(EattTest, IncomingEattConnectionByKnownDevice) {
hci_role_ = HCI_ROLE_PERIPHERAL;
+ ON_CALL(btm_api_interface_, IsEncrypted)
+ .WillByDefault(
+ [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
.WillByDefault(
[](const RawAddress& addr,
@@ -222,11 +239,69 @@
hci_role_ = HCI_ROLE_CENTRAL;
}
+TEST_F(EattTest, IncomingEattConnectionByKnownDeviceEncryptionOff) {
+ hci_role_ = HCI_ROLE_PERIPHERAL;
+ ON_CALL(btm_api_interface_, IsEncrypted)
+ .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+ return false;
+ });
+ ON_CALL(btm_api_interface_, IsLinkKeyKnown)
+ .WillByDefault(
+ [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+ ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
+ .WillByDefault(
+ [](const RawAddress& addr,
+ base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
+ std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+ return true;
+ });
+ ON_CALL(gatt_interface_, GetEattSupport)
+ .WillByDefault([](const RawAddress& addr) { return true; });
+
+ eatt_instance_->Connect(test_address);
+ std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
+
+ EXPECT_CALL(l2cap_interface_,
+ ConnectCreditBasedRsp(test_address, 1, _,
+ L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP, _))
+ .WillOnce(Return(true));
+
+ l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
+ test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+
+ hci_role_ = HCI_ROLE_CENTRAL;
+}
+
+TEST_F(EattTest, IncomingEattConnectionByUnknownDeviceEncryptionOff) {
+ std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
+
+ ON_CALL(btm_api_interface_, IsEncrypted)
+ .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+ return false;
+ });
+ ON_CALL(btm_api_interface_, IsLinkKeyKnown)
+ .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+ return false;
+ });
+ EXPECT_CALL(
+ l2cap_interface_,
+ ConnectCreditBasedRsp(test_address, 1, _,
+ L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION, _))
+ .WillOnce(Return(true));
+
+ l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
+ test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+}
+
TEST_F(EattTest, ReconnectInitiatedByRemoteSucceed) {
ConnectDeviceEattSupported(1);
DisconnectEattDevice(connected_cids_);
std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
+ ON_CALL(btm_api_interface_, IsEncrypted)
+ .WillByDefault(
+ [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+
EXPECT_CALL(
l2cap_interface_,
ConnectCreditBasedRsp(test_address, 1, incoming_cids, L2CAP_CONN_OK, _))
@@ -443,4 +518,10 @@
/* Force second disconnect */
eatt_instance_->Disconnect(test_address);
}
+
+TEST_F(EattTest, TestCollisionHandling) {
+ ConnectDeviceEattSupported(0, true /* collision*/);
+ ConnectDeviceEattSupported(5, true /* collision*/);
+}
+
} // namespace
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index 913cf61..7e6ab31 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -65,8 +65,8 @@
}
} // namespace connection_manager
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
- tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+ uint16_t payload_size) {
test_state_.attp_build_sr_msg.op_code_ = op_code;
return nullptr;
}
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index 1c96dd4..d416e98 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -21,9 +21,6 @@
#include "types/raw_address.h"
#include "utils/include/bt_utils.h"
-/** stack/btu/btu_task.cc, indirect reference, gatt_utils.cc -> libosi */
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
/** stack/gatt/connection_manager.cc */
namespace connection_manager {
bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
@@ -35,8 +32,8 @@
} // namespace connection_manager
/** stack/gatt/att_protocol.cc */
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
- tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+ uint16_t payload_size) {
return nullptr;
}
tGATT_STATUS attp_send_cl_confirmation_msg(tGATT_TCB& tcb, uint16_t cid) {
diff --git a/system/stack/test/gatt/stack_gatt_test.cc b/system/stack/test/gatt/stack_gatt_test.cc
index 7e97771..38d6cc6 100644
--- a/system/stack/test/gatt/stack_gatt_test.cc
+++ b/system/stack/test/gatt/stack_gatt_test.cc
@@ -33,8 +33,6 @@
void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
class StackGattTest : public ::testing::Test {};
namespace {
diff --git a/system/stack/test/stack_sdp_utils_test.cc b/system/stack/test/stack_sdp_utils_test.cc
new file mode 100644
index 0000000..200a429
--- /dev/null
+++ b/system/stack/test/stack_sdp_utils_test.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2022 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.
+ */
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <cstddef>
+
+#include "bt_types.h"
+#include "device/include/interop.h"
+#include "mock_btif_config.h"
+#include "stack/include/avrc_defs.h"
+#include "stack/include/sdp_api.h"
+#include "stack/sdp/sdpint.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArrayArgument;
+
+// Global trace level referred in the code under test
+uint8_t appl_trace_level = BT_TRACE_LEVEL_VERBOSE;
+
+extern "C" void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+
+namespace {
+// convenience mock
+class IopMock {
+ public:
+ MOCK_METHOD2(InteropMatchAddr,
+ bool(const interop_feature_t, const RawAddress*));
+};
+
+std::unique_ptr<IopMock> localIopMock;
+} // namespace
+
+bool interop_match_addr(const interop_feature_t feature,
+ const RawAddress* addr) {
+ return localIopMock->InteropMatchAddr(feature, addr);
+}
+
+bool osi_property_get_bool(const char* key, bool default_value) { return true; }
+
+uint8_t avrc_value[8] = {
+ ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE), // data_element
+ 6, // data_len
+ ((UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES), // uuid_element
+ 0, // uuid
+ 0, // uuid
+ ((UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES), // version_element
+ 0, // version
+ 0 // version
+};
+tSDP_ATTRIBUTE avrcp_attr = {
+ .len = 0,
+ .value_ptr = (uint8_t*)(&avrc_value),
+ .id = 0,
+ .type = 0,
+};
+
+void set_avrcp_attr(uint32_t len, uint16_t id, uint16_t uuid,
+ uint16_t version) {
+ UINT16_TO_BE_FIELD(avrc_value + 3, uuid);
+ UINT16_TO_BE_FIELD(avrc_value + 6, version);
+ avrcp_attr.len = len;
+ avrcp_attr.id = id;
+}
+
+uint16_t get_avrc_target_version(tSDP_ATTRIBUTE* p_attr) {
+ uint8_t* p_version = p_attr->value_ptr + 6;
+ uint16_t version =
+ (((uint16_t)(*(p_version))) << 8) + ((uint16_t)(*((p_version) + 1)));
+ return version;
+}
+
+class StackSdpUtilsTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ bluetooth::manager::SetMockBtifConfigInterface(&btif_config_interface_);
+ localIopMock = std::make_unique<IopMock>();
+ set_avrcp_attr(8, ATTR_ID_BT_PROFILE_DESC_LIST,
+ UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+ }
+
+ void TearDown() override {
+ bluetooth::manager::SetMockBtifConfigInterface(nullptr);
+ localIopMock.reset();
+ }
+ bluetooth::manager::MockBtifConfigInterface btif_config_interface_;
+};
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_in_iop_table) {
+ RawAddress bdaddr;
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(true));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_4);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_len) {
+ RawAddress bdaddr;
+ set_avrcp_attr(5, ATTR_ID_BT_PROFILE_DESC_LIST,
+ UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_attribute_id) {
+ RawAddress bdaddr;
+ set_avrcp_attr(8, ATTR_ID_SERVICE_CLASS_ID_LIST,
+ UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_uuid) {
+ RawAddress bdaddr;
+ set_avrcp_attr(8, ATTR_ID_BT_PROFILE_DESC_LIST, UUID_SERVCLASS_AUDIO_SOURCE,
+ AVRC_REV_1_5);
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// device's controller version older than our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_older_version) {
+ RawAddress bdaddr;
+ uint8_t config_0104[2] = {0x04, 0x01};
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(2));
+ EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+ .WillOnce(DoAll(SetArrayArgument<2>(config_0104, config_0104 + 2),
+ Return(true)));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_4);
+}
+
+// device's controller version same as our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_same_version) {
+ RawAddress bdaddr;
+ uint8_t config_0105[2] = {0x05, 0x01};
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(2));
+ EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+ .WillOnce(DoAll(SetArrayArgument<2>(config_0105, config_0105 + 2),
+ Return(true)));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// device's controller version higher than our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_newer_version) {
+ RawAddress bdaddr;
+ uint8_t config_0106[2] = {0x06, 0x01};
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(2));
+ EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+ .WillOnce(DoAll(SetArrayArgument<2>(config_0106, config_0106 + 2),
+ Return(true)));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// cannot read device's controller version from bt_config
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_no_config_value) {
+ RawAddress bdaddr;
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(0));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// read device's controller version from bt_config return only 1 byte
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_1_byte) {
+ RawAddress bdaddr;
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(1));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// read device's controller version from bt_config return 3 bytes
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_3_bytes) {
+ RawAddress bdaddr;
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(3));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// cached controller version is not valid
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_not_valid) {
+ RawAddress bdaddr;
+ uint8_t config_not_valid[2] = {0x12, 0x34};
+ EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+ .WillOnce(Return(false));
+ EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+ .WillOnce(Return(2));
+ EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+ .WillOnce(
+ DoAll(SetArrayArgument<2>(config_not_valid, config_not_valid + 2),
+ Return(true)));
+ sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+ ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc
index c20914e..abf10b9 100644
--- a/system/stack/test/stack_smp_test.cc
+++ b/system/stack/test/stack_smp_test.cc
@@ -45,6 +45,11 @@
bool get_pts_crosskey_sdp_disable(void) { return false; }
const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
config_t* get_all(void) { return nullptr; }
const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
@@ -56,6 +61,15 @@
.get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
.get_pts_smp_options = get_pts_smp_options,
.get_pts_smp_failure_case = get_pts_smp_failure_case,
+ .get_pts_force_eatt_for_notifications =
+ get_pts_force_eatt_for_notifications,
+ .get_pts_connect_eatt_unconditionally =
+ get_pts_connect_eatt_unconditionally,
+ .get_pts_connect_eatt_before_encryption =
+ get_pts_connect_eatt_before_encryption,
+ .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};
const stack_config_t* stack_config_get_interface(void) {
diff --git a/system/test/common/stack_config.cc b/system/test/common/stack_config.cc
index db34351..becbabd 100644
--- a/system/test/common/stack_config.cc
+++ b/system/test/common/stack_config.cc
@@ -30,6 +30,11 @@
bool get_pts_crosskey_sdp_disable(void) { return false; }
const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
struct config_t;
config_t* get_all(void) { return nullptr; }
struct packet_fragmenter_t;
@@ -43,6 +48,15 @@
.get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
.get_pts_smp_options = get_pts_smp_options,
.get_pts_smp_failure_case = get_pts_smp_failure_case,
+ .get_pts_force_eatt_for_notifications =
+ get_pts_force_eatt_for_notifications,
+ .get_pts_connect_eatt_unconditionally =
+ get_pts_connect_eatt_unconditionally,
+ .get_pts_connect_eatt_before_encryption =
+ get_pts_connect_eatt_before_encryption,
+ .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};
diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc
index 23884c1..f3ff188 100644
--- a/system/test/headless/headless.cc
+++ b/system/test/headless/headless.cc
@@ -109,6 +109,11 @@
LOG_INFO("%s", __func__);
}
+void le_address_associate(RawAddress* main_bd_addr,
+ RawAddress* secondary_bd_addr) {
+ LOG_INFO("%s", __func__);
+}
+
/** Bluetooth ACL connection state changed callback */
void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
bt_acl_state_t state, int transport_link_type,
@@ -171,6 +176,7 @@
.ssp_request_cb = ssp_request,
.bond_state_changed_cb = bond_state_changed,
.address_consolidate_cb = address_consolidate,
+ .le_address_associate_cb = le_address_associate,
.acl_state_changed_cb = acl_state_changed,
.thread_evt_cb = thread_event,
.dut_mode_recv_cb = dut_mode_recv,
diff --git a/system/test/mock/mock_bluetooth_interface.cc b/system/test/mock/mock_bluetooth_interface.cc
index 4312828..a245451 100644
--- a/system/test/mock/mock_bluetooth_interface.cc
+++ b/system/test/mock/mock_bluetooth_interface.cc
@@ -230,6 +230,9 @@
void invoke_address_consolidate_cb(RawAddress main_bd_addr,
RawAddress secondary_bd_addr) {}
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+ RawAddress secondary_bd_addr) {}
+
void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
bt_acl_state_t state, int transport_link_type,
bt_hci_error_code_t hci_reason) {}
diff --git a/system/test/mock/mock_stack_avrc_api.cc b/system/test/mock/mock_stack_avrc_api.cc
index 4d86f4a..c9e58a9 100644
--- a/system/test/mock/mock_stack_avrc_api.cc
+++ b/system/test/mock/mock_stack_avrc_api.cc
@@ -66,6 +66,9 @@
mock_function_count_map[__func__]++;
return 0;
}
+void AVRC_SaveControllerVersion(const RawAddress& bdaddr, uint16_t version) {
+ mock_function_count_map[__func__]++;
+}
uint16_t AVRC_PassCmd(uint8_t handle, uint8_t label, tAVRC_MSG_PASS* p_msg) {
mock_function_count_map[__func__]++;
return 0;