Loading android/pandora/test/a2dp_test.py +72 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ from bumble.avdtp import ( AVDTP_OPEN_STATE, AVDTP_PSM, AVDTP_STREAMING_STATE, AVDTP_TSEP_SRC, Listener, MediaCodecCapabilities, Protocol, Loading Loading @@ -74,6 +75,7 @@ from typing import Optional, Tuple logger = logging.getLogger(__name__) AVDTP_HANDLE_SUSPEND_CFM_BAD_STATE = 'com.android.bluetooth.flags.avdt_handle_suspend_cfm_bad_state' AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE = 'com.android.bluetooth.flags.avdt_handle_signaling_on_peer_failure' async def initiate_pairing(device, address) -> Connection: Loading Loading @@ -660,6 +662,76 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] # Wait for AVDTP Close await asyncio.wait_for(avdtp_future, timeout=10.0) @avatar.asynchronous async def test_avdt_open_after_timeout(self) -> None: """Test AVDTP automatically opens stream after timeout if peer device only configures codec. 1. Pair and Connect RD1 -> DUT 2. Connect AVDTP RD1 -> DUT but do not send AVDT Open Command 3. Check that the DUT will abort and reopen the AVDTP as initiator """ class TestAvdtProtocol(Protocol): def on_open_command(self, command): nonlocal avdtp_future logger.info("<< AVDTP Open received >>") avdtp_future.set_result(None) return super().on_open_command(command) # Enable BAD_STATE handling for server in self.devices._servers: if isinstance(server, AndroidPandoraServer): server.device.adb.shell( ['device_config override bluetooth', AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE, 'true']) # type: ignore break # Connect and pair RD1. ref1_dut, dut_ref1 = await asyncio.gather( initiate_pairing(self.ref1, self.dut.address), accept_pairing(self.dut, self.ref1.address), ) # Create a listener to wait for AVDTP open avdtp_future = asyncio.get_running_loop().create_future() # Retrieve Bumble connection object from Pandora connection token connection = pandora.get_raw_connection(device=self.ref1, connection=ref1_dut) assert connection is not None channel = await connection.create_l2cap_channel(spec=ClassicChannelSpec(psm=AVDTP_PSM)) client = TestAvdtProtocol(channel) sink = client.add_sink(sbc_codec_capabilities()) endpoints = await client.discover_remote_endpoints() logger.info(f"endpoints: {endpoints}") assert len(endpoints) >= 1 remote_source = list(endpoints)[0] assert remote_source.in_use == 0 assert remote_source.media_type == AVDTP_AUDIO_MEDIA_TYPE assert remote_source.tsep == AVDTP_TSEP_SRC logger.info(f"remote_source: {remote_source}") configuration = MediaCodecCapabilities( media_type=AVDTP_AUDIO_MEDIA_TYPE, media_codec_type=A2DP_SBC_CODEC_TYPE, media_codec_information=SbcMediaCodecInformation.from_lists( sampling_frequencies=[44100], channel_modes=[SBC_JOINT_STEREO_CHANNEL_MODE], block_lengths=[16], subbands=[8], allocation_methods=[SBC_LOUDNESS_ALLOCATION_METHOD], minimum_bitpool_value=2, maximum_bitpool_value=53, ), ) response = await remote_source.set_configuration(sink.seid, [configuration]) logger.info(f"response: {response}") # Wait for AVDTP Open from DUT await asyncio.wait_for(avdtp_future, timeout=10.0) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) Loading flags/a2dp.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -135,3 +135,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "a2dp_sm_ignore_connect_events_in_connecting_state" namespace: "bluetooth" description: "When received CONNECT event in Connecting state, with no prior DISCONNECT - ignore the event" bug: "383576378" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file system/bta/av/bta_av_aact.cc +62 −2 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "osi/include/list.h" #include "osi/include/osi.h" // UINT_TO_PTR PTR_TO_UINT #include "osi/include/properties.h" #include "sdpdefs.h" #include "stack/include/a2dp_ext.h" Loading Loading @@ -114,6 +115,13 @@ constexpr char kBtmLogTag[] = "A2DP"; /* ACL quota we are letting FW use for A2DP Offload Tx. */ #define BTA_AV_A2DP_OFFLOAD_XMIT_QUOTA 4 /* Time to wait for open from SNK when signaling is initiated from SNK. */ /* If not, we abort and try to initiate the connection as SRC. */ #ifndef BTA_AV_ACCEPT_OPEN_TIMEOUT_MS #define BTA_AV_ACCEPT_OPEN_TIMEOUT_MS (2 * 1000) /* 2 seconds */ #endif static void bta_av_accept_open_timer_cback(void* data); static void bta_av_offload_codec_builder(tBTA_AV_SCB* p_scb, tBT_A2DP_OFFLOAD* p_a2dp_offload); /* state machine states */ Loading Loading @@ -866,6 +874,9 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->avrc_ct_timer); alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION vendor_get_interface()->send_command( Loading Loading @@ -1009,7 +1020,9 @@ void bta_av_disconnect_req(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); alarm_cancel(p_scb->avrc_ct_timer); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use // the same index, it should be safe to use SCB index here. if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) { Loading Loading @@ -1117,6 +1130,14 @@ void bta_av_setconfig_rsp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { p_scb->uuid_int = p_scb->open_api.uuid; } bta_av_discover_req(p_scb, NULL); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { // Set timer to initiate stream opening if peer doesn't if (!p_scb->accept_open_timer) { p_scb->accept_open_timer = alarm_new("accept_open_timer"); } alarm_set_on_mloop(p_scb->accept_open_timer, BTA_AV_ACCEPT_OPEN_TIMEOUT_MS, bta_av_accept_open_timer_cback, UINT_TO_PTR(p_scb->hdi)); } } } Loading @@ -1136,6 +1157,9 @@ void bta_av_str_opened(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { log::verbose("peer {} bta_handle: 0x{:x}", p_scb->PeerAddress(), p_scb->hndl); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } msg.hdr.layer_specific = p_scb->hndl; msg.is_up = true; msg.peer_addr = p_scb->PeerAddress(); Loading Loading @@ -2962,8 +2986,14 @@ void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { /* API open will be handled at timeout if SNK did not start signalling. */ /* API open will be ignored if SNK starts signalling. */ } else { /* SNK did not start signalling, API was called N seconds timeout. */ /* SNK did not start signalling or failed to complete the AVDT configuration in time. */ /* API was called N seconds timeout. */ /* We need to switch to INIT state and start opening connection. */ if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { // Reset peer device bta_av_cco_close(p_scb, p_data); alarm_cancel(p_scb->avrc_ct_timer); } p_scb->coll_mask = 0; bta_av_set_scb_sst_init(p_scb); Loading Loading @@ -3332,3 +3362,33 @@ void bta_av_api_set_peer_sep(tBTA_AV_DATA* p_data) { } } } /******************************************************************************* * * Function bta_av_accept_open_timer_cback * * Description Process the timeout when SRC is accepting connection * and SNK did not open the stream. * * Returns void * ******************************************************************************/ static void bta_av_accept_open_timer_cback(void* data) { uint32_t hdi = PTR_TO_UINT(data); tBTA_AV_SCB* p_scb = NULL; if (hdi < BTA_AV_NUM_STRS) { p_scb = bta_av_cb.p_scb[hdi]; } if (p_scb == nullptr) { log::error("SCB not found for index {}", hdi); return; } /* Abort the current connection */ AVDT_AbortReq(p_scb->avdt_handle); /* Try connecting and opening as initiator with event: BTA_AV_API_OPEN_EVT */ tBTA_AV_API_OPEN* p_buf = (tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN)); memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN)); bta_sys_sendmsg(p_buf); } No newline at end of file system/bta/av/bta_av_act.cc +5 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #define LOG_TAG "bluetooth-a2dp" #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <cstddef> #include <cstdint> Loading Loading @@ -1408,7 +1409,10 @@ void bta_av_disable(tBTA_AV_CB* p_cb, tBTA_AV_DATA* /* p_data */) { p_cb->p_scb[xx]->link_signalling_timer = NULL; alarm_free(p_cb->p_scb[xx]->accept_signalling_timer); p_cb->p_scb[xx]->accept_signalling_timer = NULL; if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_free(p_cb->p_scb[xx]->accept_open_timer); p_cb->p_scb[xx]->accept_open_timer = NULL; } hdr.layer_specific = xx + 1; bta_av_api_deregister((tBTA_AV_DATA*)&hdr); disabling_in_progress = true; Loading system/bta/av/bta_av_int.h +1 −0 Original line number Diff line number Diff line Loading @@ -492,6 +492,7 @@ public: alarm_t* avrc_ct_timer; /* delay timer for AVRC CT */ alarm_t* link_signalling_timer; alarm_t* accept_signalling_timer; /* timer to monitor signalling when accepting */ alarm_t* accept_open_timer; /* timer to monitor AVDT open when accepting */ uint16_t l2c_cid; /* L2CAP channel ID */ uint16_t stream_mtu; /* MTU of stream */ uint8_t media_type; /* Media type: AVDT_MEDIA_TYPE_* */ Loading Loading
android/pandora/test/a2dp_test.py +72 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ from bumble.avdtp import ( AVDTP_OPEN_STATE, AVDTP_PSM, AVDTP_STREAMING_STATE, AVDTP_TSEP_SRC, Listener, MediaCodecCapabilities, Protocol, Loading Loading @@ -74,6 +75,7 @@ from typing import Optional, Tuple logger = logging.getLogger(__name__) AVDTP_HANDLE_SUSPEND_CFM_BAD_STATE = 'com.android.bluetooth.flags.avdt_handle_suspend_cfm_bad_state' AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE = 'com.android.bluetooth.flags.avdt_handle_signaling_on_peer_failure' async def initiate_pairing(device, address) -> Connection: Loading Loading @@ -660,6 +662,76 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] # Wait for AVDTP Close await asyncio.wait_for(avdtp_future, timeout=10.0) @avatar.asynchronous async def test_avdt_open_after_timeout(self) -> None: """Test AVDTP automatically opens stream after timeout if peer device only configures codec. 1. Pair and Connect RD1 -> DUT 2. Connect AVDTP RD1 -> DUT but do not send AVDT Open Command 3. Check that the DUT will abort and reopen the AVDTP as initiator """ class TestAvdtProtocol(Protocol): def on_open_command(self, command): nonlocal avdtp_future logger.info("<< AVDTP Open received >>") avdtp_future.set_result(None) return super().on_open_command(command) # Enable BAD_STATE handling for server in self.devices._servers: if isinstance(server, AndroidPandoraServer): server.device.adb.shell( ['device_config override bluetooth', AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE, 'true']) # type: ignore break # Connect and pair RD1. ref1_dut, dut_ref1 = await asyncio.gather( initiate_pairing(self.ref1, self.dut.address), accept_pairing(self.dut, self.ref1.address), ) # Create a listener to wait for AVDTP open avdtp_future = asyncio.get_running_loop().create_future() # Retrieve Bumble connection object from Pandora connection token connection = pandora.get_raw_connection(device=self.ref1, connection=ref1_dut) assert connection is not None channel = await connection.create_l2cap_channel(spec=ClassicChannelSpec(psm=AVDTP_PSM)) client = TestAvdtProtocol(channel) sink = client.add_sink(sbc_codec_capabilities()) endpoints = await client.discover_remote_endpoints() logger.info(f"endpoints: {endpoints}") assert len(endpoints) >= 1 remote_source = list(endpoints)[0] assert remote_source.in_use == 0 assert remote_source.media_type == AVDTP_AUDIO_MEDIA_TYPE assert remote_source.tsep == AVDTP_TSEP_SRC logger.info(f"remote_source: {remote_source}") configuration = MediaCodecCapabilities( media_type=AVDTP_AUDIO_MEDIA_TYPE, media_codec_type=A2DP_SBC_CODEC_TYPE, media_codec_information=SbcMediaCodecInformation.from_lists( sampling_frequencies=[44100], channel_modes=[SBC_JOINT_STEREO_CHANNEL_MODE], block_lengths=[16], subbands=[8], allocation_methods=[SBC_LOUDNESS_ALLOCATION_METHOD], minimum_bitpool_value=2, maximum_bitpool_value=53, ), ) response = await remote_source.set_configuration(sink.seid, [configuration]) logger.info(f"response: {response}") # Wait for AVDTP Open from DUT await asyncio.wait_for(avdtp_future, timeout=10.0) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) Loading
flags/a2dp.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -135,3 +135,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "a2dp_sm_ignore_connect_events_in_connecting_state" namespace: "bluetooth" description: "When received CONNECT event in Connecting state, with no prior DISCONNECT - ignore the event" bug: "383576378" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file
system/bta/av/bta_av_aact.cc +62 −2 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "osi/include/list.h" #include "osi/include/osi.h" // UINT_TO_PTR PTR_TO_UINT #include "osi/include/properties.h" #include "sdpdefs.h" #include "stack/include/a2dp_ext.h" Loading Loading @@ -114,6 +115,13 @@ constexpr char kBtmLogTag[] = "A2DP"; /* ACL quota we are letting FW use for A2DP Offload Tx. */ #define BTA_AV_A2DP_OFFLOAD_XMIT_QUOTA 4 /* Time to wait for open from SNK when signaling is initiated from SNK. */ /* If not, we abort and try to initiate the connection as SRC. */ #ifndef BTA_AV_ACCEPT_OPEN_TIMEOUT_MS #define BTA_AV_ACCEPT_OPEN_TIMEOUT_MS (2 * 1000) /* 2 seconds */ #endif static void bta_av_accept_open_timer_cback(void* data); static void bta_av_offload_codec_builder(tBTA_AV_SCB* p_scb, tBT_A2DP_OFFLOAD* p_a2dp_offload); /* state machine states */ Loading Loading @@ -866,6 +874,9 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->avrc_ct_timer); alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION vendor_get_interface()->send_command( Loading Loading @@ -1009,7 +1020,9 @@ void bta_av_disconnect_req(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); alarm_cancel(p_scb->avrc_ct_timer); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use // the same index, it should be safe to use SCB index here. if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) { Loading Loading @@ -1117,6 +1130,14 @@ void bta_av_setconfig_rsp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { p_scb->uuid_int = p_scb->open_api.uuid; } bta_av_discover_req(p_scb, NULL); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { // Set timer to initiate stream opening if peer doesn't if (!p_scb->accept_open_timer) { p_scb->accept_open_timer = alarm_new("accept_open_timer"); } alarm_set_on_mloop(p_scb->accept_open_timer, BTA_AV_ACCEPT_OPEN_TIMEOUT_MS, bta_av_accept_open_timer_cback, UINT_TO_PTR(p_scb->hdi)); } } } Loading @@ -1136,6 +1157,9 @@ void bta_av_str_opened(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { log::verbose("peer {} bta_handle: 0x{:x}", p_scb->PeerAddress(), p_scb->hndl); if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_cancel(p_scb->accept_open_timer); } msg.hdr.layer_specific = p_scb->hndl; msg.is_up = true; msg.peer_addr = p_scb->PeerAddress(); Loading Loading @@ -2962,8 +2986,14 @@ void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { /* API open will be handled at timeout if SNK did not start signalling. */ /* API open will be ignored if SNK starts signalling. */ } else { /* SNK did not start signalling, API was called N seconds timeout. */ /* SNK did not start signalling or failed to complete the AVDT configuration in time. */ /* API was called N seconds timeout. */ /* We need to switch to INIT state and start opening connection. */ if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { // Reset peer device bta_av_cco_close(p_scb, p_data); alarm_cancel(p_scb->avrc_ct_timer); } p_scb->coll_mask = 0; bta_av_set_scb_sst_init(p_scb); Loading Loading @@ -3332,3 +3362,33 @@ void bta_av_api_set_peer_sep(tBTA_AV_DATA* p_data) { } } } /******************************************************************************* * * Function bta_av_accept_open_timer_cback * * Description Process the timeout when SRC is accepting connection * and SNK did not open the stream. * * Returns void * ******************************************************************************/ static void bta_av_accept_open_timer_cback(void* data) { uint32_t hdi = PTR_TO_UINT(data); tBTA_AV_SCB* p_scb = NULL; if (hdi < BTA_AV_NUM_STRS) { p_scb = bta_av_cb.p_scb[hdi]; } if (p_scb == nullptr) { log::error("SCB not found for index {}", hdi); return; } /* Abort the current connection */ AVDT_AbortReq(p_scb->avdt_handle); /* Try connecting and opening as initiator with event: BTA_AV_API_OPEN_EVT */ tBTA_AV_API_OPEN* p_buf = (tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN)); memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN)); bta_sys_sendmsg(p_buf); } No newline at end of file
system/bta/av/bta_av_act.cc +5 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #define LOG_TAG "bluetooth-a2dp" #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <cstddef> #include <cstdint> Loading Loading @@ -1408,7 +1409,10 @@ void bta_av_disable(tBTA_AV_CB* p_cb, tBTA_AV_DATA* /* p_data */) { p_cb->p_scb[xx]->link_signalling_timer = NULL; alarm_free(p_cb->p_scb[xx]->accept_signalling_timer); p_cb->p_scb[xx]->accept_signalling_timer = NULL; if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { alarm_free(p_cb->p_scb[xx]->accept_open_timer); p_cb->p_scb[xx]->accept_open_timer = NULL; } hdr.layer_specific = xx + 1; bta_av_api_deregister((tBTA_AV_DATA*)&hdr); disabling_in_progress = true; Loading
system/bta/av/bta_av_int.h +1 −0 Original line number Diff line number Diff line Loading @@ -492,6 +492,7 @@ public: alarm_t* avrc_ct_timer; /* delay timer for AVRC CT */ alarm_t* link_signalling_timer; alarm_t* accept_signalling_timer; /* timer to monitor signalling when accepting */ alarm_t* accept_open_timer; /* timer to monitor AVDT open when accepting */ uint16_t l2c_cid; /* L2CAP channel ID */ uint16_t stream_mtu; /* MTU of stream */ uint8_t media_type; /* Media type: AVDT_MEDIA_TYPE_* */ Loading