diff --git a/android/app/jni/com_android_bluetooth_hfp.cpp b/android/app/jni/com_android_bluetooth_hfp.cpp index 0e3c95035bf923eede95e282f8d74a760d6dd90c..d33669f49150ef7743d6e2e85c023cf08b4eafb7 100644 --- a/android/app/jni/com_android_bluetooth_hfp.cpp +++ b/android/app/jni/com_android_bluetooth_hfp.cpp @@ -237,7 +237,8 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks { addr.get()); } - void SwbCallback(bluetooth::headset::bthf_swb_config_t swb_config, + void SwbCallback(bluetooth::headset::bthf_swb_codec_t swb_codec, + bluetooth::headset::bthf_swb_config_t swb_config, RawAddress* bd_addr) override { std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); @@ -246,8 +247,8 @@ class JniHeadsetCallbacks : bluetooth::headset::Callbacks { ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr)); if (addr.get() == nullptr) return; - sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSWB, swb_config, - addr.get()); + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSWB, swb_codec, + swb_config, addr.get()); } void AtChldCallback(bluetooth::headset::bthf_chld_type_t chld, @@ -998,7 +999,7 @@ int register_com_android_bluetooth_hfp(JNIEnv* env) { {"onSendDtmf", "(I[B)V", &method_onSendDtmf}, {"onNoiseReductionEnable", "(Z[B)V", &method_onNoiseReductionEnable}, {"onWBS", "(I[B)V", &method_onWBS}, - {"onSWB", "(I[B)V", &method_onSWB}, + {"onSWB", "(II[B)V", &method_onSWB}, {"onAtChld", "(I[B)V", &method_onAtChld}, {"onAtCnum", "([B)V", &method_onAtCnum}, {"onAtCind", "([B)V", &method_onAtCind}, diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetHalConstants.java b/android/app/src/com/android/bluetooth/hfp/HeadsetHalConstants.java index 9f680122250aecd01278a38d5121ce26e3319a3a..5d5d6966bb15986bbd7d708099bf6543f8333d8f 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetHalConstants.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetHalConstants.java @@ -75,6 +75,10 @@ public final class HeadsetHalConstants { static final int BTHF_WBS_NO = 1; static final int BTHF_WBS_YES = 2; + // match up with bthf_swb_codec_t of bt_hf.h + static final int BTHF_SWB_CODEC_LC3 = 0; + static final int BTHF_SWB_CODEC_VENDOR_APTX = 1; + // match up with bthf_swb_config_t of bt_hf.h static final int BTHF_SWB_NONE = 0; static final int BTHF_SWB_NO = 1; diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java index 4e607508595ee3b9939902a87dfd9bade02d3875..cc7e3851a1c85eaa771aa3ec162796581636df4f 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java @@ -163,9 +163,10 @@ public class HeadsetNativeInterface { sendMessageToService(event); } - private void onSWB(int codec, byte[] address) { + private void onSWB(int codec, int swb, byte[] address) { HeadsetStackEvent event = - new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SWB, codec, getDevice(address)); + new HeadsetStackEvent( + HeadsetStackEvent.EVENT_TYPE_SWB, codec, swb, getDevice(address)); sendMessageToService(event); } diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index 519ba33f9cfb90d80cc9872fb1c8a77932d104b5..126e707b74c1504f0990e91d7fffd2885dd91385 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -17,6 +17,7 @@ package com.android.bluetooth.hfp; import static android.Manifest.permission.BLUETOOTH_CONNECT; + import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import android.annotation.RequiresPermission; @@ -151,7 +152,8 @@ public class HeadsetStateMachine extends StateMachine { // Audio Parameters private boolean mHasNrecEnabled = false; private boolean mHasWbsEnabled = false; - private boolean mHasSwbEnabled = false; + private boolean mHasSwbLc3Enabled = false; + private boolean mHasSwbAptXEnabled = false; // AT Phone book keeps a group of states used by AT+CPBR commands @VisibleForTesting final AtPhonebook mPhonebook; @@ -250,7 +252,8 @@ public class HeadsetStateMachine extends StateMachine { } mHasWbsEnabled = false; mHasNrecEnabled = false; - mHasSwbEnabled = false; + mHasSwbLc3Enabled = false; + mHasSwbAptXEnabled = false; } public void dump(StringBuilder sb) { @@ -482,8 +485,9 @@ public class HeadsetStateMachine extends StateMachine { updateAgIndicatorEnableState(null); mNeedDialingOutReply = false; mHasWbsEnabled = false; - mHasSwbEnabled = false; + mHasSwbLc3Enabled = false; mHasNrecEnabled = false; + mHasSwbAptXEnabled = false; broadcastStateTransitions(); logFailureIfNeeded(); @@ -686,7 +690,7 @@ public class HeadsetStateMachine extends StateMachine { processWBSEvent(event.valueInt); break; case HeadsetStackEvent.EVENT_TYPE_SWB: - processSWBEvent(event.valueInt); + processSWBEvent(event.valueInt, event.valueInt2); break; case HeadsetStackEvent.EVENT_TYPE_BIND: processAtBind(event.valueString, event.device); @@ -1027,7 +1031,7 @@ public class HeadsetStateMachine extends StateMachine { processWBSEvent(event.valueInt); break; case HeadsetStackEvent.EVENT_TYPE_SWB: - processSWBEvent(event.valueInt); + processSWBEvent(event.valueInt, event.valueInt2); break; case HeadsetStackEvent.EVENT_TYPE_AT_CHLD: processAtChld(event.valueInt, event.device); @@ -1636,11 +1640,17 @@ public class HeadsetStateMachine extends StateMachine { private void setAudioParameters() { AudioManager am = mSystemInterface.getAudioManager(); - Log.i(TAG, "setAudioParameters for " + mDevice + ":" - + " Name=" + getCurrentDeviceName() - + " hasNrecEnabled=" + mHasNrecEnabled - + " hasWbsEnabled=" + mHasWbsEnabled); - am.setParameters("bt_lc3_swb=" + (mHasSwbEnabled ? "on" : "off")); + Log.i( + TAG, + ("setAudioParameters for " + mDevice + ":") + + (" Name=" + getCurrentDeviceName()) + + (" hasNrecEnabled=" + mHasNrecEnabled) + + (" hasWbsEnabled=" + mHasWbsEnabled) + + (" hasSwbEnabled=" + mHasSwbLc3Enabled) + + (" hasAptXSwbEnabled=" + mHasSwbAptXEnabled)); + am.setParameters("bt_lc3_swb=" + (mHasSwbLc3Enabled ? "on" : "off")); + /* AptX bt_swb: 0 -> on, 65535 -> off */ + am.setParameters("bt_swb=" + (mHasSwbAptXEnabled ? "0" : "65535")); am.setBluetoothHeadsetProperties(getCurrentDeviceName(), mHasNrecEnabled, mHasWbsEnabled); } @@ -1797,21 +1807,48 @@ public class HeadsetStateMachine extends StateMachine { log("processWBSEvent: " + prevWbs + " -> " + mHasWbsEnabled); } - private void processSWBEvent(int swbConfig) { - boolean prev_swb = mHasSwbEnabled; + private void processSWBEvent(int swbCodec, int swbConfig) { + boolean prevSwbLc3 = mHasSwbLc3Enabled; + boolean prevSwbAptx = mHasSwbAptXEnabled; + boolean success = true; + switch (swbConfig) { case HeadsetHalConstants.BTHF_SWB_YES: - mHasSwbEnabled = true; + switch (swbCodec) { + case HeadsetHalConstants.BTHF_SWB_CODEC_LC3: + mHasSwbLc3Enabled = true; + mHasWbsEnabled = false; + mHasSwbAptXEnabled = false; + break; + case HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX: + mHasSwbLc3Enabled = false; + mHasWbsEnabled = false; + mHasSwbAptXEnabled = true; + break; + default: + success = false; + break; + } break; case HeadsetHalConstants.BTHF_SWB_NO: case HeadsetHalConstants.BTHF_SWB_NONE: - mHasSwbEnabled = false; + mHasSwbLc3Enabled = false; + mHasSwbAptXEnabled = false; break; default: - Log.e(TAG, "processSWBEvent: unknown swb_config"); - return; + success = false; } - log("processSWBEvent: " + prev_swb + " -> " + mHasSwbEnabled); + + if (!success) { + Log.e( + TAG, + ("processSWBEvent failed: swbCodec: " + swbCodec) + + (" swb_config: " + swbConfig)); + return; + } + + log("processSWBEvent LC3 SWB config: " + prevSwbLc3 + " -> " + mHasSwbLc3Enabled); + log("processSWBEvent AptX SWB config: " + prevSwbAptx + " -> " + mHasSwbAptXEnabled); } @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) diff --git a/system/bta/Android.bp b/system/bta/Android.bp index d415a4988a0a2449ed168b1f2aab8dbd55c5d205..91b390c1ae9cc0cb4b4a4f26c61692ec5ac30574 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -65,6 +65,7 @@ cc_library_static { "ag/bta_ag_rfc.cc", "ag/bta_ag_sco.cc", "ag/bta_ag_sdp.cc", + "ag/bta_ag_swb_aptx.cc", "ar/bta_ar.cc", "av/bta_av_aact.cc", "av/bta_av_act.cc", diff --git a/system/bta/ag/bta_ag_act.cc b/system/bta/ag/bta_ag_act.cc index e1a81dc77c6ed7a3607b54361c936319617d67ed..64b3ff51f5555c6e5065983e68355b6a56a36f03 100644 --- a/system/bta/ag/bta_ag_act.cc +++ b/system/bta/ag/bta_ag_act.cc @@ -24,11 +24,16 @@ #include <base/logging.h> +#ifdef __ANDROID__ +#include <com_android_bluetooth_flags.h> +#endif + #include <cstdint> #include <cstring> #include "bta/ag/bta_ag_int.h" #include "bta/include/bta_dm_api.h" +#include "bta_ag_swb_aptx.h" #ifdef __ANDROID__ #include "bta/le_audio/devices.h" @@ -38,9 +43,9 @@ #include "device/include/device_iot_config.h" #include "osi/include/osi.h" // UNUSED_ATTR #include "stack/include/bt_uuid16.h" +#include "stack/include/btm_sec_api_types.h" #include "stack/include/l2c_api.h" #include "stack/include/port_api.h" -#include "stack/include/btm_sec_api_types.h" #include "stack/include/sdp_api.h" #include "types/raw_address.h" @@ -352,6 +357,7 @@ void bta_ag_rfc_fail(tBTA_AG_SCB* p_scb, UNUSED_ATTR const tBTA_AG_DATA& data) { p_scb->peer_features = 0; p_scb->peer_codecs = BTM_SCO_CODEC_CVSD; p_scb->sco_codec = BTM_SCO_CODEC_CVSD; + p_scb->is_aptx_swb_codec = false; p_scb->role = 0; p_scb->svc_conn = false; p_scb->hsp_version = HSP_VERSION_1_2; @@ -391,6 +397,8 @@ void bta_ag_rfc_close(tBTA_AG_SCB* p_scb, p_scb->codec_updated = false; p_scb->codec_fallback = false; p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; + p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + p_scb->is_aptx_swb_codec = false; p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; p_scb->role = 0; p_scb->svc_conn = false; @@ -840,12 +848,20 @@ void bta_ag_svc_conn_open(tBTA_AG_SCB* p_scb, void bta_ag_setcodec(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { tBTA_AG_PEER_CODEC codec_type = data.api_setcodec.codec; tBTA_AG_VAL val = {}; + bool aptx_voice = false; +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + aptx_voice = codec_type == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + LOG_INFO("aptx_voice=%d", aptx_voice); + } +#endif val.hdr.handle = bta_ag_scb_to_idx(p_scb); /* Check if the requested codec type is valid */ if ((codec_type != BTM_SCO_CODEC_NONE) && (codec_type != BTM_SCO_CODEC_CVSD) && - (codec_type != BTM_SCO_CODEC_MSBC) && (codec_type != BTM_SCO_CODEC_LC3)) { + (codec_type != BTM_SCO_CODEC_MSBC) && (codec_type != BTM_SCO_CODEC_LC3) && + !aptx_voice) { val.num = codec_type; val.hdr.status = BTA_AG_FAIL_RESOURCES; LOG_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type); diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc index f0064baeec95a6e83e49694d56dffa384aa9f8cd..f643f13e5da80810b3c8a8af69428cbad37bec2a 100644 --- a/system/bta/ag/bta_ag_cmd.cc +++ b/system/bta/ag/bta_ag_cmd.cc @@ -20,6 +20,10 @@ #include <base/logging.h> +#ifdef __ANDROID__ +#include <com_android_bluetooth_flags.h> +#endif + #include <cstdint> #include <cstring> @@ -27,6 +31,7 @@ #include "bta/ag/bta_ag_int.h" #include "bta/include/bta_ag_api.h" #include "bta/include/utl.h" +#include "bta_ag_swb_aptx.h" #ifdef __ANDROID__ #include "bta_le_audio_api.h" @@ -113,6 +118,10 @@ static const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] = { BTA_AG_AT_SET | BTA_AG_AT_READ | BTA_AG_AT_TEST, BTA_AG_AT_STR, 0, 0}, {"+BIEV", BTA_AG_AT_BIEV_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, {"+BAC", BTA_AG_AT_BAC_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+%QAC", BTA_AG_AT_QAC_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, + {"+%QCS", BTA_AG_AT_QCS_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, + BTA_AG_CMD_MAX_VAL}, + /* End-of-table marker used to stop lookup iteration */ {"", 0, 0, 0, 0, 0}}; @@ -165,6 +174,9 @@ static const tBTA_AG_RESULT bta_ag_result_tbl[] = { {"+CME ERROR: ", BTA_AG_LOCAL_RES_CMEE, BTA_AG_RES_FMT_INT}, {"+BCS: ", BTA_AG_LOCAL_RES_BCS, BTA_AG_RES_FMT_INT}, {"+BIND: ", BTA_AG_BIND_RES, BTA_AG_RES_FMT_STR}, + {"+%QAC: ", BTA_AG_LOCAL_RES_QAC, BTA_AG_RES_FMT_STR}, + {"+%QCS: ", BTA_AG_LOCAL_RES_QCS, BTA_AG_RES_FMT_INT}, + {"", BTA_AG_UNAT_RES, BTA_AG_RES_FMT_STR}}; static const tBTA_AG_RESULT* bta_ag_result_by_code(size_t code) { @@ -1247,11 +1259,22 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB* p_scb, uint16_t cmd, uint8_t arg_type, bool wbs_supported = hfp_hal_interface::get_wbs_supported(); bool swb_supported = hfp_hal_interface::get_swb_supported(); + bool aptx_voice = false; + +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + aptx_voice = p_scb->is_aptx_swb_codec; + LOG_VERBOSE("BTA_AG_AT_BAC_EVT aptx_voice=%d", aptx_voice); + } +#endif if (swb_supported && (p_scb->peer_codecs & BTM_SCO_CODEC_LC3) && !(p_scb->disabled_codecs & BTM_SCO_CODEC_LC3)) { p_scb->sco_codec = BTM_SCO_CODEC_LC3; LOG_VERBOSE("Received AT+BAC, updating sco codec to LC3"); + } else if (aptx_voice) { + p_scb->sco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + LOG_VERBOSE("Received AT+BAC, updating sco codec to AptX Voice"); } else if (wbs_supported && (p_scb->peer_codecs & BTM_SCO_CODEC_MSBC) && !(p_scb->disabled_codecs & BTM_SCO_CODEC_MSBC)) { p_scb->sco_codec = BTM_SCO_CODEC_MSBC; @@ -1328,6 +1351,28 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB* p_scb, uint16_t cmd, uint8_t arg_type, bta_ag_sco_open(p_scb, tBTA_AG_DATA::kEmpty); break; } +#ifdef __ANDROID__ + case BTA_AG_AT_QAC_EVT: + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + p_scb->peer_codecs |= bta_ag_parse_qac(p_arg); + // AT+%QAC needs to be responded with +%QAC + bta_ag_swb_handle_vs_at_events(p_scb, cmd, int_arg, &val); + // followed by OK + bta_ag_send_ok(p_scb); + break; + } + FALLTHROUGH_INTENDED; + case BTA_AG_AT_QCS_EVT: + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + // AT+%QCS is a response to +%QCS sent from AG. + // Send OK to BT headset + bta_ag_send_ok(p_scb); + // Handle AT+%QCS + bta_ag_swb_handle_vs_at_events(p_scb, cmd, int_arg, &val); + break; + } + FALLTHROUGH_INTENDED; +#endif default: bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); break; @@ -1896,3 +1941,46 @@ void bta_ag_send_ring(tBTA_AG_SCB* p_scb, bta_sys_start_timer(p_scb->ring_timer, BTA_AG_RING_TIMEOUT_MS, BTA_AG_RING_TIMEOUT_EVT, bta_ag_scb_to_idx(p_scb)); } + +/******************************************************************************* + * + * Function bta_ag_send_qcs + * + * Description Send +%QCS AT command to peer. + * + * Returns void + * + ******************************************************************************/ +void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) { + uint16_t codec_uuid; + if (p_scb->codec_fallback) { + if (p_scb->peer_codecs & BTM_SCO_CODEC_MSBC) { + codec_uuid = UUID_CODEC_MSBC; + } else { + codec_uuid = UUID_CODEC_CVSD; + } + } else { + codec_uuid = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + } + + LOG_VERBOSE("send +QCS codec is %d", codec_uuid); + bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QCS, NULL, codec_uuid); +} + +/******************************************************************************* + * + * Function bta_ag_send_qac + * + * Description Send +%QAC AT command to peer. + * + * Returns void + * + ******************************************************************************/ +void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) { + LOG_VERBOSE("send +QAC codecs supported"); + bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QAC, SWB_CODECS_SUPPORTED, 0); + + if (p_scb->sco_codec == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) { + p_scb->is_aptx_swb_codec = true; + } +} diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h index 05d498416d74feaa9c489796121b6639bb66a3b6..57f6704e16c9d379aef77d2c7723866028d8bb09 100644 --- a/system/bta/ag/bta_ag_int.h +++ b/system/bta/ag/bta_ag_int.h @@ -209,6 +209,14 @@ typedef enum { BTA_AG_SCO_LC3_SETTINGS_T1, } tBTA_AG_SCO_LC3_SETTINGS; +typedef enum { + BTA_AG_SCO_APTX_SWB_SETTINGS_Q0 = 0, /* preferred/default when codec is SWB */ + BTA_AG_SCO_APTX_SWB_SETTINGS_Q1 = 4, + BTA_AG_SCO_APTX_SWB_SETTINGS_Q2 = 6, + BTA_AG_SCO_APTX_SWB_SETTINGS_Q3 = 7, + BTA_AG_SCO_APTX_SWB_SETTINGS_UNKNOWN = 0xFFFF, +} tBTA_AG_SCO_APTX_SWB_SETTINGS; + /* type for each service control block */ struct tBTA_AG_SCB { char clip[BTA_AG_AT_MAX_LEN + 1]; /* number string used for CLIP */ @@ -265,6 +273,10 @@ struct tBTA_AG_SCB { impending eSCO on WB */ tBTA_AG_SCO_LC3_SETTINGS codec_lc3_settings; /* settings to be used for the impending eSCO on SWB */ + tBTA_AG_SCO_APTX_SWB_SETTINGS + codec_aptx_settings; /* settings to be used for the + aptX Voice SWB eSCO */ + bool is_aptx_swb_codec; /* Flag to determine aptX Voice SWB codec */ tBTA_AG_HF_IND peer_hf_indicators[BTA_AG_MAX_NUM_PEER_HF_IND]; /* Peer supported @@ -421,5 +433,7 @@ void bta_ag_set_sco_offload_enabled(bool value); void bta_ag_set_sco_allowed(bool value); const RawAddress& bta_ag_get_active_device(); void bta_clear_active_device(); +void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data); +void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data); #endif /* BTA_AG_INT_H */ diff --git a/system/bta/ag/bta_ag_main.cc b/system/bta/ag/bta_ag_main.cc index 4aca16e25df017d37166c1cef26764965c3744dd..625716b20ff7fbe47f548498af18e3b749cb1935 100644 --- a/system/bta/ag/bta_ag_main.cc +++ b/system/bta/ag/bta_ag_main.cc @@ -160,6 +160,9 @@ static tBTA_AG_SCB* bta_ag_scb_alloc(void) { /* set eSCO mSBC setting to T2 as the preferred */ p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; + /* set eSCO SWB setting to Q0 as the preferred */ + p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + p_scb->is_aptx_swb_codec = false; LOG_VERBOSE("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb)); break; } diff --git a/system/bta/ag/bta_ag_sco.cc b/system/bta/ag/bta_ag_sco.cc index 5015c0584c646b3de07e00beb83da54250e8fb20..39937240c65c3b65b63bcd9fc7c4eebeb0105f2f 100644 --- a/system/bta/ag/bta_ag_sco.cc +++ b/system/bta/ag/bta_ag_sco.cc @@ -25,11 +25,16 @@ #include <base/functional/bind.h> #include <base/logging.h> +#ifdef __ANDROID__ +#include <com_android_bluetooth_flags.h> +#endif + #include <cstdint> #include "bt_target.h" // Must be first to define build configuration #include "bt_trace.h" // Legacy trace logging #include "bta/ag/bta_ag_int.h" +#include "bta_ag_swb_aptx.h" #include "common/init_flags.h" #include "device/include/controller.h" #include "main/shim/dumpsys.h" @@ -195,9 +200,19 @@ static void bta_ag_sco_disc_cback(uint16_t sco_idx) { } if (handle != 0) { + bool aptx_voice = false; +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice() && + bta_ag_cb.sco.p_curr_scb->is_aptx_swb_codec == true) { + aptx_voice = bta_ag_cb.sco.p_curr_scb->inuse_codec == + BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + LOG_VERBOSE("aptx_voice=%d, inuse_codec=%d", aptx_voice, + bta_ag_cb.sco.p_curr_scb->inuse_codec); + } +#endif /* Restore settings */ if (bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_MSBC || - bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_LC3) { + bta_ag_cb.sco.p_curr_scb->inuse_codec == UUID_CODEC_LC3 || aptx_voice) { /* Bypass vendor specific and voice settings if enhanced eSCO supported */ if (!(controller_get_interface() ->supports_enhanced_setup_synchronous_connection())) { @@ -410,6 +425,15 @@ void bta_ag_create_sco(tBTA_AG_SCB* p_scb, bool is_orig) { } #endif +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + if ((p_scb->sco_codec == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) && + !p_scb->codec_fallback) { + esco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + } + } +#endif + if ((p_scb->sco_codec == BTM_SCO_CODEC_LC3) && !p_scb->codec_fallback && hfp_hal_interface::get_swb_supported()) { esco_codec = UUID_CODEC_LC3; @@ -423,6 +447,8 @@ void bta_ag_create_sco(tBTA_AG_SCB* p_scb, bool is_orig) { p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; /* Reset LC3 settings to T2 for the next audio connection */ p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; + /* Reset SWB settings to Q3 for the next audio connection */ + p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; } bool offload = hfp_hal_interface::get_offload_enabled(); @@ -445,6 +471,19 @@ void bta_ag_create_sco(tBTA_AG_SCB* p_scb, bool is_orig) { } else { params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1, offload); } +#ifdef __ANDROID__ + } else if (com::android::bluetooth::flags::hfp_codec_aptx_voice() && + (p_scb->is_aptx_swb_codec == true && !p_scb->codec_updated)) { + if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q3) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q3, true); + } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q2) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q2, true); + } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q1) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q1, true); + } else if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q0, true); + } +#endif } else { if ((p_scb->features & BTA_AG_FEAT_ESCO_S4) && (p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO_S4)) { @@ -518,6 +557,22 @@ void bta_ag_create_pending_sco(tBTA_AG_SCB* p_scb, bool is_local) { } else { params = esco_parameters_for_codec(ESCO_CODEC_LC3_T1, offload); } +#ifdef __ANDROID__ + } else if (com::android::bluetooth::flags::hfp_codec_aptx_voice() && + (p_scb->is_aptx_swb_codec == true && !p_scb->codec_updated)) { + if (p_scb->codec_aptx_settings == BTA_AG_SCO_APTX_SWB_SETTINGS_Q3) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q3, true); + } else if (p_scb->codec_aptx_settings == + BTA_AG_SCO_APTX_SWB_SETTINGS_Q2) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q2, true); + } else if (p_scb->codec_aptx_settings == + BTA_AG_SCO_APTX_SWB_SETTINGS_Q1) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q1, true); + } else if (p_scb->codec_aptx_settings == + BTA_AG_SCO_APTX_SWB_SETTINGS_Q0) { + params = esco_parameters_for_codec(ESCO_CODEC_SWB_Q0, true); + } +#endif } else if (esco_codec == UUID_CODEC_MSBC) { if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T2, offload); @@ -639,16 +694,47 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB* p_scb) { LOG_INFO("Assume CVSD by default due to mask mismatch"); p_scb->sco_codec = UUID_CODEC_CVSD; } + bool aptx_voice = false; +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + aptx_voice = p_scb->is_aptx_swb_codec; + LOG_VERBOSE("aptx_voice=%d, is_aptx_swb_codec=%d", aptx_voice, + p_scb->is_aptx_swb_codec); + } +#endif - if ((p_scb->codec_updated || p_scb->codec_fallback) && - (p_scb->features & BTA_AG_FEAT_CODEC) && - (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) { + if (((p_scb->codec_updated || p_scb->codec_fallback) && + (p_scb->features & BTA_AG_FEAT_CODEC) && + (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) || + (aptx_voice && + (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK))) { LOG_INFO("Starting codec negotiation"); /* Change the power mode to Active until SCO open is completed. */ bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); - /* Send +BCS to the peer */ - bta_ag_send_bcs(p_scb); + if (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK) { + if (p_scb->is_aptx_swb_codec == false) { + p_scb->sco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + p_scb->is_aptx_swb_codec = true; + } + LOG_VERBOSE("Sending +QCS, sco_codec=%d, is_aptx_swb_codec=%d", + p_scb->sco_codec, p_scb->is_aptx_swb_codec); + /* Send +QCS to the peer */ + bta_ag_send_qcs(p_scb, NULL); + } else { +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice() && + ((p_scb->is_aptx_swb_codec == true) && + (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK))) { + p_scb->sco_codec = BTM_SCO_CODEC_MSBC; + p_scb->is_aptx_swb_codec = false; + } +#endif + LOG_VERBOSE("Sending +BCS, sco_codec=%d, is_aptx_swb_codec=%d", + p_scb->sco_codec, p_scb->is_aptx_swb_codec); + /* Send +BCS to the peer */ + bta_ag_send_bcs(p_scb); + } /* Start timer to handle timeout */ alarm_set_on_mloop(p_scb->codec_negotiation_timer, @@ -1360,6 +1446,8 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB* p_scb, p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; /* reset to LC3 T2 settings as the preferred */ p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; + /* reset to SWB Q0 settings as the preferred */ + p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; } /******************************************************************************* @@ -1374,9 +1462,18 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB* p_scb, ******************************************************************************/ void bta_ag_sco_conn_close(tBTA_AG_SCB* p_scb, UNUSED_ATTR const tBTA_AG_DATA& data) { + bool aptx_voice = false; /* clear current scb */ bta_ag_cb.sco.p_curr_scb = nullptr; p_scb->sco_idx = BTM_INVALID_SCO_INDEX; +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + aptx_voice = p_scb->codec_fallback && + p_scb->sco_codec == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + LOG_VERBOSE("aptx_voice=%d, codec_fallback=%d, sco_codec=%d", aptx_voice, + p_scb->codec_fallback, p_scb->sco_codec); + } +#endif /* codec_fallback is set when AG is initiator and connection failed for mSBC. * OR if codec is msbc and T2 settings failed, then retry Safe T1 settings @@ -1386,7 +1483,8 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB* p_scb, (p_scb->sco_codec == BTM_SCO_CODEC_MSBC && p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1) || (p_scb->sco_codec == BTM_SCO_CODEC_LC3 && - p_scb->codec_lc3_settings == BTA_AG_SCO_LC3_SETTINGS_T1))) { + p_scb->codec_lc3_settings == BTA_AG_SCO_LC3_SETTINGS_T1) || + aptx_voice)) { bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E); } else { /* Indicate if the closing of audio is because of transfer */ @@ -1406,6 +1504,7 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB* p_scb, bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT); p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; p_scb->codec_lc3_settings = BTA_AG_SCO_LC3_SETTINGS_T2; + p_scb->codec_aptx_settings = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; } } diff --git a/system/bta/ag/bta_ag_swb_aptx.cc b/system/bta/ag/bta_ag_swb_aptx.cc new file mode 100644 index 0000000000000000000000000000000000000000..893eff90c56e5c612dd4e581185c4bdefdff5175 --- /dev/null +++ b/system/bta/ag/bta_ag_swb_aptx.cc @@ -0,0 +1,120 @@ +/* + * Copyright 2023 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 "bta_ag_swb_aptx.h" + +#include <string.h> +#include <unistd.h> + +#include "bta/ag/bta_ag_int.h" +#include "common/strings.h" +#include "device/include/interop.h" +#include "include/hardware/bt_hf.h" +#include "internal_include/bt_trace.h" +#include "stack/btm/btm_sco_hfp_hal.h" +#include "stack/include/btm_api_types.h" +#include "types/raw_address.h" +#include "utl.h" + +void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, + int16_t int_arg, tBTA_AG_VAL* val) { + switch (cmd) { + case BTA_AG_AT_QAC_EVT: + LOG_VERBOSE("BTA_AG_AT_QAC_EVT"); + p_scb->codec_updated = true; + if (p_scb->peer_codecs & BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK) { + p_scb->sco_codec = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + } else if (p_scb->peer_codecs & BTM_SCO_CODEC_MSBC) { + p_scb->sco_codec = UUID_CODEC_MSBC; + } + bta_ag_send_qac(p_scb, NULL); + LOG_VERBOSE("Received AT+QAC, updating sco codec to SWB: %d", + p_scb->sco_codec); + val->num = p_scb->peer_codecs; + break; + case BTA_AG_AT_QCS_EVT: { + tBTA_AG_PEER_CODEC codec_type, codec_sent; + alarm_cancel(p_scb->codec_negotiation_timer); + + LOG_VERBOSE("BTA_AG_AT_QCS_EVT int_arg=%d", int_arg); + switch (int_arg) { + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q0: + codec_type = BTA_AG_SCO_APTX_SWB_SETTINGS_Q0; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q1: + codec_type = BTA_AG_SCO_APTX_SWB_SETTINGS_Q1; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q2: + codec_type = BTA_AG_SCO_APTX_SWB_SETTINGS_Q2; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q3: + codec_type = BTA_AG_SCO_APTX_SWB_SETTINGS_Q3; + break; + default: + LOG_ERROR("Unknown codec_uuid %d", int_arg); + p_scb->is_aptx_swb_codec = false; + codec_type = BTM_SCO_CODEC_MSBC; + p_scb->codec_fallback = true; + p_scb->sco_codec = BTM_SCO_CODEC_MSBC; + break; + } + + if (p_scb->codec_fallback) { + codec_sent = BTM_SCO_CODEC_MSBC; + } else { + codec_sent = p_scb->sco_codec; + } + + bta_ag_sco_codec_nego(p_scb, codec_type == codec_sent); + + /* send final codec info to callback */ + val->num = codec_sent; + break; + } + } +} + +tBTA_AG_PEER_CODEC bta_ag_parse_qac(char* p_s) { + tBTA_AG_PEER_CODEC retval = BTM_SCO_CODEC_NONE; + tBTA_AG_SCO_APTX_SWB_SETTINGS codec_mode = + BTA_AG_SCO_APTX_SWB_SETTINGS_UNKNOWN; + + auto codec_modes = + bluetooth::common::StringSplit(std::string(p_s), ",", SWB_CODECS_NUMBER); + for (auto& codec_mode_str : codec_modes) { + if (!std::isdigit(*codec_mode_str.c_str())) continue; + codec_mode = static_cast<tBTA_AG_SCO_APTX_SWB_SETTINGS>( + std::atoi(codec_mode_str.c_str())); + switch (codec_mode) { + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q0: + retval |= BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q1: + retval |= BTA_AG_SCO_APTX_SWB_SETTINGS_Q1_MASK; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q2: + retval |= BTA_AG_SCO_APTX_SWB_SETTINGS_Q2_MASK; + break; + case BTA_AG_SCO_APTX_SWB_SETTINGS_Q3: + retval |= BTA_AG_SCO_APTX_SWB_SETTINGS_Q3_MASK; + break; + default: + LOG_VERBOSE("Unknown Codec UUID(%d) received\n", codec_mode); + break; + } + } + return (retval); +} diff --git a/system/bta/include/bta_ag_swb_aptx.h b/system/bta/include/bta_ag_swb_aptx.h new file mode 100644 index 0000000000000000000000000000000000000000..fd2a34892bb3e4b1b9c46fdc57ec038167f9502f --- /dev/null +++ b/system/bta/include/bta_ag_swb_aptx.h @@ -0,0 +1,38 @@ +/* + * Copyright 2023 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. + */ + +#ifndef _BTA_AG_SWB_H_ +#define _BTA_AG_SWB_H_ + +#include "bta/ag/bta_ag_int.h" +#include "device/include/esco_parameters.h" +#include "include/hardware/bt_hf.h" + +/* Events originated from HF side */ +#define BTA_AG_AT_QAC_EVT 253 +#define BTA_AG_AT_QCS_EVT 254 +#define BTA_AG_LOCAL_RES_QAC 0x108 +#define BTA_AG_LOCAL_RES_QCS 0x109 + +#define SWB_CODECS_SUPPORTED "0,4,6,7" +#define SWB_CODECS_UNSUPPORTED "0xFFFF" +#define SWB_CODECS_NUMBER 4 + +void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, + int16_t int_arg, tBTA_AG_VAL* val); +tBTA_AG_PEER_CODEC bta_ag_parse_qac(char* p_s); + +#endif //_BTA_AG_SWB_H_ diff --git a/system/btif/Android.bp b/system/btif/Android.bp index 28d5720169962f6084484d2096fad3ef338d3c07..9eaa4bf85aba3af0c79a6cb25fe9a4e71e9d6556 100644 --- a/system/btif/Android.bp +++ b/system/btif/Android.bp @@ -236,6 +236,9 @@ cc_library_static { "libbt_shim_bridge", "libstatslog_bt", ], + whole_static_libs: [ + "bluetooth_flags_c_lib", + ], shared_libs: [ "libcrypto", ], @@ -616,6 +619,7 @@ cc_test { "libhidlbase", "liblog", "libutils", + "server_configurable_flags", ], static_libs: [ "android.hardware.bluetooth.a2dp@1.0", @@ -756,6 +760,7 @@ cc_test { "libhidlbase", "liblog", "libutils", + "server_configurable_flags", ], static_libs: [ "android.hardware.bluetooth.a2dp@1.0", diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc index d55bb6f15caa96e8257665625f40445ff7921da1..e140e52222501dfbb4ecc284491b6e905759f827 100644 --- a/system/btif/src/btif_hf.cc +++ b/system/btif/src/btif_hf.cc @@ -30,6 +30,11 @@ #include <android_bluetooth_sysprop.h> #include <base/functional/callback.h> #include <base/logging.h> + +#ifdef __ANDROID__ +#include <com_android_bluetooth_flags.h> +#endif + #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> #include <cstdint> @@ -37,6 +42,7 @@ #include "bta/include/bta_ag_api.h" #include "bta/include/utl.h" +#include "bta_ag_swb_aptx.h" #include "btif/include/btif_common.h" #include "btif/include/btif_metrics_logging.h" #include "btif/include/btif_profile_queue.h" @@ -573,23 +579,43 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) { if (p_data->val.num == BTM_SCO_CODEC_CVSD) { bt_hf_callbacks->WbsCallback(BTHF_WBS_NO, &btif_hf_cb[idx].connected_bda); - bt_hf_callbacks->SwbCallback(BTHF_SWB_NO, + bt_hf_callbacks->SwbCallback(BTHF_SWB_CODEC_LC3, BTHF_SWB_NO, &btif_hf_cb[idx].connected_bda); } else if (p_data->val.num == BTM_SCO_CODEC_MSBC) { bt_hf_callbacks->WbsCallback(BTHF_WBS_YES, &btif_hf_cb[idx].connected_bda); - bt_hf_callbacks->SwbCallback(BTHF_SWB_NO, + bt_hf_callbacks->SwbCallback(BTHF_SWB_CODEC_LC3, BTHF_SWB_NO, &btif_hf_cb[idx].connected_bda); } else if (p_data->val.num == BTM_SCO_CODEC_LC3) { bt_hf_callbacks->WbsCallback(BTHF_WBS_NO, &btif_hf_cb[idx].connected_bda); - bt_hf_callbacks->SwbCallback(BTHF_SWB_YES, + bt_hf_callbacks->SwbCallback(BTHF_SWB_CODEC_LC3, BTHF_SWB_YES, &btif_hf_cb[idx].connected_bda); } else { bt_hf_callbacks->WbsCallback(BTHF_WBS_NONE, &btif_hf_cb[idx].connected_bda); - bt_hf_callbacks->SwbCallback(BTHF_SWB_NONE, - &btif_hf_cb[idx].connected_bda); +#ifdef __ANDROID__ + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + LOG_VERBOSE( + "AG final selected SWB codec is 0x%02x 0=Q0 4=Q1 6=Q2 7=Q3", + p_data->val.num); + bthf_swb_config_t config; + if (p_data->val.num == BTA_AG_SCO_APTX_SWB_SETTINGS_Q0 || + p_data->val.num == BTA_AG_SCO_APTX_SWB_SETTINGS_Q1 || + p_data->val.num == BTA_AG_SCO_APTX_SWB_SETTINGS_Q2 || + p_data->val.num == BTA_AG_SCO_APTX_SWB_SETTINGS_Q3) { + config = BTHF_SWB_YES; + } else { + config = BTHF_SWB_NO; + } + bt_hf_callbacks->SwbCallback(BTHF_SWB_CODEC_VENDOR_APTX, config, + &btif_hf_cb[idx].connected_bda); + } else +#endif // call SwbCallback with LC3 on non android target + { + bt_hf_callbacks->SwbCallback(BTHF_SWB_CODEC_LC3, BTHF_SWB_NONE, + &btif_hf_cb[idx].connected_bda); + } } break; @@ -642,14 +668,15 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) { break; case BTA_AG_AT_BCS_EVT: - LOG_VERBOSE("%s: AG final selected codec is 0x%02x 1=CVSD 2=MSBC", - __func__, p_data->val.num); + LOG_VERBOSE("AG final selected codec is 0x%02x 1=CVSD 2=MSBC", + p_data->val.num); /* No BTHF_WBS_NONE case, because HF1.6 supported device can send BCS */ /* Only CVSD is considered narrow band speech */ bt_hf_callbacks->WbsCallback( (p_data->val.num == BTM_SCO_CODEC_MSBC) ? BTHF_WBS_YES : BTHF_WBS_NO, &btif_hf_cb[idx].connected_bda); bt_hf_callbacks->SwbCallback( + BTHF_SWB_CODEC_LC3, (p_data->val.num == BTM_SCO_CODEC_LC3) ? BTHF_SWB_YES : BTHF_SWB_NO, &btif_hf_cb[idx].connected_bda); break; @@ -679,6 +706,22 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) { &btif_hf_cb[idx].connected_bda); } break; + +#ifdef __ANDROID__ + case BTA_AG_AT_QCS_EVT: + if (com::android::bluetooth::flags::hfp_codec_aptx_voice()) { + LOG_INFO("AG final selected SWB codec is 0x%02x 0=Q0 4=Q1 6=Q2 7=Q3", + p_data->val.num); + bt_hf_callbacks->SwbCallback( + BTHF_SWB_CODEC_VENDOR_APTX, + p_data->val.num <= BTA_AG_SCO_APTX_SWB_SETTINGS_Q3 ? BTHF_SWB_YES + : BTHF_SWB_NO, + &btif_hf_cb[idx].connected_bda); + break; + } + FALLTHROUGH_INTENDED; +#endif + default: LOG(WARNING) << __func__ << ": unhandled event " << event; break; diff --git a/system/device/include/esco_parameters.h b/system/device/include/esco_parameters.h index abb5afd238f4b9005abaced99cf1b28bb7676d76..5b15ca2ff2ad6009779ea8dfc0fe3f103c34bac4 100644 --- a/system/device/include/esco_parameters.h +++ b/system/device/include/esco_parameters.h @@ -38,10 +38,15 @@ typedef enum { ESCO_CODEC_MSBC_T2, ESCO_CODEC_LC3_T1, ESCO_CODEC_LC3_T2, + ESCO_CODEC_SWB_Q0, + ESCO_CODEC_SWB_Q1, + ESCO_CODEC_SWB_Q2, + ESCO_CODEC_SWB_Q3, ESCO_CODEC_UNKNOWN, // For testing } esco_codec_t; -#define ESCO_NUM_CODECS 7 +#define ESCO_LEGACY_NUM_CODECS 7 +#define ESCO_NUM_CODECS 11 // Coding Formats (BT 4.1 or later Assigned numbers) #define ESCO_CODING_FORMAT_ULAW ((uint8_t)0x00) /* u-Law log */ diff --git a/system/device/src/esco_parameters.cc b/system/device/src/esco_parameters.cc index 861dfd90ccd88308af4f969bcb3a84223eca6e7a..7a786047888b1caef597de1174709210aca0aedd 100644 --- a/system/device/src/esco_parameters.cc +++ b/system/device/src/esco_parameters.cc @@ -289,16 +289,53 @@ static const enh_esco_params_t default_esco_parameters[ESCO_NUM_CODECS] = { .retransmission_effort = ESCO_RETRANSMISSION_QUALITY, .coding_format = ESCO_CODING_FORMAT_LC3, }, -}; + // aptX Voice SWB + {.transmit_bandwidth = TXRX_64KBITS_RATE, + .receive_bandwidth = TXRX_64KBITS_RATE, + .transmit_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS, + .company_id = 0x000A, + .vendor_specific_codec_id = 0x0000}, + .receive_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS, + .company_id = 0x000A, + .vendor_specific_codec_id = 0x0000}, + .transmit_codec_frame_size = 60, + .receive_codec_frame_size = 60, + .input_bandwidth = INPUT_OUTPUT_128K_RATE, + .output_bandwidth = INPUT_OUTPUT_128K_RATE, + .input_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR, + .company_id = 0x0000, + .vendor_specific_codec_id = 0x0000}, + .output_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR, + .company_id = 0x0000, + .vendor_specific_codec_id = 0x0000}, + .input_coded_data_size = 16, + .output_coded_data_size = 16, + .input_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP, + .output_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP, + .input_pcm_payload_msb_position = 0, + .output_pcm_payload_msb_position = 0, + .input_data_path = ESCO_DATA_PATH_PCM, + .output_data_path = ESCO_DATA_PATH_PCM, + .input_transport_unit_size = 0x00, + .output_transport_unit_size = 0x00, + .max_latency_ms = 14, + .packet_types = 0x0380, + .retransmission_effort = ESCO_RETRANSMISSION_QUALITY}}; enh_esco_params_t esco_parameters_for_codec(esco_codec_t codec, bool offload) { CHECK(codec >= 0) << "codec index " << (int)codec << "< 0"; CHECK(codec < ESCO_NUM_CODECS) << "codec index " << (int)codec << " > " << ESCO_NUM_CODECS; if (offload) { + if (codec == ESCO_CODEC_SWB_Q0 || codec == ESCO_CODEC_SWB_Q1 || + codec == ESCO_CODEC_SWB_Q2 || codec == ESCO_CODEC_SWB_Q3) { + return default_esco_parameters[ESCO_CODEC_SWB_Q0]; + } return default_esco_parameters[codec]; } + CHECK(codec < ESCO_LEGACY_NUM_CODECS) + << "legacy codec index " << (int)codec << " > " << ESCO_LEGACY_NUM_CODECS; enh_esco_params_t param = default_esco_parameters[codec]; param.input_data_path = param.output_data_path = ESCO_DATA_PATH_HCI; diff --git a/system/gd/rust/topshim/hfp/hfp_shim.cc b/system/gd/rust/topshim/hfp/hfp_shim.cc index 11d124601abcf025da4017353e7b3dc4ef61fcac..9c02a9fd7c63ea97b4ed385e556e195220cc6714 100644 --- a/system/gd/rust/topshim/hfp/hfp_shim.cc +++ b/system/gd/rust/topshim/hfp/hfp_shim.cc @@ -195,9 +195,11 @@ class DBusHeadsetCallbacks : public headset::Callbacks { rusty::hfp_wbs_caps_update_callback(wbs == headset::BTHF_WBS_YES, *addr); } - void SwbCallback(headset::bthf_swb_config_t swb, RawAddress* addr) override { - LOG_INFO("SwbCallback %d from %s", swb, ADDRESS_TO_LOGGABLE_CSTR(*addr)); - rusty::hfp_swb_caps_update_callback(swb == headset::BTHF_SWB_YES, *addr); + void SwbCallback( + headset::bthf_swb_codec_t codec, headset::bthf_swb_config_t swb, RawAddress* addr) override { + LOG_INFO("SwbCallback codec:%d, swb:%d from %s", codec, swb, ADDRESS_TO_LOGGABLE_CSTR(*addr)); + rusty::hfp_swb_caps_update_callback( + (codec == headset::BTHF_SWB_CODEC_LC3 && swb == headset::BTHF_SWB_YES), *addr); } void AtChldCallback(headset::bthf_chld_type_t chld, RawAddress* bd_addr) override { diff --git a/system/include/hardware/bluetooth_headset_callbacks.h b/system/include/hardware/bluetooth_headset_callbacks.h index 2da5c1df93ae22c75118fdfaedff2fe4efc2cd0f..36ea13431d726a2e536d2ea788a2c719abfbb42e 100644 --- a/system/include/hardware/bluetooth_headset_callbacks.h +++ b/system/include/hardware/bluetooth_headset_callbacks.h @@ -117,10 +117,12 @@ class Callbacks { /** * Callback for AT+BCS and event from BAC * + * @param codec SWB codec * @param swb SWB enable, SWB disable * @param bd_addr remote device address */ - virtual void SwbCallback(bthf_swb_config_t swb, RawAddress* bd_addr) = 0; + virtual void SwbCallback(bthf_swb_codec_t codec, bthf_swb_config_t swb, + RawAddress* bd_addr) = 0; /** * Callback for call hold handling (AT+CHLD) diff --git a/system/include/hardware/bt_hf.h b/system/include/hardware/bt_hf.h index 9a72707d0b5ecfbb932706b29df08089ec4eea09..671e564084229ceb17179216b5e34625819fb8a1 100644 --- a/system/include/hardware/bt_hf.h +++ b/system/include/hardware/bt_hf.h @@ -56,8 +56,18 @@ typedef enum { BTHF_NREC_STOP, BTHF_NREC_START } bthf_nrec_t; /* WBS codec setting */ typedef enum { BTHF_WBS_NONE, BTHF_WBS_NO, BTHF_WBS_YES } bthf_wbs_config_t; +/* SWB codec */ +typedef enum { + BTHF_SWB_CODEC_LC3 = 0, + BTHF_SWB_CODEC_VENDOR_APTX +} bthf_swb_codec_t; + /* SWB codec setting */ -typedef enum { BTHF_SWB_NONE, BTHF_SWB_NO, BTHF_SWB_YES } bthf_swb_config_t; +typedef enum { + BTHF_SWB_NONE, + BTHF_SWB_NO, + BTHF_SWB_YES, +} bthf_swb_config_t; /* CHLD - Call held handling */ typedef enum { diff --git a/system/stack/include/btm_api_types.h b/system/stack/include/btm_api_types.h index 233ee2803a4d49c01fa4e91842e0a414c23d75ab..d32b6622486b63e74159d0a4c188be9e2b93bdd0 100644 --- a/system/stack/include/btm_api_types.h +++ b/system/stack/include/btm_api_types.h @@ -157,6 +157,10 @@ typedef uint8_t tBTM_SCO_TYPE; #define BTM_SCO_CODEC_CVSD 0x0001 #define BTM_SCO_CODEC_MSBC 0x0002 #define BTM_SCO_CODEC_LC3 0x0004 +#define BTA_AG_SCO_APTX_SWB_SETTINGS_Q0_MASK 0x0008 +#define BTA_AG_SCO_APTX_SWB_SETTINGS_Q1_MASK 0x0016 +#define BTA_AG_SCO_APTX_SWB_SETTINGS_Q2_MASK 0x0032 +#define BTA_AG_SCO_APTX_SWB_SETTINGS_Q3_MASK 0x0064 typedef uint16_t tBTM_SCO_CODEC_TYPE; /*******************