summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rw-r--r--AndroidTestTemplate.xml4
-rw-r--r--OWNERS_channel_sounding3
-rw-r--r--TEST_MAPPING6
-rw-r--r--android/ChannelSoundingTestApp/app/src/androidTest/java/com/android/bluetooth/channelsoundingtestapp/ExampleInstrumentedTest.java44
-rw-r--r--android/ChannelSoundingTestApp/app/src/test/java/com/android/bluetooth/channelsoundingtestapp/ExampleUnitTest.java34
-rw-r--r--android/apishim/Android.bp4
-rw-r--r--android/app/Android.bp52
-rw-r--r--android/app/OWNERS3
-rw-r--r--android/app/aidl/Android.bp2
-rw-r--r--android/app/aidl/android/bluetooth/IBluetooth.aidl6
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl2
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothAdvertise.aidl56
-rw-r--r--android/app/aidl/android/bluetooth/IBluetoothGatt.aidl60
-rw-r--r--android/app/aidl/android/bluetooth/IDistanceMeasurement.aidl44
-rw-r--r--android/app/aidl/android/bluetooth/le/IAdvertisingSetCallback.aidl2
-rw-r--r--android/app/change-ids/Android.bp31
-rw-r--r--android/app/change-ids/com/android/bluetooth/ChangeIds.java (renamed from android/app/src/com/android/bluetooth/ChangeIds.java)0
-rw-r--r--android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp20
-rw-r--r--android/app/jni/com_android_bluetooth_gatt.cpp30
-rw-r--r--android/app/proguard.flags8
-rw-r--r--android/app/src/com/android/bluetooth/Utils.java43
-rw-r--r--android/app/src/com/android/bluetooth/a2dp/A2dpService.java6
-rw-r--r--android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java7
-rw-r--r--android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java26
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptor.java2
-rw-r--r--android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java5
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientService.java165
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java87
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassConstants.java11
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java1
-rw-r--r--android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java52
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java10
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java230
-rw-r--r--android/app/src/com/android/bluetooth/btservice/PhonePolicy.java25
-rw-r--r--android/app/src/com/android/bluetooth/btservice/RemoteDevices.java324
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt175
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java267
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java51
-rw-r--r--android/app/src/com/android/bluetooth/gatt/ContextMap.java105
-rw-r--r--android/app/src/com/android/bluetooth/gatt/DistanceMeasurementBinder.java145
-rw-r--r--android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java66
-rw-r--r--android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java6
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java7
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattService.java528
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattServiceConfig.kt (renamed from android/app/src/com/android/bluetooth/gatt/GattServiceConfig.java)9
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetService.java12
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java9
-rw-r--r--android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java31
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioService.java278
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java8
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/AppScanStats.java12
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScanController.java1633
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScanManager.java118
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScanNativeInterface.java68
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java6
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/ScannerMap.java29
-rw-r--r--android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java1674
-rw-r--r--android/app/src/com/android/bluetooth/mapclient/MapClientService.java82
-rw-r--r--android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java63
-rw-r--r--android/app/src/com/android/bluetooth/mapclient/MnsService.java16
-rw-r--r--android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java3
-rw-r--r--android/app/src/com/android/bluetooth/opp/BluetoothOppService.java45
-rw-r--r--android/app/src/com/android/bluetooth/pan/PanNativeInterface.java30
-rw-r--r--android/app/src/com/android/bluetooth/pan/PanService.java176
-rw-r--r--android/app/src/com/android/bluetooth/sap/SapService.java73
-rw-r--r--android/app/src/com/android/bluetooth/tbs/TbsGatt.java123
-rw-r--r--android/app/src/com/android/bluetooth/tbs/TbsGeneric.java92
-rw-r--r--android/app/src/com/android/bluetooth/tbs/TbsService.java31
-rw-r--r--android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java38
-rw-r--r--android/app/src/com/android/bluetooth/vc/VolumeControlService.java307
-rw-r--r--android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java112
-rw-r--r--android/app/tests/unit/Android.bp1
-rw-r--r--android/app/tests/unit/AndroidTest.xml2
-rw-r--r--android/app/tests/unit/GoogleAndroidTest.xml2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/TestUtils.java20
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java36
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java23
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java19
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java124
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java241
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java33
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java315
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java59
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java11
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java25
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java37
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java195
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java95
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java9
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipPixelTest.java51
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java65
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BaseDataTest.java32
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java450
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java263
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java35
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java343
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java26
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java55
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java16
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java333
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java33
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java244
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java110
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java197
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java41
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java12
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java98
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java27
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java144
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java217
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java294
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java373
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java39
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java11
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java87
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java5
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java21
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java220
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java579
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java44
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java202
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java (renamed from android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java)124
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java28
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_scan/ScannerMapTest.java17
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java460
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java4
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java10
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSmsPduTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java6
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/map/SmsMmsContactsTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java7
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientContentTest.java53
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java120
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java552
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java196
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java8
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java65
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java11
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java347
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java114
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppManagerTest.java6
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java1
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java1
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java47
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbap/PbapStateMachineTest.java49
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java10
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java6
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientContactsStorageTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexClientTest.java4
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceTest.java46
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java4
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java235
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java202
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java123
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlOffsetDescriptorTest.java13
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java168
-rw-r--r--android/leaudio/app/src/androidTest/java/com/android/bluetooth/leaudio/ExampleInstrumentedTest.java27
-rw-r--r--android/leaudio/app/src/test/java/pl/codecoup/ehima/leaudio/ExampleUnitTest.java17
-rwxr-xr-xandroid/pandora/gen_cov.py4
-rw-r--r--android/pandora/server/configs/PtsBotTestMts.xml16
-rw-r--r--android/pandora/server/configs/pts_bot_tests_config.json66
-rw-r--r--android/pandora/server/configs/pts_bot_tests_config_auto.json64
-rw-r--r--android/pandora/server/src/A2dp.kt9
-rw-r--r--android/pandora/test/a2dp/signaling_channel.py200
-rw-r--r--android/pandora/test/a2dp/signaling_channel_test.py378
-rw-r--r--android/pandora/test/a2dp_test.py286
-rw-r--r--android/pandora/test/main.py51
-rw-r--r--apex/Android.bp96
-rw-r--r--common/Android.bp2
-rw-r--r--flags/Android.bp10
-rw-r--r--flags/BUILD.gn2
-rw-r--r--flags/a2dp.aconfig2
-rw-r--r--flags/active_device_manager.aconfig30
-rw-r--r--flags/adapter.aconfig2
-rw-r--r--flags/avrcp.aconfig2
-rw-r--r--flags/avrcp_controller.aconfig2
-rw-r--r--flags/bta_dm.aconfig19
-rw-r--r--flags/btif_dm.aconfig2
-rw-r--r--flags/btm_ble.aconfig2
-rw-r--r--flags/connectivity.aconfig10
-rw-r--r--flags/device_iot_config.aconfig9
-rw-r--r--flags/dis.aconfig2
-rw-r--r--flags/framework.aconfig12
-rw-r--r--flags/gap.aconfig72
-rw-r--r--flags/gatt.aconfig13
-rw-r--r--flags/hal.aconfig12
-rw-r--r--flags/hap.aconfig2
-rw-r--r--flags/hci.aconfig2
-rw-r--r--flags/hfp.aconfig22
-rw-r--r--flags/hfpclient.aconfig2
-rw-r--r--flags/hid.aconfig2
-rw-r--r--flags/l2cap.aconfig2
-rw-r--r--flags/le_advertising.aconfig2
-rw-r--r--flags/le_scanning.aconfig12
-rw-r--r--flags/leaudio.aconfig22
-rw-r--r--flags/mapclient.aconfig2
-rw-r--r--flags/mcp.aconfig2
-rw-r--r--flags/metric.aconfig2
-rw-r--r--flags/opp.aconfig12
-rw-r--r--flags/pairing.aconfig2
-rw-r--r--flags/pbapclient.aconfig2
-rw-r--r--flags/ranging.aconfig2
-rw-r--r--flags/rfcomm.aconfig9
-rw-r--r--flags/rnr.aconfig2
-rw-r--r--flags/sco.aconfig2
-rw-r--r--flags/sdp.aconfig2
-rw-r--r--flags/security.aconfig6
-rw-r--r--flags/service_discovery.aconfig2
-rw-r--r--flags/sockets.aconfig2
-rw-r--r--flags/system_service.aconfig2
-rw-r--r--flags/vcp.aconfig2
-rw-r--r--flags/vsc.aconfig2
-rw-r--r--framework/Android.bp4
-rw-r--r--framework/api/system-current.txt19
-rw-r--r--framework/api/system-removed.txt8
-rw-r--r--framework/java/android/bluetooth/BluetoothA2dp.java15
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java46
-rw-r--r--framework/java/android/bluetooth/BluetoothDevice.java4
-rw-r--r--framework/java/android/bluetooth/BluetoothGatt.java195
-rw-r--r--framework/java/android/bluetooth/BluetoothHapClient.java11
-rw-r--r--framework/java/android/bluetooth/BluetoothSocket.java5
-rw-r--r--framework/java/android/bluetooth/le/AdvertisingSet.java25
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java22
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeScanner.java2
-rw-r--r--framework/java/android/bluetooth/le/DistanceMeasurementManager.java53
-rw-r--r--framework/java/android/bluetooth/le/DistanceMeasurementSession.java10
-rw-r--r--framework/java/android/bluetooth/le/ScanRecord.java22
-rw-r--r--framework/tests/bumble/AndroidPhyTest.xml4
-rw-r--r--framework/tests/bumble/AndroidTest.xml4
-rw-r--r--framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt45
-rw-r--r--framework/tests/bumble/src/android/bluetooth/GattClientTest.java38
-rw-r--r--framework/tests/bumble/src/android/bluetooth/LeScanningTest.java3
-rw-r--r--framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java669
-rw-r--r--framework/tests/bumble/src/android/bluetooth/pairing/utils/IntentReceiver.java400
-rw-r--r--framework/tests/unit/AndroidTest.xml4
-rw-r--r--framework/tests/unit/src/android/bluetooth/BluetoothActivityEnergyInfoTest.java2
-rw-r--r--framework/tests/unit/src/android/bluetooth/le/ScanRecordTest.java3
-rw-r--r--framework/tests/util/src/android/bluetooth/cts/TestUtils.java19
-rw-r--r--offload/hal/Android.bp48
-rw-r--r--offload/hal/ffi.rs279
-rw-r--r--offload/hal/include/hal/ffi.h62
-rw-r--r--offload/hal/lib.rs22
-rw-r--r--offload/hal/service.rs245
-rw-r--r--offload/hci/Android.bp53
-rw-r--r--offload/hci/command.rs544
-rw-r--r--offload/hci/data.rs204
-rw-r--r--offload/hci/derive/enum_data.rs95
-rw-r--r--offload/hci/derive/lib.rs89
-rw-r--r--offload/hci/derive/return_parameters.rs116
-rw-r--r--offload/hci/derive/struct_data.rs178
-rw-r--r--offload/hci/event.rs343
-rw-r--r--offload/hci/lib.rs78
-rw-r--r--offload/hci/reader.rs99
-rw-r--r--offload/hci/status.rs95
-rw-r--r--offload/hci/writer.rs103
-rw-r--r--offload/leaudio/aidl/Android.bp33
-rw-r--r--offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxy.aidl35
-rw-r--r--offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxyCallbacks.aidl35
-rw-r--r--offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/StreamConfiguration.aidl48
-rw-r--r--offload/leaudio/hci/Android.bp46
-rw-r--r--offload/leaudio/hci/arbiter.rs154
-rw-r--r--offload/leaudio/hci/lib.rs42
-rw-r--r--offload/leaudio/hci/proxy.rs381
-rw-r--r--offload/leaudio/hci/service.rs126
-rw-r--r--offload/leaudio/hci/tests.rs914
-rw-r--r--service/Android.bp32
-rw-r--r--service/aidl/Android.bp2
-rw-r--r--service/change-ids/Android.bp2
-rw-r--r--service/src/com/android/server/bluetooth/BtPermissionUtils.java3
-rw-r--r--service/tests/AndroidTest.xml7
-rw-r--r--service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java4
-rw-r--r--sysprop/Android.bp4
-rw-r--r--sysprop/a2dp.sysprop7
-rw-r--r--system/Android.bp2
-rw-r--r--system/OWNERS6
-rw-r--r--system/audio/Android.bp4
-rw-r--r--system/audio_hal_interface/Android.bp4
-rw-r--r--system/audio_hal_interface/aidl/le_audio_utils.cc139
-rw-r--r--system/audio_hal_interface/aidl/le_audio_utils.h9
-rw-r--r--system/audio_hal_interface/aidl/le_audio_utils_unittest.cc12
-rw-r--r--system/audio_hal_interface/le_audio_software.cc26
-rw-r--r--system/audio_hal_interface/le_audio_software.h3
-rw-r--r--system/audio_hal_interface/le_audio_software_host.cc5
-rw-r--r--system/audio_hal_interface/le_audio_software_unittest.cc15
-rw-r--r--system/audio_hearing_aid_hw/Android.bp4
-rw-r--r--system/bta/Android.bp100
-rw-r--r--system/bta/ag/bta_ag_int.h20
-rw-r--r--system/bta/ag/bta_ag_sco.cc32
-rw-r--r--system/bta/aics/Android.bp4
-rw-r--r--system/bta/av/bta_av_aact.cc83
-rw-r--r--system/bta/csis/csis_client.cc3
-rw-r--r--system/bta/csis/csis_client_test.cc4
-rw-r--r--system/bta/dm/bta_dm_act.cc41
-rw-r--r--system/bta/dm/bta_dm_act.h8
-rw-r--r--system/bta/dm/bta_dm_ci.cc5
-rw-r--r--system/bta/dm/bta_dm_device_search.cc11
-rw-r--r--system/bta/dm/bta_dm_device_search.h21
-rw-r--r--system/bta/dm/bta_dm_disc.cc34
-rw-r--r--system/bta/dm/bta_dm_disc.h3
-rw-r--r--system/bta/dm/bta_dm_disc_int.h8
-rw-r--r--system/bta/dm/bta_dm_disc_sdp.cc17
-rw-r--r--system/bta/dm/bta_dm_gatt_client.cc20
-rw-r--r--system/bta/dm/bta_dm_gatt_client.h19
-rw-r--r--system/bta/dm/bta_dm_int.h18
-rw-r--r--system/bta/dm/bta_dm_pm.cc5
-rw-r--r--system/bta/dm/bta_dm_sec.cc4
-rw-r--r--system/bta/dm/bta_dm_sec_int.h10
-rw-r--r--system/bta/gatt/bta_gattc_act.cc6
-rw-r--r--system/bta/gatt/bta_gattc_api.cc8
-rw-r--r--system/bta/gatt/bta_gattc_int.h4
-rw-r--r--system/bta/gmap/gmap_client.cc18
-rw-r--r--system/bta/gmap/gmap_client_test.cc2
-rw-r--r--system/bta/gmap/gmap_server.cc4
-rw-r--r--system/bta/has/has_client.cc3
-rw-r--r--system/bta/has/has_client_test.cc4
-rw-r--r--system/bta/hearing_aid/hearing_aid.cc4
-rw-r--r--system/bta/hh/bta_hh_headtracker.cc5
-rw-r--r--system/bta/hh/bta_hh_le.cc10
-rw-r--r--system/bta/include/bta_gatt_api.h4
-rw-r--r--system/bta/include/bta_jv_api.h4
-rw-r--r--system/bta/include/bta_le_audio_api.h3
-rw-r--r--system/bta/include/bta_ras_api.h9
-rw-r--r--system/bta/jv/bta_jv_act.cc115
-rw-r--r--system/bta/jv/bta_jv_api.cc8
-rw-r--r--system/bta/jv/bta_jv_int.h4
-rw-r--r--system/bta/le_audio/broadcaster/broadcaster.cc22
-rw-r--r--system/bta/le_audio/broadcaster/broadcaster_test.cc72
-rw-r--r--system/bta/le_audio/client.cc292
-rw-r--r--system/bta/le_audio/client_linux.cc17
-rw-r--r--system/bta/le_audio/codec_manager.cc40
-rw-r--r--system/bta/le_audio/codec_manager.h23
-rw-r--r--system/bta/le_audio/codec_manager_test.cc31
-rw-r--r--system/bta/le_audio/device_groups.cc27
-rw-r--r--system/bta/le_audio/gmap_client.h12
-rw-r--r--system/bta/le_audio/le_audio_client_test.cc560
-rw-r--r--system/bta/le_audio/le_audio_set_configuration_provider_json.cc8
-rw-r--r--system/bta/le_audio/le_audio_types.cc34
-rw-r--r--system/bta/le_audio/le_audio_types.h15
-rw-r--r--system/bta/le_audio/le_audio_types_test.cc40
-rw-r--r--system/bta/le_audio/le_audio_utils.h16
-rw-r--r--system/bta/le_audio/mock_codec_manager.cc7
-rw-r--r--system/bta/le_audio/mock_codec_manager.h2
-rw-r--r--system/bta/le_audio/state_machine.cc6
-rw-r--r--system/bta/le_audio/state_machine_test.cc6
-rw-r--r--system/bta/le_audio/storage_helper.cc90
-rw-r--r--system/bta/le_audio/storage_helper.h3
-rw-r--r--system/bta/le_audio/storage_helper_test.cc37
-rw-r--r--system/bta/ras/ras_client.cc44
-rw-r--r--system/bta/ras/ras_utils_test.cc179
-rw-r--r--system/bta/test/bta_disc_test.cc84
-rw-r--r--system/bta/test/bta_dm_test.cc35
-rw-r--r--system/bta/test/bta_gatt_client_test.cc11
-rw-r--r--system/bta/test/bta_sdp_test.cc16
-rw-r--r--system/bta/test/bta_sec_test.cc11
-rw-r--r--system/bta/test/bta_test_fixtures.h3
-rw-r--r--system/bta/test/common/bta_gatt_api_mock.cc6
-rw-r--r--system/bta/test/common/bta_gatt_api_mock.h7
-rw-r--r--system/bta/test/common/btif_storage_mock.cc6
-rw-r--r--system/bta/test/common/btif_storage_mock.h2
-rw-r--r--system/bta/test/common/btm_api_mock.cc2
-rw-r--r--system/bta/vc/vc.cc33
-rw-r--r--system/bta/vc/vc_test.cc49
-rw-r--r--system/btcore/Android.bp10
-rw-r--r--system/btif/Android.bp16
-rw-r--r--system/btif/include/btif_dm.h2
-rw-r--r--system/btif/include/btif_profile_storage.h3
-rw-r--r--system/btif/include/btif_storage.h1
-rw-r--r--system/btif/src/bluetooth.cc11
-rw-r--r--system/btif/src/btif_a2dp_sink.cc10
-rw-r--r--system/btif/src/btif_a2dp_source.cc12
-rw-r--r--system/btif/src/btif_bqr.cc40
-rw-r--r--system/btif/src/btif_core.cc23
-rw-r--r--system/btif/src/btif_dm.cc182
-rw-r--r--system/btif/src/btif_gatt_client.cc8
-rw-r--r--system/btif/src/btif_hci_vs.cc2
-rw-r--r--system/btif/src/btif_hf.cc5
-rw-r--r--system/btif/src/btif_hh.cc9
-rw-r--r--system/btif/src/btif_profile_storage.cc24
-rw-r--r--system/btif/src/btif_sock_rfc.cc96
-rw-r--r--system/btif/src/btif_storage.cc74
-rw-r--r--system/btif/src/btif_util.cc1
-rw-r--r--system/btif/src/stack_manager.cc3
-rw-r--r--system/btif/test/btif_core_test.cc1
-rw-r--r--system/btif/test/btif_dm_test.cc41
-rw-r--r--system/common/Android.bp4
-rw-r--r--system/device/Android.bp7
-rw-r--r--system/device/src/device_iot_config.cc69
-rw-r--r--system/device/src/device_iot_config_int.cc13
-rw-r--r--system/device/src/interop.cc2
-rw-r--r--system/device/test/device_iot_config_test.cc398
-rw-r--r--system/embdrv/encoder_for_aptx/Android.bp4
-rw-r--r--system/embdrv/encoder_for_aptxhd/Android.bp4
-rw-r--r--system/embdrv/g722/Android.bp4
-rw-r--r--system/embdrv/sbc/decoder/Android.bp4
-rw-r--r--system/embdrv/sbc/encoder/Android.bp4
-rw-r--r--system/gd/Android.bp6
-rw-r--r--system/gd/AndroidTestTemplate.xml4
-rw-r--r--system/gd/crypto_toolbox/Android.bp2
-rw-r--r--system/gd/hal/hci_hal_host.cc1
-rw-r--r--system/gd/hal/hci_hal_host_rootcanal.cc5
-rw-r--r--system/gd/hal/ranging_hal.h6
-rw-r--r--system/gd/hal/ranging_hal_android.cc17
-rw-r--r--system/gd/hal/snoop_logger.cc71
-rw-r--r--system/gd/hal/snoop_logger_test.cc114
-rw-r--r--system/gd/hci/distance_measurement_manager.cc50
-rw-r--r--system/gd/hci/distance_measurement_manager.h30
-rw-r--r--system/gd/hci/distance_measurement_manager_mock.h4
-rw-r--r--system/gd/hci/le_advertising_manager.cc95
-rw-r--r--system/gd/hci/le_advertising_manager.h4
-rw-r--r--system/gd/metrics/counter_metrics.cc2
-rw-r--r--system/gd/metrics/counter_metrics.h6
-rw-r--r--system/gd/module.h5
-rw-r--r--system/gd/proto/Android.bp8
-rw-r--r--system/gd/rust/common/Android.bp4
-rw-r--r--system/gd/rust/topshim/le_audio/le_audio_shim.cc2
-rw-r--r--system/gd/rust/topshim/src/profiles/gatt.rs5
-rw-r--r--system/gd/rust/topshim/src/profiles/le_audio.rs23
-rw-r--r--system/gd/storage/config_keys.h2
-rw-r--r--system/gd/storage/storage_module.cc22
-rw-r--r--system/gd/storage/storage_module.h12
-rw-r--r--system/gd/storage/storage_module_test.cc4
-rw-r--r--system/hci/Android.bp4
-rw-r--r--system/include/Android.bp4
-rw-r--r--system/include/hardware/bluetooth.h15
-rw-r--r--system/include/hardware/bt_gatt_client.h2
-rw-r--r--system/include/hardware/bt_le_audio.h1
-rw-r--r--system/include/hardware/distance_measurement_interface.h4
-rw-r--r--system/internal_include/Android.bp2
-rw-r--r--system/log/Android.bp4
-rw-r--r--system/main/Android.bp8
-rw-r--r--system/main/shim/distance_measurement_manager.cc25
-rw-r--r--system/main/shim/entry.cc7
-rw-r--r--system/main/shim/shim.cc4
-rw-r--r--system/main/shim/stack.cc79
-rw-r--r--system/main/shim/stack.h20
-rw-r--r--system/main/stack_config.cc2
-rw-r--r--system/osi/Android.bp4
-rw-r--r--system/packet/Android.bp4
-rw-r--r--system/packet/avrcp/Android.bp4
-rw-r--r--system/packet/base/Android.bp4
-rw-r--r--system/pdl/hci/Android.bp8
-rw-r--r--system/pdl/l2cap/Android.bp8
-rw-r--r--system/pdl/ras/Android.bp8
-rw-r--r--system/pdl/security/Android.bp8
-rw-r--r--system/profile/avrcp/Android.bp4
-rw-r--r--system/profile/avrcp/device.cc5
-rw-r--r--system/rust/Android.bp4
-rw-r--r--system/rust/src/core/shared_box.rs6
-rw-r--r--system/stack/Android.bp23
-rw-r--r--system/stack/acl/ble_acl.cc5
-rw-r--r--system/stack/btm/btm_ble_gap.cc14
-rw-r--r--system/stack/btm/btm_ble_sec.cc2
-rw-r--r--system/stack/btm/btm_sec.cc13
-rw-r--r--system/stack/btm/btm_sec.h2
-rw-r--r--system/stack/btm/btm_security_client_interface.cc2
-rw-r--r--system/stack/connection_manager/connection_manager.cc8
-rw-r--r--system/stack/connection_manager/connection_manager.h3
-rw-r--r--system/stack/fuzzers/avrc_fuzzer.cc4
-rw-r--r--system/stack/fuzzers/bnep_fuzzer.cc4
-rw-r--r--system/stack/fuzzers/gatt_fuzzer.cc5
-rw-r--r--system/stack/fuzzers/l2cap_fuzzer.cc3
-rw-r--r--system/stack/fuzzers/rfcomm_fuzzer.cc4
-rw-r--r--system/stack/fuzzers/sdp_fuzzer.cc4
-rw-r--r--system/stack/fuzzers/smp_fuzzer.cc5
-rw-r--r--system/stack/gatt/gatt_api.cc9
-rw-r--r--system/stack/gatt/gatt_int.h4
-rw-r--r--system/stack/gatt/gatt_main.cc85
-rw-r--r--system/stack/gatt/gatt_sr.cc4
-rw-r--r--system/stack/gatt/gatt_utils.cc115
-rw-r--r--system/stack/hid/hidh_conn.cc33
-rw-r--r--system/stack/include/ble_appearance.h446
-rw-r--r--system/stack/include/bt_dev_class.h232
-rw-r--r--system/stack/include/btm_ble_api_types.h119
-rw-r--r--system/stack/include/port_api.h13
-rw-r--r--system/stack/include/security_client_callbacks.h2
-rw-r--r--system/stack/include/smp_status.h28
-rw-r--r--system/stack/rfcomm/port_api.cc44
-rw-r--r--system/stack/rfcomm/port_int.h22
-rw-r--r--system/stack/rfcomm/port_rfc.cc10
-rw-r--r--system/stack/rfcomm/port_utils.cc7
-rw-r--r--system/stack/rfcomm/rfc_port_fsm.cc76
-rw-r--r--system/stack/rfcomm/rfc_port_if.cc9
-rw-r--r--system/stack/rfcomm/rfc_utils.cc6
-rw-r--r--system/stack/smp/smp_api.cc4
-rw-r--r--system/stack/smp/smp_utils.cc2
-rw-r--r--system/stack/test/a2dp/AndroidTest.xml4
-rw-r--r--system/stack/test/a2dp/AndroidTestForce32.xml4
-rw-r--r--system/stack/test/connection_manager_test.cc2
-rw-r--r--system/stack/test/gatt/gatt_sr_test.cc11
-rw-r--r--system/stack/test/gatt/mock_gatt_utils_ref.cc1
-rw-r--r--system/stack/test/gatt/stack_gatt_test.cc1
-rw-r--r--system/stack/test/rfcomm/stack_rfcomm_port_test.cc8
-rw-r--r--system/stack/test/stack_smp_test.cc6
-rw-r--r--system/test/Android.bp9
-rw-r--r--system/test/headless/property.cc4
-rw-r--r--system/test/headless/property.h1
-rw-r--r--system/test/mock/mock_bta_dm_act.cc5
-rw-r--r--system/test/mock/mock_bta_dm_act.h9
-rw-r--r--system/test/mock/mock_bta_gattc_api.cc4
-rw-r--r--system/test/mock/mock_bta_jv_api.cc6
-rw-r--r--system/test/mock/mock_bta_leaudio.cc20
-rw-r--r--system/test/mock/mock_btif_profile_storage.cc5
-rw-r--r--system/test/mock/mock_btif_profile_storage.h9
-rw-r--r--system/test/mock/mock_main_shim.cc35
-rw-r--r--system/test/mock/mock_main_shim_entry.cc10
-rw-r--r--system/test/mock/mock_stack_btm_interface.cc2
-rw-r--r--system/test/mock/mock_stack_btm_sec.cc8
-rw-r--r--system/test/mock/mock_stack_btm_sec.h6
-rw-r--r--system/test/mock/mock_stack_gatt_main.cc3
-rw-r--r--system/test/mock/mock_stack_rfcomm_port_api.cc4
-rw-r--r--system/test/mock/mock_stack_security_client_interface.h2
-rw-r--r--system/test/suite/gatt/gatt_unittest.cc2
-rw-r--r--system/types/Android.bp4
-rw-r--r--system/udrv/Android.bp2
-rw-r--r--tools/rootcanal/hal/bluetooth_hci.cc18
-rw-r--r--tools/rootcanal/hal/bluetooth_hci.h20
-rw-r--r--tools/rootcanal/rust/src/lmp/procedure/mod.rs4
531 files changed, 19967 insertions, 10793 deletions
diff --git a/Android.bp b/Android.bp
index 7ec7305d0a..bef6c85049 100644
--- a/Android.bp
+++ b/Android.bp
@@ -73,6 +73,11 @@ cc_defaults {
],
c_std: "c99",
cpp_std: "c++20",
+ arch: {
+ riscv64: {
+ ldflags: ["-Wl,--no-relax"],
+ },
+ },
}
// List of tidy checks that are enabled for cc targets.
diff --git a/AndroidTestTemplate.xml b/AndroidTestTemplate.xml
index 8332422b34..4083baac14 100644
--- a/AndroidTestTemplate.xml
+++ b/AndroidTestTemplate.xml
@@ -39,7 +39,7 @@
<!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/OWNERS_channel_sounding b/OWNERS_channel_sounding
new file mode 100644
index 0000000000..f39c775033
--- /dev/null
+++ b/OWNERS_channel_sounding
@@ -0,0 +1,3 @@
+aliceypkuo@google.com
+ashchen@google.com
+chienyuanhuang@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5fc31de514..562f9717c8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -79,6 +79,9 @@
"name": "bluetooth_le_audio_test"
},
{
+ "name": "bluetooth_ras_test"
+ },
+ {
"name": "bluetooth_packet_parser_test"
},
{
@@ -275,6 +278,9 @@
"name": "bluetooth_le_audio_test"
},
{
+ "name": "bluetooth_ras_test"
+ },
+ {
"name": "bluetooth_packet_parser_test"
},
{
diff --git a/android/ChannelSoundingTestApp/app/src/androidTest/java/com/android/bluetooth/channelsoundingtestapp/ExampleInstrumentedTest.java b/android/ChannelSoundingTestApp/app/src/androidTest/java/com/android/bluetooth/channelsoundingtestapp/ExampleInstrumentedTest.java
deleted file mode 100644
index 2a73b92d19..0000000000
--- a/android/ChannelSoundingTestApp/app/src/androidTest/java/com/android/bluetooth/channelsoundingtestapp/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.channelsoundingtestapp;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
-
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- assertThat(appContext.getPackageName())
- .isEqualto("com.android.bluetooth.channelsoundingtestapp");
- }
-}
diff --git a/android/ChannelSoundingTestApp/app/src/test/java/com/android/bluetooth/channelsoundingtestapp/ExampleUnitTest.java b/android/ChannelSoundingTestApp/app/src/test/java/com/android/bluetooth/channelsoundingtestapp/ExampleUnitTest.java
deleted file mode 100644
index a3309495cf..0000000000
--- a/android/ChannelSoundingTestApp/app/src/test/java/com/android/bluetooth/channelsoundingtestapp/ExampleUnitTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.channelsoundingtestapp;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
-
- @Test
- public void addition_isCorrect() {
- assertThat(2 + 2).isEqualTo(4);
- }
-}
diff --git a/android/apishim/Android.bp b/android/apishim/Android.bp
index 9d980b2498..7780c014f3 100644
--- a/android/apishim/Android.bp
+++ b/android/apishim/Android.bp
@@ -23,9 +23,7 @@ java_defaults {
"androidx.annotation_annotation",
"modules-utils-build",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/android/app/Android.bp b/android/app/Android.bp
index db85914522..2da5e5ada9 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -32,9 +32,7 @@ java_library {
name: "bluetooth.mapsapi",
srcs: ["lib/mapapi/**/*.java"],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
sdk_version: "module_current",
lint: {
@@ -47,9 +45,7 @@ java_library {
srcs: [":framework-mms-shared-srcs"],
libs: ["unsupportedappusage"],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
sdk_version: "module_current",
lint: {
@@ -80,7 +76,7 @@ cc_library_shared {
"packages/modules/Bluetooth/system",
"packages/modules/Bluetooth/system/gd",
],
- // libbluetooth_jni is the jni lib included in the btservices apex.
+ // libbluetooth_jni is the jni lib included in the com.android.bt apex.
// As this library is inside an APEX the shared_libs that does not
// expose stubs are copied inside it. As a result using those as
// shared libraries is less interesting as they are not shared, so we link
@@ -164,9 +160,7 @@ cc_library_shared {
sanitize: {
scs: true,
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
@@ -200,15 +194,12 @@ cc_library {
sanitize: {
scs: true,
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
// Bluetooth APK
-
android_app {
name: "Bluetooth",
defaults: ["bluetooth_framework_errorprone_rules"],
@@ -219,6 +210,7 @@ android_app {
":system-messages-proto-src",
"proto/keystore.proto",
"src/**/*.java",
+ "src/**/*.kt",
],
proto: {
type: "lite",
@@ -234,7 +226,6 @@ android_app {
jni_uses_platform_apis: true,
libs: [
- "app-compat-annotations",
"bluetooth_constants_java",
"bluetooth_flags_java_lib",
"error_prone_annotations",
@@ -246,7 +237,6 @@ android_app {
"framework-mediaprovider.stubs.module_lib",
"framework-statsd.stubs.module_lib",
"framework-tethering.stubs.module_lib",
- "unsupportedappusage",
// Need to link the class at runtime
"framework-bluetooth.stubs.module_lib",
@@ -338,39 +328,15 @@ android_app {
enabled: true,
shrink: true,
optimize: false,
- // TODO(b/289285719): Revisit after resolving mocking issues in testing.
- proguard_compatibility: true,
proguard_flags_files: ["proguard.flags"],
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
sdk_version: "module_current",
updatable: true,
}
java_library {
- name: "bluetooth.change-ids",
- srcs: [
- "src/com/android/bluetooth/ChangeIds.java",
- ],
- libs: [
- "app-compat-annotations",
- ],
- apex_available: [
- "com.android.btservices",
- ],
- min_sdk_version: "Tiramisu",
- sdk_version: "module_current",
-}
-
-platform_compat_config {
- name: "bluetoothapk-platform-compat-config",
- src: ":bluetooth.change-ids",
-}
-
-java_library {
name: "bluetooth-proto-enums-java-gen",
installable: false,
proto: {
@@ -379,9 +345,7 @@ java_library {
srcs: [
":srcs_bluetooth_protos",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
sdk_version: "module_current",
}
diff --git a/android/app/OWNERS b/android/app/OWNERS
index 9edeffc18e..99c73be372 100644
--- a/android/app/OWNERS
+++ b/android/app/OWNERS
@@ -9,3 +9,6 @@ okamil@google.com
poahlo@google.com
siyuanh@google.com
wescande@google.com
+
+# Reviewers for Channel Sounding related files
+per-file /src/com/android/bluetooth/gatt/DistanceMeasurement*.java=file:/OWNERS_channel_sounding
diff --git a/android/app/aidl/Android.bp b/android/app/aidl/Android.bp
index 0829e0296c..edc1d050f9 100644
--- a/android/app/aidl/Android.bp
+++ b/android/app/aidl/Android.bp
@@ -17,6 +17,7 @@ filegroup {
"android/bluetooth/IBluetoothA2dp.aidl",
"android/bluetooth/IBluetoothA2dpSink.aidl",
"android/bluetooth/IBluetoothActivityEnergyInfoListener.aidl",
+ "android/bluetooth/IBluetoothAdvertise.aidl",
"android/bluetooth/IBluetoothAvrcpController.aidl",
"android/bluetooth/IBluetoothCallback.aidl",
"android/bluetooth/IBluetoothConnectionCallback.aidl",
@@ -59,6 +60,7 @@ filegroup {
"android/bluetooth/IBluetoothSocketManager.aidl",
"android/bluetooth/IBluetoothVolumeControl.aidl",
"android/bluetooth/IBluetoothVolumeControlCallback.aidl",
+ "android/bluetooth/IDistanceMeasurement.aidl",
"android/bluetooth/IncomingRfcommSocketInfo.aidl",
"android/bluetooth/le/IAdvertisingSetCallback.aidl",
"android/bluetooth/le/IDistanceMeasurementCallback.aidl",
diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl
index 158e37823f..9ab583b042 100644
--- a/android/app/aidl/android/bluetooth/IBluetooth.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl
@@ -344,4 +344,10 @@ interface IBluetooth
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
boolean isRfcommSocketOffloadSupported(in AttributionSource source);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
+ IBinder getBluetoothAdvertise();
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
+ IBinder getDistanceMeasurement();
}
diff --git a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
index bcfc95585d..fa5d1362ac 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl
@@ -53,7 +53,7 @@ interface IBluetoothA2dp {
boolean isA2dpPlaying(in BluetoothDevice device, in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
List<BluetoothCodecType> getSupportedCodecTypes();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
BluetoothCodecStatus getCodecStatus(in BluetoothDevice device, in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
oneway void setCodecConfigPreference(in BluetoothDevice device, in BluetoothCodecConfig codecConfig, in AttributionSource attributionSource);
diff --git a/android/app/aidl/android/bluetooth/IBluetoothAdvertise.aidl b/android/app/aidl/android/bluetooth/IBluetoothAdvertise.aidl
new file mode 100644
index 0000000000..bb7a49daf0
--- /dev/null
+++ b/android/app/aidl/android/bluetooth/IBluetoothAdvertise.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.bluetooth.le.IPeriodicAdvertisingCallback;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.content.AttributionSource;
+
+/**
+ * API for interacting with BLE advertising
+ * @hide
+ */
+interface IBluetoothAdvertise {
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
+ void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
+ in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
+ in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, in int gattServerIf,
+ in IAdvertisingSetCallback callback, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void stopAdvertisingSet(in IAdvertisingSetCallback callback, in AttributionSource attributionSource);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void getOwnAddress(in int advertiserId, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void setAdvertisingData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void setScanResponseData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
+ void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
+ void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in AttributionSource attributionSource);
+}
diff --git a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
index bfc380f50a..40976dbf00 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl
@@ -16,30 +16,12 @@
package android.bluetooth;
-import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattService;
-import android.bluetooth.le.AdvertiseSettings;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertisingSetParameters;
-import android.bluetooth.le.DistanceMeasurementMethod;
-import android.bluetooth.le.DistanceMeasurementParams;
-import android.bluetooth.le.IDistanceMeasurementCallback;
-import android.bluetooth.le.PeriodicAdvertisingParameters;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.bluetooth.le.ResultStorageDescriptor;
-import android.content.AttributionSource;
-import android.os.ParcelUuid;
-import android.os.WorkSource;
-
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
-import android.bluetooth.le.IAdvertisingSetCallback;
-import android.bluetooth.le.IPeriodicAdvertisingCallback;
-import android.bluetooth.le.IScannerCallback;
-
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
/**
* API for interacting with BLE / GATT
* @hide
@@ -48,31 +30,6 @@ interface IBluetoothGatt {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
- void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
- in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
- in AdvertiseData periodicData, in int duration, in int maxExtAdvEvents, in int gattServerIf,
- in IAdvertisingSetCallback callback, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void stopAdvertisingSet(in IAdvertisingSetCallback callback, in AttributionSource attributionSource);
-
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- void getOwnAddress(in int advertiserId, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void enableAdvertisingSet(in int advertiserId, in boolean enable, in int duration, in int maxExtAdvEvents, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void setAdvertisingData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void setScanResponseData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
- void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)")
- void setPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in AttributionSource attributionSource);
-
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback, boolean eatt_support, in AttributionSource attributionSource);
@@ -150,17 +107,4 @@ interface IBluetoothGatt {
void disconnectAll(in AttributionSource attributionSource);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)")
int subrateModeRequest(in int clientIf, in BluetoothDevice device, in int subrateMode, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- void startDistanceMeasurement(in ParcelUuid uuid, in DistanceMeasurementParams params, in IDistanceMeasurementCallback callback,
- in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- int stopDistanceMeasurement(in ParcelUuid uuid, in BluetoothDevice device, in int method, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- int getChannelSoundingMaxSupportedSecurityLevel(in BluetoothDevice remoteDevice, in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- int getLocalChannelSoundingMaxSupportedSecurityLevel(in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
- int[] getChannelSoundingSupportedSecurityLevels(in AttributionSource attributionSource);
}
diff --git a/android/app/aidl/android/bluetooth/IDistanceMeasurement.aidl b/android/app/aidl/android/bluetooth/IDistanceMeasurement.aidl
new file mode 100644
index 0000000000..78c793a351
--- /dev/null
+++ b/android/app/aidl/android/bluetooth/IDistanceMeasurement.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.DistanceMeasurementMethod;
+import android.bluetooth.le.DistanceMeasurementParams;
+import android.bluetooth.le.IDistanceMeasurementCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+/**
+ * API for interacting with distance measurement
+ * @hide
+ */
+interface IDistanceMeasurement {
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ void startDistanceMeasurement(in ParcelUuid uuid, in DistanceMeasurementParams params, in IDistanceMeasurementCallback callback,
+ in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ int stopDistanceMeasurement(in ParcelUuid uuid, in BluetoothDevice device, in int method, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ int getChannelSoundingMaxSupportedSecurityLevel(in BluetoothDevice remoteDevice, in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ int getLocalChannelSoundingMaxSupportedSecurityLevel(in AttributionSource attributionSource);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+ int[] getChannelSoundingSupportedSecurityLevels(in AttributionSource attributionSource);
+}
diff --git a/android/app/aidl/android/bluetooth/le/IAdvertisingSetCallback.aidl b/android/app/aidl/android/bluetooth/le/IAdvertisingSetCallback.aidl
index 35e88a4a68..449b173309 100644
--- a/android/app/aidl/android/bluetooth/le/IAdvertisingSetCallback.aidl
+++ b/android/app/aidl/android/bluetooth/le/IAdvertisingSetCallback.aidl
@@ -20,7 +20,7 @@ package android.bluetooth.le;
* @hide
*/
oneway interface IAdvertisingSetCallback {
- void onAdvertisingSetStarted(in IBinder gattBinder, in int advertiserId, in int tx_power, in int status);
+ void onAdvertisingSetStarted(in IBinder advertiseBinder, in int advertiserId, in int tx_power, in int status);
void onOwnAddressRead(in int advertiserId, in int addressType, in String address);
void onAdvertisingSetStopped(in int advertiserId);
void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status);
diff --git a/android/app/change-ids/Android.bp b/android/app/change-ids/Android.bp
new file mode 100644
index 0000000000..ab4e853c3f
--- /dev/null
+++ b/android/app/change-ids/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "bluetooth.change-ids",
+ srcs: ["com/android/bluetooth/ChangeIds.java"],
+ libs: ["app-compat-annotations"],
+ apex_available: ["com.android.bt"],
+ min_sdk_version: "Tiramisu",
+ sdk_version: "module_current",
+}
+
+platform_compat_config {
+ name: "bluetoothapk-platform-compat-config",
+ src: ":bluetooth.change-ids",
+}
diff --git a/android/app/src/com/android/bluetooth/ChangeIds.java b/android/app/change-ids/com/android/bluetooth/ChangeIds.java
index 4c06486dee..4c06486dee 100644
--- a/android/app/src/com/android/bluetooth/ChangeIds.java
+++ b/android/app/change-ids/com/android/bluetooth/ChangeIds.java
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 805e22b0df..6df96370b1 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -2228,6 +2228,25 @@ static jboolean disconnectAllAclsNative(JNIEnv* /* env */, jobject /* obj */) {
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
+static jboolean disconnectAclNative(JNIEnv* env, jobject /* obj */, jbyteArray address,
+ jint transport) {
+ log::verbose("");
+
+ if (!sBluetoothInterface) {
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (addr == nullptr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ RawAddress addr_obj = {};
+ addr_obj.FromOctets(reinterpret_cast<uint8_t*>(addr));
+
+ return sBluetoothInterface->disconnect_acl(addr_obj, transport);
+}
+
static jboolean allowWakeByHidNative(JNIEnv* /* env */, jobject /* obj */) {
log::verbose("");
@@ -2320,6 +2339,7 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
{"clearFilterAcceptListNative", "()Z",
reinterpret_cast<void*>(clearFilterAcceptListNative)},
{"disconnectAllAclsNative", "()Z", reinterpret_cast<void*>(disconnectAllAclsNative)},
+ {"disconnectAclNative", "([BI)Z", reinterpret_cast<void*>(disconnectAclNative)},
{"allowWakeByHidNative", "()Z", reinterpret_cast<void*>(allowWakeByHidNative)},
{"restoreFilterAcceptListNative", "()Z",
reinterpret_cast<void*>(restoreFilterAcceptListNative)},
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index 80e334e844..014668bfd0 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -122,6 +122,16 @@ static std::vector<uint8_t> toVector(JNIEnv* env, jbyteArray ba) {
return data_vec;
}
+static std::string jstr_to_str(JNIEnv* env, jstring js) {
+ const char* cstr = env->GetStringUTFChars(js, NULL);
+ if (cstr == nullptr) {
+ return "";
+ }
+ std::string ret = std::string(cstr);
+ env->ReleaseStringUTFChars(js, cstr);
+ return ret;
+}
+
namespace android {
/**
@@ -1155,8 +1165,10 @@ public:
void OnDistanceMeasurementResult(RawAddress address, uint32_t centimeter,
uint32_t error_centimeter, int azimuth_angle,
int error_azimuth_angle, int altitude_angle,
- int error_altitude_angle, uint64_t elapsedRealtimeNanos,
- int8_t confidence_level, uint8_t method) {
+ int error_altitude_angle, uint64_t elapsed_realtime_nanos,
+ int8_t confidence_level, double delay_spread_meters,
+ uint8_t detected_attack_level, double velocity_meters_per_second,
+ uint8_t method) {
std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid() || !mDistanceMeasurementCallbacksObj) {
@@ -1166,7 +1178,8 @@ public:
sCallbackEnv->CallVoidMethod(
mDistanceMeasurementCallbacksObj, method_onDistanceMeasurementResult, addr.get(),
centimeter, error_centimeter, azimuth_angle, error_azimuth_angle, altitude_angle,
- error_altitude_angle, elapsedRealtimeNanos, confidence_level, method);
+ error_altitude_angle, elapsed_realtime_nanos, confidence_level, delay_spread_meters,
+ detected_attack_level, velocity_meters_per_second, method);
}
};
@@ -1259,13 +1272,13 @@ static int gattClientGetDeviceTypeNative(JNIEnv* env, jobject /* object */, jstr
return sGattIf->client->get_device_type(str2addr(env, address));
}
-static void gattClientRegisterAppNative(JNIEnv* /* env */, jobject /* object */, jlong app_uuid_lsb,
- jlong app_uuid_msb, jboolean eatt_support) {
+static void gattClientRegisterAppNative(JNIEnv* env, jobject /* object */, jlong app_uuid_lsb,
+ jlong app_uuid_msb, jstring name, jboolean eatt_support) {
if (!sGattIf) {
return;
}
Uuid uuid = from_java_uuid(app_uuid_msb, app_uuid_lsb);
- sGattIf->client->register_client(uuid, eatt_support);
+ sGattIf->client->register_client(uuid, jstr_to_str(env, name).c_str(), eatt_support);
}
static void gattClientUnregisterAppNative(JNIEnv* /* env */, jobject /* object */, jint clientIf) {
@@ -2914,7 +2927,7 @@ static int register_com_android_bluetooth_gatt_distance_measurement(JNIEnv* env)
&method_onDistanceMeasurementStarted},
{"onDistanceMeasurementStopped", "(Ljava/lang/String;II)V",
&method_onDistanceMeasurementStopped},
- {"onDistanceMeasurementResult", "(Ljava/lang/String;IIIIIIJII)V",
+ {"onDistanceMeasurementResult", "(Ljava/lang/String;IIIIIIJIDIDI)V",
&method_onDistanceMeasurementResult},
};
GET_JAVA_METHODS(env, "com/android/bluetooth/gatt/DistanceMeasurementNativeInterface",
@@ -2929,7 +2942,8 @@ static int register_com_android_bluetooth_gatt_(JNIEnv* env) {
{"cleanupNative", "()V", (void*)cleanupNative},
{"gattClientGetDeviceTypeNative", "(Ljava/lang/String;)I",
(void*)gattClientGetDeviceTypeNative},
- {"gattClientRegisterAppNative", "(JJZ)V", (void*)gattClientRegisterAppNative},
+ {"gattClientRegisterAppNative", "(JJLjava/lang/String;Z)V",
+ (void*)gattClientRegisterAppNative},
{"gattClientUnregisterAppNative", "(I)V", (void*)gattClientUnregisterAppNative},
{"gattClientConnectNative", "(ILjava/lang/String;IZIZII)V",
(void*)gattClientConnectNative},
diff --git a/android/app/proguard.flags b/android/app/proguard.flags
index 75da0433f5..80b6c4a32a 100644
--- a/android/app/proguard.flags
+++ b/android/app/proguard.flags
@@ -7,4 +7,10 @@
-keep class android.support.v4.media.MediaDescriptionCompat { *; }
# Required for tests that use Mockito's thenThrow with checked exceptions.
--keepattributes Exceptions \ No newline at end of file
+-keepattributes Exceptions
+
+# Minimal set of keep rules for mocked methods with checked exceptions.
+# This can be relaxed to specific packages if that simplifies testing.
+# See also https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md#r8-full-mode.
+-keepclassmembers,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class com.android.bluetooth.BluetoothMethodProxy { public <methods>; }
+-keepclassmembers,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class com.android.obex.Operation { public <methods>; }
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index ca897af46e..0f15903dfb 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -44,6 +44,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.BroadcastOptions;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
@@ -72,6 +73,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
import org.xmlpull.v1.XmlPullParser;
@@ -169,6 +171,34 @@ public final class Utils {
}
/**
+ * Checks CoD and metadata to determine if the device is a watch
+ *
+ * @param service Adapter service
+ * @param device the remote device
+ * @return {@code true} if it's a watch, {@code false} otherwise
+ */
+ public static boolean isWatch(
+ @NonNull AdapterService service, @NonNull BluetoothDevice device) {
+ // Check CoD
+ BluetoothClass deviceClass = new BluetoothClass(service.getRemoteClass(device));
+ if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) {
+ return true;
+ }
+
+ // Check metadata
+ DatabaseManager mDbManager = service.getDatabase();
+ byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (deviceType == null) {
+ return false;
+ }
+ String deviceTypeStr = new String(deviceType);
+ if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Only exposed for testing, do not invoke this method outside of tests.
*
* @param enabled true if the dual mode state is enabled, false otherwise
@@ -845,10 +875,7 @@ public final class Utils {
return true;
}
- Log.e(
- TAG,
- "Permission denial: Need ACCESS_COARSE_LOCATION "
- + "permission to get scan results");
+ Log.e(TAG, "Need ACCESS_COARSE_LOCATION permission for " + currentAttribution);
return false;
}
@@ -889,8 +916,8 @@ public final class Utils {
Log.e(
TAG,
- "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION"
- + "permission to get scan results");
+ "Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission for "
+ + currentAttribution);
return false;
}
@@ -920,9 +947,7 @@ public final class Utils {
return true;
}
- Log.e(
- TAG,
- "Permission denial: Need ACCESS_FINE_LOCATION " + "permission to get scan results");
+ Log.e(TAG, "Need ACCESS_FINE_LOCATION permission for " + currentAttribution);
return false;
}
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 136a0d4621..100076127b 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -50,7 +50,6 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.sysprop.BluetoothProperties;
import android.util.Log;
@@ -1515,12 +1514,14 @@ public class A2dpService extends ProfileService {
@Override
public BluetoothCodecStatus getCodecStatus(
BluetoothDevice device, AttributionSource source) {
+ requireNonNull(device);
A2dpService service = getServiceAndEnforceConnect(source);
if (service == null) {
return null;
}
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ Utils.enforceCdmAssociationIfNotBluetoothPrivileged(
+ service, service.mCompanionDeviceManager, source, device);
return service.getCodecStatus(device);
}
@@ -1530,6 +1531,7 @@ public class A2dpService extends ProfileService {
BluetoothDevice device,
BluetoothCodecConfig codecConfig,
AttributionSource source) {
+ requireNonNull(device);
A2dpService service = getServiceAndEnforceConnect(source);
if (service == null) {
return;
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java b/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
index f45fc0b189..e4de73700c 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaBrowserWrapper.java
@@ -79,6 +79,7 @@ class MediaBrowserWrapper {
private final Looper mLooper;
private final String mPackageName;
private final Handler mRunHandler;
+ private final String mClassName;
private ConnectionState mBrowserConnectionState = ConnectionState.DISCONNECTED;
@@ -94,6 +95,7 @@ class MediaBrowserWrapper {
Context context, Looper looper, String packageName, String className) {
mContext = context;
mPackageName = packageName;
+ mClassName = className;
mLooper = looper;
mRunHandler = new Handler(mLooper);
mWrappedBrowser =
@@ -383,4 +385,9 @@ class MediaBrowserWrapper {
return mRunHandler;
}
}
+
+ @Override
+ public String toString() {
+ return "Browsable Package & Class Name: " + mPackageName + " " + mClassName + "\n";
+ }
}
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
index b04f684f34..00213d0ef6 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
@@ -985,6 +985,19 @@ public class MediaPlayerList {
mActivePlayerId = playerId;
+ if (Utils.isPtsTestMode()) {
+ sendFolderUpdate(true, true, false);
+ } else if (Flags.setAddressedPlayer() && Flags.browsingRefactor()) {
+ // If the browsing refactor flag is not active, addressed player should always be 0.
+ // If the new active player has been set by Addressed player key event
+ // We don't send an addressed player update.
+ if (mActivePlayerId != mAddressedPlayerId) {
+ mAddressedPlayerId = mActivePlayerId;
+ Log.d(TAG, "setActivePlayer AddressedPlayer changed to " + mAddressedPlayerId);
+ sendFolderUpdate(false, true, false);
+ }
+ }
+
MediaPlayerWrapper player = getActivePlayer();
if (player == null) return;
@@ -1002,19 +1015,6 @@ public class MediaPlayerList {
return;
}
- if (Utils.isPtsTestMode()) {
- sendFolderUpdate(true, true, false);
- } else if (Flags.setAddressedPlayer() && Flags.browsingRefactor()) {
- // If the browsing refactor flag is not active, addressed player should always be 0.
- // If the new active player has been set by Addressed player key event
- // We don't send an addressed player update.
- if (mActivePlayerId != mAddressedPlayerId) {
- mAddressedPlayerId = mActivePlayerId;
- Log.d(TAG, "setActivePlayer AddressedPlayer changed to " + mAddressedPlayerId);
- sendFolderUpdate(false, true, false);
- }
- }
-
MediaData data = player.getCurrentMediaData();
if (mAudioPlaybackIsActive) {
data.state = mCurrMediaData.state;
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptor.java b/android/app/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptor.java
index 76daff6a52..1e3be0184a 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptor.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptor.java
@@ -87,7 +87,7 @@ public class BipImageDescriptor {
* @param encoding The encoding you would like to set as a BIP spec defined string
* @return This object so you can continue building
*/
- public Builder setPropietaryEncoding(String encoding) {
+ public Builder setProprietaryEncoding(String encoding) {
mImageDescriptor.mEncoding = new BipEncoding(BipEncoding.USR_XXX, encoding);
return this;
}
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
index 67b2b4a005..7b4d7fe3b4 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
@@ -32,6 +32,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
+import android.content.AttributionSource;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
@@ -172,6 +173,10 @@ public class BatteryStateMachine extends StateMachine {
@VisibleForTesting
@SuppressLint("AndroidFrameworkRequiresPermission") // We should call internal gatt interface
boolean connectGatt() {
+ mDevice.setAttributionSource(
+ (new AttributionSource.Builder(AttributionSource.myAttributionSource()))
+ .setAttributionTag("BatteryService")
+ .build());
mBluetoothGatt =
mDevice.connectGatt(
mService,
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 2fff02ea1e..0daef0175a 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -2499,6 +2499,28 @@ public class BassClientService extends ProfileService {
BluetoothDevice srcDevice = getDeviceForSyncHandle(syncHandle);
mSyncHandleToDeviceMap.remove(syncHandle);
int broadcastId = getBroadcastIdForSyncHandle(syncHandle);
+ if (leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) {
+ synchronized (mPendingSourcesToAdd) {
+ Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
+ while (iterator.hasNext()) {
+ AddSourceData pendingSourcesToAdd = iterator.next();
+ if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId() == broadcastId) {
+ iterator.remove();
+ }
+ }
+ }
+ synchronized (mSinksWaitingForPast) {
+ Iterator<Map.Entry<BluetoothDevice, Pair<Integer, Integer>>> iterator =
+ mSinksWaitingForPast.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<BluetoothDevice, Pair<Integer, Integer>> entry = iterator.next();
+ int broadcastIdForPast = entry.getValue().first;
+ if (broadcastId == broadcastIdForPast) {
+ iterator.remove();
+ }
+ }
+ }
+ }
mSyncHandleToBroadcastIdMap.remove(syncHandle);
if (srcDevice != null) {
mPeriodicAdvertisementResultMap.get(srcDevice).remove(broadcastId);
@@ -3091,6 +3113,13 @@ public class BassClientService extends ProfileService {
mCallbacks.notifySourceAddFailed(device, sourceMetadata, statusCode);
continue;
}
+ if (!stateMachine.isBassStateReady()) {
+ Log.d(TAG, "addSource: BASS state not ready, retry later with device: " + device);
+ synchronized (mPendingSourcesToAdd) {
+ mPendingSourcesToAdd.add(new AddSourceData(device, sourceMetadata, isGroupOp));
+ }
+ continue;
+ }
if (stateMachine.hasPendingSourceOperation()) {
Log.w(
TAG,
@@ -3730,38 +3759,76 @@ public class BassClientService extends ProfileService {
}
}
- /** Handle device newly connected and its peer device still has active source */
- private void checkAndResumeBroadcast(BluetoothDevice sink) {
+ /* Handle device Bass state ready and check if assistant should resume broadcast */
+ private void handleBassStateReady(BluetoothDevice sink) {
+ // Check its peer device still has active source
Map<Integer, BluetoothLeBroadcastMetadata> entry = mBroadcastMetadataMap.get(sink);
- if (entry == null) {
- Log.d(TAG, "checkAndResumeBroadcast: no entry for device: " + sink + ", available");
- return;
+ if (entry != null) {
+ for (Map.Entry<Integer, BluetoothLeBroadcastMetadata> idMetadataIdPair :
+ entry.entrySet()) {
+ BluetoothLeBroadcastMetadata metadata = idMetadataIdPair.getValue();
+ if (metadata == null) {
+ Log.d(TAG, "handleBassStateReady: no metadata available");
+ continue;
+ }
+ for (BluetoothDevice groupDevice : getTargetDeviceList(sink, true)) {
+ if (groupDevice.equals(sink)) {
+ continue;
+ }
+ // Check peer device
+ Optional<BluetoothLeBroadcastReceiveState> receiver =
+ getOrCreateStateMachine(groupDevice).getAllSources().stream()
+ .filter(e -> e.getBroadcastId() == metadata.getBroadcastId())
+ .findAny();
+ if (receiver.isPresent()
+ && !getAllSources(sink).stream()
+ .anyMatch(
+ rs ->
+ (rs.getBroadcastId()
+ == receiver.get().getBroadcastId()))) {
+ Log.d(TAG, "handleBassStateReady: restore the source for device, " + sink);
+ addSource(sink, metadata, false);
+ return;
+ }
+ }
+ }
+ } else {
+ Log.d(TAG, "handleBassStateReady: no entry for device: " + sink + ", available");
}
- for (Map.Entry<Integer, BluetoothLeBroadcastMetadata> idMetadataIdPair : entry.entrySet()) {
- BluetoothLeBroadcastMetadata metadata = idMetadataIdPair.getValue();
- if (metadata == null) {
- Log.d(TAG, "checkAndResumeBroadcast: no metadata available");
- continue;
- }
- for (BluetoothDevice groupDevice : getTargetDeviceList(sink, true)) {
- if (groupDevice.equals(sink)) {
- continue;
+ // Continue to check if there is pending source to add due to BASS not ready
+ synchronized (mPendingSourcesToAdd) {
+ Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
+ while (iterator.hasNext()) {
+ AddSourceData pendingSourcesToAdd = iterator.next();
+ if (pendingSourcesToAdd.mSink.equals(sink)) {
+ Log.d(TAG, "handleBassStateReady: retry adding source with device, " + sink);
+ addSource(
+ pendingSourcesToAdd.mSink,
+ pendingSourcesToAdd.mSourceMetadata,
+ pendingSourcesToAdd.mIsGroupOp);
+ iterator.remove();
+ return;
}
- // Check peer device
- Optional<BluetoothLeBroadcastReceiveState> receiver =
- getOrCreateStateMachine(groupDevice).getAllSources().stream()
- .filter(e -> e.getBroadcastId() == metadata.getBroadcastId())
- .findAny();
- if (receiver.isPresent()
- && !getAllSources(sink).stream()
- .anyMatch(
- rs ->
- (rs.getBroadcastId()
- == receiver.get().getBroadcastId()))) {
- Log.d(TAG, "checkAndResumeBroadcast: restore the source for device: " + sink);
- addSource(sink, metadata, false);
+ }
+ }
+ }
+
+ /* Handle device Bass state setup failed */
+ private void handleBassStateSetupFailed(BluetoothDevice sink) {
+ // Check if there is pending source to add due to BASS not ready
+ synchronized (mPendingSourcesToAdd) {
+ Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
+ while (iterator.hasNext()) {
+ AddSourceData pendingSourcesToAdd = iterator.next();
+ if (pendingSourcesToAdd.mSink.equals(sink)) {
+ mCallbacks.notifySourceAddFailed(
+ pendingSourcesToAdd.mSink,
+ pendingSourcesToAdd.mSourceMetadata,
+ BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES);
+ iterator.remove();
+ return;
}
}
}
@@ -4009,7 +4076,10 @@ public class BassClientService extends ProfileService {
mUnicastSourceStreamStatus = Optional.of(status);
if (status == STATUS_LOCAL_STREAM_REQUESTED) {
- if (areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices())) {
+ if ((leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()
+ && hasPrimaryDeviceManagedExternalBroadcast())
+ || (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()
+ && areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()))) {
if (leaudioBroadcastAssistantPeripheralEntrustment()) {
cacheSuspendingSources(BassConstants.INVALID_BROADCAST_ID);
List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToStop =
@@ -4022,11 +4092,13 @@ public class BassClientService extends ProfileService {
suspendAllReceiversSourceSynchronization();
}
}
- for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) {
- Integer broadcastId = entry.getKey();
- PauseType pauseType = entry.getValue();
- if (pauseType != PauseType.HOST_INTENTIONAL) {
- suspendReceiversSourceSynchronization(broadcastId);
+ if (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) {
+ for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) {
+ Integer broadcastId = entry.getKey();
+ PauseType pauseType = entry.getValue();
+ if (pauseType != PauseType.HOST_INTENTIONAL) {
+ suspendReceiversSourceSynchronization(broadcastId);
+ }
}
}
} else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
@@ -4190,9 +4262,19 @@ public class BassClientService extends ProfileService {
return activeSinks;
}
+ /** Get sink devices synced to the broadcasts by broadcast id */
+ public List<BluetoothDevice> getSyncedBroadcastSinks(int broadcastId) {
+ return getConnectedDevices().stream()
+ .filter(
+ device ->
+ getAllSources(device).stream()
+ .anyMatch(rs -> rs.getBroadcastId() == broadcastId))
+ .toList();
+ }
+
private boolean isSyncedToBroadcastStream(Long syncState) {
- return syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS
- && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG;
+ return syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS
+ && syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG;
}
/** Handle broadcast state changed */
@@ -4235,6 +4317,7 @@ public class BassClientService extends ProfileService {
private static final int MSG_RECEIVESTATE_CHANGED = 12;
private static final int MSG_SOURCE_LOST = 13;
private static final int MSG_BASS_STATE_READY = 14;
+ private static final int MSG_BASS_STATE_SETUP_FAILED = 15;
@GuardedBy("mCallbacksList")
private final RemoteCallbackList<IBluetoothLeBroadcastAssistantCallback> mCallbacksList =
@@ -4298,7 +4381,12 @@ public class BassClientService extends ProfileService {
switch (msg.what) {
case MSG_BASS_STATE_READY:
sink = (BluetoothDevice) msg.obj;
- sService.checkAndResumeBroadcast(sink);
+ sService.handleBassStateReady(sink);
+ isMsgHandled = true;
+ break;
+ case MSG_BASS_STATE_SETUP_FAILED:
+ sink = (BluetoothDevice) msg.obj;
+ sService.handleBassStateSetupFailed(sink);
isMsgHandled = true;
break;
default:
@@ -4577,6 +4665,11 @@ public class BassClientService extends ProfileService {
sEventLogger.logd(TAG, "notifyBassStateReady: sink: " + sink);
obtainMessage(MSG_BASS_STATE_READY, sink).sendToTarget();
}
+
+ void notifyBassStateSetupFailed(BluetoothDevice sink) {
+ sEventLogger.logd(TAG, "notifyBassStateSetupFailed: sink: " + sink);
+ obtainMessage(MSG_BASS_STATE_SETUP_FAILED, sink).sendToTarget();
+ }
}
@Override
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
index efdc0dcae7..db1ebce250 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -47,6 +47,7 @@ import android.bluetooth.le.PeriodicAdvertisingCallback;
import android.bluetooth.le.PeriodicAdvertisingReport;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
+import android.content.AttributionSource;
import android.content.Intent;
import android.os.Binder;
import android.os.Looper;
@@ -155,6 +156,7 @@ class BassClientStateMachine extends StateMachine {
@VisibleForTesting BluetoothGattCharacteristic mBroadcastScanControlPoint;
private final Map<Integer, Boolean> mFirstTimeBisDiscoveryMap;
private int mPASyncRetryCounter = 0;
+ private boolean mBassStateReady = false;
@VisibleForTesting int mNumOfBroadcastReceiverStates = 0;
int mNumOfReadyBroadcastReceiverStates = 0;
@VisibleForTesting int mPendingOperation = -1;
@@ -939,15 +941,15 @@ class BassClientStateMachine extends StateMachine {
// Check Bis state
for (int i = 0; i < recvState.getNumSubgroups(); i++) {
Long bisState = recvState.getBisSyncState().get(i);
- if (bisState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG
- && bisState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS) {
+ if (bisState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG
+ && bisState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS) {
// Any bis synced, update status and break
syncStats.updateBisSyncedTime(SystemClock.elapsedRealtime());
syncStats.updateSyncStatus(
BluetoothStatsLog
.BROADCAST_AUDIO_SYNC_REPORTED__SYNC_STATUS__SYNC_STATUS_AUDIO_SYNC_SUCCESS);
break;
- } else if (bisState == BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) {
+ } else if (bisState == BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG) {
logBroadcastSyncStatsWithStatus(
broadcastId,
BluetoothStatsLog
@@ -1177,6 +1179,7 @@ class BassClientStateMachine extends StateMachine {
if (leaudioBroadcastResyncHelper()) {
// Notify service BASS state ready for operations
mService.getCallbacks().notifyBassStateReady(mDevice);
+ mBassStateReady = true;
}
} else {
log("Updated receiver state: " + recvState);
@@ -1533,6 +1536,7 @@ class BassClientStateMachine extends StateMachine {
+ status
+ "mBluetoothGatt"
+ mBluetoothGatt);
+ mService.getCallbacks().notifyBassStateSetupFailed(mDevice);
}
} else {
log("remote initiated callback");
@@ -1560,6 +1564,7 @@ class BassClientStateMachine extends StateMachine {
if (mNumOfReadyBroadcastReceiverStates == mNumOfBroadcastReceiverStates) {
// Notify service BASS state ready for operations
mService.getCallbacks().notifyBassStateReady(mDevice);
+ mBassStateReady = true;
}
} else {
processBroadcastReceiverStateObsolete(
@@ -1604,6 +1609,9 @@ class BassClientStateMachine extends StateMachine {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "mtu: " + mtu);
mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN;
+ } else {
+ Log.w(TAG, "onMtuChanged failed: " + status);
+ mService.getCallbacks().notifyBassStateSetupFailed(mDevice);
}
}
@@ -1797,6 +1805,10 @@ class BassClientStateMachine extends StateMachine {
mGattCallback = new GattCallback();
}
+ mDevice.setAttributionSource(
+ (new AttributionSource.Builder(AttributionSource.myAttributionSource()))
+ .setAttributionTag("BassClient")
+ .build());
BluetoothGatt gatt =
mDevice.connectGatt(
mService,
@@ -1875,6 +1887,7 @@ class BassClientStateMachine extends StateMachine {
}
mPendingOperation = -1;
mPendingMetadata = null;
+ mBassStateReady = false;
mCurrentMetadata.clear();
mPendingRemove.clear();
}
@@ -2049,15 +2062,16 @@ class BassClientStateMachine extends StateMachine {
}
}
- private static int getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels) {
- int bisSync = 0;
+ private static long getBisSyncFromChannelPreference(
+ List<BluetoothLeBroadcastChannel> channels) {
+ long bisSync = 0L;
for (BluetoothLeBroadcastChannel channel : channels) {
if (channel.isSelected()) {
if (channel.getChannelIndex() == 0) {
Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0");
continue;
}
- bisSync |= 1 << (channel.getChannelIndex() - 1);
+ bisSync |= 1L << (channel.getChannelIndex() - 1);
}
}
@@ -2106,14 +2120,14 @@ class BassClientStateMachine extends StateMachine {
for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
// BIS_Sync
- int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
- if (bisSync == 0) {
- bisSync = 0xFFFFFFFF;
+ long bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
+ if (bisSync == BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS) {
+ bisSync = BassConstants.BIS_SYNC_NO_PREFERENCE;
}
- stream.write(bisSync & 0x00000000000000FF);
- stream.write((bisSync & 0x000000000000FF00) >>> 8);
- stream.write((bisSync & 0x0000000000FF0000) >>> 16);
- stream.write((bisSync & 0x00000000FF000000) >>> 24);
+ stream.write((byte) (bisSync & 0x00000000000000FFL));
+ stream.write((byte) ((bisSync & 0x000000000000FF00L) >>> 8));
+ stream.write((byte) ((bisSync & 0x0000000000FF0000L) >>> 16));
+ stream.write((byte) ((bisSync & 0x00000000FF000000L) >>> 24));
// Metadata_Length
BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata();
@@ -2162,28 +2176,41 @@ class BassClientStateMachine extends StateMachine {
res[offset++] = (byte) numSubGroups;
for (int i = 0; i < numSubGroups; i++) {
- int bisIndexValue = 0xFFFFFFFF;
+ long bisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
+ long currentBisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
+ if (i < existingState.getBisSyncState().size()) {
+ currentBisIndexValue = existingState.getBisSyncState().get(i);
+ }
+
if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
- bisIndexValue = 0;
- } else if (metaData != null
- && (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
- || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE)) {
+ bisIndexValue = BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS;
+ } else if (metaData != null) {
bisIndexValue =
getBisSyncFromChannelPreference(
metaData.getSubgroups().get(i).getChannels());
- // Let sink decide to which BIS sync if there is no channel preference
- if (bisIndexValue == 0) {
- bisIndexValue = 0xFFFFFFFF;
+ // If updating metadata with paSync INVALID_PA_SYNC_VALUE
+ // Use bisIndexValue parsed from metadata channels
+ if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
+ || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) {
+ // Let sink decide to which BIS sync if there is no channel preference
+ if (bisIndexValue == BassConstants.BIS_SYNC_DO_NOT_SYNC_TO_BIS) {
+ bisIndexValue = BassConstants.BIS_SYNC_NO_PREFERENCE;
+ }
}
- } else if (i < existingState.getBisSyncState().size()) {
- bisIndexValue = existingState.getBisSyncState().get(i).intValue();
+ } else {
+ // Keep using BIS index from remote receive state
+ bisIndexValue = currentBisIndexValue;
}
- log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
+ log(
+ "UPDATE_BCAST_SOURCE: bisIndexValue from: "
+ + currentBisIndexValue
+ + " to: "
+ + bisIndexValue);
// BIS_Sync
- res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
- res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
- res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
- res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
+ res[offset++] = (byte) (bisIndexValue & 0x00000000000000FFL);
+ res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00L) >>> 8);
+ res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000L) >>> 16);
+ res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000L) >>> 24);
// Metadata_Length; On Modify source, don't update any Metadata
res[offset++] = 0;
}
@@ -2863,6 +2890,10 @@ class BassClientStateMachine extends StateMachine {
return mNumOfBroadcastReceiverStates;
}
+ boolean isBassStateReady() {
+ return mBassStateReady;
+ }
+
BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
return mCurrentMetadata.getOrDefault(sourceId, null);
}
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassConstants.java b/android/app/src/com/android/bluetooth/bass_client/BassConstants.java
index 981f4b6287..5efd2d4185 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassConstants.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassConstants.java
@@ -62,6 +62,9 @@ public class BassConstants {
public static final int BCAST_RCVR_STATE_BADCODE_START_IDX = 14;
public static final int BCAST_RCVR_STATE_BADCODE_SIZE = 16;
public static final int BCAST_RCVR_STATE_BIS_SYNC_SIZE = 4;
+ // BIS_Sync State value
+ public static final long BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
+ public static final long BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
// 30 secs time out for all gatt writes
public static final int GATT_TXN_TIMEOUT_MS = 30000;
public static final int SOURCE_OPERATION_TIMEOUT_MS = 3000;
@@ -75,11 +78,11 @@ public class BassConstants {
public static final int BCAST_NAME_AD_TYPE = 0x30;
public static final int BCAST_NAME_LEN_MIN = 4;
public static final int BCAST_NAME_LEN_MAX = 32;
- // PA_Sync parameter value
+ // PA_Sync parameter value in BASS operations
public static final int PA_SYNC_DO_NOT_SYNC = 0x00;
public static final int PA_SYNC_PAST_AVAILABLE = 0x01;
public static final int PA_SYNC_PAST_NOT_AVAILABLE = 0x02;
- // BIS_Sync parameter value
- public static final long BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
- public static final long BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;
+ // BIS_Sync parameter value in BASS operations
+ public static final long BIS_SYNC_DO_NOT_SYNC_TO_BIS = 0x00000000L;
+ public static final long BIS_SYNC_NO_PREFERENCE = 0xFFFFFFFFL;
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
index 981b2db0c7..9ebe10d181 100644
--- a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -50,6 +50,7 @@ public final class AbstractionLayer {
static final int BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID = 0X16;
static final int BT_PROPERTY_REMOTE_MODEL_NUM = 0x17;
static final int BT_PROPERTY_LPP_OFFLOAD_FEATURES = 0x1B;
+ static final int BT_PROPERTY_UUIDS_LE = 0x1C;
public static final int BT_DEVICE_TYPE_BREDR = 0x01;
public static final int BT_DEVICE_TYPE_BLE = 0x02;
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index 6fb1169a9e..6a7c4fd27c 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -19,7 +19,6 @@ package com.android.bluetooth.btservice;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
@@ -361,7 +360,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
if (mDbManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP)
!= BluetoothProfile.CONNECTION_POLICY_ALLOWED
|| mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- if (isWatch(device)) {
+ if (Utils.isWatch(mAdapterService, device)) {
Log.i(TAG, "Do not set hfp active for watch device " + device);
return;
}
@@ -867,9 +866,6 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
Log.d(TAG, "onAudioDevicesRemoved");
- if (!Flags.admFallbackWhenWiredAudioDisconnected()) {
- return;
- }
if (!Arrays.stream(removedDevices)
.anyMatch(AudioManagerAudioDeviceCallback::isWiredAudioHeadset)) {
return;
@@ -1236,15 +1232,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
if (Objects.equals(a2dpFallbackDevice, device)) {
Log.d(TAG, "Found an A2DP fallback device: " + device);
setA2dpActiveDevice(device);
- if (Flags.admAlwaysFallbackToAvailableDevice()) {
- setHfpActiveDevice(headsetFallbackDevice);
- } else {
- if (Objects.equals(headsetFallbackDevice, device)) {
- setHfpActiveDevice(device);
- } else {
- setHfpActiveDevice(null);
- }
- }
+ setHfpActiveDevice(headsetFallbackDevice);
/* If dual mode is enabled, LEA will be made active once all supported
classic audio profiles are made active for the device. */
if (!Utils.isDualModeAudioEnabled()) {
@@ -1275,15 +1263,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
if (Objects.equals(headsetFallbackDevice, device)) {
Log.d(TAG, "Found a HFP fallback device: " + device);
setHfpActiveDevice(device);
- if (Flags.admAlwaysFallbackToAvailableDevice()) {
- setA2dpActiveDevice(a2dpFallbackDevice);
- } else {
- if (Objects.equals(a2dpFallbackDevice, device)) {
- setA2dpActiveDevice(a2dpFallbackDevice);
- } else {
- setA2dpActiveDevice(null, true);
- }
- }
+ setA2dpActiveDevice(a2dpFallbackDevice);
if (!Utils.isDualModeAudioEnabled()) {
setLeAudioActiveDevice(null, true);
}
@@ -1367,32 +1347,6 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
}
/**
- * Checks CoD and metadata to determine if the device is a watch
- *
- * @param device the remote device
- * @return {@code true} if it's a watch, {@code false} otherwise
- */
- private boolean isWatch(BluetoothDevice device) {
- // Check CoD
- BluetoothClass deviceClass = new BluetoothClass(mAdapterService.getRemoteClass(device));
- if (deviceClass.getDeviceClass() == BluetoothClass.Device.WEARABLE_WRIST_WATCH) {
- return true;
- }
-
- // Check metadata
- byte[] deviceType = mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE);
- if (deviceType == null) {
- return false;
- }
- String deviceTypeStr = new String(deviceType);
- if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) {
- return true;
- }
-
- return false;
- }
-
- /**
* Checks if le audio broadcasting is ON
*
* @return {@code true} if is broadcasting audio, {@code false} otherwise
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
index 64b5a0dfae..244e132a66 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
@@ -316,6 +316,14 @@ public class AdapterNativeInterface {
return disconnectAllAclsNative();
}
+ boolean disconnectAllAcls(BluetoothDevice device) {
+ return disconnectAcl(device, BluetoothDevice.TRANSPORT_AUTO);
+ }
+
+ boolean disconnectAcl(BluetoothDevice device, int transport) {
+ return disconnectAclNative(Utils.getBytesFromAddress(device.getAddress()), transport);
+ }
+
boolean allowWakeByHid() {
return allowWakeByHidNative();
}
@@ -463,6 +471,8 @@ public class AdapterNativeInterface {
private native boolean disconnectAllAclsNative();
+ private native boolean disconnectAclNative(byte[] address, int transport);
+
private native boolean allowWakeByHidNative();
private native boolean restoreFilterAcceptListNative();
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 081c0b57c9..c7ce8669bc 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -59,6 +59,7 @@ import android.bluetooth.BluetoothAdapter.ActiveDeviceUse;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice.BluetoothAddress;
import android.bluetooth.BluetoothFrameworkInitializer;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
@@ -116,6 +117,7 @@ import android.sysprop.BluetoothProperties;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.bluetooth.BluetoothMetricsProto;
@@ -290,6 +292,11 @@ public class AdapterService extends Service {
private long mEnergyUsedTotalVoltAmpSecMicro;
private HashSet<String> mLeAudioAllowDevices = new HashSet<>();
+ /* List of pairs of gatt clients which controls AutoActiveMode on the device.*/
+ @VisibleForTesting
+ final List<Pair<Integer, BluetoothDevice>> mLeGattClientsControllingAutoActiveMode =
+ new ArrayList<>();
+
private BluetoothAdapter mAdapter;
@VisibleForTesting AdapterProperties mAdapterProperties;
private AdapterState mAdapterStateMachine;
@@ -4179,7 +4186,7 @@ public class AdapterService extends Service {
Set<Integer> eventCodesSet =
Arrays.stream(eventCodes).boxed().collect(Collectors.toSet());
if (eventCodesSet.stream()
- .anyMatch((n) -> (n < 0) || (n >= 0x50 && n < 0x60) || (n > 0xff))) {
+ .anyMatch((n) -> (n < 0) || (n >= 0x52 && n < 0x60) || (n > 0xff))) {
throw new IllegalArgumentException("invalid vendor-specific event code");
}
@@ -4410,6 +4417,18 @@ public class AdapterService extends Service {
service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
return service.isRfcommSocketOffloadSupported();
}
+
+ @Override
+ public IBinder getBluetoothAdvertise() {
+ AdapterService service = getService();
+ return service == null ? null : service.getBluetoothAdvertise();
+ }
+
+ @Override
+ public IBinder getDistanceMeasurement() {
+ AdapterService service = getService();
+ return service == null ? null : service.getDistanceMeasurement();
+ }
}
/**
@@ -5141,6 +5160,192 @@ public class AdapterService extends Service {
return getConnectionState(device) != BluetoothDevice.CONNECTION_STATE_DISCONNECTED;
}
+ private void addGattClientToControlAutoActiveMode(int clientIf, BluetoothDevice device) {
+ if (!Flags.allowGattConnectFromTheAppsWithoutMakingLeaudioDeviceActive()) {
+ Log.i(
+ TAG,
+ "flag: allowGattConnectFromTheAppsWithoutMakingLeaudioDeviceActive is not"
+ + " enabled");
+ return;
+ }
+
+ /* When GATT client is connecting to LeAudio device, stack should not assume that
+ * LeAudio device should be automatically connected to Audio Framework.
+ * e.g. given LeAudio device might be busy with audio streaming from another device.
+ * LeAudio shall be automatically connected to Audio Framework when
+ * 1. Remote device expects that - Targeted Announcements are used
+ * 2. User is connecting device from Settings application.
+ *
+ * Above conditions are tracked by LeAudioService. In here, there is need to notify
+ * LeAudioService that connection is made for GATT purposes, so LeAudioService can
+ * disable AutoActiveMode and make sure to not make device Active just after connection
+ * is created.
+ *
+ * Note: AutoActiveMode is by default set to true and it means that LeAudio device is ready
+ * to streaming just after connection is created. That implies that device will be connected
+ * to Audio Framework (is made Active) when connection is created.
+ */
+
+ int groupId = mLeAudioService.getGroupId(device);
+ if (groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+ /* If this is not a LeAudio device, there is nothing to do here. */
+ return;
+ }
+
+ if (mLeAudioService.getConnectionPolicy(device)
+ != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ Log.d(
+ TAG,
+ "addGattClientToControlAutoActiveMode: "
+ + device
+ + " LeAudio connection policy is not allowed");
+ return;
+ }
+
+ Log.i(
+ TAG,
+ "addGattClientToControlAutoActiveMode: clientIf: "
+ + clientIf
+ + ", "
+ + device
+ + ", groupId: "
+ + groupId);
+
+ synchronized (mLeGattClientsControllingAutoActiveMode) {
+ Pair newPair = new Pair<>(clientIf, device);
+ if (mLeGattClientsControllingAutoActiveMode.contains(newPair)) {
+ return;
+ }
+
+ for (Pair<Integer, BluetoothDevice> pair : mLeGattClientsControllingAutoActiveMode) {
+ if (pair.second.equals(device)
+ || groupId == mLeAudioService.getGroupId(pair.second)) {
+ Log.i(TAG, "addGattClientToControlAutoActiveMode: adding new client");
+ mLeGattClientsControllingAutoActiveMode.add(newPair);
+ return;
+ }
+ }
+
+ if (mLeAudioService.setAutoActiveModeState(mLeAudioService.getGroupId(device), false)) {
+ Log.i(
+ TAG,
+ "addGattClientToControlAutoActiveMode: adding new client and notifying"
+ + " leAudioService");
+ mLeGattClientsControllingAutoActiveMode.add(newPair);
+ }
+ }
+ }
+
+ /**
+ * When this is called, AdapterService is aware of user doing GATT connection over LE. Adapter
+ * service will use this information to manage internal GATT services if needed. For now,
+ * AdapterService is using this information to control Auto Active Mode for LeAudio devices.
+ *
+ * @param clientIf clientIf ClientIf which was doing GATT connection attempt
+ * @param device device Remote device to connect
+ */
+ public void notifyDirectLeGattClientConnect(int clientIf, BluetoothDevice device) {
+ if (mLeAudioService != null) {
+ addGattClientToControlAutoActiveMode(clientIf, device);
+ }
+ }
+
+ private void removeGattClientFromControlAutoActiveMode(int clientIf, BluetoothDevice device) {
+ if (mLeGattClientsControllingAutoActiveMode.isEmpty()) {
+ return;
+ }
+
+ int groupId = mLeAudioService.getGroupId(device);
+ if (groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
+ /* If this is not a LeAudio device, there is nothing to do here. */
+ return;
+ }
+
+ /* Remember if auto active mode is still disabled.
+ * If it is disabled, it means, that either User or remote device did not make an
+ * action to make LeAudio device Active.
+ * That means, AdapterService should disconnect ACL when all the clients are disconnected
+ * from the group to which the device belongs.
+ */
+ boolean isAutoActiveModeDisabled = !mLeAudioService.isAutoActiveModeEnabled(groupId);
+
+ synchronized (mLeGattClientsControllingAutoActiveMode) {
+ Log.d(
+ TAG,
+ "removeGattClientFromControlAutoActiveMode: removing clientIf:"
+ + clientIf
+ + ", "
+ + device
+ + ", groupId: "
+ + groupId);
+
+ mLeGattClientsControllingAutoActiveMode.remove(new Pair<>(clientIf, device));
+
+ if (!mLeGattClientsControllingAutoActiveMode.isEmpty()) {
+ for (Pair<Integer, BluetoothDevice> pair :
+ mLeGattClientsControllingAutoActiveMode) {
+ if (pair.second.equals(device)
+ || groupId == mLeAudioService.getGroupId(pair.second)) {
+ Log.d(
+ TAG,
+ "removeGattClientFromControlAutoActiveMode:"
+ + device
+ + " or groupId: "
+ + groupId
+ + " is still in use by clientif: "
+ + pair.first);
+ return;
+ }
+ }
+ }
+
+ /* Back auto active mode to default. */
+ mLeAudioService.setAutoActiveModeState(groupId, true);
+ }
+
+ int leConnectedState =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+
+ /* If auto active mode was disabled for the given group and is still connected
+ * make sure to disconnected all the devices from the group
+ */
+ if (isAutoActiveModeDisabled && ((getConnectionState(device) & leConnectedState) != 0)) {
+ for (BluetoothDevice dev : mLeAudioService.getGroupDevices(groupId)) {
+ /* Need to disconnect all the devices from the group as those might be connected
+ * as well especially those which migh keep the connection
+ */
+ if ((getConnectionState(dev) & leConnectedState) != 0) {
+ mNativeInterface.disconnectAcl(dev, BluetoothDevice.TRANSPORT_LE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify AdapterService about failed GATT connection attempt.
+ *
+ * @param clientIf ClientIf which was doing GATT connection attempt
+ * @param device Remote device to which connection attpemt failed
+ */
+ public void notifyGattClientConnectFailed(int clientIf, BluetoothDevice device) {
+ if (mLeAudioService != null) {
+ removeGattClientFromControlAutoActiveMode(clientIf, device);
+ }
+ }
+
+ /**
+ * Notify AdapterService about GATT connection being disconnecting or disconnected.
+ *
+ * @param clientIf ClientIf which is disconnecting or is already disconnected
+ * @param device Remote device which is disconnecting or is disconnected
+ */
+ public void notifyGattClientDisconnect(int clientIf, BluetoothDevice device) {
+ if (mLeAudioService != null) {
+ removeGattClientFromControlAutoActiveMode(clientIf, device);
+ }
+ }
+
public int getConnectionState(BluetoothDevice device) {
final String address = device.getAddress();
if (Flags.apiGetConnectionStateUsingIdentityAddress()) {
@@ -6147,6 +6352,16 @@ public class AdapterService extends Service {
}
}
+ @Nullable
+ IBinder getBluetoothAdvertise() {
+ return mGattService == null ? null : mGattService.getBluetoothAdvertise();
+ }
+
+ @Nullable
+ IBinder getDistanceMeasurement() {
+ return mGattService == null ? null : mGattService.getDistanceMeasurement();
+ }
+
@RequiresPermission(BLUETOOTH_CONNECT)
void unregAllGattClient(AttributionSource source) {
if (mGattService != null) {
@@ -6226,10 +6441,9 @@ public class AdapterService extends Service {
Log.w(TAG, "GATT Service is not running!");
return;
}
- if (Flags.scanManagerRefactor()) {
- mScanController.notifyProfileConnectionStateChange(profile, fromState, toState);
- } else {
- mGattService.notifyProfileConnectionStateChange(profile, fromState, toState);
+ ScanController controller = getBluetoothScanController();
+ if (controller != null) {
+ controller.notifyProfileConnectionStateChange(profile, fromState, toState);
}
}
@@ -6556,6 +6770,12 @@ public class AdapterService extends Service {
}
writer.println();
+ writer.println("LE Gatt clients controlling AutoActiveMode:");
+ for (Pair<Integer, BluetoothDevice> pair : mLeGattClientsControllingAutoActiveMode) {
+ writer.println(" clientIf:" + pair.first + " " + pair.second);
+ }
+ writer.println();
+
mAdapterStateMachine.dump(fd, writer, args);
StringBuilder sb = new StringBuilder();
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index 475da0dba1..47a5d9991f 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -274,6 +274,27 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
&& hap.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN;
}
+ private boolean shouldBlockBroadcastForHapDevice(BluetoothDevice device, ParcelUuid[] uuids) {
+ if (!Flags.leaudioDisableBroadcastForHapDevice()) {
+ Log.i(TAG, "disableBroadcastForHapDevice: Flag is disabled");
+ return false;
+ }
+
+ HapClientService hap = mFactory.getHapClientService();
+ if (hap == null) {
+ Log.e(TAG, "shouldBlockBroadcastForHapDevice: No HapClientService");
+ return false;
+ }
+
+ if (!SystemProperties.getBoolean(SYSPROP_HAP_ENABLED, true)) {
+ Log.i(TAG, "shouldBlockBroadcastForHapDevice: SystemProperty is overridden to false");
+ return false;
+ }
+
+ return Utils.arrayContains(uuids, BluetoothUuid.HAS)
+ && hap.getConnectionPolicy(device) == CONNECTION_POLICY_ALLOWED;
+ }
+
// Policy implementation, all functions MUST be private
private void processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids) {
String log = "processInitProfilePriorities(" + device + "): ";
@@ -518,7 +539,7 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
if ((bcService != null)
&& Utils.arrayContains(uuids, BluetoothUuid.BASS)
&& (bcService.getConnectionPolicy(device) == CONNECTION_POLICY_UNKNOWN)) {
- if (isLeAudioProfileAllowed) {
+ if (isLeAudioProfileAllowed && !shouldBlockBroadcastForHapDevice(device, uuids)) {
Log.d(TAG, log + "Setting BASS priority");
if (mAutoConnectProfilesSupported) {
bcService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
@@ -531,7 +552,7 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
CONNECTION_POLICY_ALLOWED);
}
} else {
- Log.d(TAG, log + "LE_AUDIO is not allowed: Clear BASS priority");
+ Log.d(TAG, log + "LE_AUDIO Broadcast is not allowed: Clear BASS priority");
mAdapterService
.getDatabase()
.setProfileConnectionPolicy(
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index f0bad17e64..f297d905ac 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -345,7 +345,8 @@ public class RemoteDevices {
private String mModelName;
@VisibleForTesting int mBondState;
@VisibleForTesting int mDeviceType;
- @VisibleForTesting ParcelUuid[] mUuids;
+ @VisibleForTesting ParcelUuid[] mUuidsBrEdr;
+ @VisibleForTesting ParcelUuid[] mUuidsLe;
private BluetoothSinkAudioPolicy mAudioPolicy;
DeviceProperties() {
@@ -506,20 +507,71 @@ public class RemoteDevices {
}
/**
- * @return the mUuids
+ * @return the UUIDs on LE and Classic transport
*/
ParcelUuid[] getUuids() {
synchronized (mObject) {
- return mUuids;
+ /* When we bond dual mode device, and discover LE and Classic services, stack would
+ * return LE and Classic UUIDs separately, but Java apps expect them merged.
+ */
+ int combinedUuidsLength =
+ (mUuidsBrEdr != null ? mUuidsBrEdr.length : 0)
+ + (mUuidsLe != null ? mUuidsLe.length : 0);
+ if (!Flags.separateServiceStorage() || combinedUuidsLength == 0) {
+ return mUuidsBrEdr;
+ }
+
+ java.util.LinkedHashSet<ParcelUuid> result =
+ new java.util.LinkedHashSet<ParcelUuid>();
+ if (mUuidsBrEdr != null) {
+ for (ParcelUuid uuid : mUuidsBrEdr) {
+ result.add(uuid);
+ }
+ }
+
+ if (mUuidsLe != null) {
+ for (ParcelUuid uuid : mUuidsLe) {
+ result.add(uuid);
+ }
+ }
+
+ return result.toArray(new ParcelUuid[combinedUuidsLength]);
+ }
+ }
+
+ /**
+ * @return just classic transport UUIDS
+ */
+ ParcelUuid[] getUuidsBrEdr() {
+ synchronized (mObject) {
+ return mUuidsBrEdr;
+ }
+ }
+
+ /**
+ * @param uuids the mUuidsBrEdr to set
+ */
+ void setUuidsBrEdr(ParcelUuid[] uuids) {
+ synchronized (mObject) {
+ this.mUuidsBrEdr = uuids;
+ }
+ }
+
+ /**
+ * @return the mUuidsLe
+ */
+ ParcelUuid[] getUuidsLe() {
+ synchronized (mObject) {
+ return mUuidsLe;
}
}
/**
- * @param uuids the mUuids to set
+ * @param uuids the mUuidsLe to set
*/
- void setUuids(ParcelUuid[] uuids) {
+ void setUuidsLe(ParcelUuid[] uuids) {
synchronized (mObject) {
- this.mUuids = uuids;
+ this.mUuidsLe = uuids;
}
}
@@ -636,7 +688,8 @@ public class RemoteDevices {
cachedBluetoothDevice issued a connect using the local cached copy of uuids,
without waiting for the ACTION_UUID intent.
This was resulting in multiple calls to connect().*/
- mUuids = null;
+ mUuidsBrEdr = null;
+ mUuidsLe = null;
mAlias = null;
}
}
@@ -988,147 +1041,168 @@ public class RemoteDevices {
return;
}
+ boolean uuids_updated = false;
+
for (int j = 0; j < types.length; j++) {
type = types[j];
val = values[j];
- if (val.length > 0) {
- synchronized (mObject) {
- debugLog("Update property, device=" + bdDevice + ", type: " + type);
- switch (type) {
- case AbstractionLayer.BT_PROPERTY_BDNAME:
- final String newName = new String(val);
- if (newName.equals(deviceProperties.getName())) {
- debugLog("Skip name update for " + bdDevice);
- break;
- }
- deviceProperties.setName(newName);
- List<String> wordBreakdownList =
- MetricsLogger.getInstance().getWordBreakdownList(newName);
- if (SdkLevel.isAtLeastU()) {
- MetricsLogger.getInstance()
- .uploadRestrictedBluetothDeviceName(wordBreakdownList);
- }
- intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
- intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mAdapterService.sendBroadcast(
- intent,
- BLUETOOTH_CONNECT,
- Utils.getTempBroadcastOptions().toBundle());
- debugLog("Remote device name is: " + deviceProperties.getName());
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
- deviceProperties.setAlias(bdDevice, new String(val));
- debugLog("Remote device alias is: " + deviceProperties.getAlias());
- break;
- case AbstractionLayer.BT_PROPERTY_BDADDR:
- deviceProperties.setAddress(val);
- debugLog(
- "Remote Address is:"
- + Utils.getRedactedAddressStringFromByte(val));
+ if (val.length == 0) {
+ continue;
+ }
+
+ synchronized (mObject) {
+ debugLog("Update property, device=" + bdDevice + ", type: " + type);
+ switch (type) {
+ case AbstractionLayer.BT_PROPERTY_BDNAME:
+ final String newName = new String(val);
+ if (newName.equals(deviceProperties.getName())) {
+ debugLog("Skip name update for " + bdDevice);
break;
- case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
- final int newBluetoothClass = Utils.byteArrayToInt(val);
- if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
- debugLog(
- "Skip class update, device="
- + bdDevice
- + ", cod=0x"
- + Integer.toHexString(newBluetoothClass));
- break;
- }
- deviceProperties.setBluetoothClass(newBluetoothClass);
- intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
- intent.putExtra(
- BluetoothDevice.EXTRA_CLASS,
- new BluetoothClass(deviceProperties.getBluetoothClass()));
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mAdapterService.sendBroadcast(
- intent,
- BLUETOOTH_CONNECT,
- Utils.getTempBroadcastOptions().toBundle());
+ }
+ deviceProperties.setName(newName);
+ List<String> wordBreakdownList =
+ MetricsLogger.getInstance().getWordBreakdownList(newName);
+ if (SdkLevel.isAtLeastU()) {
+ MetricsLogger.getInstance()
+ .uploadRestrictedBluetothDeviceName(wordBreakdownList);
+ }
+ intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
+ intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mAdapterService.sendBroadcast(
+ intent,
+ BLUETOOTH_CONNECT,
+ Utils.getTempBroadcastOptions().toBundle());
+ debugLog("Remote device name is: " + deviceProperties.getName());
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
+ deviceProperties.setAlias(bdDevice, new String(val));
+ debugLog("Remote device alias is: " + deviceProperties.getAlias());
+ break;
+ case AbstractionLayer.BT_PROPERTY_BDADDR:
+ deviceProperties.setAddress(val);
+ debugLog(
+ "Remote Address is:" + Utils.getRedactedAddressStringFromByte(val));
+ break;
+ case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
+ final int newBluetoothClass = Utils.byteArrayToInt(val);
+ if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
debugLog(
- "Remote class update, device="
+ "Skip class update, device="
+ bdDevice
+ ", cod=0x"
+ Integer.toHexString(newBluetoothClass));
break;
- case AbstractionLayer.BT_PROPERTY_UUIDS:
+ }
+ deviceProperties.setBluetoothClass(newBluetoothClass);
+ intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
+ intent.putExtra(
+ BluetoothDevice.EXTRA_CLASS,
+ new BluetoothClass(deviceProperties.getBluetoothClass()));
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mAdapterService.sendBroadcast(
+ intent,
+ BLUETOOTH_CONNECT,
+ Utils.getTempBroadcastOptions().toBundle());
+ debugLog(
+ "Remote class update, device="
+ + bdDevice
+ + ", cod=0x"
+ + Integer.toHexString(newBluetoothClass));
+ break;
+ case AbstractionLayer.BT_PROPERTY_UUIDS:
+ case AbstractionLayer.BT_PROPERTY_UUIDS_LE:
+ if (type == AbstractionLayer.BT_PROPERTY_UUIDS) {
final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
- if (areUuidsEqual(newUuids, deviceProperties.getUuids())) {
+ if (areUuidsEqual(newUuids, deviceProperties.getUuidsBrEdr())) {
// SDP Skip adding UUIDs to property cache if equal
debugLog("Skip uuids update for " + bdDevice.getAddress());
MetricsLogger.getInstance()
.cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
break;
}
- deviceProperties.setUuids(newUuids);
- if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
- // SDP Adding UUIDs to property cache and sending intent
- MetricsLogger.getInstance()
- .cacheCount(
- BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
- mAdapterService.deviceUuidUpdated(bdDevice);
- sendUuidIntent(bdDevice, deviceProperties, true);
- } else if (mAdapterService.getState()
- == BluetoothAdapter.STATE_BLE_ON) {
- // SDP Adding UUIDs to property cache but with no intent
- MetricsLogger.getInstance()
- .cacheCount(
- BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
- mAdapterService.deviceUuidUpdated(bdDevice);
- } else {
- // SDP Silently dropping UUIDs and with no intent
+ deviceProperties.setUuidsBrEdr(newUuids);
+ } else if (type == AbstractionLayer.BT_PROPERTY_UUIDS_LE) {
+ final ParcelUuid[] newUuidsLe = Utils.byteArrayToUuid(val);
+ if (areUuidsEqual(newUuidsLe, deviceProperties.getUuidsLe())) {
+ // SDP Skip adding UUIDs to property cache if equal
+ debugLog("Skip LE uuids update for " + bdDevice.getAddress());
MetricsLogger.getInstance()
- .cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1);
- }
- break;
- case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
- if (deviceProperties.isConsolidated()) {
+ .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
break;
}
- // The device type from hal layer, defined in bluetooth.h,
- // matches the type defined in BluetoothDevice.java
- deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
- // RSSI from hal is in one byte
- deviceProperties.setRssi(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
- deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
- deviceProperties.setAshaCapability(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
- deviceProperties.setAshaTruncatedHiSyncId(val[0]);
- break;
- case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
- final String modelName = new String(val);
- debugLog("Remote device model name: " + modelName);
- deviceProperties.setModelName(modelName);
- BluetoothStatsLog.write(
- BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
- mAdapterService.obfuscateAddress(bdDevice),
- BluetoothProtoEnums.DEVICE_INFO_INTERNAL,
- LOG_SOURCE_DIS,
- null,
- modelName,
- null,
- null,
- mAdapterService.getMetricId(bdDevice),
- bdDevice.getAddressType(),
- 0,
- 0,
- 0);
+ deviceProperties.setUuidsLe(newUuidsLe);
+ }
+ uuids_updated = true;
+ break;
+ case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
+ if (deviceProperties.isConsolidated()) {
break;
- }
+ }
+ // The device type from hal layer, defined in bluetooth.h,
+ // matches the type defined in BluetoothDevice.java
+ deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
+ // RSSI from hal is in one byte
+ deviceProperties.setRssi(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
+ deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
+ deviceProperties.setAshaCapability(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
+ deviceProperties.setAshaTruncatedHiSyncId(val[0]);
+ break;
+ case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
+ final String modelName = new String(val);
+ debugLog("Remote device model name: " + modelName);
+ deviceProperties.setModelName(modelName);
+ BluetoothStatsLog.write(
+ BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
+ mAdapterService.obfuscateAddress(bdDevice),
+ BluetoothProtoEnums.DEVICE_INFO_INTERNAL,
+ LOG_SOURCE_DIS,
+ null,
+ modelName,
+ null,
+ null,
+ mAdapterService.getMetricId(bdDevice),
+ bdDevice.getAddressType(),
+ 0,
+ 0,
+ 0);
+ break;
}
}
}
+
+ if (!uuids_updated) {
+ return;
+ }
+
+ /* uuids_updated == true
+ * We might have received LE and BREDR UUIDS separately, ensure that UUID intent is sent
+ * just once */
+
+ if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
+ // SDP Adding UUIDs to property cache and sending intent
+ MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
+ mAdapterService.deviceUuidUpdated(bdDevice);
+ sendUuidIntent(bdDevice, deviceProperties, true);
+ } else if (mAdapterService.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ // SDP Adding UUIDs to property cache but with no intent
+ MetricsLogger.getInstance()
+ .cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
+ mAdapterService.deviceUuidUpdated(bdDevice);
+ } else {
+ // SDP Silently dropping UUIDs and with no intent
+ MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1);
+ }
}
void deviceFoundCallback(byte[] address) {
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt
new file mode 100644
index 0000000000..66415cd82f
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt
+
+import android.Manifest.permission.BLUETOOTH_ADVERTISE
+import android.Manifest.permission.BLUETOOTH_PRIVILEGED
+import android.annotation.RequiresPermission
+import android.bluetooth.IBluetoothAdvertise
+import android.bluetooth.le.AdvertiseData
+import android.bluetooth.le.AdvertisingSetParameters
+import android.bluetooth.le.IAdvertisingSetCallback
+import android.bluetooth.le.PeriodicAdvertisingParameters
+import android.content.AttributionSource
+import android.content.Context
+import com.android.bluetooth.Utils
+
+class AdvertiseBinder(
+ private val mContext: Context,
+ private val mAdvertiseManager: AdvertiseManager,
+) : IBluetoothAdvertise.Stub() {
+ @Volatile private var mIsAvailable = true
+
+ fun cleanup() {
+ mIsAvailable = false
+ }
+
+ @RequiresPermission(BLUETOOTH_ADVERTISE)
+ private fun getManager(source: AttributionSource): AdvertiseManager? {
+ if (!Utils.checkAdvertisePermissionForDataDelivery(mContext, source, "AdvertiseManager")) {
+ return null
+ }
+ return if (mIsAvailable) mAdvertiseManager else null
+ }
+
+ override fun startAdvertisingSet(
+ parameters: AdvertisingSetParameters,
+ advertiseData: AdvertiseData?,
+ scanResponse: AdvertiseData?,
+ periodicParameters: PeriodicAdvertisingParameters?,
+ periodicData: AdvertiseData?,
+ duration: Int,
+ maxExtAdvEvents: Int,
+ serverIf: Int,
+ callback: IAdvertisingSetCallback,
+ source: AttributionSource,
+ ) {
+ if (
+ parameters.ownAddressType != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT ||
+ serverIf != 0 ||
+ parameters.isDirected
+ ) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
+ }
+
+ getManager(source)?.let {
+ it.doOnAdvertiseThread {
+ it.startAdvertisingSet(
+ parameters,
+ advertiseData,
+ scanResponse,
+ periodicParameters,
+ periodicData,
+ duration,
+ maxExtAdvEvents,
+ serverIf,
+ callback,
+ source,
+ )
+ }
+ }
+ }
+
+ override fun stopAdvertisingSet(callback: IAdvertisingSetCallback, source: AttributionSource) {
+ getManager(source)?.let { it.doOnAdvertiseThread { it.stopAdvertisingSet(callback) } }
+ }
+
+ override fun getOwnAddress(advertiserId: Int, source: AttributionSource) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
+ getManager(source)?.let { it.doOnAdvertiseThread { it.getOwnAddress(advertiserId) } }
+ }
+
+ override fun enableAdvertisingSet(
+ advertiserId: Int,
+ enable: Boolean,
+ duration: Int,
+ maxExtAdvEvents: Int,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread {
+ it.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents)
+ }
+ }
+ }
+
+ override fun setAdvertisingData(
+ advertiserId: Int,
+ data: AdvertiseData?,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setAdvertisingData(advertiserId, data) }
+ }
+ }
+
+ override fun setScanResponseData(
+ advertiserId: Int,
+ data: AdvertiseData?,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setScanResponseData(advertiserId, data) }
+ }
+ }
+
+ override fun setAdvertisingParameters(
+ advertiserId: Int,
+ parameters: AdvertisingSetParameters,
+ source: AttributionSource,
+ ) {
+ if (
+ parameters.ownAddressType != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT ||
+ parameters.isDirected
+ ) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null)
+ }
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setAdvertisingParameters(advertiserId, parameters) }
+ }
+ }
+
+ override fun setPeriodicAdvertisingParameters(
+ advertiserId: Int,
+ parameters: PeriodicAdvertisingParameters?,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingParameters(advertiserId, parameters) }
+ }
+ }
+
+ override fun setPeriodicAdvertisingData(
+ advertiserId: Int,
+ data: AdvertiseData?,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingData(advertiserId, data) }
+ }
+ }
+
+ override fun setPeriodicAdvertisingEnable(
+ advertiserId: Int,
+ enable: Boolean,
+ source: AttributionSource,
+ ) {
+ getManager(source)?.let {
+ it.doOnAdvertiseThread { it.setPeriodicAdvertisingEnable(advertiserId, enable) }
+ }
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index f3ccb8a9c1..6ed719cedc 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -24,44 +24,52 @@ import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.content.AttributionSource;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
-import com.android.internal.annotations.GuardedBy;
+import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
-/**
- * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests.
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+/** Manages Bluetooth LE advertising operations. */
public class AdvertiseManager {
private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager";
- private final GattService mService;
+ private static final long RUN_SYNC_WAIT_TIME_MS = 2000L;
+
+ private final AdapterService mService;
private final AdvertiseManagerNativeInterface mNativeInterface;
+ private final AdvertiseBinder mAdvertiseBinder;
private final AdvertiserMap mAdvertiserMap;
- @GuardedBy("itself")
private final Map<IBinder, AdvertiserInfo> mAdvertisers = new HashMap<>();
- private Handler mHandler;
- static int sTempRegistrationId = -1;
+ private final Handler mHandler;
+ private volatile boolean mIsAvailable = true;
+ @VisibleForTesting int mTempRegistrationId = -1;
- AdvertiseManager(GattService service) {
- this(service, AdvertiseManagerNativeInterface.getInstance(), new AdvertiserMap());
+ AdvertiseManager(AdapterService service, Looper advertiseLooper) {
+ this(
+ service,
+ advertiseLooper,
+ AdvertiseManagerNativeInterface.getInstance(),
+ new AdvertiserMap());
}
@VisibleForTesting
AdvertiseManager(
- GattService service,
+ AdapterService service,
+ Looper advertiseLooper,
AdvertiseManagerNativeInterface nativeInterface,
AdvertiserMap advertiserMap) {
Log.d(TAG, "advertise manager created");
@@ -69,40 +77,30 @@ public class AdvertiseManager {
mNativeInterface = nativeInterface;
mAdvertiserMap = advertiserMap;
- // Start a HandlerThread that handles advertising operations
mNativeInterface.init(this);
- HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
- thread.start();
- mHandler = new Handler(thread.getLooper());
- }
-
- // TODO(b/327849650): We shouldn't need this, it should be safe to do in the cleanup method. But
- // it would be a logic change.
- void clear() {
- mAdvertiserMap.clear();
+ mHandler = new Handler(advertiseLooper);
+ mAdvertiseBinder = new AdvertiseBinder(service, this);
}
void cleanup() {
Log.d(TAG, "cleanup()");
- mNativeInterface.cleanup();
- synchronized (mAdvertisers) {
- mAdvertisers.clear();
- }
- sTempRegistrationId = -1;
-
- if (mHandler != null) {
- // Shut down the thread
- mHandler.removeCallbacksAndMessages(null);
- Looper looper = mHandler.getLooper();
- if (looper != null) {
- looper.quit();
- }
- mHandler = null;
- }
+ mIsAvailable = false;
+ mHandler.removeCallbacksAndMessages(null);
+ forceRunSyncOnAdvertiseThread(
+ () -> {
+ mAdvertiserMap.clear();
+ mAdvertiseBinder.cleanup();
+ mNativeInterface.cleanup();
+ mAdvertisers.clear();
+ });
}
void dump(StringBuilder sb) {
- mAdvertiserMap.dump(sb);
+ forceRunSyncOnAdvertiseThread(() -> mAdvertiserMap.dump(sb));
+ }
+
+ AdvertiseBinder getBinder() {
+ return mAdvertiseBinder;
}
static class AdvertiserInfo {
@@ -122,8 +120,12 @@ public class AdvertiseManager {
}
}
+ private interface CallbackWrapper {
+ void call() throws RemoteException;
+ }
+
IBinder toBinder(IAdvertisingSetCallback e) {
- return ((IInterface) e).asBinder();
+ return e.asBinder();
}
class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient {
@@ -138,25 +140,22 @@ public class AdvertiseManager {
@Override
public void binderDied() {
Log.d(TAG, "Binder is dead - unregistering advertising set (" + mPackageName + ")!");
- stopAdvertisingSet(callback);
+ doOnAdvertiseThread(() -> stopAdvertisingSet(callback));
}
}
- Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) {
+ private Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) {
Map.Entry<IBinder, AdvertiserInfo> entry = null;
- synchronized (mAdvertisers) {
- for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) {
- if (e.getValue().id == advertiserId) {
- entry = e;
- break;
- }
+ for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) {
+ if (e.getValue().id == advertiserId) {
+ entry = e;
+ break;
}
}
return entry;
}
- void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
- throws Exception {
+ void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) {
Log.d(
TAG,
"onAdvertisingSetStarted() - regId="
@@ -165,6 +164,7 @@ public class AdvertiseManager {
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(regId);
@@ -184,27 +184,24 @@ public class AdvertiseManager {
} else {
IBinder binder = entry.getKey();
binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
- synchronized (mAdvertisers) {
- mAdvertisers.remove(binder);
- }
+ mAdvertisers.remove(binder);
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId);
if (stats != null) {
- int instanceCount;
- synchronized (mAdvertisers) {
- instanceCount = mAdvertisers.size();
- }
- stats.recordAdvertiseStop(instanceCount);
+ stats.recordAdvertiseStop(mAdvertisers.size());
stats.recordAdvertiseErrorCount(status);
}
mAdvertiserMap.removeAppAdvertiseStats(regId);
}
- IBinder gattBinder = mService.getBinder();
- callback.onAdvertisingSetStarted(gattBinder, advertiserId, txPower, status);
+ sendToCallback(
+ advertiserId,
+ () ->
+ callback.onAdvertisingSetStarted(
+ mAdvertiseBinder, advertiserId, txPower, status));
}
- void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
+ void onAdvertisingEnabled(int advertiserId, boolean enable, int status) {
Log.d(
TAG,
"onAdvertisingSetEnabled() - advertiserId="
@@ -213,6 +210,7 @@ public class AdvertiseManager {
+ enable
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -224,16 +222,13 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingEnabled(advertiserId, enable, status);
+ sendToCallback(
+ advertiserId, () -> callback.onAdvertisingEnabled(advertiserId, enable, status));
if (!enable && status != 0) {
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
if (stats != null) {
- int instanceCount;
- synchronized (mAdvertisers) {
- instanceCount = mAdvertisers.size();
- }
- stats.recordAdvertiseStop(instanceCount);
+ stats.recordAdvertiseStop(mAdvertisers.size());
}
}
}
@@ -249,15 +244,18 @@ public class AdvertiseManager {
int serverIf,
IAdvertisingSetCallback callback,
AttributionSource attrSource) {
+ checkThread();
// If we are using an isolated server, force usage of an NRPA
if (serverIf != 0
&& parameters.getOwnAddressType()
!= AdvertisingSetParameters.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE) {
Log.w(TAG, "Cannot advertise an isolated GATT server using a resolvable address");
try {
- IBinder gattBinder = mService.getBinder();
callback.onAdvertisingSetStarted(
- gattBinder, 0x00, 0x00, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
+ mAdvertiseBinder,
+ 0x00,
+ 0x00,
+ AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
} catch (RemoteException exception) {
Log.e(TAG, "Failed to callback:" + Log.getStackTraceString(exception));
}
@@ -281,7 +279,7 @@ public class AdvertiseManager {
throw new IllegalArgumentException("Can't link to advertiser's death");
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
byte[] advDataBytes = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName);
byte[] scanResponseBytes =
@@ -289,10 +287,8 @@ public class AdvertiseManager {
byte[] periodicDataBytes =
AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName);
- int cbId = --sTempRegistrationId;
- synchronized (mAdvertisers) {
- mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
- }
+ int cbId = --mTempRegistrationId;
+ mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
@@ -321,18 +317,20 @@ public class AdvertiseManager {
} catch (IllegalArgumentException e) {
try {
binder.unlinkToDeath(deathRecipient, 0);
- IBinder gattBinder = mService.getBinder();
callback.onAdvertisingSetStarted(
- gattBinder, 0x00, 0x00, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
+ mAdvertiseBinder,
+ 0x00,
+ 0x00,
+ AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
} catch (RemoteException exception) {
Log.e(TAG, "Failed to callback:" + Log.getStackTraceString(exception));
}
}
}
- void onOwnAddressRead(int advertiserId, int addressType, String address)
- throws RemoteException {
+ void onOwnAddressRead(int advertiserId, int addressType, String address) {
Log.d(TAG, "onOwnAddressRead() advertiserId=" + advertiserId);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -341,10 +339,12 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onOwnAddressRead(advertiserId, addressType, address);
+ sendToCallback(
+ advertiserId, () -> callback.onOwnAddressRead(advertiserId, addressType, address));
}
void getOwnAddress(int advertiserId) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "getOwnAddress() - bad advertiserId " + advertiserId);
@@ -354,13 +354,11 @@ public class AdvertiseManager {
}
void stopAdvertisingSet(IAdvertisingSetCallback callback) {
+ checkThread();
IBinder binder = toBinder(callback);
Log.d(TAG, "stopAdvertisingSet() " + binder);
- AdvertiserInfo adv;
- synchronized (mAdvertisers) {
- adv = mAdvertisers.remove(binder);
- }
+ AdvertiserInfo adv = mAdvertisers.remove(binder);
if (adv == null) {
Log.e(TAG, "stopAdvertisingSet() - no client found for callback");
return;
@@ -387,6 +385,7 @@ public class AdvertiseManager {
}
void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "enableAdvertisingSet() - bad advertiserId " + advertiserId);
@@ -398,12 +397,13 @@ public class AdvertiseManager {
}
void setAdvertisingData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setAdvertisingData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setAdvertisingData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -420,12 +420,13 @@ public class AdvertiseManager {
}
void setScanResponseData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setScanResponseData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setScanResponseData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -442,6 +443,7 @@ public class AdvertiseManager {
}
void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setAdvertisingParameters() - bad advertiserId " + advertiserId);
@@ -454,6 +456,7 @@ public class AdvertiseManager {
void setPeriodicAdvertisingParameters(
int advertiserId, PeriodicAdvertisingParameters parameters) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingParameters() - bad advertiserId " + advertiserId);
@@ -465,12 +468,13 @@ public class AdvertiseManager {
}
void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingData() - bad advertiserId " + advertiserId);
return;
}
- String deviceName = AdapterService.getAdapterService().getName();
+ String deviceName = mService.getName();
try {
mNativeInterface.setPeriodicAdvertisingData(
advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
@@ -487,6 +491,7 @@ public class AdvertiseManager {
}
void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
Log.w(TAG, "setPeriodicAdvertisingEnable() - bad advertiserId " + advertiserId);
@@ -495,7 +500,8 @@ public class AdvertiseManager {
mNativeInterface.setPeriodicAdvertisingEnable(advertiserId, enable);
}
- void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ void onAdvertisingDataSet(int advertiserId, int status) {
+ checkThread();
Log.d(TAG, "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status);
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
@@ -505,10 +511,11 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingDataSet(advertiserId, status);
+ sendToCallback(advertiserId, () -> callback.onAdvertisingDataSet(advertiserId, status));
}
- void onScanResponseDataSet(int advertiserId, int status) throws Exception {
+ void onScanResponseDataSet(int advertiserId, int status) {
+ checkThread();
Log.d(TAG, "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status);
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
@@ -518,11 +525,10 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onScanResponseDataSet(advertiserId, status);
+ sendToCallback(advertiserId, () -> callback.onScanResponseDataSet(advertiserId, status));
}
- void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
- throws Exception {
+ void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
Log.d(
TAG,
"onAdvertisingParametersUpdated() advertiserId="
@@ -531,6 +537,7 @@ public class AdvertiseManager {
+ txPower
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -539,16 +546,19 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onAdvertisingParametersUpdated(advertiserId, txPower, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onAdvertisingParametersUpdated(advertiserId, txPower, status));
}
- void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
+ void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingParametersUpdated() advertiserId="
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -559,16 +569,19 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status));
}
- void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingDataSet() advertiserId="
+ advertiserId
+ ", status="
+ status);
+ checkThread();
Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
if (entry == null) {
@@ -577,11 +590,11 @@ public class AdvertiseManager {
}
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingDataSet(advertiserId, status);
+ sendToCallback(
+ advertiserId, () -> callback.onPeriodicAdvertisingDataSet(advertiserId, status));
}
- void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
- throws Exception {
+ void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
Log.d(
TAG,
"onPeriodicAdvertisingEnabled() advertiserId="
@@ -594,13 +607,65 @@ public class AdvertiseManager {
Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId);
return;
}
+ checkThread();
IAdvertisingSetCallback callback = entry.getValue().callback;
- callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+ sendToCallback(
+ advertiserId,
+ () -> callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status));
AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
if (stats != null) {
stats.onPeriodicAdvertiseEnabled(enable);
}
}
+
+ void doOnAdvertiseThread(Runnable r) {
+ if (mIsAvailable) {
+ if (Flags.advertiseThread()) {
+ mHandler.post(
+ () -> {
+ if (mIsAvailable) {
+ r.run();
+ }
+ });
+ } else {
+ r.run();
+ }
+ }
+ }
+
+ private void forceRunSyncOnAdvertiseThread(Runnable r) {
+ if (!Flags.advertiseThread()) {
+ r.run();
+ return;
+ }
+ final CompletableFuture<Void> future = new CompletableFuture<>();
+ mHandler.postAtFrontOfQueue(
+ () -> {
+ r.run();
+ future.complete(null);
+ });
+ try {
+ future.get(RUN_SYNC_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ Log.w(TAG, "Unable to complete sync task: " + e);
+ }
+ }
+
+ private void checkThread() {
+ if (Flags.advertiseThread()
+ && !mHandler.getLooper().isCurrentThread()
+ && !Utils.isInstrumentationTestMode()) {
+ throw new IllegalStateException("Not on advertise thread");
+ }
+ }
+
+ private void sendToCallback(int advertiserId, CallbackWrapper wrapper) {
+ try {
+ wrapper.call();
+ } catch (RemoteException e) {
+ Log.i(TAG, "RemoteException in callback for advertiserId: " + advertiserId);
+ }
+ }
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
index 215c709970..6cdd47f97e 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
@@ -19,11 +19,12 @@ package com.android.bluetooth.gatt;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.PeriodicAdvertisingParameters;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
/** Native interface for AdvertiseManager */
-@VisibleForTesting
+@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public class AdvertiseManagerNativeInterface {
private static final String TAG = AdvertiseManagerNativeInterface.class.getSimpleName();
@@ -121,43 +122,47 @@ public class AdvertiseManagerNativeInterface {
setPeriodicAdvertisingEnableNative(advertiserId, enable);
}
- void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
- throws Exception {
- mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status);
+ void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status));
}
- void onOwnAddressRead(int advertiserId, int addressType, String address) throws Exception {
- mManager.onOwnAddressRead(advertiserId, addressType, address);
+ void onOwnAddressRead(int advertiserId, int addressType, String address) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onOwnAddressRead(advertiserId, addressType, address));
}
- void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
- mManager.onAdvertisingEnabled(advertiserId, enable, status);
+ void onAdvertisingEnabled(int advertiserId, boolean enable, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingEnabled(advertiserId, enable, status));
}
- void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
- mManager.onAdvertisingDataSet(advertiserId, status);
+ void onAdvertisingDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(() -> mManager.onAdvertisingDataSet(advertiserId, status));
}
- void onScanResponseDataSet(int advertiserId, int status) throws Exception {
- mManager.onScanResponseDataSet(advertiserId, status);
+ void onScanResponseDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(() -> mManager.onScanResponseDataSet(advertiserId, status));
}
- void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
- throws Exception {
- mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status);
+ void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status));
}
- void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
- mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
+ void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status));
}
- void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
- mManager.onPeriodicAdvertisingDataSet(advertiserId, status);
+ void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingDataSet(advertiserId, status));
}
- void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
- throws Exception {
- mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+ void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) {
+ mManager.doOnAdvertiseThread(
+ () -> mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status));
}
private native void initializeNative();
diff --git a/android/app/src/com/android/bluetooth/gatt/ContextMap.java b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
index e43927a4b0..8ba3e3b671 100644
--- a/android/app/src/com/android/bluetooth/gatt/ContextMap.java
+++ b/android/app/src/com/android/bluetooth/gatt/ContextMap.java
@@ -27,9 +27,11 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.GuardedBy;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -49,6 +51,9 @@ import java.util.function.Predicate;
*/
public class ContextMap<C> {
private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap";
+ private static final DateTimeFormatter sDateFormat =
+ DateTimeFormatter.ofPattern("MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
+ private static final int MAX_LAST_RECORDS = 5;
/** Connection class helps map connection IDs to device addresses. */
public static class Connection {
@@ -142,12 +147,47 @@ public class ContextMap<C> {
}
}
+ private class AppRecord {
+ public final UUID uuid;
+ public final String appName;
+ @Nullable public final String attributionTag;
+ public final Instant registerTime;
+
+ public int clientIf;
+ public RemoveReason reason;
+ @Nullable public Instant unregisterTime;
+
+ AppRecord(App app) {
+ uuid = app.uuid;
+ appName = app.name;
+ attributionTag = app.attributionTag;
+
+ registerTime = Instant.now();
+ }
+ }
+
+ public enum RemoveReason {
+ REASON_UNREGISTER_ALL,
+ REASON_UNREGISTER_CLIENT,
+ REASON_UNREGISTER_SERVER,
+ REASON_BINDER_DIED,
+ REASON_REGISTER_FAILED,
+
+ REASON_UNKNOWN
+ }
+
/** Our internal application list */
private final Object mAppsLock = new Object();
@GuardedBy("mAppsLock")
private List<App> mApps = new ArrayList<>();
+ @GuardedBy("mAppsLock")
+ private final List<AppRecord> mOngoingRecords = new ArrayList<>();
+
+ @GuardedBy("mAppsLock")
+ private final List<AppRecord> mLastRecords = new ArrayList<>();
+
/** Internal list of connected devices */
private List<Connection> mConnections = new ArrayList<>();
@@ -164,12 +204,14 @@ public class ContextMap<C> {
synchronized (mAppsLock) {
App app = new App(uuid, callback, appUid, appName, attrSource);
mApps.add(app);
+ recordRegisterApp(app);
+
return app;
}
}
/** Remove the context for a given UUID */
- public void remove(UUID uuid) {
+ public void remove(UUID uuid, RemoveReason reason) {
synchronized (mAppsLock) {
Iterator<App> i = mApps.iterator();
while (i.hasNext()) {
@@ -177,6 +219,7 @@ public class ContextMap<C> {
if (entry.uuid.equals(uuid)) {
entry.unlinkToDeath();
i.remove();
+ recordUnregisterApp(entry, reason);
break;
}
}
@@ -184,7 +227,7 @@ public class ContextMap<C> {
}
/** Remove the context for a given application ID. */
- public void remove(int id) {
+ public void remove(int id, RemoveReason reason) {
boolean find = false;
synchronized (mAppsLock) {
Iterator<App> i = mApps.iterator();
@@ -194,6 +237,7 @@ public class ContextMap<C> {
find = true;
entry.unlinkToDeath();
i.remove();
+ recordUnregisterApp(entry, reason);
break;
}
}
@@ -226,18 +270,7 @@ public class ContextMap<C> {
/** Remove a connection with the given ID. */
void removeConnection(int id, int connId) {
synchronized (mConnectionsLock) {
- if (Flags.bleContextMapRemoveFix()) {
- mConnections.removeIf(conn -> conn.appId == id && conn.connId == connId);
- } else {
- Iterator<Connection> i = mConnections.iterator();
- while (i.hasNext()) {
- Connection connection = i.next();
- if (connection.connId == connId) {
- i.remove();
- break;
- }
- }
- }
+ mConnections.removeIf(conn -> conn.appId == id && conn.connId == connId);
}
}
@@ -360,6 +393,7 @@ public class ContextMap<C> {
entry.unlinkToDeath();
}
mApps.clear();
+ mOngoingRecords.clear();
}
synchronized (mConnectionsLock) {
@@ -381,7 +415,46 @@ public class ContextMap<C> {
/** Logs debug information. */
protected void dump(StringBuilder sb) {
synchronized (mAppsLock) {
- sb.append(" Entries: ").append(mApps.size()).append("\n\n");
+ sb.append(" Entries: ").append(mApps.size()).append("\n");
+ sb.append(" Last apps: ").append("\n");
+ for (AppRecord record : mLastRecords) {
+ sb.append(" ")
+ .append(sDateFormat.format(record.registerTime))
+ .append(" ~ ")
+ .append(sDateFormat.format(record.unregisterTime))
+ .append(" app_if: ")
+ .append(record.clientIf)
+ .append(", appName: ")
+ .append(record.appName);
+ if (record.attributionTag != null) {
+ sb.append(", tag: ").append(record.attributionTag);
+ }
+ sb.append(", reason: ").append(record.reason).append("\n");
+ }
+ sb.append("\n");
+ }
+ }
+
+ @GuardedBy("mAppsLock")
+ private void recordRegisterApp(App app) {
+ mOngoingRecords.add(new AppRecord(app));
+ }
+
+ @GuardedBy("mAppsLock")
+ private void recordUnregisterApp(App app, RemoveReason reason) {
+ for (int i = 0; i < mOngoingRecords.size(); i++) {
+ if (app.uuid.equals(mOngoingRecords.get(i).uuid)) {
+ AppRecord record = mOngoingRecords.remove(i);
+ record.clientIf = app.id;
+ record.reason = reason;
+ record.unregisterTime = Instant.now();
+
+ if (mLastRecords.size() >= MAX_LAST_RECORDS) {
+ mLastRecords.remove(0);
+ }
+ mLastRecords.add(record);
+ break;
+ }
}
}
}
diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementBinder.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementBinder.java
new file mode 100644
index 0000000000..3a23b6899d
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementBinder.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.IDistanceMeasurement;
+import android.bluetooth.le.ChannelSoundingParams;
+import android.bluetooth.le.DistanceMeasurementMethod;
+import android.bluetooth.le.DistanceMeasurementParams;
+import android.bluetooth.le.IDistanceMeasurementCallback;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.ParcelUuid;
+
+import com.android.bluetooth.Utils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class DistanceMeasurementBinder extends IDistanceMeasurement.Stub {
+ private static final String TAG = DistanceMeasurementBinder.class.getSimpleName();
+ private final Context mContext;
+ private final DistanceMeasurementManager mDistanceMeasurementManager;
+ private volatile boolean mIsAvailable = true;
+
+ DistanceMeasurementBinder(Context context, DistanceMeasurementManager manager) {
+ mContext = context;
+ mDistanceMeasurementManager = manager;
+ }
+
+ void cleanup() {
+ mIsAvailable = false;
+ }
+
+ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+ @Nullable
+ private DistanceMeasurementManager getManager(AttributionSource source, String method) {
+ if (!mIsAvailable
+ || !callerIsSystemOrActiveOrManagedUser(
+ mContext, TAG, "DistanceMeasurement " + method)
+ || !Utils.checkConnectPermissionForDataDelivery(
+ mContext, source, "DistanceMeasurement " + method)) {
+ return null;
+ }
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return mDistanceMeasurementManager;
+ }
+
+ @Override
+ public List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(
+ AttributionSource source) {
+ DistanceMeasurementManager manager =
+ getManager(source, "getSupportedDistanceMeasurementMethods");
+ if (manager == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(manager.getSupportedDistanceMeasurementMethods());
+ }
+
+ @Override
+ public void startDistanceMeasurement(
+ ParcelUuid uuid,
+ DistanceMeasurementParams distanceMeasurementParams,
+ IDistanceMeasurementCallback callback,
+ AttributionSource source) {
+ DistanceMeasurementManager manager = getManager(source, "startDistanceMeasurement");
+ if (manager == null) {
+ return;
+ }
+ manager.startDistanceMeasurement(uuid.getUuid(), distanceMeasurementParams, callback);
+ }
+
+ @Override
+ public int stopDistanceMeasurement(
+ ParcelUuid uuid, BluetoothDevice device, int method, AttributionSource source) {
+ if (!mIsAvailable) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ } else if (!callerIsSystemOrActiveOrManagedUser(mContext, TAG, "stopDistanceMeasurement")) {
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
+ } else if (!Utils.checkConnectPermissionForDataDelivery(
+ mContext, source, "DistanceMeasurement stopDistanceMeasurement")) {
+ return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
+ }
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ return mDistanceMeasurementManager.stopDistanceMeasurement(
+ uuid.getUuid(), device, method, false);
+ }
+
+ @Override
+ public int getChannelSoundingMaxSupportedSecurityLevel(
+ BluetoothDevice remoteDevice, AttributionSource source) {
+ DistanceMeasurementManager manager =
+ getManager(source, "getChannelSoundingMaxSupportedSecurityLevel");
+ if (manager == null) {
+ return ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
+ }
+ return manager.getChannelSoundingMaxSupportedSecurityLevel(remoteDevice);
+ }
+
+ @Override
+ public int getLocalChannelSoundingMaxSupportedSecurityLevel(AttributionSource source) {
+ DistanceMeasurementManager manager =
+ getManager(source, "getLocalChannelSoundingMaxSupportedSecurityLevel");
+ if (manager == null) {
+ return ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
+ }
+ return manager.getLocalChannelSoundingMaxSupportedSecurityLevel();
+ }
+
+ @Override
+ public int[] getChannelSoundingSupportedSecurityLevels(AttributionSource source) {
+ DistanceMeasurementManager manager =
+ getManager(source, "getChannelSoundingSupportedSecurityLevels");
+
+ if (manager == null) {
+ return new int[0];
+ }
+ return manager.getChannelSoundingSupportedSecurityLevels().stream()
+ .mapToInt(i -> i)
+ .toArray();
+ }
+}
diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
index 65e401bd97..8817c2bf66 100644
--- a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java
@@ -52,15 +52,22 @@ public class DistanceMeasurementManager {
private static final int CS_MEDIUM_FREQUENCY_INTERVAL_MS = 3000;
private static final int CS_HIGH_FREQUENCY_INTERVAL_MS = 200;
+ // sync with system/gd/hic/DistanceMeasurementManager
+ private static final int INVALID_AZIMUTH_ANGLE_DEGREE = -1;
+ private static final int INVALID_ALTITUDE_ANGLE_DEGREE = -91;
+
private final AdapterService mAdapterService;
private final HandlerThread mHandlerThread;
- DistanceMeasurementNativeInterface mDistanceMeasurementNativeInterface;
+ private final DistanceMeasurementNativeInterface mDistanceMeasurementNativeInterface;
+ private final DistanceMeasurementBinder mDistanceMeasurementBinder;
private final ConcurrentHashMap<String, CopyOnWriteArraySet<DistanceMeasurementTracker>>
mRssiTrackers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, CopyOnWriteArraySet<DistanceMeasurementTracker>>
mCsTrackers = new ConcurrentHashMap<>();
private final boolean mHasChannelSoundingFeature;
+ private volatile boolean mIsTurnedOff = false;
+
/** Constructor of {@link DistanceMeasurementManager}. */
DistanceMeasurementManager(AdapterService adapterService) {
mAdapterService = adapterService;
@@ -70,6 +77,7 @@ public class DistanceMeasurementManager {
mHandlerThread.start();
mDistanceMeasurementNativeInterface = DistanceMeasurementNativeInterface.getInstance();
mDistanceMeasurementNativeInterface.init(this);
+ mDistanceMeasurementBinder = new DistanceMeasurementBinder(adapterService, this);
if (Flags.channelSounding25q2Apis()) {
mHasChannelSoundingFeature =
adapterService
@@ -81,7 +89,26 @@ public class DistanceMeasurementManager {
}
void cleanup() {
+ mIsTurnedOff = true;
+ mDistanceMeasurementBinder.cleanup();
mDistanceMeasurementNativeInterface.cleanup();
+ Log.d(TAG, "stop all sessions as BT is off");
+ for (String addressForCs : mCsTrackers.keySet()) {
+ onDistanceMeasurementStopped(
+ addressForCs,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING);
+ }
+ for (String addressForRssi : mRssiTrackers.keySet()) {
+ onDistanceMeasurementStopped(
+ addressForRssi,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI);
+ }
+ }
+
+ DistanceMeasurementBinder getBinder() {
+ return mDistanceMeasurementBinder;
}
DistanceMeasurementMethod[] getSupportedDistanceMeasurementMethods() {
@@ -102,6 +129,12 @@ public class DistanceMeasurementManager {
void startDistanceMeasurement(
UUID uuid, DistanceMeasurementParams params, IDistanceMeasurementCallback callback) {
+ if (mIsTurnedOff) {
+ Log.d(TAG, "BT is turned off, no new request is allowed.");
+ invokeStartFail(
+ callback, params.getDevice(), BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
+ return;
+ }
Log.i(
TAG,
"startDistanceMeasurement:"
@@ -471,6 +504,9 @@ public class DistanceMeasurementManager {
int errorAltitudeAngle,
long elapsedRealtimeNanos,
int confidenceLevel,
+ double delaySpreadMeters,
+ int detectedAttackLevel,
+ double velocityMetersPerSecond,
int method) {
logd(
"onDistanceMeasurementResult "
@@ -482,16 +518,31 @@ public class DistanceMeasurementManager {
DistanceMeasurementResult.Builder builder =
new DistanceMeasurementResult.Builder(centimeter / 100.0, errorCentimeter / 100.0)
.setMeasurementTimestampNanos(elapsedRealtimeNanos);
- if (confidenceLevel != -1) {
- builder.setConfidenceLevel(confidenceLevel / 100.0);
- }
- DistanceMeasurementResult result = builder.build();
+
switch (method) {
case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
- handleRssiResult(address, result);
+ handleRssiResult(address, builder.build());
break;
case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING:
- handleCsResult(address, result);
+ if (azimuthAngle != INVALID_AZIMUTH_ANGLE_DEGREE) {
+ builder.setAzimuthAngle(azimuthAngle);
+ builder.setErrorAzimuthAngle(errorAzimuthAngle);
+ }
+ if (altitudeAngle != INVALID_ALTITUDE_ANGLE_DEGREE) {
+ builder.setAltitudeAngle(altitudeAngle);
+ builder.setErrorAltitudeAngle(errorAltitudeAngle);
+ }
+ if (confidenceLevel != -1) {
+ builder.setConfidenceLevel(confidenceLevel / 100.0);
+ }
+ if (delaySpreadMeters >= 0) {
+ builder.setDelaySpreadMeters(delaySpreadMeters);
+ }
+ if (velocityMetersPerSecond >= 0) {
+ builder.setVelocityMetersPerSecond(velocityMetersPerSecond);
+ }
+ builder.setDetectedAttackLevel(detectedAttackLevel);
+ handleCsResult(address, builder.build());
break;
default:
Log.w(TAG, "onDistanceMeasurementResult: invalid method " + method);
@@ -538,4 +589,5 @@ public class DistanceMeasurementManager {
private static void logd(String msg) {
Log.d(TAG, msg);
}
+
}
diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
index 7009dde697..ad8a7f9bd4 100644
--- a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
@@ -107,6 +107,9 @@ public class DistanceMeasurementNativeInterface {
int errorAltitudeAngle,
long elapsedRealtimeNanos,
int confidenceLevel,
+ double delayedSpreadMeters,
+ int detectedAttackLevel,
+ double velocityMetersPerSecond,
int method) {
mDistanceMeasurementManager.onDistanceMeasurementResult(
address,
@@ -118,6 +121,9 @@ public class DistanceMeasurementNativeInterface {
errorAltitudeAngle,
elapsedRealtimeNanos,
confidenceLevel,
+ delayedSpreadMeters,
+ detectedAttackLevel,
+ velocityMetersPerSecond,
method);
}
diff --git a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
index af59e0c8f6..ad4d9538e7 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
@@ -282,7 +282,7 @@ public class GattNativeInterface {
private native int gattClientGetDeviceTypeNative(String address);
private native void gattClientRegisterAppNative(
- long appUuidLsb, long appUuidMsb, boolean eattSupport);
+ long appUuidLsb, long appUuidMsb, String name, boolean eattSupport);
private native void gattClientUnregisterAppNative(int clientIf);
@@ -427,8 +427,9 @@ public class GattNativeInterface {
/**
* Register the given client It will invoke {@link #onClientRegistered(int, int, long, long)}.
*/
- public void gattClientRegisterApp(long appUuidLsb, long appUuidMsb, boolean eattSupport) {
- gattClientRegisterAppNative(appUuidLsb, appUuidMsb, eattSupport);
+ public void gattClientRegisterApp(
+ long appUuidLsb, long appUuidMsb, String name, boolean eattSupport) {
+ gattClientRegisterAppNative(appUuidLsb, appUuidMsb, name, eattSupport);
}
/** Unregister the client */
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 2927132986..e9563f8908 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -16,13 +16,13 @@
package com.android.bluetooth.gatt;
-import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.checkCallerTargetSdk;
+import static com.android.bluetooth.util.AttributionSourceUtil.getLastAttributionTag;
import static java.util.Objects.requireNonNull;
@@ -43,14 +43,6 @@ import android.bluetooth.BluetoothUtils;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertisingSetParameters;
-import android.bluetooth.le.ChannelSoundingParams;
-import android.bluetooth.le.DistanceMeasurementMethod;
-import android.bluetooth.le.DistanceMeasurementParams;
-import android.bluetooth.le.IAdvertisingSetCallback;
-import android.bluetooth.le.IDistanceMeasurementCallback;
-import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
import android.content.pm.PackageManager;
@@ -59,6 +51,7 @@ import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -127,6 +120,9 @@ public class GattService extends ProfileService {
private static final Map<String, Integer> EARLY_MTU_EXCHANGE_PACKAGES =
Map.of("com.teslamotors", GATT_MTU_MAX);
+ private static final Map<String, String> GATT_CLIENTS_NOTIFY_TO_ADAPTER_PACKAGES =
+ Map.of("com.google.android.gms", "com.google.android.gms.findmydevice");
+
@VisibleForTesting static final int GATT_CLIENT_LIMIT_PER_APP = 32;
@Nullable public final ScanController mScanController;
@@ -161,6 +157,7 @@ public class GattService extends ProfileService {
private final DistanceMeasurementManager mDistanceMeasurementManager;
private final ActivityManager mActivityManager;
private final PackageManager mPackageManager;
+ private final HandlerThread mHandlerThread;
public GattService(AdapterService adapterService) {
super(requireNonNull(adapterService));
@@ -174,7 +171,12 @@ public class GattService extends ProfileService {
mNativeInterface = GattObjectsFactory.getInstance().getNativeInterface();
mNativeInterface.init(this);
- mAdvertiseManager = new AdvertiseManager(this);
+
+ // Create a thread to handle LE operations
+ mHandlerThread = new HandlerThread("Bluetooth LE");
+ mHandlerThread.start();
+
+ mAdvertiseManager = new AdvertiseManager(mAdapterService, mHandlerThread.getLooper());
if (!Flags.scanManagerRefactor()) {
mScanController = new ScanController(adapterService);
@@ -214,7 +216,6 @@ public class GattService extends ProfileService {
if (mScanController != null) {
mScanController.stop();
}
- mAdvertiseManager.clear();
mClientMap.clear();
mRestrictedHandles.clear();
mServerMap.clear();
@@ -229,6 +230,7 @@ public class GattService extends ProfileService {
mNativeInterface.cleanup();
mAdvertiseManager.cleanup();
mDistanceMeasurementManager.cleanup();
+ mHandlerThread.quit();
}
/** This is only used when Flags.scanManagerRefactor() is true. */
@@ -284,13 +286,6 @@ public class GattService extends ProfileService {
return restrictedHandles != null && restrictedHandles.contains(handle);
}
- /** Notify Scan manager of bluetooth profile connection state changes */
- public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) {
- if (mScanController != null) {
- mScanController.notifyProfileConnectionStateChange(profile, fromState, toState);
- }
- }
-
class ServerDeathRecipient implements IBinder.DeathRecipient {
int mAppIf;
private String mPackageName;
@@ -323,7 +318,8 @@ public class GattService extends ProfileService {
Log.d(
TAG,
"Binder is dead - unregistering client (" + mPackageName + " " + mAppIf + ")!");
- unregisterClient(mAppIf, getAttributionSource());
+ unregisterClient(
+ mAppIf, getAttributionSource(), ContextMap.RemoveReason.REASON_BINDER_DIED);
}
}
@@ -382,7 +378,8 @@ public class GattService extends ProfileService {
if (service == null) {
return;
}
- service.unregisterClient(clientIf, attributionSource);
+ service.unregisterClient(
+ clientIf, attributionSource, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
}
@Override
@@ -833,133 +830,6 @@ public class GattService extends ProfileService {
}
@Override
- public void startAdvertisingSet(
- AdvertisingSetParameters parameters,
- AdvertiseData advertiseData,
- AdvertiseData scanResponse,
- PeriodicAdvertisingParameters periodicParameters,
- AdvertiseData periodicData,
- int duration,
- int maxExtAdvEvents,
- int serverIf,
- IAdvertisingSetCallback callback,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.startAdvertisingSet(
- parameters,
- advertiseData,
- scanResponse,
- periodicParameters,
- periodicData,
- duration,
- maxExtAdvEvents,
- serverIf,
- callback,
- attributionSource);
- }
-
- @Override
- public void stopAdvertisingSet(
- IAdvertisingSetCallback callback, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.stopAdvertisingSet(callback, attributionSource);
- }
-
- @Override
- public void getOwnAddress(int advertiserId, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.getOwnAddress(advertiserId, attributionSource);
- }
-
- @Override
- public void enableAdvertisingSet(
- int advertiserId,
- boolean enable,
- int duration,
- int maxExtAdvEvents,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.enableAdvertisingSet(
- advertiserId, enable, duration, maxExtAdvEvents, attributionSource);
- }
-
- @Override
- public void setAdvertisingData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setAdvertisingData(advertiserId, data, attributionSource);
- }
-
- @Override
- public void setScanResponseData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setScanResponseData(advertiserId, data, attributionSource);
- }
-
- @Override
- public void setAdvertisingParameters(
- int advertiserId,
- AdvertisingSetParameters parameters,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setAdvertisingParameters(advertiserId, parameters, attributionSource);
- }
-
- @Override
- public void setPeriodicAdvertisingParameters(
- int advertiserId,
- PeriodicAdvertisingParameters parameters,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setPeriodicAdvertisingParameters(advertiserId, parameters, attributionSource);
- }
-
- @Override
- public void setPeriodicAdvertisingData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setPeriodicAdvertisingData(advertiserId, data, attributionSource);
- }
-
- @Override
- public void setPeriodicAdvertisingEnable(
- int advertiserId, boolean enable, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.setPeriodicAdvertisingEnable(advertiserId, enable, attributionSource);
- }
-
- @Override
public void disconnectAll(AttributionSource attributionSource) {
GattService service = getService();
if (service == null) {
@@ -967,119 +837,7 @@ public class GattService extends ProfileService {
}
service.disconnectAll(attributionSource);
}
-
- @Override
- public List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "GattService getSupportedDistanceMeasurementMethods")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- attributionSource,
- "GattService getSupportedDistanceMeasurementMethods")) {
- return Collections.emptyList();
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return Arrays.asList(service.getSupportedDistanceMeasurementMethods());
- }
-
- @Override
- public void startDistanceMeasurement(
- ParcelUuid uuid,
- DistanceMeasurementParams distanceMeasurementParams,
- IDistanceMeasurementCallback callback,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "startDistanceMeasurement")
- || !Utils.checkConnectPermissionForDataDelivery(
- service, attributionSource, "GattService startDistanceMeasurement")) {
- return;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- service.startDistanceMeasurement(uuid.getUuid(), distanceMeasurementParams, callback);
- }
-
- @Override
- public int stopDistanceMeasurement(
- ParcelUuid uuid,
- BluetoothDevice device,
- int method,
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
- } else if (!callerIsSystemOrActiveOrManagedUser(
- service, TAG, "stopDistanceMeasurement")) {
- return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
- } else if (!Utils.checkConnectPermissionForDataDelivery(
- service, attributionSource, "GattService stopDistanceMeasurement")) {
- return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.stopDistanceMeasurement(uuid.getUuid(), device, method);
- }
-
- @Override
- public int getChannelSoundingMaxSupportedSecurityLevel(
- BluetoothDevice remoteDevice, AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "GattService getChannelSoundingMaxSupportedSecurityLevel")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- attributionSource,
- "GattService getChannelSoundingMaxSupportedSecurityLevel")) {
- return ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getChannelSoundingMaxSupportedSecurityLevel(remoteDevice);
- }
-
- @Override
- public int getLocalChannelSoundingMaxSupportedSecurityLevel(
- AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service,
- TAG,
- "GattService getLocalChannelSoundingMaxSupportedSecurityLevel")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- attributionSource,
- "GattService getLocalChannelSoundingMaxSupportedSecurityLevel")) {
- return ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getLocalChannelSoundingMaxSupportedSecurityLevel();
- }
-
- @Override
- public int[] getChannelSoundingSupportedSecurityLevels(
- AttributionSource attributionSource) {
- GattService service = getService();
-
- if (service == null
- || !callerIsSystemOrActiveOrManagedUser(
- service, TAG, "GattService getChannelSoundingSupportedSecurityLevels")
- || !Utils.checkConnectPermissionForDataDelivery(
- service,
- attributionSource,
- "GattService getChannelSoundingSupportedSecurityLevels")) {
- return new int[0];
- }
- service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- return service.getChannelSoundingSupportedSecurityLevels().stream()
- .mapToInt(i -> i)
- .toArray();
- }
}
- ;
/**************************************************************************
* Callback functions - CLIENT
@@ -1095,7 +853,7 @@ public class GattService extends ProfileService {
app.id = clientIf;
app.linkToDeath(new ClientDeathRecipient(clientIf, app.name));
} else {
- mClientMap.remove(uuid);
+ mClientMap.remove(uuid, ContextMap.RemoveReason.REASON_REGISTER_FAILED);
}
app.callback.onClientRegistered(status, clientIf);
}
@@ -1109,7 +867,9 @@ public class GattService extends ProfileService {
+ ", connId="
+ connId
+ ", address="
- + BluetoothUtils.toAnonymizedAddress(address));
+ + BluetoothUtils.toAnonymizedAddress(address)
+ + ", status="
+ + status);
int connectionState = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
if (status == 0) {
mClientMap.addConnection(clientIf, connId, address);
@@ -1123,7 +883,10 @@ public class GattService extends ProfileService {
mPermits.putIfAbsent(address, -1);
}
connectionState = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
+ } else {
+ mAdapterService.notifyGattClientConnectFailed(clientIf, getDevice(address));
}
+
ContextMap<IBluetoothGattCallback>.App app = mClientMap.getById(clientIf);
if (app != null) {
app.callback.onClientConnectionState(
@@ -1152,6 +915,7 @@ public class GattService extends ProfileService {
+ BluetoothUtils.toAnonymizedAddress(address));
mClientMap.removeConnection(clientIf, connId);
+ mAdapterService.notifyGattClientDisconnect(clientIf, getDevice(address));
ContextMap<IBluetoothGattCallback>.App app = mClientMap.getById(clientIf);
mRestrictedHandles.remove(connId);
@@ -1745,193 +1509,13 @@ public class GattService extends ProfileService {
public void unregAll(AttributionSource attributionSource) {
for (Integer appId : mClientMap.getAllAppsIds()) {
Log.d(TAG, "unreg:" + appId);
- unregisterClient(appId, attributionSource);
- }
- }
-
- /**************************************************************************
- * ADVERTISING SET
- *************************************************************************/
- @RequiresPermission(
- allOf = {
- BLUETOOTH_ADVERTISE,
- BLUETOOTH_PRIVILEGED,
- },
- conditional = true)
- void startAdvertisingSet(
- AdvertisingSetParameters parameters,
- AdvertiseData advertiseData,
- AdvertiseData scanResponse,
- PeriodicAdvertisingParameters periodicParameters,
- AdvertiseData periodicData,
- int duration,
- int maxExtAdvEvents,
- int serverIf,
- IAdvertisingSetCallback callback,
- AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService startAdvertisingSet")) {
- return;
- }
- if (parameters.getOwnAddressType() != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
- || serverIf != 0
- || parameters.isDirected()) {
- this.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- mAdvertiseManager.startAdvertisingSet(
- parameters,
- advertiseData,
- scanResponse,
- periodicParameters,
- periodicData,
- duration,
- maxExtAdvEvents,
- serverIf,
- callback,
- attributionSource);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void stopAdvertisingSet(IAdvertisingSetCallback callback, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService stopAdvertisingSet")) {
- return;
- }
- mAdvertiseManager.stopAdvertisingSet(callback);
- }
-
- @RequiresPermission(
- allOf = {
- BLUETOOTH_ADVERTISE,
- BLUETOOTH_PRIVILEGED,
- })
- void getOwnAddress(int advertiserId, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService getOwnAddress")) {
- return;
- }
- this.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- mAdvertiseManager.getOwnAddress(advertiserId);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void enableAdvertisingSet(
- int advertiserId,
- boolean enable,
- int duration,
- int maxExtAdvEvents,
- AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService enableAdvertisingSet")) {
- return;
- }
- mAdvertiseManager.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void setAdvertisingData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setAdvertisingData")) {
- return;
- }
- mAdvertiseManager.setAdvertisingData(advertiserId, data);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void setScanResponseData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setScanResponseData")) {
- return;
- }
- mAdvertiseManager.setScanResponseData(advertiserId, data);
- }
-
- @RequiresPermission(
- allOf = {
- BLUETOOTH_ADVERTISE,
- BLUETOOTH_PRIVILEGED,
- },
- conditional = true)
- void setAdvertisingParameters(
- int advertiserId,
- AdvertisingSetParameters parameters,
- AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setAdvertisingParameters")) {
- return;
- }
- if (parameters.getOwnAddressType() != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
- || parameters.isDirected()) {
- this.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- mAdvertiseManager.setAdvertisingParameters(advertiserId, parameters);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void setPeriodicAdvertisingParameters(
- int advertiserId,
- PeriodicAdvertisingParameters parameters,
- AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setPeriodicAdvertisingParameters")) {
- return;
- }
- mAdvertiseManager.setPeriodicAdvertisingParameters(advertiserId, parameters);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void setPeriodicAdvertisingData(
- int advertiserId, AdvertiseData data, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setPeriodicAdvertisingData")) {
- return;
+ unregisterClient(
+ appId, attributionSource, ContextMap.RemoveReason.REASON_UNREGISTER_ALL);
}
- mAdvertiseManager.setPeriodicAdvertisingData(advertiserId, data);
- }
-
- @RequiresPermission(BLUETOOTH_ADVERTISE)
- void setPeriodicAdvertisingEnable(
- int advertiserId, boolean enable, AttributionSource attributionSource) {
- if (!Utils.checkAdvertisePermissionForDataDelivery(
- this, attributionSource, "GattService setPeriodicAdvertisingEnable")) {
- return;
+ for (Integer appId : mServerMap.getAllAppsIds()) {
+ Log.d(TAG, "unreg:" + appId);
+ unregisterServer(appId, attributionSource);
}
- mAdvertiseManager.setPeriodicAdvertisingEnable(advertiserId, enable);
- }
-
- /**************************************************************************
- * Distance Measurement
- *************************************************************************/
-
- DistanceMeasurementMethod[] getSupportedDistanceMeasurementMethods() {
- return mDistanceMeasurementManager.getSupportedDistanceMeasurementMethods();
- }
-
- void startDistanceMeasurement(
- UUID uuid,
- DistanceMeasurementParams distanceMeasurementParams,
- IDistanceMeasurementCallback callback) {
- mDistanceMeasurementManager.startDistanceMeasurement(
- uuid, distanceMeasurementParams, callback);
- }
-
- int stopDistanceMeasurement(UUID uuid, BluetoothDevice device, int method) {
- return mDistanceMeasurementManager.stopDistanceMeasurement(uuid, device, method, false);
- }
-
- int getChannelSoundingMaxSupportedSecurityLevel(BluetoothDevice remoteDevice) {
- return mDistanceMeasurementManager.getChannelSoundingMaxSupportedSecurityLevel(
- remoteDevice);
- }
-
- int getLocalChannelSoundingMaxSupportedSecurityLevel() {
- return mDistanceMeasurementManager.getLocalChannelSoundingMaxSupportedSecurityLevel();
- }
-
- Set<Integer> getChannelSoundingSupportedSecurityLevels() {
- return mDistanceMeasurementManager.getChannelSoundingSupportedSecurityLevels();
}
/**************************************************************************
@@ -1959,14 +1543,26 @@ public class GattService extends ProfileService {
return;
}
- Log.d(TAG, "registerClient() - UUID=" + uuid);
+ String name = attributionSource.getPackageName();
+ String tag = getLastAttributionTag(attributionSource);
+ String myPackage = AttributionSource.myAttributionSource().getPackageName();
+ if (myPackage.equals(name) && tag != null) {
+ /* For clients created by Bluetooth stack, use just tag as name */
+ name = tag;
+ } else if (tag != null) {
+ name = name + "[" + tag + "]";
+ }
+
+ Log.d(TAG, "registerClient() - UUID=" + uuid + " name=" + name);
mClientMap.add(uuid, callback, this, attributionSource);
+
mNativeInterface.gattClientRegisterApp(
- uuid.getLeastSignificantBits(), uuid.getMostSignificantBits(), eatt_support);
+ uuid.getLeastSignificantBits(), uuid.getMostSignificantBits(), name, eatt_support);
}
@RequiresPermission(BLUETOOTH_CONNECT)
- void unregisterClient(int clientIf, AttributionSource attributionSource) {
+ void unregisterClient(
+ int clientIf, AttributionSource attributionSource, ContextMap.RemoveReason reason) {
if (!Utils.checkConnectPermissionForDataDelivery(
this, attributionSource, "GattService unregisterClient")) {
return;
@@ -1982,7 +1578,7 @@ public class GattService extends ProfileService {
BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__END,
attributionSource.getUid());
}
- mClientMap.remove(clientIf);
+ mClientMap.remove(clientIf, reason);
mNativeInterface.gattClientUnregisterApp(clientIf);
}
@@ -2055,6 +1651,21 @@ public class GattService extends ProfileService {
}
}
+ if (transport != BluetoothDevice.TRANSPORT_BREDR && isDirect && !opportunistic) {
+ String attributionTag = getLastAttributionTag(attributionSource);
+ if (packageName != null && attributionTag != null) {
+ for (Map.Entry<String, String> entry :
+ GATT_CLIENTS_NOTIFY_TO_ADAPTER_PACKAGES.entrySet()) {
+ if (packageName.contains(entry.getKey())
+ && attributionTag.contains(entry.getValue())) {
+ mAdapterService.notifyDirectLeGattClientConnect(
+ clientIf, getDevice(address));
+ break;
+ }
+ }
+ }
+ }
+
mNativeInterface.gattClientConnect(
clientIf,
address,
@@ -2093,6 +1704,9 @@ public class GattService extends ProfileService {
.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_DISCONNECT_JAVA,
BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__START,
attributionSource.getUid());
+
+ mAdapterService.notifyGattClientDisconnect(clientIf, getDevice(address));
+
mNativeInterface.gattClientDisconnect(clientIf, address, connId != null ? connId : 0);
}
@@ -3096,7 +2710,7 @@ public class GattService extends ProfileService {
deleteServices(serverIf);
- mServerMap.remove(serverIf);
+ mServerMap.remove(serverIf, ContextMap.RemoveReason.REASON_UNREGISTER_SERVER);
mNativeInterface.gattServerUnregisterApp(serverIf);
}
@@ -3350,6 +2964,18 @@ public class GattService extends ProfileService {
}
/**************************************************************************
+ * Binder functions
+ *************************************************************************/
+
+ public IBinder getBluetoothAdvertise() {
+ return mAdvertiseManager.getBinder();
+ }
+
+ public IBinder getDistanceMeasurement() {
+ return mDistanceMeasurementManager.getBinder();
+ }
+
+ /**************************************************************************
* Private functions
*************************************************************************/
diff --git a/android/app/src/com/android/bluetooth/gatt/GattServiceConfig.java b/android/app/src/com/android/bluetooth/gatt/GattServiceConfig.kt
index cbf574639c..c31521d6a4 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattServiceConfig.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattServiceConfig.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.bluetooth.gatt;
+package com.android.bluetooth.gatt
-/** GattService configuration. */
-public class GattServiceConfig {
- public static final String TAG_PREFIX = "BtGatt.";
- public static final boolean DEBUG_ADMIN = false;
+object GattServiceConfig {
+ @JvmField val TAG_PREFIX = "BtGatt."
+ @JvmField val DEBUG_ADMIN = false
}
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index 69feb368ef..949db48483 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -28,7 +28,6 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
@@ -2644,16 +2643,7 @@ public class HeadsetService extends ProfileService {
List<BluetoothDevice> fallbackCandidates = getConnectedDevices();
List<BluetoothDevice> uninterestedCandidates = new ArrayList<>();
for (BluetoothDevice device : fallbackCandidates) {
- byte[] deviceType =
- dbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE);
- BluetoothClass deviceClass =
- new BluetoothClass(
- mAdapterService.getRemoteDevices().getBluetoothClass(device));
- if ((deviceClass != null
- && deviceClass.getMajorDeviceClass()
- == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
- || (deviceType != null
- && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
+ if (Utils.isWatch(mAdapterService, device)) {
uninterestedCandidates.add(device);
}
}
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index fd56faa613..fb0ed9d2f0 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -55,7 +55,6 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -1122,12 +1121,6 @@ class HeadsetStateMachine extends StateMachine {
}
}
break;
- case INTENT_SCO_VOLUME_CHANGED:
- if (Flags.hfpAllowVolumeChangeWithoutSco()) {
- // when flag is removed, remove INTENT_SCO_VOLUME_CHANGED case in AudioOn
- processIntentScoVolume((Intent) message.obj, mDevice);
- }
- break;
case INTENT_CONNECTION_ACCESS_REPLY:
handleAccessPermissionResult((Intent) message.obj);
break;
@@ -1630,8 +1623,6 @@ class HeadsetStateMachine extends StateMachine {
break;
}
case INTENT_SCO_VOLUME_CHANGED:
- // TODO: b/362313390 Remove this case once the fix is in place because this
- // message will be handled by the ConnectedBase state.
processIntentScoVolume((Intent) message.obj, mDevice);
break;
case STACK_EVENT:
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index a48abbb9d3..0f654b8ccd 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -48,7 +48,6 @@ 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.bluetooth.flags.Flags;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -180,18 +179,12 @@ public class HeadsetClientService extends ProfileService {
int hfToAmVol(int hfVol) {
int amRange = mMaxAmVcVol - mMinAmVcVol;
int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
- int amVol = 0;
- if (Flags.headsetClientAmHfVolumeSymmetric()) {
- amVol =
- (int)
- Math.round(
- (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)
- * ((double) amRange / hfRange))
- + mMinAmVcVol;
- } else {
- int amOffset = (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange;
- amVol = mMinAmVcVol + amOffset;
- }
+ int amVol =
+ (int)
+ Math.round(
+ (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)
+ * ((double) amRange / hfRange))
+ + mMinAmVcVol;
Log.d(TAG, "HF -> AM " + hfVol + " " + amVol);
return amVol;
}
@@ -199,15 +192,9 @@ public class HeadsetClientService extends ProfileService {
int amToHfVol(int amVol) {
int amRange = (mMaxAmVcVol > mMinAmVcVol) ? (mMaxAmVcVol - mMinAmVcVol) : 1;
int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
- int hfVol = 0;
- if (Flags.headsetClientAmHfVolumeSymmetric()) {
- hfVol =
- (int) Math.round((amVol - mMinAmVcVol) * ((double) hfRange / amRange))
- + MIN_HFP_SCO_VOICE_CALL_VOLUME;
- } else {
- int hfOffset = (hfRange * (amVol - mMinAmVcVol)) / amRange;
- hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset;
- }
+ int hfVol =
+ (int) Math.round((amVol - mMinAmVcVol) * ((double) hfRange / amRange))
+ + MIN_HFP_SCO_VOICE_CALL_VOLUME;
Log.d(TAG, "AM -> HF " + amVol + " " + hfVol);
return hfVol;
}
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 d05b6777f2..bb5346fe6e 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -102,6 +102,7 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -142,6 +143,9 @@ public class LeAudioService extends ProfileService {
/** Indicates group is active */
private static final int ACTIVE_STATE_ACTIVE = 0x02;
+ /** Filter for Targeted Announcements */
+ static final byte[] CAP_TARGETED_ANNOUNCEMENT_PAYLOAD = new byte[] {0x01};
+
/** This is used by application read-only for checking the fallback active group id. */
public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID =
"bluetooth_le_broadcast_fallback_active_group_id";
@@ -255,6 +259,7 @@ public class LeAudioService extends ProfileService {
mInputSelectableConfig = new ArrayList<>();
mOutputSelectableConfig = new ArrayList<>();
mInactivatedDueToContextType = false;
+ mAutoActiveModeEnabled = true;
}
Integer mGroupId;
@@ -270,6 +275,7 @@ public class LeAudioService extends ProfileService {
List<BluetoothLeAudioCodecConfig> mInputSelectableConfig;
List<BluetoothLeAudioCodecConfig> mOutputSelectableConfig;
Boolean mInactivatedDueToContextType;
+ Boolean mAutoActiveModeEnabled;
private Integer mActiveState;
private Integer mAllowedSinkContexts;
@@ -816,7 +822,7 @@ public class LeAudioService extends ProfileService {
}
@VisibleForTesting
- static synchronized void setLeAudioService(LeAudioService instance) {
+ public static synchronized void setLeAudioService(LeAudioService instance) {
Log.d(TAG, "setLeAudioService(): set to: " + instance);
sLeAudioService = instance;
}
@@ -1650,6 +1656,31 @@ public class LeAudioService extends ProfileService {
&& groupId == mUnicastGroupIdDeactivatedForBroadcastTransition;
}
+ /** Get local broadcast receiving devices */
+ public Set<BluetoothDevice> getLocalBroadcastReceivers() {
+ if (mBroadcastDescriptors == null) {
+ Log.e(TAG, "getLocalBroadcastReceivers: Invalid Broadcast Descriptors");
+ return Collections.emptySet();
+ }
+
+ BassClientService bassClientService = getBassClientService();
+ if (bassClientService == null) {
+ Log.e(TAG, "getLocalBroadcastReceivers: Bass service not available");
+ return Collections.emptySet();
+ }
+
+ Set<BluetoothDevice> deviceList = new HashSet<>();
+ for (Map.Entry<Integer, LeAudioBroadcastDescriptor> entry :
+ mBroadcastDescriptors.entrySet()) {
+ if (!entry.getValue().mState.equals(LeAudioStackEvent.BROADCAST_STATE_STOPPED)) {
+ List<BluetoothDevice> devices =
+ bassClientService.getSyncedBroadcastSinks(entry.getKey());
+ deviceList.addAll(devices);
+ }
+ }
+ return deviceList;
+ }
+
private boolean areBroadcastsAllStopped() {
if (mBroadcastDescriptors == null) {
Log.e(TAG, "areBroadcastsAllStopped: Invalid Broadcast Descriptors");
@@ -1987,12 +2018,37 @@ public class LeAudioService extends ProfileService {
mExposedActiveDevice = device;
}
+ boolean isAnyGroupDisabledFromAutoActiveMode() {
+ mGroupReadLock.lock();
+ try {
+ for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry :
+ mGroupDescriptorsView.entrySet()) {
+ LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue();
+ if (!groupDescriptor.mAutoActiveModeEnabled) {
+ Log.d(
+ TAG,
+ "isAnyGroupDisabledFromAutoActiveMode: disabled groupId "
+ + groupEntry.getKey());
+ return true;
+ }
+ }
+ } finally {
+ mGroupReadLock.unlock();
+ }
+ return false;
+ }
+
boolean isScannerNeeded() {
if (mDeviceDescriptors.isEmpty() || !mBluetoothEnabled) {
Log.d(TAG, "isScannerNeeded: false, mBluetoothEnabled: " + mBluetoothEnabled);
return false;
}
+ if (isAnyGroupDisabledFromAutoActiveMode()) {
+ Log.d(TAG, "isScannerNeeded true, some group has disabled Auto Active Mode");
+ return true;
+ }
+
if (allLeAudioDevicesConnected()) {
Log.d(TAG, "isScannerNeeded: all devices connected, scanner not needed");
return false;
@@ -2027,66 +2083,92 @@ public class LeAudioService extends ProfileService {
private class AudioServerScanCallback extends IScannerCallback.Stub {
// See BluetoothLeScanner.BleScanCallbackWrapper.mScannerId
- int mScannerId = 0;
+ static final int SCANNER_NOT_INITIALIZED = -2;
+ static final int SCANNER_INITIALIZING = -1;
+ int mScannerId = SCANNER_NOT_INITIALIZED;
synchronized void startBackgroundScan() {
- if (mScannerId != 0) {
- Log.d(TAG, "Scanner is already registered with id " + mScannerId);
+ if (mScannerId >= 0) {
+ Log.i(
+ TAG,
+ "startBackgroundScan: Scanner is already registered with id " + mScannerId);
return;
}
+
+ if (mScannerId == SCANNER_INITIALIZING) {
+ Log.i(TAG, "startBackgroundScan: Scanner is already initializing");
+ return;
+ }
+
+ mScannerId = SCANNER_INITIALIZING;
+
mAdapterService
.getBluetoothScanController()
- .getTransitionalScanHelper()
.registerScannerInternal(this, getAttributionSource(), null);
}
synchronized void stopBackgroundScan() {
- if (mScannerId == 0) {
- Log.d(TAG, "Scanner is already unregistered");
+ if (mScannerId < 0) {
+ Log.d(TAG, "Scanner is not running (mScannerId=" + mScannerId + ")");
return;
}
mAdapterService
.getBluetoothScanController()
- .getTransitionalScanHelper()
.stopScanInternal(mScannerId);
- mAdapterService
- .getBluetoothScanController()
- .getTransitionalScanHelper()
- .unregisterScannerInternal(mScannerId);
- mScannerId = 0;
+ mAdapterService.getBluetoothScanController().unregisterScannerInternal(mScannerId);
+ mScannerId = SCANNER_NOT_INITIALIZED;
}
@Override
public synchronized void onScannerRegistered(int status, int scannerId) {
+ Log.d(TAG, "onScannerRegistered: status: " + status + ", id:" + scannerId);
+ if (status != 0) {
+ mScannerId = SCANNER_NOT_INITIALIZED;
+ return;
+ }
mScannerId = scannerId;
- /* Filter we are building here will not match to anything.
- * Eventually we should be able to start scan from native when
- * b/276350722 is done
- */
ScanFilter filter =
new ScanFilter.Builder()
- .setServiceData(BluetoothUuid.LE_AUDIO, new byte[] {0x11})
+ .setServiceData(BluetoothUuid.CAP, CAP_TARGETED_ANNOUNCEMENT_PAYLOAD)
.build();
ScanSettings settings =
new ScanSettings.Builder()
.setLegacy(false)
+ .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode(ScanSettings.SCAN_MODE_BALANCED)
.setPhy(BluetoothDevice.PHY_LE_1M)
.build();
mAdapterService
.getBluetoothScanController()
- .getTransitionalScanHelper()
.startScanInternal(scannerId, settings, List.of(filter));
}
- // Eventually we should be able to start scan from native when b/276350722 is done
- // All the result returned here are ignored
@Override
- public void onScanResult(ScanResult scanResult) {}
+ public void onScanResult(ScanResult scanResult) {
+ Log.d(TAG, "onScanResult: " + scanResult.getDevice());
+ BluetoothDevice device = scanResult.getDevice();
+ if (device == null) {
+ return;
+ }
+
+ int groupId = getGroupId(device);
+ LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
+ if (descriptor == null) {
+ return;
+ }
+
+ if (!descriptor.mAutoActiveModeEnabled) {
+ Log.i(TAG, "onScanResult: GroupId: " + groupId + " is getting Active");
+ descriptor.mAutoActiveModeEnabled = true;
+ if (!getConnectedPeerDevices(groupId).isEmpty()) {
+ setActiveDevice(device);
+ }
+ }
+ }
@Override
public void onBatchScanResults(List<ScanResult> batchResults) {}
@@ -2385,6 +2467,25 @@ public class LeAudioService extends ProfileService {
}
}
+ private void clearAutoActiveModeToDefault() {
+ mGroupReadLock.lock();
+ try {
+ for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry :
+ mGroupDescriptorsView.entrySet()) {
+ LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue();
+ if (!groupDescriptor.mAutoActiveModeEnabled) {
+ Log.d(
+ TAG,
+ "mAutoActiveModeEnabled back to default for groupId: "
+ + groupEntry.getKey());
+ groupDescriptor.mAutoActiveModeEnabled = true;
+ }
+ }
+ } finally {
+ mGroupReadLock.unlock();
+ }
+ }
+
/**
* Set the active device group.
*
@@ -2403,6 +2504,9 @@ public class LeAudioService extends ProfileService {
groupId = descriptor.mGroupId;
+ /* User force device being active, clear the flag */
+ clearAutoActiveModeToDefault();
+
if (!isGroupAvailableForStream(groupId)) {
Log.e(
TAG,
@@ -2429,11 +2533,10 @@ public class LeAudioService extends ProfileService {
+ ", mExposedActiveDevice: "
+ mExposedActiveDevice);
- if (!Flags.leaudioBroadcastPrimaryGroupSelection()
- && isBroadcastActive()
- && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID
- && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) {
-
+ /* Replace fallback unicast and monitoring input device if device is active local
+ * broadcaster.
+ */
+ if (isAnyBroadcastInStreamingState()) {
LeAudioGroupDescriptor fallbackGroupDescriptor = getGroupDescriptor(groupId);
// If broadcast is ongoing and need to update unicast fallback active group
@@ -3112,12 +3215,16 @@ public class LeAudioService extends ProfileService {
}
}
+ private boolean isAnyBroadcastInStreamingState() {
+ return mBroadcastDescriptors.values().stream()
+ .anyMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING));
+ }
+
void transitionFromBroadcastToUnicast() {
if (mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) {
Log.d(TAG, "No deactivated group due for broadcast transmission");
// Notify audio manager
- if (mBroadcastDescriptors.values().stream()
- .noneMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING))) {
+ if (!isAnyBroadcastInStreamingState()) {
updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false);
}
return;
@@ -3883,11 +3990,7 @@ public class LeAudioService extends ProfileService {
}
// Notify audio manager
- if (mBroadcastDescriptors.values().stream()
- .anyMatch(
- d ->
- d.mState.equals(
- LeAudioStackEvent.BROADCAST_STATE_STREAMING))) {
+ if (isAnyBroadcastInStreamingState()) {
if (!Objects.equals(device, mActiveBroadcastAudioDevice)) {
updateBroadcastActiveDevice(device, mActiveBroadcastAudioDevice, true);
}
@@ -4026,11 +4129,6 @@ public class LeAudioService extends ProfileService {
return;
}
- if (descriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) {
- /* In case device is still in the group, let's remove it */
- mNativeInterface.groupRemoveNode(descriptor.mGroupId, device);
- }
-
descriptor.mGroupId = LE_AUDIO_GROUP_ID_INVALID;
descriptor.mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID;
descriptor.mDirection = AUDIO_DIRECTION_NONE;
@@ -4181,6 +4279,7 @@ public class LeAudioService extends ProfileService {
if (getConnectedPeerDevices(groupId).isEmpty()) {
descriptor.mIsConnected = false;
+ descriptor.mAutoActiveModeEnabled = true;
descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0;
if (descriptor.isActive()) {
/* Notify Native layer */
@@ -4523,6 +4622,91 @@ public class LeAudioService extends ProfileService {
}
/**
+ * Set auto active mode state.
+ *
+ * <p>Auto Active Mode by default is set to true and it means, that after ACL connection is
+ * created to LeAudio device which is part of the group, can be connected to Audio Framework
+ * (set as Active).
+ *
+ * <p>If Auto Active Mode is set to false, it means that after LeAudio device is connected for
+ * given group, the function isGroupAvailableForStream(groupId) will return false and
+ * ActiveDeviceManager will not make this group active.
+ *
+ * <p>This mode can change internally when two things happen: 1. LeAudioService detects Targeted
+ * Announcements from the device which belongs to the group.
+ * 2. @BluetoothLeAudio.setActiveDevice() is called with a device which belongs to the group.
+ *
+ * <p>Note: Auto Active Mode can be disabled only when all devices from the group are
+ * disconnected.
+ *
+ * @param groupId LeAudio group id which Auto Active Mode should be changed.
+ * @param enabled true when Auto Active Mode should be enabled (default value), false otherwise.
+ * @return true when Auto Active Mode is set, false otherwise
+ */
+ public boolean setAutoActiveModeState(int groupId, boolean enabled) {
+ Log.d(TAG, "setAutoActiveModeState: groupId: " + groupId + " enabled: " + enabled);
+
+ mGroupReadLock.lock();
+ try {
+ LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
+ if (descriptor == null) {
+ Log.i(
+ TAG,
+ "setAutoActiveModeState: groupId: "
+ + groupId
+ + " is not known LeAudio group");
+ return false;
+ }
+
+ /* Disabling Auto Active Mode is allowed only when all the devices from the group
+ * are disconnected */
+ if (!enabled && descriptor.mIsConnected) {
+ Log.i(
+ TAG,
+ "setAutoActiveModeState: GroupId: " + groupId + " is already connected ");
+ return false;
+ }
+
+ Log.i(TAG, "setAutoActiveModeState: groupId: " + groupId + ", enabled: " + enabled);
+ descriptor.mAutoActiveModeEnabled = enabled;
+ return true;
+ } finally {
+ mGroupReadLock.unlock();
+ }
+ }
+
+ /**
+ * Is Auto Active Mode enabled
+ *
+ * @param groupId LeAudio group id which Auto Active Mode should be taken.
+ * @return true when Auto Active Mode is enabled, false otherwise
+ */
+ public boolean isAutoActiveModeEnabled(int groupId) {
+ mGroupReadLock.lock();
+ try {
+ LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
+ if (descriptor == null) {
+ Log.i(
+ TAG,
+ "isAutoActiveModeEnabled: groupId: "
+ + groupId
+ + " is not known LeAudio group");
+ return false;
+ }
+ Log.i(
+ TAG,
+ "isAutoActiveModeEnabled: groupId: "
+ + groupId
+ + ", mAutoActiveModeEnabled: "
+ + descriptor.mAutoActiveModeEnabled);
+ return descriptor.mAutoActiveModeEnabled;
+
+ } finally {
+ mGroupReadLock.unlock();
+ }
+ }
+
+ /**
* Check if group is available for streaming. If there is no available context types then group
* is not available for streaming.
*
@@ -4534,9 +4718,21 @@ public class LeAudioService extends ProfileService {
try {
LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
if (descriptor == null) {
- Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId);
+ Log.e(
+ TAG,
+ "isGroupAvailableForStream: No valid descriptor for groupId: " + groupId);
return false;
}
+
+ if (!descriptor.mAutoActiveModeEnabled) {
+ Log.e(
+ TAG,
+ "isGroupAvailableForStream: Auto Active Mode is disabled for groupId: "
+ + groupId);
+ return false;
+ }
+
+ Log.i(TAG, " descriptor.mAvailableContexts: " + descriptor.mAvailableContexts);
return descriptor.mAvailableContexts != null && descriptor.mAvailableContexts != 0;
} finally {
mGroupReadLock.unlock();
@@ -6048,6 +6244,8 @@ public class LeAudioService extends ProfileService {
sb,
"mInactivatedDueToContextType: "
+ groupDescriptor.mInactivatedDueToContextType);
+ ProfileService.println(
+ sb, "mAutoActiveModeEnabled: " + groupDescriptor.mAutoActiveModeEnabled);
for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry :
mDeviceDescriptors.entrySet()) {
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
index ee743f3265..8d1fec5c72 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
@@ -50,6 +50,7 @@ import android.os.Message;
import android.util.Log;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -276,7 +277,12 @@ final class LeAudioStateMachine extends StateMachine {
switch (message.what) {
case CONNECT:
- deferMessage(message);
+ if (Flags.leaudioSmIgnoreConnectEventsInConnectingState()
+ && !hasDeferredMessages(DISCONNECT)) {
+ Log.w(TAG, "Connecting: CONNECT ignored: " + mDevice);
+ } else {
+ deferMessage(message);
+ }
break;
case CONNECT_TIMEOUT:
Log.w(TAG, "Connecting connection timeout: " + mDevice);
diff --git a/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
index 0780d54189..dc7284b94b 100644
--- a/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
+++ b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
@@ -65,8 +65,8 @@ public class AppScanStats {
// ScannerMap here is needed to grab Apps
ScannerMap mScannerMap;
- // TransitionalScanHelper is needed to add scan event protos to be dumped later
- final TransitionalScanHelper mScanHelper;
+ // ScanController is needed to add scan event protos to be dumped later
+ final ScanController mScanController;
// Battery stats is used to keep track of scans and result stats
BatteryStatsManager mBatteryStatsManager;
@@ -173,13 +173,13 @@ public class AppScanStats {
WorkSource source,
ScannerMap map,
AdapterService adapterService,
- TransitionalScanHelper scanHelper,
+ ScanController scanController,
TimeProvider timeProvider) {
mAdapterService = requireNonNull(adapterService);
mTimeProvider = requireNonNull(timeProvider);
mAppName = name;
mScannerMap = map;
- mScanHelper = scanHelper;
+ mScanController = scanController;
mBatteryStatsManager = adapterService.getSystemService(BatteryStatsManager.class);
if (source == null) {
@@ -310,7 +310,7 @@ public class AppScanStats {
.setEventTimeMillis(System.currentTimeMillis())
.setInitiator(truncateAppName(mAppName))
.build();
- mScanHelper.addScanEvent(scanEvent);
+ mScanController.addScanEvent(scanEvent);
if (!isScanning()) {
mScanStartTime = startTime;
@@ -362,7 +362,7 @@ public class AppScanStats {
.setInitiator(truncateAppName(mAppName))
.setNumberResults(scan.results)
.build();
- mScanHelper.addScanEvent(scanEvent);
+ mScanController.addScanEvent(scanEvent);
mTotalScanTime += scanDuration;
long activeDuration = scanDuration - scan.suspendDuration;
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanController.java b/android/app/src/com/android/bluetooth/le_scan/ScanController.java
index e992a8da16..7f39fdaaf1 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScanController.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScanController.java
@@ -16,48 +16,90 @@
package com.android.bluetooth.le_scan;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+
+import static com.android.bluetooth.Utils.checkCallerTargetSdk;
+
import static java.util.Objects.requireNonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUtils;
import android.bluetooth.IBluetoothScan;
+import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.IPeriodicAdvertisingCallback;
import android.bluetooth.le.IScannerCallback;
+import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
+import android.content.Intent;
+import android.net.MacAddress;
+import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.text.format.DateUtils;
import android.util.Log;
import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.bluetooth.R;
+import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.BluetoothAdapterProxy;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.util.NumberUtils;
+import com.android.internal.annotations.VisibleForTesting;
import libcore.util.HexEncoding;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
public class ScanController {
private static final String TAG = ScanController.class.getSimpleName();
- public final TransitionalScanHelper mTransitionalScanHelper;
- public final HandlerThread mScanThread;
+ // Batch scan related constants.
+ private static final int TRUNCATED_RESULT_SIZE = 11;
- private final BluetoothScanBinder mBinder;
+ /** The default floor value for LE batch scan report delays greater than 0 */
+ static final long DEFAULT_REPORT_DELAY_FLOOR = 5000L;
- private boolean mIsAvailable;
+ private static final int NUM_SCAN_EVENTS_KEPT = 20;
- private volatile boolean mTestModeEnabled = false;
- private final Looper mMainLooper;
- private Handler mTestModeHandler;
- private final Object mTestModeLock = new Object();
+ // onFoundLost related constants
+ @VisibleForTesting static final int ADVT_STATE_ONFOUND = 0;
+ private static final int ADVT_STATE_ONLOST = 1;
+
+ private static final int ET_LEGACY_MASK = 0x10;
/** Example raw beacons captured from a Blue Charm BC011 */
private static final String[] TEST_MODE_BEACONS =
@@ -69,15 +111,103 @@ public class ScanController {
"0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000",
};
+ static class PendingIntentInfo {
+ public PendingIntent intent;
+ public ScanSettings settings;
+ public List<ScanFilter> filters;
+ public String callingPackage;
+ public int callingUid;
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof PendingIntentInfo)) {
+ return false;
+ }
+ return intent.equals(((PendingIntentInfo) other).intent);
+ }
+
+ @Override
+ public int hashCode() {
+ return intent == null ? 0 : intent.hashCode();
+ }
+ }
+
+ private final PendingIntent.CancelListener mScanIntentCancelListener =
+ new PendingIntent.CancelListener() {
+ public void onCanceled(PendingIntent intent) {
+ Log.d(TAG, "scanning PendingIntent canceled");
+ stopScanInternal(intent);
+ }
+ };
+
+ private final AdapterService mAdapterService;
+
+ private final HashMap<Integer, Integer> mFilterIndexToMsftAdvMonitorMap = new HashMap<>();
+ private final String mExposureNotificationPackage;
+
+ private final AppOpsManager mAppOps;
+ private final CompanionDeviceManager mCompanionManager;
+ private final PeriodicScanManager mPeriodicScanManager;
+ private final ScanManager mScanManager;
+
+ public final HandlerThread mScanThread;
+
+ private final BluetoothScanBinder mBinder;
+
+ /** Internal list of scan events to use with the proto */
+ private final ArrayDeque<BluetoothMetricsProto.ScanEvent> mScanEvents =
+ new ArrayDeque<>(NUM_SCAN_EVENTS_KEPT);
+
+ private final Predicate<ScanResult> mLocationDenylistPredicate;
+
+ private ScannerMap mScannerMap = new ScannerMap();
+
+ private boolean mIsAvailable;
+
+ private volatile boolean mTestModeEnabled = false;
+ private final Looper mMainLooper;
+ private Handler mTestModeHandler;
+ private final Object mTestModeLock = new Object();
+
public ScanController(AdapterService adapterService) {
- mTransitionalScanHelper =
- new TransitionalScanHelper(requireNonNull(adapterService), () -> mTestModeEnabled);
+ mAdapterService = requireNonNull(adapterService);
+ mExposureNotificationPackage =
+ mAdapterService.getString(R.string.exposure_notification_package);
+ mLocationDenylistPredicate =
+ (scanResult) -> {
+ final MacAddress parsedAddress =
+ MacAddress.fromString(scanResult.getDevice().getAddress());
+ if (mAdapterService
+ .getLocationDenylistMac()
+ .test(parsedAddress.toByteArray())) {
+ Log.v(TAG, "Skipping device matching denylist: " + scanResult.getDevice());
+ return true;
+ }
+ final ScanRecord scanRecord = scanResult.getScanRecord();
+ if (scanRecord.matchesAnyField(
+ mAdapterService.getLocationDenylistAdvertisingData())) {
+ Log.v(TAG, "Skipping data matching denylist: " + scanRecord);
+ return true;
+ }
+ return false;
+ };
mMainLooper = adapterService.getMainLooper();
mBinder = new BluetoothScanBinder(this);
mIsAvailable = true;
mScanThread = new HandlerThread("BluetoothScanManager");
mScanThread.start();
- mTransitionalScanHelper.start(mScanThread.getLooper());
+ mAppOps = mAdapterService.getSystemService(AppOpsManager.class);
+ mCompanionManager = mAdapterService.getSystemService(CompanionDeviceManager.class);
+ mScanManager =
+ ScanObjectsFactory.getInstance()
+ .createScanManager(
+ mAdapterService,
+ this,
+ BluetoothAdapterProxy.getInstance(),
+ mScanThread.getLooper());
+
+ mPeriodicScanManager =
+ ScanObjectsFactory.getInstance().createPeriodicScanManager(mAdapterService);
}
public void stop() {
@@ -85,17 +215,23 @@ public class ScanController {
mIsAvailable = false;
mBinder.clearScanController();
mScanThread.quitSafely();
- mTransitionalScanHelper.stop();
- mTransitionalScanHelper.cleanup();
+ mScannerMap.clear();
+ mScanManager.cleanup();
+ mPeriodicScanManager.cleanup();
}
- /** Notify Scan manager of bluetooth profile connection state changes */
- public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) {
- mTransitionalScanHelper.notifyProfileConnectionStateChange(profile, fromState, toState);
+ ScannerMap getScannerMap() {
+ return mScannerMap;
}
- public TransitionalScanHelper getTransitionalScanHelper() {
- return mTransitionalScanHelper;
+ @VisibleForTesting
+ void setScannerMap(ScannerMap scannerMap) {
+ mScannerMap = scannerMap;
+ }
+
+ /** Notify Scan manager of bluetooth profile connection state changes */
+ public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) {
+ mScanManager.handleBluetoothProfileConnectionStateChanged(profile, fromState, toState);
}
public IBinder getBinder() {
@@ -113,7 +249,7 @@ public class ScanController {
return;
}
for (String test : TEST_MODE_BEACONS) {
- mTransitionalScanHelper.onScanResultInternal(
+ onScanResultInternal(
0x1b,
0x1,
"DD:34:02:05:5C:4D",
@@ -141,18 +277,1456 @@ public class ScanController {
}
}
+ /**************************************************************************
+ * Callback functions - CLIENT
+ *************************************************************************/
+
+ // EN format defined here:
+ // https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
+ private static final byte[] EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE =
+ new byte[] {
+ // size 2, flag field, flags byte (value is not important)
+ (byte) 0x02, (byte) 0x01
+ };
+
+ private static final int EXPOSURE_NOTIFICATION_FLAGS_LENGTH = 0x2 + 1;
+ private static final byte[] EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE =
+ new byte[] {
+ // size 3, complete 16 bit UUID, EN UUID
+ (byte) 0x03, (byte) 0x03, (byte) 0x6F, (byte) 0xFD,
+ // size 23, data for 16 bit UUID, EN UUID
+ (byte) 0x17, (byte) 0x16, (byte) 0x6F, (byte) 0xFD,
+ // ...payload
+ };
+ private static final int EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH = 0x03 + 0x17 + 2;
+
+ private static boolean arrayStartsWith(byte[] array, byte[] prefix) {
+ if (array.length < prefix.length) {
+ return false;
+ }
+ for (int i = 0; i < prefix.length; i++) {
+ if (prefix[i] != array[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private ScanResult getSanitizedExposureNotification(ScanResult result) {
+ ScanRecord record = result.getScanRecord();
+ // Remove the flags part of the payload, if present
+ if (record.getBytes().length > EXPOSURE_NOTIFICATION_FLAGS_LENGTH
+ && arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE)) {
+ record =
+ ScanRecord.parseFromBytes(
+ Arrays.copyOfRange(
+ record.getBytes(),
+ EXPOSURE_NOTIFICATION_FLAGS_LENGTH,
+ record.getBytes().length));
+ }
+
+ if (record.getBytes().length != EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH) {
+ return null;
+ }
+ if (!arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE)) {
+ return null;
+ }
+
+ return new ScanResult(null, 0, 0, 0, 0, 0, result.getRssi(), 0, record, 0);
+ }
+
+ /** Callback method for a scan result. */
+ void onScanResult(
+ int eventType,
+ int addressType,
+ String address,
+ int primaryPhy,
+ int secondaryPhy,
+ int advertisingSid,
+ int txPower,
+ int rssi,
+ int periodicAdvInt,
+ byte[] advData,
+ String originalAddress) {
+ // When in testing mode, ignore all real-world events
+ if (mTestModeEnabled) return;
+
+ AppScanStats.recordScanRadioResultCount();
+ onScanResultInternal(
+ eventType,
+ addressType,
+ address,
+ primaryPhy,
+ secondaryPhy,
+ advertisingSid,
+ txPower,
+ rssi,
+ periodicAdvInt,
+ advData,
+ originalAddress);
+ }
+
+ private void onScanResultInternal(
+ int eventType,
+ int addressType,
+ String address,
+ int primaryPhy,
+ int secondaryPhy,
+ int advertisingSid,
+ int txPower,
+ int rssi,
+ int periodicAdvInt,
+ byte[] advData,
+ String originalAddress) {
+ Log.v(
+ TAG,
+ "onScanResult() - eventType=0x"
+ + Integer.toHexString(eventType)
+ + ", addressType="
+ + addressType
+ + ", address="
+ + BluetoothUtils.toAnonymizedAddress(address)
+ + ", primaryPhy="
+ + primaryPhy
+ + ", secondaryPhy="
+ + secondaryPhy
+ + ", advertisingSid=0x"
+ + Integer.toHexString(advertisingSid)
+ + ", txPower="
+ + txPower
+ + ", rssi="
+ + rssi
+ + ", periodicAdvInt=0x"
+ + Integer.toHexString(periodicAdvInt)
+ + ", originalAddress="
+ + originalAddress);
+
+ String identityAddress = mAdapterService.getIdentityAddress(address);
+ if (!address.equals(identityAddress)) {
+ Log.v(
+ TAG,
+ "found identityAddress of "
+ + address
+ + ", replace originalAddress as "
+ + identityAddress);
+ originalAddress = identityAddress;
+ }
+
+ byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62);
+
+ for (ScanClient client : mScanManager.getRegularScanQueue()) {
+ ScannerMap.ScannerApp app = mScannerMap.getById(client.scannerId);
+ if (app == null) {
+ Log.v(TAG, "App is null; skip.");
+ continue;
+ }
+
+ BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(address, addressType);
+
+ ScanSettings settings = client.settings;
+ byte[] scanRecordData;
+ // This is for compatibility with applications that assume fixed size scan data.
+ if (settings.getLegacy()) {
+ if ((eventType & ET_LEGACY_MASK) == 0) {
+ // If this is legacy scan, but nonlegacy result - skip.
+ Log.v(TAG, "Legacy scan, non legacy result; skip.");
+ continue;
+ } else {
+ // Some apps are used to fixed-size advertise data.
+ scanRecordData = legacyAdvData;
+ }
+ } else {
+ scanRecordData = advData;
+ }
+
+ ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordData);
+ ScanResult result =
+ new ScanResult(
+ device,
+ eventType,
+ primaryPhy,
+ secondaryPhy,
+ advertisingSid,
+ txPower,
+ rssi,
+ periodicAdvInt,
+ scanRecord,
+ SystemClock.elapsedRealtimeNanos());
+
+ if (client.hasDisavowedLocation) {
+ if (mLocationDenylistPredicate.test(result)) {
+ Log.i(TAG, "Skipping client for location deny list");
+ continue;
+ }
+ }
+
+ boolean hasPermission = hasScanResultPermission(client);
+ if (!hasPermission) {
+ for (String associatedDevice : client.associatedDevices) {
+ if (associatedDevice.equalsIgnoreCase(address)) {
+ hasPermission = true;
+ break;
+ }
+ }
+ }
+ if (!hasPermission && client.eligibleForSanitizedExposureNotification) {
+ ScanResult sanitized = getSanitizedExposureNotification(result);
+ if (sanitized != null) {
+ hasPermission = true;
+ result = sanitized;
+ }
+ }
+ boolean matchResult = matchesFilters(client, result, originalAddress);
+ if (!hasPermission || !matchResult) {
+ Log.v(
+ TAG,
+ "Skipping client: permission=" + hasPermission + " matches=" + matchResult);
+ continue;
+ }
+
+ int callbackType = settings.getCallbackType();
+ if (!(callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
+ || callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH)) {
+ Log.v(TAG, "Skipping client: CALLBACK_TYPE_ALL_MATCHES");
+ continue;
+ }
+
+ try {
+ app.mAppScanStats.addResult(client.scannerId);
+ if (app.mCallback != null) {
+ app.mCallback.onScanResult(result);
+ } else {
+ Log.v(TAG, "Callback is null, sending scan results by pendingIntent");
+ // Send the PendingIntent
+ ArrayList<ScanResult> results = new ArrayList<>();
+ results.add(result);
+ sendResultsByPendingIntent(
+ app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+ }
+ } catch (RemoteException | PendingIntent.CanceledException e) {
+ Log.e(TAG, "Exception: " + e);
+ handleDeadScanClient(client);
+ }
+ }
+ }
+
+ private void sendResultByPendingIntent(
+ PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) {
+ ArrayList<ScanResult> results = new ArrayList<>();
+ results.add(result);
+ try {
+ sendResultsByPendingIntent(pii, results, callbackType);
+ } catch (PendingIntent.CanceledException e) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ stopScanInternal(client.scannerId);
+ unregisterScannerInternal(client.scannerId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @SuppressWarnings("NonApiType")
+ private void sendResultsByPendingIntent(
+ PendingIntentInfo pii, ArrayList<ScanResult> results, int callbackType)
+ throws PendingIntent.CanceledException {
+ Intent extrasIntent = new Intent();
+ extrasIntent.putParcelableArrayListExtra(
+ BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, results);
+ extrasIntent.putExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType);
+ pii.intent.send(mAdapterService, 0, extrasIntent);
+ }
+
+ private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode)
+ throws PendingIntent.CanceledException {
+ Intent extrasIntent = new Intent();
+ extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode);
+ pii.intent.send(mAdapterService, 0, extrasIntent);
+ }
+
+ /** Callback method for scanner registration. */
+ void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
+ throws RemoteException {
+ UUID uuid = new UUID(uuidMsb, uuidLsb);
+ Log.d(
+ TAG,
+ "onScannerRegistered() - UUID="
+ + uuid
+ + ", scannerId="
+ + scannerId
+ + ", status="
+ + status);
+
+ // First check the callback map
+ ScannerMap.ScannerApp cbApp = mScannerMap.getByUuid(uuid);
+ if (cbApp != null) {
+ if (status == 0) {
+ cbApp.mId = scannerId;
+ // If app is callback based, setup a death recipient. App will initiate the start.
+ // Otherwise, if PendingIntent based, start the scan directly.
+ if (cbApp.mCallback != null) {
+ cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.mName));
+ } else {
+ continuePiStartScan(scannerId, cbApp);
+ }
+ } else {
+ mScannerMap.remove(scannerId);
+ }
+ if (cbApp.mCallback != null) {
+ cbApp.mCallback.onScannerRegistered(status, scannerId);
+ }
+ }
+ }
+
+ /** Determines if the given scan client has the appropriate permissions to receive callbacks. */
+ private boolean hasScanResultPermission(final ScanClient client) {
+ if (client.hasNetworkSettingsPermission
+ || client.hasNetworkSetupWizardPermission
+ || client.hasScanWithoutLocationPermission) {
+ return true;
+ }
+ if (client.hasDisavowedLocation) {
+ return true;
+ }
+ return client.hasLocationPermission
+ && !Utils.blockedByLocationOff(mAdapterService, client.userHandle);
+ }
+
+ // Check if a scan record matches a specific filters.
+ private boolean matchesFilters(ScanClient client, ScanResult scanResult) {
+ return matchesFilters(client, scanResult, null);
+ }
+
+ // Check if a scan record matches a specific filters or original address
+ private boolean matchesFilters(
+ ScanClient client, ScanResult scanResult, String originalAddress) {
+ if (client.filters == null || client.filters.isEmpty()) {
+ // TODO: Do we really wanna return true here?
+ return true;
+ }
+ for (ScanFilter filter : client.filters) {
+ // Need to check the filter matches, and the original address without changing the API
+ if (filter.matches(scanResult)) {
+ return true;
+ }
+ if (originalAddress != null
+ && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void handleDeadScanClient(ScanClient client) {
+ if (client.appDied) {
+ Log.w(TAG, "Already dead client " + client.scannerId);
+ return;
+ }
+ client.appDied = true;
+ if (client.stats != null) {
+ client.stats.isAppDead = true;
+ }
+ stopScanInternal(client.scannerId);
+ }
+
+ /** Callback method for scan filter enablement/disablement. */
+ void onScanFilterEnableDisabled(int action, int status, int clientIf) {
+ Log.d(
+ TAG,
+ "onScanFilterEnableDisabled() - clientIf="
+ + clientIf
+ + ", status="
+ + status
+ + ", action="
+ + action);
+ mScanManager.callbackDone(clientIf, status);
+ }
+
+ /** Callback method for configuration of scan filter params. */
+ void onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace) {
+ Log.d(
+ TAG,
+ "onScanFilterParamsConfigured() - clientIf="
+ + clientIf
+ + ", status="
+ + status
+ + ", action="
+ + action
+ + ", availableSpace="
+ + availableSpace);
+ mScanManager.callbackDone(clientIf, status);
+ }
+
+ /** Callback method for configuration of scan filter. */
+ void onScanFilterConfig(
+ int action, int status, int clientIf, int filterType, int availableSpace) {
+ Log.d(
+ TAG,
+ "onScanFilterConfig() - clientIf="
+ + clientIf
+ + ", action = "
+ + action
+ + " status = "
+ + status
+ + ", filterType="
+ + filterType
+ + ", availableSpace="
+ + availableSpace);
+
+ mScanManager.callbackDone(clientIf, status);
+ }
+
+ /** Callback method for configuration of batch scan storage. */
+ void onBatchScanStorageConfigured(int status, int clientIf) {
+ Log.d(TAG, "onBatchScanStorageConfigured() - clientIf=" + clientIf + ", status=" + status);
+ mScanManager.callbackDone(clientIf, status);
+ }
+
+ /** Callback method for start/stop of batch scan. */
+ // TODO: split into two different callbacks : onBatchScanStarted and onBatchScanStopped.
+ void onBatchScanStartStopped(int startStopAction, int status, int clientIf) {
+ Log.d(
+ TAG,
+ "onBatchScanStartStopped() - clientIf="
+ + clientIf
+ + ", status="
+ + status
+ + ", startStopAction="
+ + startStopAction);
+ mScanManager.callbackDone(clientIf, status);
+ }
+
+ ScanClient findBatchScanClientById(int scannerId) {
+ for (ScanClient client : mScanManager.getBatchScanQueue()) {
+ if (client.scannerId == scannerId) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+ /** Callback method for batch scan reports */
+ void onBatchScanReports(
+ int status, int scannerId, int reportType, int numRecords, byte[] recordData)
+ throws RemoteException {
+ // When in testing mode, ignore all real-world events
+ if (mTestModeEnabled) return;
+
+ AppScanStats.recordBatchScanRadioResultCount(numRecords);
+ onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData);
+ }
+
+ @VisibleForTesting
+ void onBatchScanReportsInternal(
+ int status, int scannerId, int reportType, int numRecords, byte[] recordData)
+ throws RemoteException {
+ Log.d(
+ TAG,
+ "onBatchScanReports() - scannerId="
+ + scannerId
+ + ", status="
+ + status
+ + ", reportType="
+ + reportType
+ + ", numRecords="
+ + numRecords);
+
+ Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
+ if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
+ // We only support single client for truncated mode.
+ ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
+ if (app == null) {
+ return;
+ }
+
+ ScanClient client = findBatchScanClientById(scannerId);
+ if (client == null) {
+ return;
+ }
+
+ ArrayList<ScanResult> permittedResults;
+ if (hasScanResultPermission(client)) {
+ permittedResults = new ArrayList<ScanResult>(results);
+ } else {
+ permittedResults = new ArrayList<ScanResult>();
+ for (ScanResult scanResult : results) {
+ for (String associatedDevice : client.associatedDevices) {
+ if (associatedDevice.equalsIgnoreCase(
+ scanResult.getDevice().getAddress())) {
+ permittedResults.add(scanResult);
+ }
+ }
+ }
+ }
+
+ if (client.hasDisavowedLocation) {
+ permittedResults.removeIf(mLocationDenylistPredicate);
+ }
+ if (permittedResults.isEmpty()) {
+ mScanManager.callbackDone(scannerId, status);
+ return;
+ }
+
+ if (app.mCallback != null) {
+ app.mCallback.onBatchScanResults(permittedResults);
+ mScanManager.batchScanResultDelivered();
+ } else {
+ // PendingIntent based
+ try {
+ sendResultsByPendingIntent(
+ app.mInfo, permittedResults, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+ } catch (PendingIntent.CanceledException e) {
+ Log.d(TAG, "Exception while sending result", e);
+ }
+ }
+ } else {
+ for (ScanClient client : mScanManager.getFullBatchScanQueue()) {
+ // Deliver results for each client.
+ deliverBatchScan(client, results);
+ }
+ }
+ mScanManager.callbackDone(scannerId, status);
+ }
+
+ @SuppressWarnings("NonApiType")
+ private void sendBatchScanResults(
+ ScannerMap.ScannerApp app, ScanClient client, ArrayList<ScanResult> results) {
+ if (results.isEmpty()) {
+ return;
+ }
+ try {
+ if (app.mCallback != null) {
+ if (mScanManager.isAutoBatchScanClientEnabled(client)) {
+ Log.d(TAG, "sendBatchScanResults() to onScanResult()" + client);
+ for (ScanResult result : results) {
+ app.mAppScanStats.addResult(client.scannerId);
+ app.mCallback.onScanResult(result);
+ }
+ } else {
+ Log.d(TAG, "sendBatchScanResults() to onBatchScanResults()" + client);
+ app.mCallback.onBatchScanResults(results);
+ }
+ } else {
+ sendResultsByPendingIntent(
+ app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+ }
+ } catch (RemoteException | PendingIntent.CanceledException e) {
+ Log.e(TAG, "Exception: " + e);
+ handleDeadScanClient(client);
+ }
+ mScanManager.batchScanResultDelivered();
+ }
+
+ // Check and deliver scan results for different scan clients.
+ private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults)
+ throws RemoteException {
+ ScannerMap.ScannerApp app = mScannerMap.getById(client.scannerId);
+ if (app == null) {
+ return;
+ }
+
+ ArrayList<ScanResult> permittedResults;
+ if (hasScanResultPermission(client)) {
+ permittedResults = new ArrayList<ScanResult>(allResults);
+ } else {
+ permittedResults = new ArrayList<ScanResult>();
+ for (ScanResult scanResult : allResults) {
+ for (String associatedDevice : client.associatedDevices) {
+ if (associatedDevice.equalsIgnoreCase(scanResult.getDevice().getAddress())) {
+ permittedResults.add(scanResult);
+ }
+ }
+ }
+ }
+
+ if (client.filters == null || client.filters.isEmpty()) {
+ sendBatchScanResults(app, client, permittedResults);
+ return;
+ }
+ // Reconstruct the scan results.
+ ArrayList<ScanResult> results = new ArrayList<ScanResult>();
+ for (ScanResult scanResult : permittedResults) {
+ if (matchesFilters(client, scanResult)) {
+ results.add(scanResult);
+ }
+ }
+ sendBatchScanResults(app, client, results);
+ }
+
+ private Set<ScanResult> parseBatchScanResults(
+ int numRecords, int reportType, byte[] batchRecord) {
+ if (numRecords == 0) {
+ return Collections.emptySet();
+ }
+ Log.d(TAG, "current time is " + SystemClock.elapsedRealtimeNanos());
+ if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
+ return parseTruncatedResults(numRecords, batchRecord);
+ } else {
+ return parseFullResults(numRecords, batchRecord);
+ }
+ }
+
+ private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) {
+ Log.d(TAG, "batch record " + Arrays.toString(batchRecord));
+ Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
+ long now = SystemClock.elapsedRealtimeNanos();
+ for (int i = 0; i < numRecords; ++i) {
+ byte[] record =
+ extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE, TRUNCATED_RESULT_SIZE);
+ byte[] address = extractBytes(record, 0, 6);
+ reverse(address);
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+ int rssi = record[8];
+ long timestampNanos = now - parseTimestampNanos(extractBytes(record, 9, 2));
+ results.add(
+ new ScanResult(
+ device, ScanRecord.parseFromBytes(new byte[0]), rssi, timestampNanos));
+ }
+ return results;
+ }
+
+ @VisibleForTesting
+ long parseTimestampNanos(byte[] data) {
+ long timestampUnit = NumberUtils.littleEndianByteArrayToInt(data);
+ // Timestamp is in every 50 ms.
+ return TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
+ }
+
+ private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) {
+ Log.d(TAG, "Batch record : " + Arrays.toString(batchRecord));
+ Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
+ int position = 0;
+ long now = SystemClock.elapsedRealtimeNanos();
+ while (position < batchRecord.length) {
+ byte[] address = extractBytes(batchRecord, position, 6);
+ // TODO: remove temp hack.
+ reverse(address);
+ BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+ position += 6;
+ // Skip address type.
+ position++;
+ // Skip tx power level.
+ position++;
+ int rssi = batchRecord[position++];
+ long timestampNanos = now - parseTimestampNanos(extractBytes(batchRecord, position, 2));
+ position += 2;
+
+ // Combine advertise packet and scan response packet.
+ int advertisePacketLen = batchRecord[position++];
+ byte[] advertiseBytes = extractBytes(batchRecord, position, advertisePacketLen);
+ position += advertisePacketLen;
+ int scanResponsePacketLen = batchRecord[position++];
+ byte[] scanResponseBytes = extractBytes(batchRecord, position, scanResponsePacketLen);
+ position += scanResponsePacketLen;
+ byte[] scanRecord = new byte[advertisePacketLen + scanResponsePacketLen];
+ System.arraycopy(advertiseBytes, 0, scanRecord, 0, advertisePacketLen);
+ System.arraycopy(
+ scanResponseBytes, 0, scanRecord, advertisePacketLen, scanResponsePacketLen);
+ Log.d(TAG, "ScanRecord : " + Arrays.toString(scanRecord));
+ results.add(
+ new ScanResult(
+ device, ScanRecord.parseFromBytes(scanRecord), rssi, timestampNanos));
+ }
+ return results;
+ }
+
+ // Reverse byte array.
+ private void reverse(byte[] address) {
+ int len = address.length;
+ for (int i = 0; i < len / 2; ++i) {
+ byte b = address[i];
+ address[i] = address[len - 1 - i];
+ address[len - 1 - i] = b;
+ }
+ }
+
+ // Helper method to extract bytes from byte array.
+ private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
+ byte[] bytes = new byte[length];
+ System.arraycopy(scanRecord, start, bytes, 0, length);
+ return bytes;
+ }
+
+ void onBatchScanThresholdCrossed(int clientIf) {
+ Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf);
+ flushPendingBatchResultsInternal(clientIf);
+ }
+
+ AdvtFilterOnFoundOnLostInfo createOnTrackAdvFoundLostObject(
+ int clientIf,
+ int advPktLen,
+ byte[] advPkt,
+ int scanRspLen,
+ byte[] scanRsp,
+ int filtIndex,
+ int advState,
+ int advInfoPresent,
+ String address,
+ int addrType,
+ int txPower,
+ int rssiValue,
+ int timeStamp) {
+
+ return new AdvtFilterOnFoundOnLostInfo(
+ clientIf,
+ advPktLen,
+ advPkt,
+ scanRspLen,
+ scanRsp,
+ filtIndex,
+ advState,
+ advInfoPresent,
+ address,
+ addrType,
+ txPower,
+ rssiValue,
+ timeStamp);
+ }
+
+ void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo) throws RemoteException {
+ Log.d(
+ TAG,
+ "onTrackAdvFoundLost() - scannerId= "
+ + trackingInfo.getClientIf()
+ + " address = "
+ + trackingInfo.getAddress()
+ + " addressType = "
+ + trackingInfo.getAddressType()
+ + " adv_state = "
+ + trackingInfo.getAdvState());
+
+ ScannerMap.ScannerApp app = mScannerMap.getById(trackingInfo.getClientIf());
+ if (app == null) {
+ Log.e(TAG, "app is null");
+ return;
+ }
+
+ BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter()
+ .getRemoteLeDevice(
+ trackingInfo.getAddress(), trackingInfo.getAddressType());
+ int advertiserState = trackingInfo.getAdvState();
+ ScanResult result =
+ new ScanResult(
+ device,
+ ScanRecord.parseFromBytes(trackingInfo.getResult()),
+ trackingInfo.getRSSIValue(),
+ SystemClock.elapsedRealtimeNanos());
+
+ for (ScanClient client : mScanManager.getRegularScanQueue()) {
+ if (client.scannerId == trackingInfo.getClientIf()) {
+ ScanSettings settings = client.settings;
+ if ((advertiserState == ADVT_STATE_ONFOUND)
+ && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
+ != 0)) {
+ if (app.mCallback != null) {
+ app.mCallback.onFoundOrLost(true, result);
+ } else {
+ sendResultByPendingIntent(
+ app.mInfo, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client);
+ }
+ } else if ((advertiserState == ADVT_STATE_ONLOST)
+ && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST)
+ != 0)) {
+ if (app.mCallback != null) {
+ app.mCallback.onFoundOrLost(false, result);
+ } else {
+ sendResultByPendingIntent(
+ app.mInfo, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client);
+ }
+ } else {
+ Log.d(
+ TAG,
+ "Not reporting onlost/onfound : "
+ + advertiserState
+ + " scannerId = "
+ + client.scannerId
+ + " callbackType "
+ + settings.getCallbackType());
+ }
+ }
+ }
+ }
+
+ /** Callback method for configuration of scan parameters. */
+ void onScanParamSetupCompleted(int status, int scannerId) {
+ Log.d(TAG, "onScanParamSetupCompleted() - scannerId=" + scannerId + ", status=" + status);
+ ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
+ if (app == null || app.mCallback == null) {
+ Log.e(TAG, "Advertise app or callback is null");
+ return;
+ }
+ }
+
+ // callback from ScanManager for dispatch of errors apps.
+ void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException {
+ ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
+ if (app == null) {
+ Log.e(TAG, "App null");
+ return;
+ }
+ if (app.mCallback != null) {
+ app.mCallback.onScanManagerErrorCallback(errorCode);
+ } else {
+ try {
+ sendErrorByPendingIntent(app.mInfo, errorCode);
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Error sending error code via PendingIntent:" + e);
+ }
+ }
+ }
+
+ int msftMonitorHandleFromFilterIndex(int filterIndex) {
+ if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
+ Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist");
+ return -1;
+ }
+ return mFilterIndexToMsftAdvMonitorMap.get(filterIndex);
+ }
+
+ void onMsftAdvMonitorAdd(int filterIndex, int monitorHandle, int status) {
+ if (status != 0) {
+ Log.e(
+ TAG,
+ "Error adding advertisement monitor with filter index '" + filterIndex + "'");
+ return;
+ }
+ if (mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
+ Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' already added");
+ return;
+ }
+ mFilterIndexToMsftAdvMonitorMap.put(filterIndex, monitorHandle);
+ }
+
+ void onMsftAdvMonitorRemove(int filterIndex, int status) {
+ if (status != 0) {
+ Log.e(
+ TAG,
+ "Error removing advertisement monitor with filter index '" + filterIndex + "'");
+ }
+ if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) {
+ Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist");
+ return;
+ }
+ mFilterIndexToMsftAdvMonitorMap.remove(filterIndex);
+ }
+
+ void onMsftAdvMonitorEnable(int status) {
+ if (status != 0) {
+ Log.e(TAG, "Error enabling advertisement monitor");
+ }
+ }
+
+ /**************************************************************************
+ * GATT Service functions - Shared CLIENT/SERVER
+ *************************************************************************/
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void registerScanner(
+ IScannerCallback callback, WorkSource workSource, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper registerScanner")) {
+ return;
+ }
+
+ enforceImpersonatationPermissionIfNeeded(workSource);
+
+ AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid());
+ if (app != null
+ && app.isScanningTooFrequently()
+ && !Utils.checkCallerHasPrivilegedPermission(mAdapterService)) {
+ Log.e(TAG, "App '" + app.mAppName + "' is scanning too frequently");
+ try {
+ callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception: " + e);
+ }
+ return;
+ }
+ registerScannerInternal(callback, attributionSource, workSource);
+ }
+
+ /** Intended for internal use within the Bluetooth app. Bypass permission check */
+ public void registerScannerInternal(
+ IScannerCallback callback, AttributionSource attrSource, WorkSource workSource) {
+ UUID uuid = UUID.randomUUID();
+ Log.d(TAG, "registerScanner() - UUID=" + uuid);
+
+ mScannerMap.add(uuid, attrSource, workSource, callback, mAdapterService, this);
+ mScanManager.registerScanner(uuid);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private void unregisterScanner(int scannerId, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper unregisterScanner")) {
+ return;
+ }
+
+ unregisterScannerInternal(scannerId);
+ }
+
+ /** Intended for internal use within the Bluetooth app. Bypass permission check */
+ public void unregisterScannerInternal(int scannerId) {
+ Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId);
+ mScannerMap.remove(scannerId);
+ mScanManager.unregisterScanner(scannerId);
+ }
+
+ private List<String> getAssociatedDevices(String callingPackage) {
+ if (mCompanionManager == null) {
+ return Collections.emptyList();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mCompanionManager.getAllAssociations().stream()
+ .filter(
+ info ->
+ info.getPackageName().equals(callingPackage)
+ && !info.isSelfManaged()
+ && info.getDeviceMacAddress() != null)
+ .map(AssociationInfo::getDeviceMacAddress)
+ .map(MacAddress::toString)
+ .collect(Collectors.toList());
+ } catch (SecurityException se) {
+ // Not an app with associated devices
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot check device associations for " + callingPackage, e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return Collections.emptyList();
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private void startScan(
+ int scannerId,
+ ScanSettings settings,
+ List<ScanFilter> filters,
+ AttributionSource attributionSource) {
+ Log.d(TAG, "start scan with filters");
+
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "Starting GATT scan.")) {
+ return;
+ }
+
+ enforcePrivilegedPermissionIfNeeded(settings);
+ String callingPackage = attributionSource.getPackageName();
+ settings = enforceReportDelayFloor(settings);
+ enforcePrivilegedPermissionIfNeeded(filters);
+ final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
+ scanClient.userHandle = Binder.getCallingUserHandle();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ scanClient.eligibleForSanitizedExposureNotification =
+ callingPackage.equals(mExposureNotificationPackage);
+
+ scanClient.hasDisavowedLocation =
+ Utils.hasDisavowedLocationForScan(
+ mAdapterService, attributionSource, mTestModeEnabled);
+
+ scanClient.isQApp =
+ checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q);
+ if (!scanClient.hasDisavowedLocation) {
+ if (scanClient.isQApp) {
+ scanClient.hasLocationPermission =
+ Utils.checkCallerHasFineLocation(
+ mAdapterService, attributionSource, scanClient.userHandle);
+ } else {
+ scanClient.hasLocationPermission =
+ Utils.checkCallerHasCoarseOrFineLocation(
+ mAdapterService, attributionSource, scanClient.userHandle);
+ }
+ }
+ scanClient.hasNetworkSettingsPermission =
+ Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
+ scanClient.hasNetworkSetupWizardPermission =
+ Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
+ scanClient.hasScanWithoutLocationPermission =
+ Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
+ scanClient.associatedDevices = getAssociatedDevices(callingPackage);
+
+ startScan(scannerId, settings, filters, scanClient);
+ }
+
+ /** Intended for internal use within the Bluetooth app. Bypass permission check */
+ public void startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters) {
+ final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
+ scanClient.userHandle = Binder.getCallingUserHandle();
+ scanClient.eligibleForSanitizedExposureNotification = false;
+ scanClient.hasDisavowedLocation = false;
+ scanClient.isQApp = true;
+ scanClient.hasNetworkSettingsPermission =
+ Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
+ scanClient.hasNetworkSetupWizardPermission =
+ Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
+ scanClient.hasScanWithoutLocationPermission =
+ Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
+ scanClient.associatedDevices = Collections.emptyList();
+
+ startScan(scannerId, settings, filters, scanClient);
+ }
+
+ private void startScan(
+ int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient) {
+ AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
+ if (app != null) {
+ scanClient.stats = app;
+ boolean isFilteredScan = (filters != null) && !filters.isEmpty();
+ boolean isCallbackScan = false;
+
+ ScannerMap.ScannerApp cbApp = mScannerMap.getById(scannerId);
+ if (cbApp != null) {
+ isCallbackScan = cbApp.mCallback != null;
+ }
+ app.recordScanStart(
+ settings,
+ filters,
+ isFilteredScan,
+ isCallbackScan,
+ scannerId,
+ cbApp == null ? null : cbApp.mAttributionTag);
+ }
+
+ mScanManager.startScan(scanClient);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private void registerPiAndStartScan(
+ PendingIntent pendingIntent,
+ ScanSettings settings,
+ List<ScanFilter> filters,
+ AttributionSource attributionSource) {
+ Log.d(TAG, "start scan with filters, for PendingIntent");
+
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "Starting GATT scan.")) {
+ return;
+ }
+ enforcePrivilegedPermissionIfNeeded(settings);
+ settings = enforceReportDelayFloor(settings);
+ enforcePrivilegedPermissionIfNeeded(filters);
+ UUID uuid = UUID.randomUUID();
+ String callingPackage = attributionSource.getPackageName();
+ int callingUid = attributionSource.getUid();
+ PendingIntentInfo piInfo = new PendingIntentInfo();
+ piInfo.intent = pendingIntent;
+ piInfo.settings = settings;
+ piInfo.filters = filters;
+ piInfo.callingPackage = callingPackage;
+ piInfo.callingUid = callingUid;
+ Log.d(
+ TAG,
+ "startScan(PI) -"
+ + (" UUID=" + uuid)
+ + (" Package=" + callingPackage)
+ + (" UID=" + callingUid));
+
+ // Don't start scan if the Pi scan already in mScannerMap.
+ if (mScannerMap.getByPendingIntentInfo(piInfo) != null) {
+ Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap.");
+ return;
+ }
+
+ ScannerMap.ScannerApp app =
+ mScannerMap.add(uuid, attributionSource, piInfo, mAdapterService, this);
+
+ app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid());
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ app.mEligibleForSanitizedExposureNotification =
+ callingPackage.equals(mExposureNotificationPackage);
+
+ app.mHasDisavowedLocation =
+ Utils.hasDisavowedLocationForScan(
+ mAdapterService, attributionSource, mTestModeEnabled);
+
+ if (!app.mHasDisavowedLocation) {
+ try {
+ if (checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q)) {
+ app.mHasLocationPermission =
+ Utils.checkCallerHasFineLocation(
+ mAdapterService, attributionSource, app.mUserHandle);
+ } else {
+ app.mHasLocationPermission =
+ Utils.checkCallerHasCoarseOrFineLocation(
+ mAdapterService, attributionSource, app.mUserHandle);
+ }
+ } catch (SecurityException se) {
+ // No need to throw here. Just mark as not granted.
+ app.mHasLocationPermission = false;
+ }
+ }
+ app.mHasNetworkSettingsPermission =
+ Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
+ app.mHasNetworkSetupWizardPermission =
+ Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
+ app.mHasScanWithoutLocationPermission =
+ Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
+ app.mAssociatedDevices = getAssociatedDevices(callingPackage);
+ mScanManager.registerScanner(uuid);
+
+ // If this fails, we should stop the scan immediately.
+ if (!pendingIntent.addCancelListener(Runnable::run, mScanIntentCancelListener)) {
+ Log.d(TAG, "scanning PendingIntent is already cancelled, stopping scan.");
+ stopScan(pendingIntent, attributionSource);
+ }
+ }
+
+ /** Start a scan with pending intent. */
+ @VisibleForTesting
+ void continuePiStartScan(int scannerId, ScannerMap.ScannerApp app) {
+ final PendingIntentInfo piInfo = app.mInfo;
+ final ScanClient scanClient =
+ new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.callingUid);
+ scanClient.hasLocationPermission = app.mHasLocationPermission;
+ scanClient.userHandle = app.mUserHandle;
+ scanClient.isQApp = checkCallerTargetSdk(mAdapterService, app.mName, Build.VERSION_CODES.Q);
+ scanClient.eligibleForSanitizedExposureNotification =
+ app.mEligibleForSanitizedExposureNotification;
+ scanClient.hasNetworkSettingsPermission = app.mHasNetworkSettingsPermission;
+ scanClient.hasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission;
+ scanClient.hasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission;
+ scanClient.associatedDevices = app.mAssociatedDevices;
+ scanClient.hasDisavowedLocation = app.mHasDisavowedLocation;
+
+ AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId);
+ if (scanStats != null) {
+ scanClient.stats = scanStats;
+ boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
+ scanStats.recordScanStart(
+ piInfo.settings,
+ piInfo.filters,
+ isFilteredScan,
+ false,
+ scannerId,
+ app.mAttributionTag);
+ }
+
+ mScanManager.startScan(scanClient);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void flushPendingBatchResults(int scannerId, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper flushPendingBatchResults")) {
+ return;
+ }
+ flushPendingBatchResultsInternal(scannerId);
+ }
+
+ private void flushPendingBatchResultsInternal(int scannerId) {
+ Log.d(TAG, "flushPendingBatchResultsInternal - scannerId=" + scannerId);
+ mScanManager.flushBatchScanResults(new ScanClient(scannerId));
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private void stopScan(int scannerId, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper stopScan")) {
+ return;
+ }
+ stopScanInternal(scannerId);
+ }
+
+ /** Intended for internal use within the Bluetooth app. Bypass permission check */
+ public void stopScanInternal(int scannerId) {
+ int scanQueueSize =
+ mScanManager.getBatchScanQueue().size() + mScanManager.getRegularScanQueue().size();
+ Log.d(TAG, "stopScan() - queue size =" + scanQueueSize);
+
+ AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
+ if (app != null) {
+ app.recordScanStop(scannerId);
+ }
+
+ mScanManager.stopScan(scannerId);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private void stopScan(PendingIntent intent, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper stopScan")) {
+ return;
+ }
+ stopScanInternal(intent);
+ }
+
+ /** Intended for internal use within the Bluetooth app. Bypass permission check */
+ private void stopScanInternal(PendingIntent intent) {
+ PendingIntentInfo pii = new PendingIntentInfo();
+ pii.intent = intent;
+ ScannerMap.ScannerApp app = mScannerMap.getByPendingIntentInfo(pii);
+ Log.v(TAG, "stopScan(PendingIntent): app found = " + app);
+ if (app != null) {
+ intent.removeCancelListener(mScanIntentCancelListener);
+ final int scannerId = app.mId;
+ stopScanInternal(scannerId);
+ // Also unregister the scanner
+ unregisterScannerInternal(scannerId);
+ }
+ }
+
+ /**************************************************************************
+ * PERIODIC SCANNING
+ *************************************************************************/
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void registerSync(
+ ScanResult scanResult,
+ int skip,
+ int timeout,
+ IPeriodicAdvertisingCallback callback,
+ AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper registerSync")) {
+ return;
+ }
+ mPeriodicScanManager.startSync(scanResult, skip, timeout, callback);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void unregisterSync(
+ IPeriodicAdvertisingCallback callback, AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper unregisterSync")) {
+ return;
+ }
+ mPeriodicScanManager.stopSync(callback);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void transferSync(
+ BluetoothDevice bda,
+ int serviceData,
+ int syncHandle,
+ AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper transferSync")) {
+ return;
+ }
+ mPeriodicScanManager.transferSync(bda, serviceData, syncHandle);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ @VisibleForTesting
+ void transferSetInfo(
+ BluetoothDevice bda,
+ int serviceData,
+ int advHandle,
+ IPeriodicAdvertisingCallback callback,
+ AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper transferSetInfo")) {
+ return;
+ }
+ mPeriodicScanManager.transferSetInfo(bda, serviceData, advHandle, callback);
+ }
+
+ @RequiresPermission(BLUETOOTH_SCAN)
+ private int numHwTrackFiltersAvailable(AttributionSource attributionSource) {
+ if (!Utils.checkScanPermissionForDataDelivery(
+ mAdapterService, attributionSource, "ScanHelper numHwTrackFiltersAvailable")) {
+ return 0;
+ }
+ return (mAdapterService.getTotalNumOfTrackableAdvertisements()
+ - mScanManager.getCurrentUsedTrackingAdvertisement());
+ }
+
+ /**
+ * DeathRecipient handler used to unregister applications that disconnect ungracefully (ie.
+ * crash or forced close).
+ */
+ class ScannerDeathRecipient implements IBinder.DeathRecipient {
+ int mScannerId;
+ private String mPackageName;
+
+ ScannerDeathRecipient(int scannerId, String packageName) {
+ mScannerId = scannerId;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(
+ TAG,
+ "Binder is dead - unregistering scanner ("
+ + mPackageName
+ + " "
+ + mScannerId
+ + ")!");
+
+ ScanClient client = getScanClient(mScannerId);
+ if (client != null) {
+ handleDeadScanClient(client);
+ }
+ }
+
+ private ScanClient getScanClient(int clientIf) {
+ for (ScanClient client : mScanManager.getRegularScanQueue()) {
+ if (client.scannerId == clientIf) {
+ return client;
+ }
+ }
+ for (ScanClient client : mScanManager.getBatchScanQueue()) {
+ if (client.scannerId == clientIf) {
+ return client;
+ }
+ }
+ return null;
+ }
+ }
+
+ private boolean needsPrivilegedPermissionForScan(ScanSettings settings) {
+ // BLE scan only mode needs special permission.
+ if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
+ return true;
+ }
+
+ // Regular scan, no special permission.
+ if (settings == null) {
+ return false;
+ }
+
+ // Ambient discovery mode, needs privileged permission.
+ if (settings.getScanMode() == ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY) {
+ return true;
+ }
+
+ // Regular scan, no special permission.
+ if (settings.getReportDelayMillis() == 0) {
+ return false;
+ }
+
+ // Batch scan, truncated mode needs permission.
+ return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_ABBREVIATED;
+ }
+
+ /*
+ * The ScanFilter#setDeviceAddress API overloads are @SystemApi access methods. This
+ * requires that the permissions be BLUETOOTH_PRIVILEGED.
+ */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters) {
+ Log.d(TAG, "enforcePrivilegedPermissionIfNeeded(" + filters + ")");
+ // Some 3p API cases may have null filters, need to allow
+ if (filters != null) {
+ for (ScanFilter filter : filters) {
+ // The only case to enforce here is if there is an address
+ // If there is an address, enforce if the correct combination criteria is met.
+ if (filter.getDeviceAddress() != null) {
+ // At this point we have an address, that means a caller used the
+ // setDeviceAddress(address) public API for the ScanFilter
+ // We don't want to enforce if the type is PUBLIC and the IRK is null
+ // However, if we have a different type that means the caller used a new
+ // @SystemApi such as setDeviceAddress(address, type) or
+ // setDeviceAddress(address, type, irk) which are both @SystemApi and require
+ // permissions to be enforced
+ if (filter.getAddressType() == BluetoothDevice.ADDRESS_TYPE_PUBLIC
+ && filter.getIrk() == null) {
+ // Do not enforce
+ } else {
+ mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void enforcePrivilegedPermissionIfNeeded(ScanSettings settings) {
+ if (needsPrivilegedPermissionForScan(settings)) {
+ mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+ }
+ }
+
+ // Enforce caller has UPDATE_DEVICE_STATS permission, which allows the caller to blame other
+ // apps for Bluetooth usage. A {@link SecurityException} will be thrown if the caller app does
+ // not have UPDATE_DEVICE_STATS permission.
+ @RequiresPermission(UPDATE_DEVICE_STATS)
+ private void enforceImpersonatationPermission() {
+ mAdapterService.enforceCallingOrSelfPermission(
+ UPDATE_DEVICE_STATS, "Need UPDATE_DEVICE_STATS permission");
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void enforceImpersonatationPermissionIfNeeded(WorkSource workSource) {
+ if (workSource != null) {
+ enforceImpersonatationPermission();
+ }
+ }
+
+ /**
+ * Ensures the report delay is either 0 or at least the floor value (5000ms)
+ *
+ * @param settings are the scan settings passed into a request to start le scanning
+ * @return the passed in ScanSettings object if the report delay is 0 or above the floor value;
+ * a new ScanSettings object with the report delay being the floor value if the original
+ * report delay was between 0 and the floor value (exclusive of both)
+ */
+ @VisibleForTesting
+ ScanSettings enforceReportDelayFloor(ScanSettings settings) {
+ if (settings.getReportDelayMillis() == 0) {
+ return settings;
+ }
+
+ // Need to clear identity to pass device config permission check
+ final long callerToken = Binder.clearCallingIdentity();
+ try {
+ long floor =
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_BLUETOOTH,
+ "report_delay",
+ DEFAULT_REPORT_DELAY_FLOOR);
+
+ if (settings.getReportDelayMillis() > floor) {
+ return settings;
+ } else {
+ return new ScanSettings.Builder()
+ .setCallbackType(settings.getCallbackType())
+ .setLegacy(settings.getLegacy())
+ .setMatchMode(settings.getMatchMode())
+ .setNumOfMatches(settings.getNumOfMatches())
+ .setPhy(settings.getPhy())
+ .setReportDelay(floor)
+ .setScanMode(settings.getScanMode())
+ .setScanResultType(settings.getScanResultType())
+ .build();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callerToken);
+ }
+ }
+
+ void addScanEvent(BluetoothMetricsProto.ScanEvent event) {
+ synchronized (mScanEvents) {
+ if (mScanEvents.size() == NUM_SCAN_EVENTS_KEPT) {
+ mScanEvents.remove();
+ }
+ mScanEvents.add(event);
+ }
+ }
+
public void dumpRegisterId(StringBuilder sb) {
sb.append(" Scanner:\n");
- mTransitionalScanHelper.getScannerMap().dumpApps(sb, ProfileService::println);
+ mScannerMap.dumpApps(sb, ProfileService::println);
}
public void dump(StringBuilder sb) {
sb.append("GATT Scanner Map\n");
- mTransitionalScanHelper.getScannerMap().dump(sb);
+ mScannerMap.dump(sb);
}
public void dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder) {
- mTransitionalScanHelper.dumpProto(builder);
+ synchronized (mScanEvents) {
+ builder.addAllScanEvent(mScanEvents);
+ }
}
static class BluetoothScanBinder extends IBluetoothScan.Stub {
@@ -172,7 +1746,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.registerScanner(callback, workSource, attributionSource);
}
@@ -183,7 +1756,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.unregisterScanner(scannerId, attributionSource);
}
@@ -198,7 +1770,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.startScan(scannerId, settings, filters, attributionSource);
}
@@ -213,7 +1784,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.registerPiAndStartScan(intent, settings, filters, attributionSource);
}
@@ -223,7 +1793,7 @@ public class ScanController {
if (mScanController == null) {
return;
}
- mScanController.getTransitionalScanHelper().stopScan(scannerId, attributionSource);
+ mScanController.stopScan(scannerId, attributionSource);
}
@Override
@@ -232,7 +1802,7 @@ public class ScanController {
if (mScanController == null) {
return;
}
- mScanController.getTransitionalScanHelper().stopScan(intent, attributionSource);
+ mScanController.stopScan(intent, attributionSource);
}
@Override
@@ -242,7 +1812,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.flushPendingBatchResults(scannerId, attributionSource);
}
@@ -258,7 +1827,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.registerSync(scanResult, skip, timeout, callback, attributionSource);
}
@@ -269,7 +1837,7 @@ public class ScanController {
if (mScanController == null) {
return;
}
- mScanController.getTransitionalScanHelper().unregisterSync(callback, attributionSource);
+ mScanController.unregisterSync(callback, attributionSource);
}
@Override
@@ -283,7 +1851,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.transferSync(bda, serviceData, syncHandle, attributionSource);
}
@@ -299,7 +1866,6 @@ public class ScanController {
return;
}
mScanController
- .getTransitionalScanHelper()
.transferSetInfo(bda, serviceData, advHandle, callback, attributionSource);
}
@@ -310,7 +1876,6 @@ public class ScanController {
return 0;
}
return mScanController
- .getTransitionalScanHelper()
.numHwTrackFiltersAvailable(attributionSource);
}
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java
index 453b5849fd..e946b3afcf 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java
@@ -85,7 +85,7 @@ public class ScanManager {
public static final int SCAN_MODE_SCREEN_OFF_BALANCED_WINDOW_MS = 183;
public static final int SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS = 730;
- // Result type defined in bt stack. Need to be accessed by TransitionalScanHelper.
+ // Result type defined in bt stack. Need to be accessed by ScanController.
static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
static final int SCAN_RESULT_TYPE_FULL = 2;
static final int SCAN_RESULT_TYPE_BOTH = 3;
@@ -119,7 +119,7 @@ public class ScanManager {
@GuardedBy("mCurUsedTrackableAdvertisementsLock")
private int mCurUsedTrackableAdvertisements = 0;
- private final TransitionalScanHelper mScanHelper;
+ private final ScanController mScanController;
private final AdapterService mAdapterService;
private final TimeProvider mTimeProvider;
private ScanNative mScanNative;
@@ -146,6 +146,7 @@ public class ScanManager {
@VisibleForTesting boolean mIsConnecting;
@VisibleForTesting int mProfilesConnecting;
private int mProfilesConnected, mProfilesDisconnecting;
+ private final BatchScanThrottler mBatchScanThrottler;
@VisibleForTesting
static class UidImportance {
@@ -158,9 +159,9 @@ public class ScanManager {
}
}
- public ScanManager(
+ ScanManager(
AdapterService adapterService,
- TransitionalScanHelper scanHelper,
+ ScanController scanController,
BluetoothAdapterProxy bluetoothAdapterProxy,
Looper looper,
TimeProvider timeProvider) {
@@ -169,10 +170,10 @@ public class ScanManager {
mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
mSuspendedScanClients =
Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
- mScanHelper = scanHelper;
+ mScanController = scanController;
mAdapterService = adapterService;
mTimeProvider = timeProvider;
- mScanNative = new ScanNative(scanHelper);
+ mScanNative = new ScanNative(scanController);
mDisplayManager = mAdapterService.getSystemService(DisplayManager.class);
mActivityManager = mAdapterService.getSystemService(ActivityManager.class);
mLocationManager = mAdapterService.getSystemService(LocationManager.class);
@@ -202,13 +203,13 @@ public class ScanManager {
IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
locationIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mAdapterService.registerReceiver(mLocationReceiver, locationIntentFilter);
+ mBatchScanThrottler = new BatchScanThrottler(timeProvider, mScreenOn);
}
- public void cleanup() {
+ void cleanup() {
mRegularScanClients.clear();
mBatchClients.clear();
mSuspendedScanClients.clear();
- mScanNative.cleanup();
if (mActivityManager != null) {
try {
@@ -225,6 +226,8 @@ public class ScanManager {
// Shut down the thread
mHandler.removeCallbacksAndMessages(null);
+ mScanNative.cleanup();
+
try {
mAdapterService.unregisterReceiver(mLocationReceiver);
} catch (IllegalArgumentException e) {
@@ -232,16 +235,16 @@ public class ScanManager {
}
}
- public void registerScanner(UUID uuid) {
+ void registerScanner(UUID uuid) {
mScanNative.registerScanner(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
}
- public void unregisterScanner(int scannerId) {
+ void unregisterScanner(int scannerId) {
mScanNative.unregisterScanner(scannerId);
}
/** Returns the regular scan queue. */
- public Set<ScanClient> getRegularScanQueue() {
+ Set<ScanClient> getRegularScanQueue() {
return mRegularScanClients;
}
@@ -251,12 +254,12 @@ public class ScanManager {
}
/** Returns batch scan queue. */
- public Set<ScanClient> getBatchScanQueue() {
+ Set<ScanClient> getBatchScanQueue() {
return mBatchClients;
}
/** Returns a set of full batch scan clients. */
- public Set<ScanClient> getFullBatchScanQueue() {
+ Set<ScanClient> getFullBatchScanQueue() {
// TODO: split full batch scan clients and truncated batch clients so we don't need to
// construct this every time.
Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
@@ -268,12 +271,12 @@ public class ScanManager {
return fullBatchClients;
}
- public void startScan(ScanClient client) {
+ void startScan(ScanClient client) {
Log.d(TAG, "startScan() " + client);
sendMessage(MSG_START_BLE_SCAN, client);
}
- public void stopScan(int scannerId) {
+ void stopScan(int scannerId) {
ScanClient client = mScanNative.getBatchScanClient(scannerId);
if (client == null) {
client = mScanNative.getRegularScanClient(scannerId);
@@ -284,14 +287,18 @@ public class ScanManager {
sendMessage(MSG_STOP_BLE_SCAN, client);
}
- public void flushBatchScanResults(ScanClient client) {
+ void flushBatchScanResults(ScanClient client) {
sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
}
- public void callbackDone(int scannerId, int status) {
+ void callbackDone(int scannerId, int status) {
mScanNative.callbackDone(scannerId, status);
}
+ void batchScanResultDelivered() {
+ mBatchScanThrottler.resetBackoff();
+ }
+
private void sendMessage(int what, ScanClient client) {
mHandler.obtainMessage(what, client).sendToTarget();
}
@@ -304,10 +311,16 @@ public class ScanManager {
return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported();
}
- public boolean isAutoBatchScanClientEnabled(ScanClient client) {
+ boolean isAutoBatchScanClientEnabled(ScanClient client) {
return mScanNative.isAutoBatchScanClientEnabled(client);
}
+ int getCurrentUsedTrackingAdvertisement() {
+ synchronized (mCurUsedTrackableAdvertisementsLock) {
+ return mCurUsedTrackableAdvertisements;
+ }
+ }
+
// Handler class that handles BLE scan operations.
@VisibleForTesting
class ClientHandler extends Handler {
@@ -492,7 +505,7 @@ public class ScanManager {
}
if (client.appDied) {
Log.d(TAG, "app died, unregister scanner - " + client.scannerId);
- mScanHelper.unregisterScannerInternal(client.scannerId);
+ mScanController.unregisterScannerInternal(client.scannerId);
}
}
@@ -533,6 +546,7 @@ public class ScanManager {
}
mScreenOn = false;
Log.d(TAG, "handleScreenOff()");
+ mBatchScanThrottler.onScreenOn(false);
handleSuspendScans();
updateRegularScanClientsScreenOff();
updateRegularScanToBatchScanClients();
@@ -863,6 +877,7 @@ public class ScanManager {
}
mScreenOn = true;
Log.d(TAG, "handleScreenOn()");
+ mBatchScanThrottler.onScreenOn(true);
updateBatchScanToRegularScanClients();
handleResumeScans();
updateRegularScanClientsScreenOn();
@@ -920,14 +935,14 @@ public class ScanManager {
/** Parameters for batch scans. */
static class BatchScanParams {
- public int scanMode;
- public int fullScanscannerId;
- public int truncatedScanscannerId;
+ @VisibleForTesting int mScanMode;
+ private int mFullScanscannerId;
+ private int mTruncatedScanscannerId;
BatchScanParams() {
- scanMode = -1;
- fullScanscannerId = -1;
- truncatedScanscannerId = -1;
+ mScanMode = -1;
+ mFullScanscannerId = -1;
+ mTruncatedScanscannerId = -1;
}
@Override
@@ -938,20 +953,14 @@ public class ScanManager {
if (!(obj instanceof BatchScanParams other)) {
return false;
}
- return scanMode == other.scanMode
- && fullScanscannerId == other.fullScanscannerId
- && truncatedScanscannerId == other.truncatedScanscannerId;
+ return mScanMode == other.mScanMode
+ && mFullScanscannerId == other.mFullScanscannerId
+ && mTruncatedScanscannerId == other.mTruncatedScanscannerId;
}
@Override
public int hashCode() {
- return Objects.hash(scanMode, fullScanscannerId, truncatedScanscannerId);
- }
- }
-
- public int getCurrentUsedTrackingAdvertisement() {
- synchronized (mCurUsedTrackableAdvertisementsLock) {
- return mCurUsedTrackableAdvertisements;
+ return Objects.hash(mScanMode, mFullScanscannerId, mTruncatedScanscannerId);
}
}
@@ -1010,9 +1019,9 @@ public class ScanManager {
private final MsftAdvMonitorMergedPatternList mMsftAdvMonitorMergedPatternList =
new MsftAdvMonitorMergedPatternList();
- ScanNative(TransitionalScanHelper scanHelper) {
+ ScanNative(ScanController scanController) {
mNativeInterface = ScanObjectsFactory.getInstance().getScanNativeInterface();
- mNativeInterface.init(scanHelper);
+ mNativeInterface.init(scanController);
mFilterIndexStack = new ArrayDeque<Integer>();
mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
@@ -1261,9 +1270,9 @@ public class ScanManager {
waitForCallback();
resetCountDownLatch();
int scanInterval =
- Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
+ Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.mScanMode));
int scanWindow =
- Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
+ Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.mScanMode));
mNativeInterface.gattClientStartBatchScan(
scannerId,
resultType,
@@ -1297,15 +1306,15 @@ public class ScanManager {
BatchScanParams params = new BatchScanParams();
ScanClient winner = getAggressiveClient(mBatchClients);
if (winner != null) {
- params.scanMode = winner.settings.getScanMode();
+ params.mScanMode = winner.settings.getScanMode();
}
// TODO: split full batch scan results and truncated batch scan results to different
// collections.
for (ScanClient client : mBatchClients) {
if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
- params.fullScanscannerId = client.scannerId;
+ params.mFullScanscannerId = client.scannerId;
} else {
- params.truncatedScanscannerId = client.scannerId;
+ params.mTruncatedScanscannerId = client.scannerId;
}
}
return params;
@@ -1358,7 +1367,10 @@ public class ScanManager {
if (mBatchClients.isEmpty()) {
return;
}
- long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
+ long batchTriggerIntervalMillis =
+ Flags.batchScanOptimization()
+ ? mBatchScanThrottler.getBatchTriggerIntervalMillis(mBatchClients)
+ : getBatchTriggerIntervalMillis();
// Allows the alarm to be triggered within
// [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
long windowLengthMillis = batchTriggerIntervalMillis / 10;
@@ -1386,7 +1398,7 @@ public class ScanManager {
"Error freeing for onfound/onlost filter resources "
+ entriesToFreePerFilter);
try {
- mScanHelper.onScanManagerErrorCallback(
+ mScanController.onScanManagerErrorCallback(
client.scannerId, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
} catch (RemoteException e) {
Log.e(TAG, "failed on onScanManagerCallback at freeing", e);
@@ -1493,16 +1505,16 @@ public class ScanManager {
void flushBatchResults(int scannerId) {
Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId);
- if (mBatchScanParams.fullScanscannerId != -1) {
+ if (mBatchScanParams.mFullScanscannerId != -1) {
resetCountDownLatch();
mNativeInterface.gattClientReadScanReports(
- mBatchScanParams.fullScanscannerId, SCAN_RESULT_TYPE_FULL);
+ mBatchScanParams.mFullScanscannerId, SCAN_RESULT_TYPE_FULL);
waitForCallback();
}
- if (mBatchScanParams.truncatedScanscannerId != -1) {
+ if (mBatchScanParams.mTruncatedScanscannerId != -1) {
resetCountDownLatch();
mNativeInterface.gattClientReadScanReports(
- mBatchScanParams.truncatedScanscannerId, SCAN_RESULT_TYPE_TRUNCATED);
+ mBatchScanParams.mTruncatedScanscannerId, SCAN_RESULT_TYPE_TRUNCATED);
waitForCallback();
}
setBatchAlarm();
@@ -1587,7 +1599,7 @@ public class ScanManager {
mAdapterService.getTotalNumOfTrackableAdvertisements());
}
try {
- mScanHelper.onScanManagerErrorCallback(
+ mScanController.onScanManagerErrorCallback(
scannerId, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
} catch (RemoteException e) {
Log.e(TAG, "failed on onScanManagerCallback", e);
@@ -1661,13 +1673,13 @@ public class ScanManager {
/** Return batch scan result type value defined in bt stack. */
private int getResultType(BatchScanParams params) {
- if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) {
+ if (params.mFullScanscannerId != -1 && params.mTruncatedScanscannerId != -1) {
return SCAN_RESULT_TYPE_BOTH;
}
- if (params.truncatedScanscannerId != -1) {
+ if (params.mTruncatedScanscannerId != -1) {
return SCAN_RESULT_TYPE_TRUNCATED;
}
- if (params.fullScanscannerId != -1) {
+ if (params.mFullScanscannerId != -1) {
return SCAN_RESULT_TYPE_FULL;
}
return -1;
@@ -2113,7 +2125,7 @@ public class ScanManager {
new ActivityManager.OnUidImportanceListener() {
@Override
public void onUidImportance(final int uid, final int importance) {
- if (mScanHelper.getScannerMap().getAppScanStatsByUid(uid) != null) {
+ if (mScanController.getScannerMap().getAppScanStatsByUid(uid) != null) {
Message message = new Message();
message.what = MSG_IMPORTANCE_CHANGE;
message.obj = new UidImportance(uid, importance);
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanNativeInterface.java b/android/app/src/com/android/bluetooth/le_scan/ScanNativeInterface.java
index 9f0ebcc979..1b14b7e500 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScanNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScanNativeInterface.java
@@ -33,7 +33,7 @@ public class ScanNativeInterface {
private static ScanNativeInterface sInterface;
private CountDownLatch mLatch = new CountDownLatch(1);
- @Nullable private TransitionalScanHelper mScanHelper;
+ @Nullable private ScanController mScanController;
private ScanNativeInterface() {}
@@ -59,8 +59,8 @@ public class ScanNativeInterface {
}
}
- void init(TransitionalScanHelper scanHelper) {
- mScanHelper = scanHelper;
+ void init(ScanController scanController) {
+ mScanController = scanController;
initializeNative();
}
@@ -202,7 +202,7 @@ public class ScanNativeInterface {
/** Remove a MSFT Advertisement Monitor */
public void gattClientMsftAdvMonitorRemove(int filter_index) {
- int monitor_handle = mScanHelper.msftMonitorHandleFromFilterIndex(filter_index);
+ int monitor_handle = mScanController.msftMonitorHandleFromFilterIndex(filter_index);
if (monitor_handle < 0) return;
gattClientMsftAdvMonitorRemoveNative(filter_index, monitor_handle);
}
@@ -278,11 +278,11 @@ public class ScanNativeInterface {
int periodicAdvInt,
byte[] advData,
String originalAddress) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScanResult(
+ mScanController.onScanResult(
eventType,
addressType,
address,
@@ -298,70 +298,70 @@ public class ScanNativeInterface {
void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
throws RemoteException {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScannerRegistered(status, scannerId, uuidLsb, uuidMsb);
+ mScanController.onScannerRegistered(status, scannerId, uuidLsb, uuidMsb);
}
void onScanFilterEnableDisabled(int action, int status, int clientIf) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScanFilterEnableDisabled(action, status, clientIf);
+ mScanController.onScanFilterEnableDisabled(action, status, clientIf);
}
void onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScanFilterParamsConfigured(action, status, clientIf, availableSpace);
+ mScanController.onScanFilterParamsConfigured(action, status, clientIf, availableSpace);
}
void onScanFilterConfig(
int action, int status, int clientIf, int filterType, int availableSpace) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScanFilterConfig(action, status, clientIf, filterType, availableSpace);
+ mScanController.onScanFilterConfig(action, status, clientIf, filterType, availableSpace);
}
void onBatchScanStorageConfigured(int status, int clientIf) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onBatchScanStorageConfigured(status, clientIf);
+ mScanController.onBatchScanStorageConfigured(status, clientIf);
}
void onBatchScanStartStopped(int startStopAction, int status, int clientIf) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onBatchScanStartStopped(startStopAction, status, clientIf);
+ mScanController.onBatchScanStartStopped(startStopAction, status, clientIf);
}
void onBatchScanReports(
int status, int scannerId, int reportType, int numRecords, byte[] recordData)
throws RemoteException {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onBatchScanReports(status, scannerId, reportType, numRecords, recordData);
+ mScanController.onBatchScanReports(status, scannerId, reportType, numRecords, recordData);
}
void onBatchScanThresholdCrossed(int clientIf) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onBatchScanThresholdCrossed(clientIf);
+ mScanController.onBatchScanThresholdCrossed(clientIf);
}
@Nullable
@@ -379,11 +379,11 @@ public class ScanNativeInterface {
int txPower,
int rssiValue,
int timeStamp) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return null;
}
- return mScanHelper.createOnTrackAdvFoundLostObject(
+ return mScanController.createOnTrackAdvFoundLostObject(
clientIf,
advPktLen,
advPkt,
@@ -400,42 +400,42 @@ public class ScanNativeInterface {
}
void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo) throws RemoteException {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onTrackAdvFoundLost(trackingInfo);
+ mScanController.onTrackAdvFoundLost(trackingInfo);
}
void onScanParamSetupCompleted(int status, int scannerId) throws RemoteException {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onScanParamSetupCompleted(status, scannerId);
+ mScanController.onScanParamSetupCompleted(status, scannerId);
}
void onMsftAdvMonitorAdd(int filter_index, int monitor_handle, int status) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onMsftAdvMonitorAdd(filter_index, monitor_handle, status);
+ mScanController.onMsftAdvMonitorAdd(filter_index, monitor_handle, status);
}
void onMsftAdvMonitorRemove(int filter_index, int status) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onMsftAdvMonitorRemove(filter_index, status);
+ mScanController.onMsftAdvMonitorRemove(filter_index, status);
}
void onMsftAdvMonitorEnable(int status) {
- if (mScanHelper == null) {
+ if (mScanController == null) {
Log.e(TAG, "Scan helper is null!");
return;
}
- mScanHelper.onMsftAdvMonitorEnable(status);
+ mScanController.onMsftAdvMonitorEnable(status);
}
}
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java b/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java
index f3fba49c79..3d5521786c 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java
@@ -68,18 +68,18 @@ public class ScanObjectsFactory {
* Create an instance of ScanManager
*
* @param adapterService an AdapterService instance
- * @param scanHelper a TransitionalScanHelper instance
+ * @param scanController a ScanController instance
* @param bluetoothAdapterProxy a bluetoothAdapterProxy instance
* @param looper the looper to be used for processing messages
* @return the created ScanManager instance
*/
public ScanManager createScanManager(
AdapterService adapterService,
- TransitionalScanHelper scanHelper,
+ ScanController scanController,
BluetoothAdapterProxy bluetoothAdapterProxy,
Looper looper) {
return new ScanManager(
- adapterService, scanHelper, bluetoothAdapterProxy, looper, getSystemClock());
+ adapterService, scanController, bluetoothAdapterProxy, looper, getSystemClock());
}
public PeriodicScanManager createPeriodicScanManager(AdapterService adapterService) {
diff --git a/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java b/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java
index 00d9641032..a31f6b1c75 100644
--- a/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java
+++ b/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java
@@ -56,18 +56,25 @@ public class ScannerMap {
WorkSource workSource,
IScannerCallback callback,
AdapterService adapterService,
- TransitionalScanHelper scanHelper) {
- return add(uuid, attributionSource, workSource, callback, null, adapterService, scanHelper);
+ ScanController scanController) {
+ return add(
+ uuid,
+ attributionSource,
+ workSource,
+ callback,
+ null,
+ adapterService,
+ scanController);
}
/** Add an entry to the application context list with a pending intent. */
ScannerApp add(
UUID uuid,
AttributionSource attributionSource,
- TransitionalScanHelper.PendingIntentInfo piInfo,
+ ScanController.PendingIntentInfo piInfo,
AdapterService adapterService,
- TransitionalScanHelper scanHelper) {
- return add(uuid, attributionSource, null, null, piInfo, adapterService, scanHelper);
+ ScanController scanController) {
+ return add(uuid, attributionSource, null, null, piInfo, adapterService, scanController);
}
private ScannerApp add(
@@ -75,9 +82,9 @@ public class ScannerMap {
AttributionSource attributionSource,
@Nullable WorkSource workSource,
@Nullable IScannerCallback callback,
- @Nullable TransitionalScanHelper.PendingIntentInfo piInfo,
+ @Nullable ScanController.PendingIntentInfo piInfo,
AdapterService adapterService,
- TransitionalScanHelper scanHelper) {
+ ScanController scanController) {
int appUid;
String appName = null;
if (piInfo != null) {
@@ -99,7 +106,7 @@ public class ScannerMap {
workSource,
this,
adapterService,
- scanHelper,
+ scanController,
getSystemClock());
mAppScanStatsMap.put(appUid, appScanStats);
}
@@ -175,7 +182,7 @@ public class ScannerMap {
}
/** Get an application context by the pending intent info object. */
- ScannerApp getByPendingIntentInfo(TransitionalScanHelper.PendingIntentInfo info) {
+ ScannerApp getByPendingIntentInfo(ScanController.PendingIntentInfo info) {
ScannerApp app =
getAppByPredicate(entry -> entry.mInfo != null && entry.mInfo.equals(info));
if (app == null) {
@@ -219,7 +226,7 @@ public class ScannerMap {
public static class ScannerApp {
/** Context information */
- @Nullable TransitionalScanHelper.PendingIntentInfo mInfo;
+ @Nullable ScanController.PendingIntentInfo mInfo;
/** Statistics for this app */
AppScanStats mAppScanStats;
@@ -269,7 +276,7 @@ public class ScannerMap {
UUID uuid,
@Nullable String attributionTag,
@Nullable IScannerCallback callback,
- @Nullable TransitionalScanHelper.PendingIntentInfo info,
+ @Nullable ScanController.PendingIntentInfo info,
String name,
AppScanStats appScanStats) {
this.mUuid = uuid;
diff --git a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java b/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java
deleted file mode 100644
index 102d0d0649..0000000000
--- a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java
+++ /dev/null
@@ -1,1674 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.le_scan;
-
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
-import static android.Manifest.permission.BLUETOOTH_SCAN;
-import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-
-import static com.android.bluetooth.Utils.checkCallerTargetSdk;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothUtils;
-import android.bluetooth.le.BluetoothLeScanner;
-import android.bluetooth.le.IPeriodicAdvertisingCallback;
-import android.bluetooth.le.IScannerCallback;
-import android.bluetooth.le.ScanCallback;
-import android.bluetooth.le.ScanFilter;
-import android.bluetooth.le.ScanRecord;
-import android.bluetooth.le.ScanResult;
-import android.bluetooth.le.ScanSettings;
-import android.companion.AssociationInfo;
-import android.companion.CompanionDeviceManager;
-import android.content.AttributionSource;
-import android.content.Intent;
-import android.net.MacAddress;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.provider.DeviceConfig;
-import android.util.Log;
-
-import com.android.bluetooth.BluetoothMetricsProto;
-import com.android.bluetooth.R;
-import com.android.bluetooth.Utils;
-import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.BluetoothAdapterProxy;
-import com.android.bluetooth.gatt.GattServiceConfig;
-import com.android.bluetooth.util.NumberUtils;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-/**
- * A helper class which contains all scan related functions extracted from {@link
- * com.android.bluetooth.gatt.GattService}. The purpose of this class is to preserve scan
- * functionality within GattService and provide the same functionality in {@link ScanController}.
- */
-public class TransitionalScanHelper {
- private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanHelper";
-
- // Batch scan related constants.
- private static final int TRUNCATED_RESULT_SIZE = 11;
-
- /** The default floor value for LE batch scan report delays greater than 0 */
- @VisibleForTesting static final long DEFAULT_REPORT_DELAY_FLOOR = 5000;
-
- private static final int NUM_SCAN_EVENTS_KEPT = 20;
-
- // onFoundLost related constants
- @VisibleForTesting static final int ADVT_STATE_ONFOUND = 0;
- private static final int ADVT_STATE_ONLOST = 1;
-
- private static final int ET_LEGACY_MASK = 0x10;
-
- /** Keep the arguments passed in for the PendingIntent. */
- public static class PendingIntentInfo {
- public PendingIntent intent;
- public ScanSettings settings;
- public List<ScanFilter> filters;
- public String callingPackage;
- public int callingUid;
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof PendingIntentInfo)) {
- return false;
- }
- return intent.equals(((PendingIntentInfo) other).intent);
- }
-
- @Override
- public int hashCode() {
- return intent == null ? 0 : intent.hashCode();
- }
- }
-
- public interface TestModeAccessor {
- /** Indicates if bluetooth test mode is enabled. */
- boolean isTestModeEnabled();
- }
-
- private final PendingIntent.CancelListener mScanIntentCancelListener =
- new PendingIntent.CancelListener() {
- public void onCanceled(PendingIntent intent) {
- Log.d(TAG, "scanning PendingIntent canceled");
- stopScanInternal(intent);
- }
- };
-
- private final AdapterService mAdapterService;
- private final TestModeAccessor mTestModeAccessor;
- private final HashMap<Integer, Integer> mFilterIndexToMsftAdvMonitorMap = new HashMap<>();
- private final String mExposureNotificationPackage;
-
- private AppOpsManager mAppOps;
- private CompanionDeviceManager mCompanionManager;
- private PeriodicScanManager mPeriodicScanManager;
- private ScanManager mScanManager;
-
- private ScannerMap mScannerMap = new ScannerMap();
-
- public ScannerMap getScannerMap() {
- return mScannerMap;
- }
-
- @VisibleForTesting
- public void setScannerMap(ScannerMap scannerMap) {
- mScannerMap = scannerMap;
- }
-
- /** Internal list of scan events to use with the proto */
- private final ArrayDeque<BluetoothMetricsProto.ScanEvent> mScanEvents =
- new ArrayDeque<>(NUM_SCAN_EVENTS_KEPT);
-
- private final Predicate<ScanResult> mLocationDenylistPredicate;
-
- public TransitionalScanHelper(
- AdapterService adapterService, TestModeAccessor testModeAccessor) {
- mAdapterService = requireNonNull(adapterService);
- mExposureNotificationPackage =
- mAdapterService.getString(R.string.exposure_notification_package);
- mTestModeAccessor = testModeAccessor;
- mLocationDenylistPredicate =
- (scanResult) -> {
- final MacAddress parsedAddress =
- MacAddress.fromString(scanResult.getDevice().getAddress());
- if (mAdapterService
- .getLocationDenylistMac()
- .test(parsedAddress.toByteArray())) {
- Log.v(TAG, "Skipping device matching denylist: " + scanResult.getDevice());
- return true;
- }
- final ScanRecord scanRecord = scanResult.getScanRecord();
- if (scanRecord.matchesAnyField(
- mAdapterService.getLocationDenylistAdvertisingData())) {
- Log.v(TAG, "Skipping data matching denylist: " + scanRecord);
- return true;
- }
- return false;
- };
- }
-
- /**
- * Starts the LE scanning component.
- *
- * @param looper for scan operations
- */
- public void start(Looper looper) {
- mAppOps = mAdapterService.getSystemService(AppOpsManager.class);
- mCompanionManager = mAdapterService.getSystemService(CompanionDeviceManager.class);
- mScanManager =
- ScanObjectsFactory.getInstance()
- .createScanManager(
- mAdapterService,
- this,
- BluetoothAdapterProxy.getInstance(),
- looper);
-
- mPeriodicScanManager =
- ScanObjectsFactory.getInstance().createPeriodicScanManager(mAdapterService);
- }
-
- /** Stops the scanning component. */
- public void stop() {
- mScannerMap.clear();
- }
-
- /** Cleans up the scanning component. */
- public void cleanup() {
- if (mScanManager != null) {
- mScanManager.cleanup();
- }
- if (mPeriodicScanManager != null) {
- mPeriodicScanManager.cleanup();
- }
- }
-
- /** Notifies scan manager of bluetooth profile connection state changes */
- public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) {
- if (mScanManager == null) {
- Log.w(TAG, "scan manager is null");
- return;
- }
- mScanManager.handleBluetoothProfileConnectionStateChanged(profile, fromState, toState);
- }
-
- public int getCurrentUsedTrackingAdvertisement() {
- return mScanManager.getCurrentUsedTrackingAdvertisement();
- }
-
- /**************************************************************************
- * Callback functions - CLIENT
- *************************************************************************/
-
- // EN format defined here:
- // https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
- private static final byte[] EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE =
- new byte[] {
- // size 2, flag field, flags byte (value is not important)
- (byte) 0x02, (byte) 0x01
- };
-
- private static final int EXPOSURE_NOTIFICATION_FLAGS_LENGTH = 0x2 + 1;
- private static final byte[] EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE =
- new byte[] {
- // size 3, complete 16 bit UUID, EN UUID
- (byte) 0x03, (byte) 0x03, (byte) 0x6F, (byte) 0xFD,
- // size 23, data for 16 bit UUID, EN UUID
- (byte) 0x17, (byte) 0x16, (byte) 0x6F, (byte) 0xFD,
- // ...payload
- };
- private static final int EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH = 0x03 + 0x17 + 2;
-
- private static boolean arrayStartsWith(byte[] array, byte[] prefix) {
- if (array.length < prefix.length) {
- return false;
- }
- for (int i = 0; i < prefix.length; i++) {
- if (prefix[i] != array[i]) {
- return false;
- }
- }
- return true;
- }
-
- private ScanResult getSanitizedExposureNotification(ScanResult result) {
- ScanRecord record = result.getScanRecord();
- // Remove the flags part of the payload, if present
- if (record.getBytes().length > EXPOSURE_NOTIFICATION_FLAGS_LENGTH
- && arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE)) {
- record =
- ScanRecord.parseFromBytes(
- Arrays.copyOfRange(
- record.getBytes(),
- EXPOSURE_NOTIFICATION_FLAGS_LENGTH,
- record.getBytes().length));
- }
-
- if (record.getBytes().length != EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH) {
- return null;
- }
- if (!arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE)) {
- return null;
- }
-
- return new ScanResult(null, 0, 0, 0, 0, 0, result.getRssi(), 0, record, 0);
- }
-
- /** Callback method for a scan result. */
- public void onScanResult(
- int eventType,
- int addressType,
- String address,
- int primaryPhy,
- int secondaryPhy,
- int advertisingSid,
- int txPower,
- int rssi,
- int periodicAdvInt,
- byte[] advData,
- String originalAddress) {
- // When in testing mode, ignore all real-world events
- if (mTestModeAccessor.isTestModeEnabled()) return;
-
- AppScanStats.recordScanRadioResultCount();
- onScanResultInternal(
- eventType,
- addressType,
- address,
- primaryPhy,
- secondaryPhy,
- advertisingSid,
- txPower,
- rssi,
- periodicAdvInt,
- advData,
- originalAddress);
- }
-
- // TODO(b/327849650): Refactor to reduce the visibility of this method.
- public void onScanResultInternal(
- int eventType,
- int addressType,
- String address,
- int primaryPhy,
- int secondaryPhy,
- int advertisingSid,
- int txPower,
- int rssi,
- int periodicAdvInt,
- byte[] advData,
- String originalAddress) {
- Log.v(
- TAG,
- "onScanResult() - eventType=0x"
- + Integer.toHexString(eventType)
- + ", addressType="
- + addressType
- + ", address="
- + BluetoothUtils.toAnonymizedAddress(address)
- + ", primaryPhy="
- + primaryPhy
- + ", secondaryPhy="
- + secondaryPhy
- + ", advertisingSid=0x"
- + Integer.toHexString(advertisingSid)
- + ", txPower="
- + txPower
- + ", rssi="
- + rssi
- + ", periodicAdvInt=0x"
- + Integer.toHexString(periodicAdvInt)
- + ", originalAddress="
- + originalAddress);
-
- String identityAddress = mAdapterService.getIdentityAddress(address);
- if (!address.equals(identityAddress)) {
- Log.v(
- TAG,
- "found identityAddress of "
- + address
- + ", replace originalAddress as "
- + identityAddress);
- originalAddress = identityAddress;
- }
-
- byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62);
-
- for (ScanClient client : mScanManager.getRegularScanQueue()) {
- ScannerMap.ScannerApp app = mScannerMap.getById(client.scannerId);
- if (app == null) {
- Log.v(TAG, "App is null; skip.");
- continue;
- }
-
- BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(address, addressType);
-
- ScanSettings settings = client.settings;
- byte[] scanRecordData;
- // This is for compatibility with applications that assume fixed size scan data.
- if (settings.getLegacy()) {
- if ((eventType & ET_LEGACY_MASK) == 0) {
- // If this is legacy scan, but nonlegacy result - skip.
- Log.v(TAG, "Legacy scan, non legacy result; skip.");
- continue;
- } else {
- // Some apps are used to fixed-size advertise data.
- scanRecordData = legacyAdvData;
- }
- } else {
- scanRecordData = advData;
- }
-
- ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordData);
- ScanResult result =
- new ScanResult(
- device,
- eventType,
- primaryPhy,
- secondaryPhy,
- advertisingSid,
- txPower,
- rssi,
- periodicAdvInt,
- scanRecord,
- SystemClock.elapsedRealtimeNanos());
-
- if (client.hasDisavowedLocation) {
- if (mLocationDenylistPredicate.test(result)) {
- Log.i(TAG, "Skipping client for location deny list");
- continue;
- }
- }
-
- boolean hasPermission = hasScanResultPermission(client);
- if (!hasPermission) {
- for (String associatedDevice : client.associatedDevices) {
- if (associatedDevice.equalsIgnoreCase(address)) {
- hasPermission = true;
- break;
- }
- }
- }
- if (!hasPermission && client.eligibleForSanitizedExposureNotification) {
- ScanResult sanitized = getSanitizedExposureNotification(result);
- if (sanitized != null) {
- hasPermission = true;
- result = sanitized;
- }
- }
- boolean matchResult = matchesFilters(client, result, originalAddress);
- if (!hasPermission || !matchResult) {
- Log.v(
- TAG,
- "Skipping client: permission=" + hasPermission + " matches=" + matchResult);
- continue;
- }
-
- int callbackType = settings.getCallbackType();
- if (!(callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
- || callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH)) {
- Log.v(TAG, "Skipping client: CALLBACK_TYPE_ALL_MATCHES");
- continue;
- }
-
- try {
- app.mAppScanStats.addResult(client.scannerId);
- if (app.mCallback != null) {
- app.mCallback.onScanResult(result);
- } else {
- Log.v(TAG, "Callback is null, sending scan results by pendingIntent");
- // Send the PendingIntent
- ArrayList<ScanResult> results = new ArrayList<>();
- results.add(result);
- sendResultsByPendingIntent(
- app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
- }
- } catch (RemoteException | PendingIntent.CanceledException e) {
- Log.e(TAG, "Exception: " + e);
- handleDeadScanClient(client);
- }
- }
- }
-
- private void sendResultByPendingIntent(
- PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) {
- ArrayList<ScanResult> results = new ArrayList<>();
- results.add(result);
- try {
- sendResultsByPendingIntent(pii, results, callbackType);
- } catch (PendingIntent.CanceledException e) {
- final long token = Binder.clearCallingIdentity();
- try {
- stopScanInternal(client.scannerId);
- unregisterScannerInternal(client.scannerId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @SuppressWarnings("NonApiType")
- private void sendResultsByPendingIntent(
- PendingIntentInfo pii, ArrayList<ScanResult> results, int callbackType)
- throws PendingIntent.CanceledException {
- Intent extrasIntent = new Intent();
- extrasIntent.putParcelableArrayListExtra(
- BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, results);
- extrasIntent.putExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType);
- pii.intent.send(mAdapterService, 0, extrasIntent);
- }
-
- private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode)
- throws PendingIntent.CanceledException {
- Intent extrasIntent = new Intent();
- extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode);
- pii.intent.send(mAdapterService, 0, extrasIntent);
- }
-
- /** Callback method for scanner registration. */
- public void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)
- throws RemoteException {
- UUID uuid = new UUID(uuidMsb, uuidLsb);
- Log.d(
- TAG,
- "onScannerRegistered() - UUID="
- + uuid
- + ", scannerId="
- + scannerId
- + ", status="
- + status);
-
- // First check the callback map
- ScannerMap.ScannerApp cbApp = mScannerMap.getByUuid(uuid);
- if (cbApp != null) {
- if (status == 0) {
- cbApp.mId = scannerId;
- // If app is callback based, setup a death recipient. App will initiate the start.
- // Otherwise, if PendingIntent based, start the scan directly.
- if (cbApp.mCallback != null) {
- cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.mName));
- } else {
- continuePiStartScan(scannerId, cbApp);
- }
- } else {
- mScannerMap.remove(scannerId);
- }
- if (cbApp.mCallback != null) {
- cbApp.mCallback.onScannerRegistered(status, scannerId);
- }
- }
- }
-
- /** Determines if the given scan client has the appropriate permissions to receive callbacks. */
- private boolean hasScanResultPermission(final ScanClient client) {
- if (client.hasNetworkSettingsPermission
- || client.hasNetworkSetupWizardPermission
- || client.hasScanWithoutLocationPermission) {
- return true;
- }
- if (client.hasDisavowedLocation) {
- return true;
- }
- return client.hasLocationPermission
- && !Utils.blockedByLocationOff(mAdapterService, client.userHandle);
- }
-
- // Check if a scan record matches a specific filters.
- private boolean matchesFilters(ScanClient client, ScanResult scanResult) {
- return matchesFilters(client, scanResult, null);
- }
-
- // Check if a scan record matches a specific filters or original address
- private boolean matchesFilters(
- ScanClient client, ScanResult scanResult, String originalAddress) {
- if (client.filters == null || client.filters.isEmpty()) {
- // TODO: Do we really wanna return true here?
- return true;
- }
- for (ScanFilter filter : client.filters) {
- // Need to check the filter matches, and the original address without changing the API
- if (filter.matches(scanResult)) {
- return true;
- }
- if (originalAddress != null
- && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) {
- return true;
- }
- }
- return false;
- }
-
- private void handleDeadScanClient(ScanClient client) {
- if (client.appDied) {
- Log.w(TAG, "Already dead client " + client.scannerId);
- return;
- }
- client.appDied = true;
- if (client.stats != null) {
- client.stats.isAppDead = true;
- }
- stopScanInternal(client.scannerId);
- }
-
- /** Callback method for scan filter enablement/disablement. */
- public void onScanFilterEnableDisabled(int action, int status, int clientIf) {
- Log.d(
- TAG,
- "onScanFilterEnableDisabled() - clientIf="
- + clientIf
- + ", status="
- + status
- + ", action="
- + action);
- mScanManager.callbackDone(clientIf, status);
- }
-
- /** Callback method for configuration of scan filter params. */
- public void onScanFilterParamsConfigured(
- int action, int status, int clientIf, int availableSpace) {
- Log.d(
- TAG,
- "onScanFilterParamsConfigured() - clientIf="
- + clientIf
- + ", status="
- + status
- + ", action="
- + action
- + ", availableSpace="
- + availableSpace);
- mScanManager.callbackDone(clientIf, status);
- }
-
- /** Callback method for configuration of scan filter. */
- public void onScanFilterConfig(
- int action, int status, int clientIf, int filterType, int availableSpace) {
- Log.d(
- TAG,
- "onScanFilterConfig() - clientIf="
- + clientIf
- + ", action = "
- + action
- + " status = "
- + status
- + ", filterType="
- + filterType
- + ", availableSpace="
- + availableSpace);
-
- mScanManager.callbackDone(clientIf, status);
- }
-
- /** Callback method for configuration of batch scan storage. */
- public void onBatchScanStorageConfigured(int status, int clientIf) {
- Log.d(TAG, "onBatchScanStorageConfigured() - clientIf=" + clientIf + ", status=" + status);
- mScanManager.callbackDone(clientIf, status);
- }
-
- /** Callback method for start/stop of batch scan. */
- // TODO: split into two different callbacks : onBatchScanStarted and onBatchScanStopped.
- public void onBatchScanStartStopped(int startStopAction, int status, int clientIf) {
- Log.d(
- TAG,
- "onBatchScanStartStopped() - clientIf="
- + clientIf
- + ", status="
- + status
- + ", startStopAction="
- + startStopAction);
- mScanManager.callbackDone(clientIf, status);
- }
-
- ScanClient findBatchScanClientById(int scannerId) {
- for (ScanClient client : mScanManager.getBatchScanQueue()) {
- if (client.scannerId == scannerId) {
- return client;
- }
- }
- return null;
- }
-
- /** Callback method for batch scan reports */
- public void onBatchScanReports(
- int status, int scannerId, int reportType, int numRecords, byte[] recordData)
- throws RemoteException {
- // When in testing mode, ignore all real-world events
- if (mTestModeAccessor.isTestModeEnabled()) return;
-
- AppScanStats.recordBatchScanRadioResultCount(numRecords);
- onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData);
- }
-
- @VisibleForTesting
- void onBatchScanReportsInternal(
- int status, int scannerId, int reportType, int numRecords, byte[] recordData)
- throws RemoteException {
- Log.d(
- TAG,
- "onBatchScanReports() - scannerId="
- + scannerId
- + ", status="
- + status
- + ", reportType="
- + reportType
- + ", numRecords="
- + numRecords);
-
- Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
- if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
- // We only support single client for truncated mode.
- ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
- if (app == null) {
- return;
- }
-
- ScanClient client = findBatchScanClientById(scannerId);
- if (client == null) {
- return;
- }
-
- ArrayList<ScanResult> permittedResults;
- if (hasScanResultPermission(client)) {
- permittedResults = new ArrayList<ScanResult>(results);
- } else {
- permittedResults = new ArrayList<ScanResult>();
- for (ScanResult scanResult : results) {
- for (String associatedDevice : client.associatedDevices) {
- if (associatedDevice.equalsIgnoreCase(
- scanResult.getDevice().getAddress())) {
- permittedResults.add(scanResult);
- }
- }
- }
- if (permittedResults.isEmpty()) {
- return;
- }
- }
-
- if (client.hasDisavowedLocation) {
- permittedResults.removeIf(mLocationDenylistPredicate);
- }
-
- if (app.mCallback != null) {
- app.mCallback.onBatchScanResults(permittedResults);
- } else {
- // PendingIntent based
- try {
- sendResultsByPendingIntent(
- app.mInfo, permittedResults, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
- } catch (PendingIntent.CanceledException e) {
- Log.d(TAG, "Exception while sending result", e);
- }
- }
- } else {
- for (ScanClient client : mScanManager.getFullBatchScanQueue()) {
- // Deliver results for each client.
- deliverBatchScan(client, results);
- }
- }
- mScanManager.callbackDone(scannerId, status);
- }
-
- @SuppressWarnings("NonApiType")
- private void sendBatchScanResults(
- ScannerMap.ScannerApp app, ScanClient client, ArrayList<ScanResult> results) {
- try {
- if (app.mCallback != null) {
- if (mScanManager.isAutoBatchScanClientEnabled(client)) {
- Log.d(TAG, "sendBatchScanResults() to onScanResult()" + client);
- for (ScanResult result : results) {
- app.mAppScanStats.addResult(client.scannerId);
- app.mCallback.onScanResult(result);
- }
- } else {
- Log.d(TAG, "sendBatchScanResults() to onBatchScanResults()" + client);
- app.mCallback.onBatchScanResults(results);
- }
- } else {
- sendResultsByPendingIntent(
- app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
- }
- } catch (RemoteException | PendingIntent.CanceledException e) {
- Log.e(TAG, "Exception: " + e);
- handleDeadScanClient(client);
- }
- }
-
- // Check and deliver scan results for different scan clients.
- private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults)
- throws RemoteException {
- ScannerMap.ScannerApp app = mScannerMap.getById(client.scannerId);
- if (app == null) {
- return;
- }
-
- ArrayList<ScanResult> permittedResults;
- if (hasScanResultPermission(client)) {
- permittedResults = new ArrayList<ScanResult>(allResults);
- } else {
- permittedResults = new ArrayList<ScanResult>();
- for (ScanResult scanResult : allResults) {
- for (String associatedDevice : client.associatedDevices) {
- if (associatedDevice.equalsIgnoreCase(scanResult.getDevice().getAddress())) {
- permittedResults.add(scanResult);
- }
- }
- }
- if (permittedResults.isEmpty()) {
- return;
- }
- }
-
- if (client.filters == null || client.filters.isEmpty()) {
- sendBatchScanResults(app, client, permittedResults);
- // TODO: Question to reviewer: Shouldn't there be a return here?
- }
- // Reconstruct the scan results.
- ArrayList<ScanResult> results = new ArrayList<ScanResult>();
- for (ScanResult scanResult : permittedResults) {
- if (matchesFilters(client, scanResult)) {
- results.add(scanResult);
- }
- }
- sendBatchScanResults(app, client, results);
- }
-
- private Set<ScanResult> parseBatchScanResults(
- int numRecords, int reportType, byte[] batchRecord) {
- if (numRecords == 0) {
- return Collections.emptySet();
- }
- Log.d(TAG, "current time is " + SystemClock.elapsedRealtimeNanos());
- if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) {
- return parseTruncatedResults(numRecords, batchRecord);
- } else {
- return parseFullResults(numRecords, batchRecord);
- }
- }
-
- private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) {
- Log.d(TAG, "batch record " + Arrays.toString(batchRecord));
- Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
- long now = SystemClock.elapsedRealtimeNanos();
- for (int i = 0; i < numRecords; ++i) {
- byte[] record =
- extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE, TRUNCATED_RESULT_SIZE);
- byte[] address = extractBytes(record, 0, 6);
- reverse(address);
- BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
- int rssi = record[8];
- long timestampNanos = now - parseTimestampNanos(extractBytes(record, 9, 2));
- results.add(
- new ScanResult(
- device, ScanRecord.parseFromBytes(new byte[0]), rssi, timestampNanos));
- }
- return results;
- }
-
- @VisibleForTesting
- long parseTimestampNanos(byte[] data) {
- long timestampUnit = NumberUtils.littleEndianByteArrayToInt(data);
- // Timestamp is in every 50 ms.
- return TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
- }
-
- private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) {
- Log.d(TAG, "Batch record : " + Arrays.toString(batchRecord));
- Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
- int position = 0;
- long now = SystemClock.elapsedRealtimeNanos();
- while (position < batchRecord.length) {
- byte[] address = extractBytes(batchRecord, position, 6);
- // TODO: remove temp hack.
- reverse(address);
- BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
- position += 6;
- // Skip address type.
- position++;
- // Skip tx power level.
- position++;
- int rssi = batchRecord[position++];
- long timestampNanos = now - parseTimestampNanos(extractBytes(batchRecord, position, 2));
- position += 2;
-
- // Combine advertise packet and scan response packet.
- int advertisePacketLen = batchRecord[position++];
- byte[] advertiseBytes = extractBytes(batchRecord, position, advertisePacketLen);
- position += advertisePacketLen;
- int scanResponsePacketLen = batchRecord[position++];
- byte[] scanResponseBytes = extractBytes(batchRecord, position, scanResponsePacketLen);
- position += scanResponsePacketLen;
- byte[] scanRecord = new byte[advertisePacketLen + scanResponsePacketLen];
- System.arraycopy(advertiseBytes, 0, scanRecord, 0, advertisePacketLen);
- System.arraycopy(
- scanResponseBytes, 0, scanRecord, advertisePacketLen, scanResponsePacketLen);
- Log.d(TAG, "ScanRecord : " + Arrays.toString(scanRecord));
- results.add(
- new ScanResult(
- device, ScanRecord.parseFromBytes(scanRecord), rssi, timestampNanos));
- }
- return results;
- }
-
- // Reverse byte array.
- private void reverse(byte[] address) {
- int len = address.length;
- for (int i = 0; i < len / 2; ++i) {
- byte b = address[i];
- address[i] = address[len - 1 - i];
- address[len - 1 - i] = b;
- }
- }
-
- // Helper method to extract bytes from byte array.
- private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
- byte[] bytes = new byte[length];
- System.arraycopy(scanRecord, start, bytes, 0, length);
- return bytes;
- }
-
- public void onBatchScanThresholdCrossed(int clientIf) {
- Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf);
- flushPendingBatchResultsInternal(clientIf);
- }
-
- public AdvtFilterOnFoundOnLostInfo createOnTrackAdvFoundLostObject(
- int clientIf,
- int advPktLen,
- byte[] advPkt,
- int scanRspLen,
- byte[] scanRsp,
- int filtIndex,
- int advState,
- int advInfoPresent,
- String address,
- int addrType,
- int txPower,
- int rssiValue,
- int timeStamp) {
-
- return new AdvtFilterOnFoundOnLostInfo(
- clientIf,
- advPktLen,
- advPkt,
- scanRspLen,
- scanRsp,
- filtIndex,
- advState,
- advInfoPresent,
- address,
- addrType,
- txPower,
- rssiValue,
- timeStamp);
- }
-
- public void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo)
- throws RemoteException {
- Log.d(
- TAG,
- "onTrackAdvFoundLost() - scannerId= "
- + trackingInfo.getClientIf()
- + " address = "
- + trackingInfo.getAddress()
- + " addressType = "
- + trackingInfo.getAddressType()
- + " adv_state = "
- + trackingInfo.getAdvState());
-
- ScannerMap.ScannerApp app = mScannerMap.getById(trackingInfo.getClientIf());
- if (app == null) {
- Log.e(TAG, "app is null");
- return;
- }
-
- BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter()
- .getRemoteLeDevice(
- trackingInfo.getAddress(), trackingInfo.getAddressType());
- int advertiserState = trackingInfo.getAdvState();
- ScanResult result =
- new ScanResult(
- device,
- ScanRecord.parseFromBytes(trackingInfo.getResult()),
- trackingInfo.getRSSIValue(),
- SystemClock.elapsedRealtimeNanos());
-
- for (ScanClient client : mScanManager.getRegularScanQueue()) {
- if (client.scannerId == trackingInfo.getClientIf()) {
- ScanSettings settings = client.settings;
- if ((advertiserState == ADVT_STATE_ONFOUND)
- && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
- != 0)) {
- if (app.mCallback != null) {
- app.mCallback.onFoundOrLost(true, result);
- } else {
- sendResultByPendingIntent(
- app.mInfo, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client);
- }
- } else if ((advertiserState == ADVT_STATE_ONLOST)
- && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST)
- != 0)) {
- if (app.mCallback != null) {
- app.mCallback.onFoundOrLost(false, result);
- } else {
- sendResultByPendingIntent(
- app.mInfo, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client);
- }
- } else {
- Log.d(
- TAG,
- "Not reporting onlost/onfound : "
- + advertiserState
- + " scannerId = "
- + client.scannerId
- + " callbackType "
- + settings.getCallbackType());
- }
- }
- }
- }
-
- /** Callback method for configuration of scan parameters. */
- public void onScanParamSetupCompleted(int status, int scannerId) {
- Log.d(TAG, "onScanParamSetupCompleted() - scannerId=" + scannerId + ", status=" + status);
- ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
- if (app == null || app.mCallback == null) {
- Log.e(TAG, "Advertise app or callback is null");
- return;
- }
- }
-
- // callback from ScanManager for dispatch of errors apps.
- public void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException {
- ScannerMap.ScannerApp app = mScannerMap.getById(scannerId);
- if (app == null) {
- Log.e(TAG, "App null");
- return;
- }
- if (app.mCallback != null) {
- app.mCallback.onScanManagerErrorCallback(errorCode);
- } else {
- try {
- sendErrorByPendingIntent(app.mInfo, errorCode);
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Error sending error code via PendingIntent:" + e);
- }
- }
- }
-
- public int msftMonitorHandleFromFilterIndex(int filter_index) {
- if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
- Log.e(TAG, "Monitor with filter_index'" + filter_index + "' does not exist");
- return -1;
- }
- return mFilterIndexToMsftAdvMonitorMap.get(filter_index);
- }
-
- public void onMsftAdvMonitorAdd(int filter_index, int monitor_handle, int status) {
- if (status != 0) {
- Log.e(
- TAG,
- "Error adding advertisement monitor with filter index '" + filter_index + "'");
- return;
- }
- if (mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
- Log.e(TAG, "Monitor with filter_index'" + filter_index + "' already added");
- return;
- }
- mFilterIndexToMsftAdvMonitorMap.put(filter_index, monitor_handle);
- }
-
- public void onMsftAdvMonitorRemove(int filter_index, int status) {
- if (status != 0) {
- Log.e(
- TAG,
- "Error removing advertisement monitor with filter index '"
- + filter_index
- + "'");
- }
- if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filter_index)) {
- Log.e(TAG, "Monitor with filter_index'" + filter_index + "' does not exist");
- return;
- }
- mFilterIndexToMsftAdvMonitorMap.remove(filter_index);
- }
-
- public void onMsftAdvMonitorEnable(int status) {
- if (status != 0) {
- Log.e(TAG, "Error enabling advertisement monitor");
- }
- }
-
- /**************************************************************************
- * GATT Service functions - Shared CLIENT/SERVER
- *************************************************************************/
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void registerScanner(
- IScannerCallback callback, WorkSource workSource, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper registerScanner")) {
- return;
- }
-
- enforceImpersonatationPermissionIfNeeded(workSource);
-
- AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid());
- if (app != null
- && app.isScanningTooFrequently()
- && !Utils.checkCallerHasPrivilegedPermission(mAdapterService)) {
- Log.e(TAG, "App '" + app.mAppName + "' is scanning too frequently");
- try {
- callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1);
- } catch (RemoteException e) {
- Log.e(TAG, "Exception: " + e);
- }
- return;
- }
- registerScannerInternal(callback, attributionSource, workSource);
- }
-
- /** Intended for internal use within the Bluetooth app. Bypass permission check */
- public void registerScannerInternal(
- IScannerCallback callback, AttributionSource attrSource, WorkSource workSource) {
- UUID uuid = UUID.randomUUID();
- Log.d(TAG, "registerScanner() - UUID=" + uuid);
-
- mScannerMap.add(uuid, attrSource, workSource, callback, mAdapterService, this);
- mScanManager.registerScanner(uuid);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void unregisterScanner(int scannerId, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper unregisterScanner")) {
- return;
- }
-
- unregisterScannerInternal(scannerId);
- }
-
- /** Intended for internal use within the Bluetooth app. Bypass permission check */
- public void unregisterScannerInternal(int scannerId) {
- Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId);
- mScannerMap.remove(scannerId);
- mScanManager.unregisterScanner(scannerId);
- }
-
- private List<String> getAssociatedDevices(String callingPackage) {
- if (mCompanionManager == null) {
- return Collections.emptyList();
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- return mCompanionManager.getAllAssociations().stream()
- .filter(
- info ->
- info.getPackageName().equals(callingPackage)
- && !info.isSelfManaged()
- && info.getDeviceMacAddress() != null)
- .map(AssociationInfo::getDeviceMacAddress)
- .map(MacAddress::toString)
- .collect(Collectors.toList());
- } catch (SecurityException se) {
- // Not an app with associated devices
- } catch (Exception e) {
- Log.e(TAG, "Cannot check device associations for " + callingPackage, e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return Collections.emptyList();
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void startScan(
- int scannerId,
- ScanSettings settings,
- List<ScanFilter> filters,
- AttributionSource attributionSource) {
- Log.d(TAG, "start scan with filters");
-
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "Starting GATT scan.")) {
- return;
- }
-
- enforcePrivilegedPermissionIfNeeded(settings);
- String callingPackage = attributionSource.getPackageName();
- settings = enforceReportDelayFloor(settings);
- enforcePrivilegedPermissionIfNeeded(filters);
- final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
- scanClient.userHandle = Binder.getCallingUserHandle();
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- scanClient.eligibleForSanitizedExposureNotification =
- callingPackage.equals(mExposureNotificationPackage);
-
- scanClient.hasDisavowedLocation =
- Utils.hasDisavowedLocationForScan(
- mAdapterService, attributionSource, mTestModeAccessor.isTestModeEnabled());
-
- scanClient.isQApp =
- checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q);
- if (!scanClient.hasDisavowedLocation) {
- if (scanClient.isQApp) {
- scanClient.hasLocationPermission =
- Utils.checkCallerHasFineLocation(
- mAdapterService, attributionSource, scanClient.userHandle);
- } else {
- scanClient.hasLocationPermission =
- Utils.checkCallerHasCoarseOrFineLocation(
- mAdapterService, attributionSource, scanClient.userHandle);
- }
- }
- scanClient.hasNetworkSettingsPermission =
- Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
- scanClient.hasNetworkSetupWizardPermission =
- Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
- scanClient.hasScanWithoutLocationPermission =
- Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
- scanClient.associatedDevices = getAssociatedDevices(callingPackage);
-
- startScan(scannerId, settings, filters, scanClient);
- }
-
- /** Intended for internal use within the Bluetooth app. Bypass permission check */
- public void startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters) {
- final ScanClient scanClient = new ScanClient(scannerId, settings, filters);
- scanClient.userHandle = Binder.getCallingUserHandle();
- scanClient.eligibleForSanitizedExposureNotification = false;
- scanClient.hasDisavowedLocation = false;
- scanClient.isQApp = true;
- scanClient.hasNetworkSettingsPermission =
- Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
- scanClient.hasNetworkSetupWizardPermission =
- Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
- scanClient.hasScanWithoutLocationPermission =
- Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
- scanClient.associatedDevices = Collections.emptyList();
-
- startScan(scannerId, settings, filters, scanClient);
- }
-
- private void startScan(
- int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient) {
- AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
- if (app != null) {
- scanClient.stats = app;
- boolean isFilteredScan = (filters != null) && !filters.isEmpty();
- boolean isCallbackScan = false;
-
- ScannerMap.ScannerApp cbApp = mScannerMap.getById(scannerId);
- if (cbApp != null) {
- isCallbackScan = cbApp.mCallback != null;
- }
- app.recordScanStart(
- settings,
- filters,
- isFilteredScan,
- isCallbackScan,
- scannerId,
- cbApp == null ? null : cbApp.mAttributionTag);
- }
-
- mScanManager.startScan(scanClient);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void registerPiAndStartScan(
- PendingIntent pendingIntent,
- ScanSettings settings,
- List<ScanFilter> filters,
- AttributionSource attributionSource) {
- Log.d(TAG, "start scan with filters, for PendingIntent");
-
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "Starting GATT scan.")) {
- return;
- }
- enforcePrivilegedPermissionIfNeeded(settings);
- settings = enforceReportDelayFloor(settings);
- enforcePrivilegedPermissionIfNeeded(filters);
- UUID uuid = UUID.randomUUID();
- String callingPackage = attributionSource.getPackageName();
- int callingUid = attributionSource.getUid();
- PendingIntentInfo piInfo = new PendingIntentInfo();
- piInfo.intent = pendingIntent;
- piInfo.settings = settings;
- piInfo.filters = filters;
- piInfo.callingPackage = callingPackage;
- piInfo.callingUid = callingUid;
- Log.d(
- TAG,
- "startScan(PI) -"
- + (" UUID=" + uuid)
- + (" Package=" + callingPackage)
- + (" UID=" + callingUid));
-
- // Don't start scan if the Pi scan already in mScannerMap.
- if (mScannerMap.getByPendingIntentInfo(piInfo) != null) {
- Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap.");
- return;
- }
-
- ScannerMap.ScannerApp app =
- mScannerMap.add(uuid, attributionSource, piInfo, mAdapterService, this);
-
- app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid());
- mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- app.mEligibleForSanitizedExposureNotification =
- callingPackage.equals(mExposureNotificationPackage);
-
- app.mHasDisavowedLocation =
- Utils.hasDisavowedLocationForScan(
- mAdapterService, attributionSource, mTestModeAccessor.isTestModeEnabled());
-
- if (!app.mHasDisavowedLocation) {
- try {
- if (checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q)) {
- app.mHasLocationPermission =
- Utils.checkCallerHasFineLocation(
- mAdapterService, attributionSource, app.mUserHandle);
- } else {
- app.mHasLocationPermission =
- Utils.checkCallerHasCoarseOrFineLocation(
- mAdapterService, attributionSource, app.mUserHandle);
- }
- } catch (SecurityException se) {
- // No need to throw here. Just mark as not granted.
- app.mHasLocationPermission = false;
- }
- }
- app.mHasNetworkSettingsPermission =
- Utils.checkCallerHasNetworkSettingsPermission(mAdapterService);
- app.mHasNetworkSetupWizardPermission =
- Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService);
- app.mHasScanWithoutLocationPermission =
- Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService);
- app.mAssociatedDevices = getAssociatedDevices(callingPackage);
- mScanManager.registerScanner(uuid);
-
- // If this fails, we should stop the scan immediately.
- if (!pendingIntent.addCancelListener(Runnable::run, mScanIntentCancelListener)) {
- Log.d(TAG, "scanning PendingIntent is already cancelled, stopping scan.");
- stopScan(pendingIntent, attributionSource);
- }
- }
-
- /** Start a scan with pending intent. */
- public void continuePiStartScan(int scannerId, ScannerMap.ScannerApp app) {
- final PendingIntentInfo piInfo = app.mInfo;
- final ScanClient scanClient =
- new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.callingUid);
- scanClient.hasLocationPermission = app.mHasLocationPermission;
- scanClient.userHandle = app.mUserHandle;
- scanClient.isQApp = checkCallerTargetSdk(mAdapterService, app.mName, Build.VERSION_CODES.Q);
- scanClient.eligibleForSanitizedExposureNotification =
- app.mEligibleForSanitizedExposureNotification;
- scanClient.hasNetworkSettingsPermission = app.mHasNetworkSettingsPermission;
- scanClient.hasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission;
- scanClient.hasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission;
- scanClient.associatedDevices = app.mAssociatedDevices;
- scanClient.hasDisavowedLocation = app.mHasDisavowedLocation;
-
- AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId);
- if (scanStats != null) {
- scanClient.stats = scanStats;
- boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
- scanStats.recordScanStart(
- piInfo.settings,
- piInfo.filters,
- isFilteredScan,
- false,
- scannerId,
- app.mAttributionTag);
- }
-
- mScanManager.startScan(scanClient);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void flushPendingBatchResults(int scannerId, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper flushPendingBatchResults")) {
- return;
- }
- flushPendingBatchResultsInternal(scannerId);
- }
-
- private void flushPendingBatchResultsInternal(int scannerId) {
- Log.d(TAG, "flushPendingBatchResultsInternal - scannerId=" + scannerId);
- mScanManager.flushBatchScanResults(new ScanClient(scannerId));
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void stopScan(int scannerId, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper stopScan")) {
- return;
- }
- stopScanInternal(scannerId);
- }
-
- /** Intended for internal use within the Bluetooth app. Bypass permission check */
- public void stopScanInternal(int scannerId) {
- int scanQueueSize =
- mScanManager.getBatchScanQueue().size() + mScanManager.getRegularScanQueue().size();
- Log.d(TAG, "stopScan() - queue size =" + scanQueueSize);
-
- AppScanStats app = mScannerMap.getAppScanStatsById(scannerId);
- if (app != null) {
- app.recordScanStop(scannerId);
- }
-
- mScanManager.stopScan(scannerId);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void stopScan(PendingIntent intent, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper stopScan")) {
- return;
- }
- stopScanInternal(intent);
- }
-
- /** Intended for internal use within the Bluetooth app. Bypass permission check */
- private void stopScanInternal(PendingIntent intent) {
- PendingIntentInfo pii = new PendingIntentInfo();
- pii.intent = intent;
- ScannerMap.ScannerApp app = mScannerMap.getByPendingIntentInfo(pii);
- Log.v(TAG, "stopScan(PendingIntent): app found = " + app);
- if (app != null) {
- intent.removeCancelListener(mScanIntentCancelListener);
- final int scannerId = app.mId;
- stopScanInternal(scannerId);
- // Also unregister the scanner
- unregisterScannerInternal(scannerId);
- }
- }
-
- /**************************************************************************
- * PERIODIC SCANNING
- *************************************************************************/
- @RequiresPermission(BLUETOOTH_SCAN)
- public void registerSync(
- ScanResult scanResult,
- int skip,
- int timeout,
- IPeriodicAdvertisingCallback callback,
- AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper registerSync")) {
- return;
- }
- mPeriodicScanManager.startSync(scanResult, skip, timeout, callback);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void unregisterSync(
- IPeriodicAdvertisingCallback callback, AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper unregisterSync")) {
- return;
- }
- mPeriodicScanManager.stopSync(callback);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void transferSync(
- BluetoothDevice bda,
- int serviceData,
- int syncHandle,
- AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper transferSync")) {
- return;
- }
- mPeriodicScanManager.transferSync(bda, serviceData, syncHandle);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public void transferSetInfo(
- BluetoothDevice bda,
- int serviceData,
- int advHandle,
- IPeriodicAdvertisingCallback callback,
- AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper transferSetInfo")) {
- return;
- }
- mPeriodicScanManager.transferSetInfo(bda, serviceData, advHandle, callback);
- }
-
- @RequiresPermission(BLUETOOTH_SCAN)
- public int numHwTrackFiltersAvailable(AttributionSource attributionSource) {
- if (!Utils.checkScanPermissionForDataDelivery(
- mAdapterService, attributionSource, "ScanHelper numHwTrackFiltersAvailable")) {
- return 0;
- }
- return (mAdapterService.getTotalNumOfTrackableAdvertisements()
- - getCurrentUsedTrackingAdvertisement());
- }
-
- /**
- * DeathRecipient handler used to unregister applications that disconnect ungracefully (ie.
- * crash or forced close).
- */
- class ScannerDeathRecipient implements IBinder.DeathRecipient {
- int mScannerId;
- private String mPackageName;
-
- ScannerDeathRecipient(int scannerId, String packageName) {
- mScannerId = scannerId;
- mPackageName = packageName;
- }
-
- @Override
- public void binderDied() {
- Log.d(
- TAG,
- "Binder is dead - unregistering scanner ("
- + mPackageName
- + " "
- + mScannerId
- + ")!");
-
- ScanClient client = getScanClient(mScannerId);
- if (client != null) {
- handleDeadScanClient(client);
- }
- }
-
- private ScanClient getScanClient(int clientIf) {
- for (ScanClient client : mScanManager.getRegularScanQueue()) {
- if (client.scannerId == clientIf) {
- return client;
- }
- }
- for (ScanClient client : mScanManager.getBatchScanQueue()) {
- if (client.scannerId == clientIf) {
- return client;
- }
- }
- return null;
- }
- }
-
- private boolean needsPrivilegedPermissionForScan(ScanSettings settings) {
- // BLE scan only mode needs special permission.
- if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) {
- return true;
- }
-
- // Regular scan, no special permission.
- if (settings == null) {
- return false;
- }
-
- // Ambient discovery mode, needs privileged permission.
- if (settings.getScanMode() == ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY) {
- return true;
- }
-
- // Regular scan, no special permission.
- if (settings.getReportDelayMillis() == 0) {
- return false;
- }
-
- // Batch scan, truncated mode needs permission.
- return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_ABBREVIATED;
- }
-
- /*
- * The {@link ScanFilter#setDeviceAddress} API overloads are @SystemApi access methods. This
- * requires that the permissions be BLUETOOTH_PRIVILEGED.
- */
- @SuppressLint("AndroidFrameworkRequiresPermission")
- private void enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters) {
- Log.d(TAG, "enforcePrivilegedPermissionIfNeeded(" + filters + ")");
- // Some 3p API cases may have null filters, need to allow
- if (filters != null) {
- for (ScanFilter filter : filters) {
- // The only case to enforce here is if there is an address
- // If there is an address, enforce if the correct combination criteria is met.
- if (filter.getDeviceAddress() != null) {
- // At this point we have an address, that means a caller used the
- // setDeviceAddress(address) public API for the ScanFilter
- // We don't want to enforce if the type is PUBLIC and the IRK is null
- // However, if we have a different type that means the caller used a new
- // @SystemApi such as setDeviceAddress(address, type) or
- // setDeviceAddress(address, type, irk) which are both @SystemApi and require
- // permissions to be enforced
- if (filter.getAddressType() == BluetoothDevice.ADDRESS_TYPE_PUBLIC
- && filter.getIrk() == null) {
- // Do not enforce
- } else {
- mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- }
- }
- }
- }
-
- @SuppressLint("AndroidFrameworkRequiresPermission")
- private void enforcePrivilegedPermissionIfNeeded(ScanSettings settings) {
- if (needsPrivilegedPermissionForScan(settings)) {
- mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
- }
- }
-
- // Enforce caller has UPDATE_DEVICE_STATS permission, which allows the caller to blame other
- // apps for Bluetooth usage. A {@link SecurityException} will be thrown if the caller app does
- // not have UPDATE_DEVICE_STATS permission.
- @RequiresPermission(UPDATE_DEVICE_STATS)
- private void enforceImpersonatationPermission() {
- mAdapterService.enforceCallingOrSelfPermission(
- UPDATE_DEVICE_STATS, "Need UPDATE_DEVICE_STATS permission");
- }
-
- @SuppressLint("AndroidFrameworkRequiresPermission")
- private void enforceImpersonatationPermissionIfNeeded(WorkSource workSource) {
- if (workSource != null) {
- enforceImpersonatationPermission();
- }
- }
-
- /**
- * Ensures the report delay is either 0 or at least the floor value (5000ms)
- *
- * @param settings are the scan settings passed into a request to start le scanning
- * @return the passed in ScanSettings object if the report delay is 0 or above the floor value;
- * a new ScanSettings object with the report delay being the floor value if the original
- * report delay was between 0 and the floor value (exclusive of both)
- */
- @VisibleForTesting
- ScanSettings enforceReportDelayFloor(ScanSettings settings) {
- if (settings.getReportDelayMillis() == 0) {
- return settings;
- }
-
- // Need to clear identity to pass device config permission check
- final long callerToken = Binder.clearCallingIdentity();
- try {
- long floor =
- DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_BLUETOOTH,
- "report_delay",
- DEFAULT_REPORT_DELAY_FLOOR);
-
- if (settings.getReportDelayMillis() > floor) {
- return settings;
- } else {
- return new ScanSettings.Builder()
- .setCallbackType(settings.getCallbackType())
- .setLegacy(settings.getLegacy())
- .setMatchMode(settings.getMatchMode())
- .setNumOfMatches(settings.getNumOfMatches())
- .setPhy(settings.getPhy())
- .setReportDelay(floor)
- .setScanMode(settings.getScanMode())
- .setScanResultType(settings.getScanResultType())
- .build();
- }
- } finally {
- Binder.restoreCallingIdentity(callerToken);
- }
- }
-
- public void addScanEvent(BluetoothMetricsProto.ScanEvent event) {
- synchronized (mScanEvents) {
- if (mScanEvents.size() == NUM_SCAN_EVENTS_KEPT) {
- mScanEvents.remove();
- }
- mScanEvents.add(event);
- }
- }
-
- public void dumpProto(BluetoothMetricsProto.BluetoothLog.Builder builder) {
- synchronized (mScanEvents) {
- builder.addAllScanEvent(mScanEvents);
- }
- }
-}
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index ecc0ac4d0f..851ead30be 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -19,6 +19,9 @@ package com.android.bluetooth.mapclient;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static java.util.Objects.requireNonNull;
+import static java.util.Objects.requireNonNullElseGet;
+
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.PendingIntent;
@@ -29,7 +32,6 @@ import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothMapClient;
import android.bluetooth.SdpMasRecord;
import android.content.AttributionSource;
-import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -50,7 +52,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class MapClientService extends ProfileService {
@@ -60,24 +61,40 @@ public class MapClientService extends ProfileService {
private final Map<BluetoothDevice, MceStateMachine> mMapInstanceMap =
new ConcurrentHashMap<>(1);
- private MnsService mMnsServer;
- private AdapterService mAdapterService;
- private DatabaseManager mDatabaseManager;
- private static MapClientService sMapClientService;
- @VisibleForTesting private Handler mHandler;
+ private final AdapterService mAdapterService;
+ private final DatabaseManager mDatabaseManager;
+ private final MnsService mMnsServer;
+ private final Looper mStateMachinesLooper;
+ private final Handler mHandler;
- private Looper mSmLooper;
+ private static MapClientService sMapClientService;
- public MapClientService(Context ctx) {
- super(ctx);
+ public MapClientService(AdapterService adapterService) {
+ this(adapterService, null, null);
}
@VisibleForTesting
- MapClientService(Context ctx, Looper looper, MnsService mnsServer) {
- this(ctx);
- mSmLooper = looper;
- mMnsServer = mnsServer;
+ MapClientService(AdapterService adapterService, Looper looper, MnsService mnsServer) {
+ super(requireNonNull(adapterService));
+ mAdapterService = adapterService;
+ mDatabaseManager = requireNonNull(adapterService.getDatabase());
+ mMnsServer = requireNonNullElseGet(mnsServer, () -> new MnsService(this));
+
+ if (looper == null) {
+ mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
+ mStateMachinesLooper = null;
+ } else {
+ mHandler = new Handler(looper);
+
+ // MapClient is only using a common state machine looper for test.
+ // In real device, it use a thread per device connected to avoid congestion.
+ mStateMachinesLooper = looper;
+ }
+
+ removeUncleanAccounts();
+ MapClientContent.clearAllContent(this);
+ setMapClientService(this);
}
public static boolean isEnabled() {
@@ -172,10 +189,11 @@ public class MapClientService extends ProfileService {
// When creating a new statemachine, its state is set to CONNECTING - which will trigger
// connect.
MceStateMachine mapStateMachine;
- if (mSmLooper != null) {
- mapStateMachine = new MceStateMachine(this, device, mSmLooper);
+ if (mStateMachinesLooper != null) {
+ mapStateMachine =
+ new MceStateMachine(this, device, mAdapterService, mStateMachinesLooper);
} else {
- mapStateMachine = new MceStateMachine(this, device);
+ mapStateMachine = new MceStateMachine(this, device, mAdapterService);
}
mMapInstanceMap.put(device, mapStateMachine);
}
@@ -291,33 +309,10 @@ public class MapClientService extends ProfileService {
}
@Override
- public synchronized void start() {
- Log.d(TAG, "start()");
-
- mAdapterService = AdapterService.getAdapterService();
- mDatabaseManager =
- Objects.requireNonNull(
- AdapterService.getAdapterService().getDatabase(),
- "DatabaseManager cannot be null when MapClientService starts");
-
- mHandler = new Handler(Looper.getMainLooper());
-
- if (mMnsServer == null) {
- mMnsServer = new MnsService(this);
- }
-
- removeUncleanAccounts();
- MapClientContent.clearAllContent(this);
- setMapClientService(this);
- }
-
- @Override
public synchronized void stop() {
Log.d(TAG, "stop()");
- if (mMnsServer != null) {
- mMnsServer.stop();
- }
+ mMnsServer.stop();
for (MceStateMachine stateMachine : mMapInstanceMap.values()) {
if (stateMachine.getState() == BluetoothAdapter.STATE_CONNECTED) {
stateMachine.disconnect();
@@ -327,10 +322,7 @@ public class MapClientService extends ProfileService {
mMapInstanceMap.clear();
// Unregister Handler and stop all queued messages.
- if (mHandler != null) {
- mHandler.removeCallbacksAndMessages(null);
- mHandler = null;
- }
+ mHandler.removeCallbacksAndMessages(null);
}
@Override
diff --git a/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java b/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java
index b24ddade19..2c3420af84 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java
@@ -42,6 +42,8 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.RECEIVE_SMS;
+import static java.util.Objects.requireNonNull;
+
import android.app.Activity;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
@@ -112,11 +114,11 @@ class MceStateMachine extends StateMachine {
// messaging app takes that responsibility.
private static final Boolean SAVE_OUTBOUND_MESSAGES = true;
@VisibleForTesting static final Duration DISCONNECT_TIMEOUT = Duration.ofSeconds(3);
- private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(10);
+ @VisibleForTesting static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(10);
private static final int MAX_MESSAGES = 20;
private static final int MSG_CONNECT = 1;
private static final int MSG_DISCONNECT = 2;
- static final int MSG_CONNECTING_TIMEOUT = 3;
+ private static final int MSG_CONNECTING_TIMEOUT = 3;
private static final int MSG_DISCONNECTING_TIMEOUT = 4;
// Constants for SDP. Note that these values come from the native stack, but no centralized
@@ -147,22 +149,26 @@ class MceStateMachine extends StateMachine {
private static final String SEND_MESSAGE_TYPE =
"persist.bluetooth.pts.mapclient.sendmessagetype";
+ private final State mDisconnected = new Disconnected();
+ private final State mConnecting = new Connecting();
+ private final State mConnected = new Connected();
+ private final State mDisconnecting = new Disconnecting();
+
+ private final HashMap<String, Bmessage> mSentMessageLog = new HashMap<>(MAX_MESSAGES);
+ private final HashMap<Bmessage, PendingIntent> mSentReceiptRequested =
+ new HashMap<>(MAX_MESSAGES);
+ private final HashMap<Bmessage, PendingIntent> mDeliveryReceiptRequested =
+ new HashMap<>(MAX_MESSAGES);
+
+ private final BluetoothDevice mDevice;
+ private final MapClientService mService;
+ private final AdapterService mAdapterService;
+
// Connectivity States
private int mPreviousState = BluetoothProfile.STATE_DISCONNECTED;
private int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
- private State mDisconnected;
- private State mConnecting;
- private State mConnected;
- private State mDisconnecting;
-
- private final BluetoothDevice mDevice;
- private MapClientService mService;
private MasClient mMasClient;
private MapClientContent mDatabase;
- private HashMap<String, Bmessage> mSentMessageLog = new HashMap<>(MAX_MESSAGES);
- private HashMap<Bmessage, PendingIntent> mSentReceiptRequested = new HashMap<>(MAX_MESSAGES);
- private HashMap<Bmessage, PendingIntent> mDeliveryReceiptRequested =
- new HashMap<>(MAX_MESSAGES);
private final Object mLock = new Object();
@@ -225,25 +231,33 @@ class MceStateMachine extends StateMachine {
ConcurrentHashMap<String, MessageMetadata> mMessages =
new ConcurrentHashMap<String, MessageMetadata>();
- MceStateMachine(MapClientService service, BluetoothDevice device) {
+ MceStateMachine(
+ MapClientService service, BluetoothDevice device, AdapterService adapterService) {
super(TAG); // Create a state machine with its own separate thread
+ mAdapterService = requireNonNull(adapterService);
mService = service;
mDevice = device;
initStateMachine();
}
- MceStateMachine(MapClientService service, BluetoothDevice device, Looper looper) {
- this(service, device, null, null, looper);
+ MceStateMachine(
+ MapClientService service,
+ BluetoothDevice device,
+ AdapterService adapterService,
+ Looper looper) {
+ this(service, device, adapterService, looper, null, null);
}
@VisibleForTesting
MceStateMachine(
MapClientService service,
BluetoothDevice device,
+ AdapterService adapterService,
+ Looper looper,
MasClient masClient,
- MapClientContent database,
- Looper looper) {
- super(TAG, looper);
+ MapClientContent database) {
+ super(TAG, requireNonNull(looper));
+ mAdapterService = requireNonNull(adapterService);
mService = service;
mMasClient = masClient;
mDevice = device;
@@ -254,10 +268,6 @@ class MceStateMachine extends StateMachine {
private void initStateMachine() {
mPreviousState = BluetoothProfile.STATE_DISCONNECTED;
- mDisconnected = new Disconnected();
- mConnecting = new Connecting();
- mDisconnecting = new Disconnecting();
- mConnected = new Connected();
addState(mDisconnected);
addState(mConnecting);
@@ -302,11 +312,8 @@ class MceStateMachine extends StateMachine {
}
setState(state);
- AdapterService adapterService = AdapterService.getAdapterService();
- if (adapterService != null) {
- adapterService.updateProfileConnectionAdapterProperties(
- mDevice, BluetoothProfile.MAP_CLIENT, state, prevState);
- }
+ mAdapterService.updateProfileConnectionAdapterProperties(
+ mDevice, BluetoothProfile.MAP_CLIENT, state, prevState);
Intent intent = new Intent(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
diff --git a/android/app/src/com/android/bluetooth/mapclient/MnsService.java b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
index 9b86757da5..99cee09481 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MnsService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
@@ -34,22 +34,20 @@ import java.io.IOException;
public class MnsService {
private static final String TAG = MnsService.class.getSimpleName();
- static final int MSG_EVENT = 1;
- /* for Client */
- static final int EVENT_REPORT = 1001;
- /* MAP version 1.4 */
- private static final int MNS_VERSION = 0x0104;
+ static final int EVENT_REPORT = 1001; // for Client
+ private static final int MNS_VERSION = 0x0104; // MAP version 1.4
private final SocketAcceptor mAcceptThread = new SocketAcceptor();
+ private final MapClientService mMapClientService;
+
private ObexServerSockets mServerSockets;
- private MapClientService mContext;
private volatile boolean mShutdown = false; // Used to interrupt socket accept thread
private int mSdpHandle = -1;
- MnsService(MapClientService context) {
+ MnsService(MapClientService service) {
Log.v(TAG, "MnsService()");
- mContext = context;
+ mMapClientService = service;
mServerSockets = ObexServerSockets.create(mAcceptThread);
SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
if (!nativeInterface.isAvailable()) {
@@ -116,7 +114,7 @@ public class MnsService {
public synchronized boolean onConnect(BluetoothDevice device, BluetoothSocket socket) {
Log.d(TAG, "onConnect" + device + " SOCKET: " + socket);
/* Signal to the service that we have received an incoming connection.*/
- MceStateMachine stateMachine = mContext.getMceStateMachineForDevice(device);
+ MceStateMachine stateMachine = mMapClientService.getMceStateMachineForDevice(device);
if (stateMachine == null) {
Log.e(
TAG,
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
index 6efdb17d66..7bf72bcee1 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
@@ -51,7 +51,6 @@ import android.util.Log;
import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.R;
import com.android.bluetooth.Utils;
-import com.android.bluetooth.flags.Flags;
import com.google.common.annotations.VisibleForTesting;
@@ -234,7 +233,7 @@ class BluetoothOppNotification {
case NOTIFY:
synchronized (BluetoothOppNotification.this) {
if (mPendingUpdate > 0 && mUpdateNotificationThread == null) {
- Log.v(TAG, "new notify threadi!");
+ Log.v(TAG, "new notify thread!");
mUpdateNotificationThread = new NotificationUpdateThread();
mUpdateNotificationThread.start();
Log.v(TAG, "send delay message");
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
index a8c653db5c..b912e8e966 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -51,7 +51,6 @@ import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
@@ -135,7 +134,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
private BluetoothShareContentObserver mObserver;
/** Class to handle Notification Manager updates */
- @VisibleForTesting BluetoothOppNotification mNotifier;
+ @VisibleForTesting final BluetoothOppNotification mNotifier;
private boolean mPendingUpdate;
@@ -236,28 +235,6 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_WARN,
0);
}
- }
-
- public static boolean isEnabled() {
- return BluetoothProperties.isProfileOppEnabled().orElse(false);
- }
-
- @Override
- protected IProfileServiceBinder initBinder() {
- return new OppBinder();
- }
-
- private static class OppBinder extends Binder implements IProfileServiceBinder {
-
- OppBinder() {}
-
- @Override
- public void cleanup() {}
- }
-
- @Override
- public void start() {
- Log.v(TAG, "start()");
setComponentAvailable(OPP_PROVIDER, true);
setComponentAvailable(INCOMING_FILE_CONFIRM_ACTIVITY, true);
@@ -282,6 +259,15 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
setBluetoothOppService(this);
}
+ public static boolean isEnabled() {
+ return BluetoothProperties.isProfileOppEnabled().orElse(false);
+ }
+
+ @Override
+ protected IProfileServiceBinder initBinder() {
+ return null;
+ }
+
@Override
public void stop() {
if (sBluetoothOppService == null) {
@@ -457,7 +443,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
BluetoothStatsLog
.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION,
7);
- Log.e(TAG, "close tranport error");
+ Log.e(TAG, "close transport error");
}
} else {
Log.i(TAG, "OPP busy! Retry after 1 second");
@@ -623,9 +609,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
}
}
- if (mNotifier != null) {
- mNotifier.cancelOppNotifications();
- }
+ mNotifier.cancelOppNotifications();
}
/* suppose we auto accept an incoming OPUSH connection */
@@ -740,8 +724,9 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
mPendingUpdate = false;
}
Cursor cursor =
- getContentResolver()
- .query(
+ BluetoothMethodProxy.getInstance()
+ .contentResolverQuery(
+ getContentResolver(),
BluetoothShare.CONTENT_URI,
null,
null,
diff --git a/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java b/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java
index 0580c58560..cb4f561b73 100644
--- a/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java
@@ -22,41 +22,19 @@ import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
/** Provides Bluetooth Pan native interface for the Pan service */
public class PanNativeInterface {
private static final String TAG = PanNativeInterface.class.getSimpleName();
- private PanService mPanService;
- @GuardedBy("INSTANCE_LOCK")
- private static PanNativeInterface sInstance;
+ private final PanService mPanService;
- private static final Object INSTANCE_LOCK = new Object();
-
- private PanNativeInterface() {}
-
- /** Get singleton instance. */
- public static PanNativeInterface getInstance() {
- synchronized (INSTANCE_LOCK) {
- if (sInstance == null) {
- sInstance = new PanNativeInterface();
- }
- return sInstance;
- }
- }
-
- /** Set singleton instance. */
- @VisibleForTesting
- public static void setInstance(PanNativeInterface instance) {
- synchronized (INSTANCE_LOCK) {
- sInstance = instance;
- }
+ PanNativeInterface(PanService panService) {
+ mPanService = panService;
}
- void init(PanService panService) {
- mPanService = panService;
+ void init() {
initializeNative();
}
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index ee57196ac4..d59d74e0c5 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -19,6 +19,10 @@ package com.android.bluetooth.pan;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.bluetooth.BluetoothUtils.logRemoteException;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.Objects.requireNonNullElseGet;
import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
@@ -29,7 +33,6 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothPan;
import android.bluetooth.IBluetoothPanCallback;
import android.content.AttributionSource;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Resources.NotFoundException;
import android.net.TetheringInterface;
@@ -57,53 +60,49 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/** Provides Bluetooth Pan Device profile, as a service in the Bluetooth application. */
public class PanService extends ProfileService {
private static final String TAG = PanService.class.getSimpleName();
+
private static PanService sPanService;
private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
- @VisibleForTesting ConcurrentHashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
-
- private int mMaxPanDevices;
- private String mPanIfName;
- @VisibleForTesting boolean mIsTethering = false;
- private HashMap<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks;
-
- private TetheringManager mTetheringManager;
- private DatabaseManager mDatabaseManager;
- @VisibleForTesting UserManager mUserManager;
-
private static final int MESSAGE_CONNECT = 1;
private static final int MESSAGE_DISCONNECT = 2;
private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
- private boolean mTetherOn = false;
- private BluetoothTetheringNetworkFactory mNetworkFactory;
- private boolean mStarted = false;
+ @VisibleForTesting
+ final ConcurrentHashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices =
+ new ConcurrentHashMap<>();
- private AdapterService mAdapterService;
+ private final Map<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks = new HashMap<>();
+ private final AdapterService mAdapterService;
+ private final PanNativeInterface mNativeInterface;
+ private final DatabaseManager mDatabaseManager;
+ private final TetheringManager mTetheringManager;
+ private final UserManager mUserManager;
+ private final int mMaxPanDevices;
- @VisibleForTesting PanNativeInterface mNativeInterface;
+ private String mPanIfName;
+ @VisibleForTesting boolean mIsTethering = false;
+ private boolean mTetherOn = false;
+ private BluetoothTetheringNetworkFactory mNetworkFactory;
- TetheringManager.TetheringEventCallback mTetheringCallback =
+ final TetheringManager.TetheringEventCallback mTetheringCallback =
new TetheringManager.TetheringEventCallback() {
@Override
public void onError(TetheringInterface iface, int error) {
if (mIsTethering && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
- // tethering is fail because of @TetheringIfaceError error.
+ // Tethering fail because of @TetheringIfaceError error.
Log.e(TAG, "Error setting up tether interface: " + error);
- for (Map.Entry device : mPanDevices.entrySet()) {
+ for (BluetoothDevice device : mPanDevices.keySet()) {
mNativeInterface.disconnect(
Flags.panUseIdentityAddress()
- ? Utils.getByteBrEdrAddress(
- (BluetoothDevice) device.getKey())
- : Utils.getByteAddress(
- (BluetoothDevice) device.getKey()));
+ ? Utils.getByteBrEdrAddress(mAdapterService, device)
+ : Utils.getByteAddress(device));
}
mPanDevices.clear();
mIsTethering = false;
@@ -111,8 +110,35 @@ public class PanService extends ProfileService {
}
};
- public PanService(Context ctx) {
- super(ctx);
+ public PanService(AdapterService adapterService) {
+ this(adapterService, null);
+ }
+
+ @VisibleForTesting
+ PanService(AdapterService adapterService, PanNativeInterface nativeInterface) {
+ super(requireNonNull(adapterService));
+ mAdapterService = adapterService;
+ mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
+ mNativeInterface =
+ requireNonNullElseGet(nativeInterface, () -> new PanNativeInterface(this));
+ mUserManager = requireNonNull(getSystemService(UserManager.class));
+ mTetheringManager = requireNonNull(getSystemService(TetheringManager.class));
+
+ int maxPanDevice;
+ try {
+ maxPanDevice =
+ getResources()
+ .getInteger(com.android.bluetooth.R.integer.config_max_pan_devices);
+ } catch (NotFoundException e) {
+ maxPanDevice = BLUETOOTH_MAX_PAN_CONNECTIONS;
+ }
+ mMaxPanDevices = maxPanDevice;
+
+ mNativeInterface.init();
+
+ mTetheringManager.registerTetheringEventCallback(
+ new HandlerExecutor(new Handler(Looper.getMainLooper())), mTetheringCallback);
+ setPanService(this);
}
public static boolean isEnabled() {
@@ -143,50 +169,8 @@ public class PanService extends ProfileService {
}
@Override
- public void start() {
- mAdapterService =
- Objects.requireNonNull(
- AdapterService.getAdapterService(),
- "AdapterService cannot be null when PanService starts");
- mDatabaseManager =
- Objects.requireNonNull(
- AdapterService.getAdapterService().getDatabase(),
- "DatabaseManager cannot be null when PanService starts");
- mNativeInterface =
- Objects.requireNonNull(
- PanNativeInterface.getInstance(),
- "PanNativeInterface cannot be null when PanService starts");
-
- mBluetoothTetheringCallbacks = new HashMap<>();
- mPanDevices = new ConcurrentHashMap<BluetoothDevice, BluetoothPanDevice>();
- try {
- mMaxPanDevices =
- getResources()
- .getInteger(com.android.bluetooth.R.integer.config_max_pan_devices);
- } catch (NotFoundException e) {
- mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
- }
- mNativeInterface.init(this);
-
- mUserManager = getSystemService(UserManager.class);
-
- mTetheringManager = getSystemService(TetheringManager.class);
- mTetheringManager.registerTetheringEventCallback(
- new HandlerExecutor(new Handler(Looper.getMainLooper())), mTetheringCallback);
- setPanService(this);
- mStarted = true;
- }
-
- @Override
public void stop() {
- if (!mStarted) {
- Log.w(TAG, "stop() called before start()");
- return;
- }
- if (mTetheringManager != null) {
- mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
- mTetheringManager = null;
- }
+ mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
mNativeInterface.cleanup();
mHandler.removeCallbacksAndMessages(null);
}
@@ -196,29 +180,25 @@ public class PanService extends ProfileService {
// TODO(b/72948646): this should be moved to stop()
setPanService(null);
- mUserManager = null;
-
- if (mPanDevices != null) {
- int[] desiredStates = {
- BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_DISCONNECTING
- };
- List<BluetoothDevice> devList = getDevicesMatchingConnectionStates(desiredStates);
- for (BluetoothDevice device : devList) {
- BluetoothPanDevice panDevice = mPanDevices.get(device);
- Log.d(TAG, "panDevice: " + panDevice + " device address: " + device);
- if (panDevice != null) {
- handlePanDeviceStateChange(
- device,
- mPanIfName,
- BluetoothProfile.STATE_DISCONNECTED,
- panDevice.mLocalRole,
- panDevice.mRemoteRole);
- }
+ int[] desiredStates = {
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING
+ };
+ List<BluetoothDevice> devList = getDevicesMatchingConnectionStates(desiredStates);
+ for (BluetoothDevice device : devList) {
+ BluetoothPanDevice panDevice = mPanDevices.get(device);
+ Log.d(TAG, "panDevice: " + panDevice + " device address: " + device);
+ if (panDevice != null) {
+ handlePanDeviceStateChange(
+ device,
+ mPanIfName,
+ BluetoothProfile.STATE_DISCONNECTED,
+ panDevice.mLocalRole,
+ panDevice.mRemoteRole);
}
- mPanDevices.clear();
}
+ mPanDevices.clear();
}
private final Handler mHandler =
@@ -230,7 +210,8 @@ public class PanService extends ProfileService {
BluetoothDevice connectDevice = (BluetoothDevice) msg.obj;
if (!mNativeInterface.connect(
Flags.identityAddressNullIfNotKnown()
- ? Utils.getByteBrEdrAddress(connectDevice)
+ ? Utils.getByteBrEdrAddress(
+ mAdapterService, connectDevice)
: mAdapterService.getByteIdentityAddress(
connectDevice))) {
handlePanDeviceStateChange(
@@ -251,7 +232,8 @@ public class PanService extends ProfileService {
BluetoothDevice disconnectDevice = (BluetoothDevice) msg.obj;
if (!mNativeInterface.disconnect(
Flags.identityAddressNullIfNotKnown()
- ? Utils.getByteBrEdrAddress(disconnectDevice)
+ ? Utils.getByteBrEdrAddress(
+ mAdapterService, disconnectDevice)
: mAdapterService.getByteIdentityAddress(
disconnectDevice))) {
handlePanDeviceStateChange(
@@ -663,7 +645,7 @@ public class PanService extends ProfileService {
mPanDevices.remove(device);
mNativeInterface.disconnect(
Flags.panUseIdentityAddress()
- ? Utils.getByteBrEdrAddress(device)
+ ? Utils.getByteBrEdrAddress(mAdapterService, device)
: Utils.getByteAddress(device));
return;
}
@@ -675,7 +657,7 @@ public class PanService extends ProfileService {
cb.onAvailable(iface);
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ logRemoteException(TAG, e);
}
}
} else if (state == BluetoothProfile.STATE_DISCONNECTED) {
@@ -690,12 +672,12 @@ public class PanService extends ProfileService {
cb.onUnavailable();
}
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ logRemoteException(TAG, e);
}
mIsTethering = false;
}
}
- } else if (mStarted) {
+ } else {
// PANU Role = reverse Tether
Log.d(
TAG,
diff --git a/android/app/src/com/android/bluetooth/sap/SapService.java b/android/app/src/com/android/bluetooth/sap/SapService.java
index 6401b27f82..861553477b 100644
--- a/android/app/src/com/android/bluetooth/sap/SapService.java
+++ b/android/app/src/com/android/bluetooth/sap/SapService.java
@@ -19,6 +19,8 @@ package com.android.bluetooth.sap;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
@@ -60,15 +62,10 @@ import java.util.Collections;
import java.util.List;
public class SapService extends ProfileService implements AdapterService.BluetoothStateCallback {
+ private static final String TAG = "SapService";
private static final String SDP_SAP_SERVICE_NAME = "SIM Access";
private static final int SDP_SAP_VERSION = 0x0102;
- private static final String TAG = "SapService";
-
- /**
- * To log debug/verbose in SAP, use the command "setprop log.tag.SapService DEBUG" or "setprop
- * log.tag.SapService VERBOSE" and then "adb root" + "adb shell "stop; start""
- */
/* Message ID's */
private static final int START_LISTENER = 1;
@@ -100,8 +97,9 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
"com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT";
private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000;
+ private final AdapterService mAdapterService;
+
private PowerManager.WakeLock mWakeLock = null;
- private AdapterService mAdapterService;
private SocketAcceptThread mAcceptThread = null;
private BluetoothServerSocket mServerSocket = null;
private int mSdpHandle = -1;
@@ -113,9 +111,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
private SapServer mSapServer = null;
private AlarmManager mAlarmManager = null;
private boolean mRemoveTimeoutMsg = false;
-
private boolean mIsWaitingAuthorization = false;
- private boolean mIsRegistered = false;
private static SapService sSapService;
@@ -123,9 +119,22 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
BluetoothUuid.SAP,
};
- public SapService(Context ctx) {
- super(ctx);
+ public SapService(AdapterService adapterService) {
+ super(requireNonNull(adapterService));
+ mAdapterService = adapterService;
BluetoothSap.invalidateBluetoothGetConnectionStateCache();
+
+ IntentFilter filter = new IntentFilter();
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+ filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
+
+ registerReceiver(mSapReceiver, filter);
+
+ mAdapterService.registerBluetoothStateCallback(getMainExecutor(), this);
+ // start RFCOMM listener
+ mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
+ setSapService(this);
}
public static boolean isEnabled() {
@@ -148,7 +157,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
private void removeSdpRecord() {
SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
- if (mAdapterService != null && mSdpHandle >= 0 && nativeInterface.isAvailable()) {
+ if (mSdpHandle >= 0 && nativeInterface.isAvailable()) {
Log.v(TAG, "Removing SDP record handle: " + mSdpHandle);
nativeInterface.removeSdpRecord(mSdpHandle);
mSdpHandle = -1;
@@ -203,9 +212,6 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
if (!initSocketOK) {
// Need to break out of this loop if BT is being turned off.
- if (mAdapterService == null) {
- break;
- }
int state = mAdapterService.getState();
if ((state != BluetoothAdapter.STATE_TURNING_ON)
&& (state != BluetoothAdapter.STATE_ON)) {
@@ -636,7 +642,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
enforceCallingOrSelfPermission(
BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
- AdapterService.getAdapterService()
+ mAdapterService
.getDatabase()
.setProfileConnectionPolicy(device, BluetoothProfile.SAP, connectionPolicy);
if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
@@ -659,7 +665,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
public int getConnectionPolicy(BluetoothDevice device) {
enforceCallingOrSelfPermission(
BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
- return AdapterService.getAdapterService()
+ return mAdapterService
.getDatabase()
.getProfileConnectionPolicy(device, BluetoothProfile.SAP);
}
@@ -670,41 +676,10 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo
}
@Override
- public void start() {
- Log.v(TAG, "start()");
- IntentFilter filter = new IntentFilter();
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
- filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
-
- try {
- registerReceiver(mSapReceiver, filter);
- mIsRegistered = true;
- } catch (Exception e) {
- Log.w(TAG, "Unable to register sap receiver", e);
- }
- mInterrupted = false;
- mAdapterService = AdapterService.getAdapterService();
- mAdapterService.registerBluetoothStateCallback(getMainExecutor(), this);
- // start RFCOMM listener
- mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
- setSapService(this);
- }
-
- @Override
public void stop() {
Log.v(TAG, "stop()");
- if (!mIsRegistered) {
- Log.i(TAG, "Avoid unregister when receiver it is not registered");
- return;
- }
setSapService(null);
- try {
- mIsRegistered = false;
- unregisterReceiver(mSapReceiver);
- } catch (Exception e) {
- Log.w(TAG, "Unable to unregister sap receiver", e);
- }
+ unregisterReceiver(mSapReceiver);
mAdapterService.unregisterBluetoothStateCallback(this);
setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
sendShutdownMessage();
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
index 803b249a3d..9403b5fc93 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGatt.java
@@ -19,6 +19,8 @@ package com.android.bluetooth.tbs;
import static android.bluetooth.BluetoothDevice.METADATA_GTBS_CCCD;
+import static java.util.Objects.requireNonNull;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
@@ -27,7 +29,6 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
-import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -48,12 +49,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.UUID;
public class TbsGatt {
-
- private static final String TAG = "TbsGatt";
+ private static final String TAG = TbsGatt.class.getSimpleName();
private static final String UUID_PREFIX = "0000";
private static final String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb";
@@ -66,51 +65,50 @@ public class TbsGatt {
@VisibleForTesting static final UUID UUID_BEARER_TECHNOLOGY = makeUuid("2BB5");
@VisibleForTesting static final UUID UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST = makeUuid("2BB6");
@VisibleForTesting static final UUID UUID_BEARER_LIST_CURRENT_CALLS = makeUuid("2BB9");
- @VisibleForTesting static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA");
+ private static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA");
@VisibleForTesting static final UUID UUID_STATUS_FLAGS = makeUuid("2BBB");
@VisibleForTesting static final UUID UUID_CALL_STATE = makeUuid("2BBD");
@VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT = makeUuid("2BBE");
-
- @VisibleForTesting
- static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF");
-
+ private static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF");
@VisibleForTesting static final UUID UUID_TERMINATION_REASON = makeUuid("2BC0");
@VisibleForTesting static final UUID UUID_INCOMING_CALL = makeUuid("2BC1");
@VisibleForTesting static final UUID UUID_CALL_FRIENDLY_NAME = makeUuid("2BC2");
-
@VisibleForTesting
static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = makeUuid("2902");
@VisibleForTesting static final int STATUS_FLAG_INBAND_RINGTONE_ENABLED = 0x0001;
@VisibleForTesting static final int STATUS_FLAG_SILENT_MODE_ENABLED = 0x0002;
- @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001;
- @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002;
-
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05;
+ private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001;
+ private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00;
+ static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00;
+ static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01;
+ static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02;
+ static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03;
+ static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04;
+ static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05;
- @VisibleForTesting
- public static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01;
+ static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00;
+ static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01;
+ static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02;
+ static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03;
+ static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04;
+ static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06;
- @VisibleForTesting
- public static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02;
+ private final Object mPendingGattOperationsLock = new Object();
+ private final Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>();
- @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04;
- @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_LACK_OF_RESOURCES = 0x05;
+ @GuardedBy("mPendingGattOperationsLock")
+ private final Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations =
+ new HashMap<>();
- @VisibleForTesting
- public static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06;
+ private final Map<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues = new HashMap<>();
- private final Object mPendingGattOperationsLock = new Object();
- private final Context mContext;
+ private final AdapterService mAdapterService;
+ private final TbsService mTbsService;
+ private final Handler mHandler;
+ private final BluetoothGattServerProxy mBluetoothGattServer;
private final GattCharacteristic mBearerProviderNameCharacteristic;
private final GattCharacteristic mBearerUciCharacteristic;
private final GattCharacteristic mBearerTechnologyCharacteristic;
@@ -124,18 +122,10 @@ public class TbsGatt {
private final GattCharacteristic mTerminationReasonCharacteristic;
private final GattCharacteristic mIncomingCallCharacteristic;
private final GattCharacteristic mCallFriendlyNameCharacteristic;
- private boolean mSilentMode = false;
- private Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>();
-
- @GuardedBy("mPendingGattOperationsLock")
- private Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>();
- private BluetoothGattServerProxy mBluetoothGattServer;
- private Handler mHandler;
private Callback mCallback;
- private AdapterService mAdapterService;
- private HashMap<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues;
- private TbsService mTbsService;
+
+ private boolean mSilentMode = false;
private static final int LOG_NB_EVENTS = 200;
private BluetoothEventLogger mEventLogger = null;
@@ -242,12 +232,19 @@ public class TbsGatt {
public byte[] mValue;
}
- TbsGatt(TbsService tbsService) {
- mContext = tbsService;
- mAdapterService =
- Objects.requireNonNull(
- AdapterService.getAdapterService(),
- "AdapterService shouldn't be null when creating TbsGatt");
+ TbsGatt(AdapterService adapterService, TbsService tbsService) {
+ this(adapterService, tbsService, new BluetoothGattServerProxy(adapterService));
+ }
+
+ @VisibleForTesting
+ TbsGatt(
+ AdapterService adapterService,
+ TbsService tbsService,
+ BluetoothGattServerProxy gattServerProxy) {
+ mTbsService = requireNonNull(tbsService);
+ mAdapterService = requireNonNull(adapterService);
+ mBluetoothGattServer = requireNonNull(gattServerProxy);
+ mHandler = new Handler(Looper.getMainLooper());
mBearerProviderNameCharacteristic =
new GattCharacteristic(
@@ -317,13 +314,6 @@ public class TbsGatt {
| BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
- mTbsService = tbsService;
- mBluetoothGattServer = null;
- }
-
- @VisibleForTesting
- void setBluetoothGattServerForTesting(BluetoothGattServerProxy proxy) {
- mBluetoothGattServer = proxy;
}
public boolean init(
@@ -335,7 +325,6 @@ public class TbsGatt {
String providerName,
int technology,
Callback callback) {
- mCccDescriptorValues = new HashMap<>();
mBearerProviderNameCharacteristic.setValue(providerName);
mBearerTechnologyCharacteristic.setValue(new byte[] {(byte) (technology & 0xFF)});
mBearerUciCharacteristic.setValue(uci);
@@ -344,11 +333,6 @@ public class TbsGatt {
setCallControlPointOptionalOpcodes(isLocalHoldOpcodeSupported, isJoinOpcodeSupported);
mStatusFlagsCharacteristic.setValue(0, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
mCallback = callback;
- mHandler = new Handler(Looper.getMainLooper());
-
- if (mBluetoothGattServer == null) {
- mBluetoothGattServer = new BluetoothGattServerProxy(mContext);
- }
if (!mBluetoothGattServer.open(mGattServerCallback)) {
Log.e(TAG, " Could not open Gatt server");
@@ -381,22 +365,14 @@ public class TbsGatt {
mEventLogger.add("Initialized");
mAdapterService.registerBluetoothStateCallback(
- mContext.getMainExecutor(), mBluetoothStateChangeCallback);
+ mAdapterService.getMainExecutor(), mBluetoothStateChangeCallback);
return true;
}
public void cleanup() {
mAdapterService.unregisterBluetoothStateCallback(mBluetoothStateChangeCallback);
- if (mBluetoothGattServer == null) {
- return;
- }
mBluetoothGattServer.close();
- mBluetoothGattServer = null;
- }
-
- public Context getContext() {
- return mContext;
}
private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) {
@@ -506,19 +482,14 @@ public class TbsGatt {
BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) {
if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return;
if (value == null) return;
- if (mBluetoothGattServer != null) {
- mBluetoothGattServer.notifyCharacteristicChanged(
- device, characteristic, false, value);
- }
+ mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false, value);
}
private void notifyCharacteristicChanged(
BluetoothDevice device, BluetoothGattCharacteristic characteristic) {
if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return;
- if (mBluetoothGattServer != null) {
- mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
- }
+ mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);
}
public void notifyWithValue(
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index 957e1e2b69..b49eee0a03 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -17,6 +17,8 @@
package com.android.bluetooth.tbs;
+import static java.util.Objects.requireNonNull;
+
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothLeCall;
@@ -50,8 +52,7 @@ import java.util.UUID;
/** Container class to store TBS instances */
public class TbsGeneric {
-
- private static final String TAG = "TbsGeneric";
+ private static final String TAG = TbsGeneric.class.getSimpleName();
private static final String UCI = "GTBS";
private static final String DEFAULT_PROVIDER_NAME = "none";
@@ -121,17 +122,20 @@ public class TbsGeneric {
}
}
- private boolean mIsInitialized = false;
- private TbsGatt mTbsGatt = null;
- private List<Bearer> mBearerList = new ArrayList<>();
+ private final List<Bearer> mBearerList = new ArrayList<>();
+ private final Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>();
+ private final Receiver mReceiver = new Receiver();
+ private final ServiceFactory mFactory = new ServiceFactory();
+
+ private final TbsGatt mTbsGatt;
+ private final Context mContext;
+
+ private boolean mIsInitialized;
private int mLastIndexAssigned = TbsCall.INDEX_UNASSIGNED;
- private Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>();
private Bearer mForegroundBearer = null;
private int mLastRequestIdAssigned = 0;
private List<String> mUriSchemes = new ArrayList<>(Arrays.asList("tel"));
- private Receiver mReceiver = null;
private int mStoredRingerMode = -1;
- private final ServiceFactory mFactory = new ServiceFactory();
private LeAudioService mLeAudioService;
private final class Receiver extends BroadcastReceiver {
@@ -162,9 +166,9 @@ public class TbsGeneric {
}
;
- public synchronized boolean init(TbsGatt tbsGatt) {
- Log.d(TAG, "init");
- mTbsGatt = tbsGatt;
+ TbsGeneric(Context ctx, TbsGatt tbsGatt) {
+ mTbsGatt = requireNonNull(tbsGatt);
+ mContext = requireNonNull(ctx);
int ccid =
ContentControlIdKeeper.acquireCcid(
@@ -173,7 +177,7 @@ public class TbsGeneric {
if (!isCcidValid(ccid)) {
Log.e(TAG, " CCID is not valid");
cleanup();
- return false;
+ return;
}
if (!mTbsGatt.init(
@@ -187,15 +191,10 @@ public class TbsGeneric {
mTbsGattCallback)) {
Log.e(TAG, " TbsGatt init failed");
cleanup();
- return false;
+ return;
}
- AudioManager audioManager = mTbsGatt.getContext().getSystemService(AudioManager.class);
- if (audioManager == null) {
- Log.w(TAG, " AudioManager is not available");
- cleanup();
- return false;
- }
+ AudioManager audioManager = requireNonNull(mContext.getSystemService(AudioManager.class));
// read initial value of ringer mode
mStoredRingerMode = audioManager.getRingerMode();
@@ -206,25 +205,20 @@ public class TbsGeneric {
mTbsGatt.clearSilentModeFlag();
}
- mReceiver = new Receiver();
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- mTbsGatt.getContext().registerReceiver(mReceiver, filter);
+ mContext.registerReceiver(mReceiver, filter);
mIsInitialized = true;
- return true;
}
public synchronized void cleanup() {
Log.d(TAG, "cleanup");
- if (mTbsGatt != null) {
- if (mReceiver != null) {
- mTbsGatt.getContext().unregisterReceiver(mReceiver);
- }
- mTbsGatt.cleanup();
- mTbsGatt = null;
+ if (mIsInitialized) {
+ mContext.unregisterReceiver(mReceiver);
}
+ mTbsGatt.cleanup();
mIsInitialized = false;
}
@@ -236,9 +230,7 @@ public class TbsGeneric {
*/
public synchronized void onDeviceAuthorizationSet(BluetoothDevice device) {
// Notify TBS GATT service instance in case of pending operations
- if (mTbsGatt != null) {
- mTbsGatt.onDeviceAuthorizationSet(device);
- }
+ mTbsGatt.onDeviceAuthorizationSet(device);
}
/**
@@ -247,10 +239,6 @@ public class TbsGeneric {
* @param device device for which inband ringtone has been set
*/
public synchronized void setInbandRingtoneSupport(BluetoothDevice device) {
- if (mTbsGatt == null) {
- Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null");
- return;
- }
mTbsGatt.setInbandRingtoneFlag(device);
}
@@ -260,10 +248,6 @@ public class TbsGeneric {
* @param device device for which inband ringtone has been cleared
*/
public synchronized void clearInbandRingtoneSupport(BluetoothDevice device) {
- if (mTbsGatt == null) {
- Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null");
- return;
- }
mTbsGatt.clearInbandRingtoneFlag(device);
}
@@ -767,7 +751,7 @@ public class TbsGeneric {
Log.i(TAG, "originate uri=" + uri);
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mTbsGatt.getContext().startActivity(intent);
+ mContext.startActivity(intent);
mTbsGatt.setCallControlPointResult(
device,
TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE,
@@ -844,6 +828,20 @@ public class TbsGeneric {
return;
}
+ if (shouldBlockTbsForBroadcastReceiver(device)) {
+ Log.w(
+ TAG,
+ "Blocking TBS operation for non-primary device in broadcast,"
+ + " opcode = "
+ + callControlRequestOpcodeStr(opcode));
+ mTbsGatt.setCallControlPointResult(
+ device,
+ opcode,
+ 0,
+ TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE);
+ return;
+ }
+
int result;
switch (opcode) {
@@ -1245,6 +1243,20 @@ public class TbsGeneric {
return false;
}
+ private boolean shouldBlockTbsForBroadcastReceiver(BluetoothDevice device) {
+ if (device == null) {
+ Log.w(TAG, "shouldBlockTbsForBroadcastReceiver: Ignore null device");
+ return false;
+ }
+ if (!isLeAudioServiceAvailable()) {
+ Log.w(TAG, "shouldBlockTbsForBroadcastReceiver: LeAudioService is not available");
+ return false;
+ }
+
+ return mLeAudioService.getLocalBroadcastReceivers().contains(device)
+ && !mLeAudioService.isPrimaryDevice(device);
+ }
+
/**
* Dump status of TBS service along with related objects
*
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsService.java b/android/app/src/com/android/bluetooth/tbs/TbsService.java
index dc3819eec1..263f765e53 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsService.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsService.java
@@ -20,6 +20,8 @@ package com.android.bluetooth.tbs;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeCall;
@@ -27,13 +29,13 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothLeCallControl;
import android.bluetooth.IBluetoothLeCallControlCallback;
import android.content.AttributionSource;
-import android.content.Context;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.sysprop.BluetoothProperties;
import android.util.Log;
import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,16 +46,20 @@ import java.util.Map;
import java.util.UUID;
public class TbsService extends ProfileService {
-
- private static final String TAG = "TbsService";
+ private static final String TAG = TbsService.class.getSimpleName();
private static TbsService sTbsService;
+
private final Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>();
+ private final TbsGeneric mTbsGeneric;
- private final TbsGeneric mTbsGeneric = new TbsGeneric();
+ public TbsService(AdapterService adapterService) {
+ super(requireNonNull(adapterService));
- public TbsService(Context ctx) {
- super(ctx);
+ // Mark service as started
+ setTbsService(this);
+
+ mTbsGeneric = new TbsGeneric(adapterService, new TbsGatt(adapterService, this));
}
public static boolean isEnabled() {
@@ -66,19 +72,6 @@ public class TbsService extends ProfileService {
}
@Override
- public void start() {
- Log.d(TAG, "start()");
- if (sTbsService != null) {
- throw new IllegalStateException("start() called twice");
- }
-
- // Mark service as started
- setTbsService(this);
-
- mTbsGeneric.init(new TbsGatt(this));
- }
-
- @Override
public void stop() {
Log.d(TAG, "stop()");
if (sTbsService == null) {
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index 2fa94bd27e..eacdefbb06 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -148,8 +148,6 @@ public class BluetoothInCallService extends InCallService {
private final CallInfo mCallInfo;
- protected boolean mOnCreateCalled = false;
-
private int mMaxNumberOfCalls = 0;
private BluetoothAdapter mAdapter = null;
@@ -367,7 +365,6 @@ public class BluetoothInCallService extends InCallService {
private BluetoothInCallService(CallInfo callInfo) {
Log.i(TAG, "BluetoothInCallService is created");
mCallInfo = Objects.requireNonNullElseGet(callInfo, () -> new CallInfo());
- sInstance = this;
mExecutor = Executors.newSingleThreadExecutor();
}
@@ -546,10 +543,6 @@ public class BluetoothInCallService extends InCallService {
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, MODIFY_PHONE_STATE})
public boolean listCurrentCalls() {
synchronized (LOCK) {
- if (!mOnCreateCalled) {
- Log.w(TAG, "listcurrentCalls() is called before onCreate()");
- return false;
- }
enforceModifyPermission();
// only log if it is after we recently updated the headset state or else it can
// clog the android log since this can be queried every second.
@@ -780,25 +773,28 @@ public class BluetoothInCallService extends InCallService {
@Override
public void onCreate() {
- Log.d(TAG, "onCreate");
super.onCreate();
- mAdapter = requireNonNull(getSystemService(BluetoothManager.class)).getAdapter();
- mTelephonyManager = requireNonNull(getSystemService(TelephonyManager.class));
- mTelecomManager = requireNonNull(getSystemService(TelecomManager.class));
- mAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
- mAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.LE_CALL_CONTROL);
- mBluetoothAdapterReceiver = new BluetoothAdapterReceiver();
- IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- registerReceiver(mBluetoothAdapterReceiver, intentFilter);
- mOnCreateCalled = true;
+ synchronized (LOCK) {
+ Log.d(TAG, "onCreate");
+ mAdapter = requireNonNull(getSystemService(BluetoothManager.class)).getAdapter();
+ mTelephonyManager = requireNonNull(getSystemService(TelephonyManager.class));
+ mTelecomManager = requireNonNull(getSystemService(TelecomManager.class));
+ mAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.HEADSET);
+ mAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.LE_CALL_CONTROL);
+ mBluetoothAdapterReceiver = new BluetoothAdapterReceiver();
+ IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ registerReceiver(mBluetoothAdapterReceiver, intentFilter);
+ sInstance = this;
+ }
}
@Override
public void onDestroy() {
- Log.d(TAG, "onDestroy");
- clear();
- mOnCreateCalled = false;
+ synchronized (LOCK) {
+ Log.d(TAG, "onDestroy");
+ clear();
+ }
super.onDestroy();
}
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index e0868e416a..d4309af577 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -31,6 +31,9 @@ import static android.bluetooth.IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVAL
import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static android.bluetooth.IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME;
+import static com.android.bluetooth.flags.Flags.leaudioBroadcastVolumeControlPrimaryGroupOnly;
+import static com.android.bluetooth.flags.Flags.vcpDeviceVolumeApiImprovements;
+
import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;
@@ -64,7 +67,6 @@ 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.bluetooth.flags.Flags;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -117,9 +119,12 @@ public class VolumeControlService extends ProfileService {
new HashMap<>();
private final Map<BluetoothDevice, VolumeControlInputDescriptor> mAudioInputs =
new ConcurrentHashMap<>();
- private final Map<Integer, Integer> mGroupVolumeCache = new HashMap<>();
- private final Map<Integer, Boolean> mGroupMuteCache = new HashMap<>();
- private final Map<BluetoothDevice, Integer> mDeviceVolumeCache = new HashMap<>();
+ private final Map<Integer, Integer> mGroupVolumeCache = new ConcurrentHashMap<>();
+ private final Map<Integer, Boolean> mGroupMuteCache = new ConcurrentHashMap<>();
+ private final Map<BluetoothDevice, Integer> mDeviceVolumeCache = new ConcurrentHashMap<>();
+ private final Map<BluetoothDevice, Boolean> mDeviceMuteCache = new ConcurrentHashMap<>();
+
+ private Boolean mIgnoreSetVolumeFromAF = false;
@VisibleForTesting ServiceFactory mFactory = new ServiceFactory();
@@ -198,6 +203,7 @@ public class VolumeControlService extends ProfileService {
mGroupVolumeCache.clear();
mGroupMuteCache.clear();
mDeviceVolumeCache.clear();
+ mDeviceMuteCache.clear();
synchronized (mCallbacks) {
mCallbacks.kill();
@@ -529,7 +535,8 @@ public class VolumeControlService extends ProfileService {
mNativeInterface.setExtAudioOutVolumeOffset(device, instanceId, volumeOffset);
}
- void setDeviceVolume(BluetoothDevice device, int volume, boolean isGroupOp) {
+ @VisibleForTesting
+ synchronized void setDeviceVolume(BluetoothDevice device, int volume, boolean isGroupOp) {
Log.d(
TAG,
"setDeviceVolume: " + device + ", volume: " + volume + ", isGroupOp: " + isGroupOp);
@@ -551,18 +558,60 @@ public class VolumeControlService extends ProfileService {
Log.i(TAG, "Setting individual device volume");
mDeviceVolumeCache.put(device, volume);
mNativeInterface.setVolume(device, volume);
+
+ if (vcpDeviceVolumeApiImprovements()) {
+ // We only receive the volume change and mute state needs to be acquired manually
+ Boolean isStreamMute =
+ mAudioManager.isStreamMute(getBluetoothContextualVolumeStream());
+ adjustDeviceMute(device, volume, isStreamMute);
+ }
+ }
+ }
+
+ private void adjustDeviceMute(BluetoothDevice device, int volume, Boolean isStreamMute) {
+ Boolean isMute = getMute(device);
+ if (!isMute.equals(isStreamMute)) {
+ Log.d(
+ TAG,
+ "Mute state mismatch, stream mute: "
+ + isStreamMute
+ + ", device mute: "
+ + isMute
+ + ", new volume: "
+ + volume);
+ if (isStreamMute) {
+ Log.i(TAG, "Mute the device " + device);
+ mute(device);
+ }
+ if (!isStreamMute && (volume > 0)) {
+ Log.i(TAG, "Unmute the device " + device);
+ unmute(device);
+ }
}
}
- public void setGroupVolume(int groupId, int volume) {
+ public synchronized void setGroupVolume(int groupId, int volume) {
Log.d(TAG, "setGroupVolume: " + groupId + ", volume: " + volume);
+ if (mIgnoreSetVolumeFromAF) {
+ Log.d(TAG, "setGroupVolume ignored (from AF) because persisted/cached volume was used");
+ mIgnoreSetVolumeFromAF = false;
+ return;
+ }
+
if (volume < 0) {
Log.w(TAG, "Tried to set invalid volume " + volume + ". Ignored.");
return;
}
- mGroupVolumeCache.put(groupId, volume);
+ synchronized (mDeviceVolumeCache) {
+ mGroupVolumeCache.put(groupId, volume);
+ if (vcpDeviceVolumeApiImprovements()) {
+ for (BluetoothDevice dev : getGroupDevices(groupId)) {
+ mDeviceVolumeCache.put(dev, volume);
+ }
+ }
+ }
mNativeInterface.setGroupVolume(groupId, volume);
// We only receive the volume change and mute state needs to be acquired manually
@@ -578,7 +627,7 @@ public class VolumeControlService extends ProfileService {
* have to explicitly unmute the remote device.
*/
if (!isGroupMute.equals(isStreamMute)) {
- Log.w(
+ Log.d(
TAG,
"Mute state mismatch, stream mute: "
+ isStreamMute
@@ -594,21 +643,60 @@ public class VolumeControlService extends ProfileService {
Log.i(TAG, "Unmute the group " + groupId);
unmuteGroup(groupId);
}
+ } else if (vcpDeviceVolumeApiImprovements()) {
+ for (BluetoothDevice device : getGroupDevices(groupId)) {
+ adjustDeviceMute(device, volume, isStreamMute);
+ }
}
}
+ /**
+ * Get group cached volume. If not cached, then try to read from any device from this group.
+ *
+ * @param groupId the group identifier
+ * @return the cached volume
+ */
public int getGroupVolume(int groupId) {
- return mGroupVolumeCache.getOrDefault(groupId, VOLUME_CONTROL_UNKNOWN_VOLUME);
+ if (vcpDeviceVolumeApiImprovements()) {
+ synchronized (mDeviceVolumeCache) {
+ Integer volume = mGroupVolumeCache.get(groupId);
+ if (volume != null) {
+ return volume;
+ }
+ Log.d(TAG, "No group volume available");
+ for (BluetoothDevice device : getGroupDevices(groupId)) {
+ volume = mDeviceVolumeCache.get(device);
+ if (volume != null) {
+ Log.w(TAG, "Volume taken from device: " + device);
+ return volume;
+ }
+ }
+ return VOLUME_CONTROL_UNKNOWN_VOLUME;
+ }
+ } else {
+ return mGroupVolumeCache.getOrDefault(groupId, VOLUME_CONTROL_UNKNOWN_VOLUME);
+ }
}
/**
- * Get device cached volume.
+ * Get device cached volume. If not cached, then try to read from its group.
*
* @param device the device
* @return the cached volume
*/
public int getDeviceVolume(BluetoothDevice device) {
- return mDeviceVolumeCache.getOrDefault(device, VOLUME_CONTROL_UNKNOWN_VOLUME);
+ if (vcpDeviceVolumeApiImprovements()) {
+ synchronized (mDeviceVolumeCache) {
+ Integer volume = mDeviceVolumeCache.get(device);
+ if (volume != null) {
+ return volume;
+ }
+ return mGroupVolumeCache.getOrDefault(
+ getGroupId(device), VOLUME_CONTROL_UNKNOWN_VOLUME);
+ }
+ } else {
+ return mDeviceVolumeCache.getOrDefault(device, VOLUME_CONTROL_UNKNOWN_VOLUME);
+ }
}
/**
@@ -617,7 +705,7 @@ public class VolumeControlService extends ProfileService {
* @param groupId the group identifier
* @param active indicator if group is active or not
*/
- public void setGroupActive(int groupId, boolean active) {
+ public synchronized void setGroupActive(int groupId, boolean active) {
Log.d(TAG, "setGroupActive: " + groupId + ", active: " + active);
if (!active) {
/* For now we don't need to handle group inactivation */
@@ -634,27 +722,75 @@ public class VolumeControlService extends ProfileService {
}
/**
+ * Get device cached mute status. If not cached, then try to read from its group.
+ *
+ * @param device the device
+ * @return mute status
+ */
+ public Boolean getMute(BluetoothDevice device) {
+ synchronized (mDeviceMuteCache) {
+ Boolean isMute = mDeviceMuteCache.get(device);
+ if (isMute != null) {
+ return isMute;
+ }
+ return mGroupMuteCache.getOrDefault(getGroupId(device), false);
+ }
+ }
+
+ /**
+ * Get group cached mute status. If not cached, then try to read from any device from this
+ * group.
+ *
* @param groupId the group identifier
+ * @return mute status
*/
public Boolean getGroupMute(int groupId) {
- return mGroupMuteCache.getOrDefault(groupId, false);
+ if (vcpDeviceVolumeApiImprovements()) {
+ synchronized (mDeviceMuteCache) {
+ Boolean isMute = mGroupMuteCache.get(groupId);
+ if (isMute != null) {
+ return isMute;
+ }
+ for (BluetoothDevice device : getGroupDevices(groupId)) {
+ isMute = mDeviceMuteCache.get(device);
+ if (isMute != null) {
+ return isMute;
+ }
+ }
+ return false;
+ }
+ } else {
+ return mGroupMuteCache.getOrDefault(groupId, false);
+ }
}
public void mute(BluetoothDevice device) {
+ mDeviceMuteCache.put(device, true);
mNativeInterface.mute(device);
}
public void muteGroup(int groupId) {
- mGroupMuteCache.put(groupId, true);
+ synchronized (mDeviceMuteCache) {
+ mGroupMuteCache.put(groupId, true);
+ for (BluetoothDevice dev : getGroupDevices(groupId)) {
+ mDeviceMuteCache.put(dev, true);
+ }
+ }
mNativeInterface.muteGroup(groupId);
}
public void unmute(BluetoothDevice device) {
+ mDeviceMuteCache.put(device, false);
mNativeInterface.unmute(device);
}
public void unmuteGroup(int groupId) {
- mGroupMuteCache.put(groupId, false);
+ synchronized (mDeviceMuteCache) {
+ mGroupMuteCache.put(groupId, false);
+ for (BluetoothDevice dev : getGroupDevices(groupId)) {
+ mDeviceMuteCache.put(dev, false);
+ }
+ }
mNativeInterface.unmuteGroup(groupId);
}
@@ -724,7 +860,7 @@ public class VolumeControlService extends ProfileService {
notifyNewCallbackOfKnownVolumeInfo(callback);
}
- public void handleGroupNodeAdded(int groupId, BluetoothDevice device) {
+ public synchronized void handleGroupNodeAdded(int groupId, BluetoothDevice device) {
// Ignore disconnected device, its volume will be set once it connects
synchronized (mStateMachines) {
VolumeControlStateMachine sm = mStateMachines.get(device);
@@ -737,18 +873,33 @@ public class VolumeControlService extends ProfileService {
}
// If group volume has already changed, the new group member should set it
- Integer groupVolume = getGroupVolume(groupId);
- if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
- Log.i(TAG, "Setting value:" + groupVolume + " to " + device);
- mNativeInterface.setVolume(device, groupVolume);
- }
-
- Boolean isGroupMuted = getGroupMute(groupId);
- Log.i(TAG, "Setting mute:" + isGroupMuted + " to " + device);
- if (isGroupMuted) {
- mNativeInterface.mute(device);
+ if (vcpDeviceVolumeApiImprovements()) {
+ int volume = getDeviceVolume(device);
+ if (volume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
+ Log.i(TAG, "Setting device/group volume:" + volume + " to the device:" + device);
+ setDeviceVolume(device, volume, false);
+ Boolean isDeviceMuted = getMute(device);
+ Log.i(TAG, "Setting mute:" + isDeviceMuted + " to " + device);
+ if (isDeviceMuted) {
+ mute(device);
+ } else {
+ unmute(device);
+ }
+ }
} else {
- mNativeInterface.unmute(device);
+ Integer groupVolume = getGroupVolume(groupId);
+ if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
+ Log.i(TAG, "Setting value:" + groupVolume + " to " + device);
+ mNativeInterface.setVolume(device, groupVolume);
+ }
+
+ Boolean isGroupMuted = getGroupMute(groupId);
+ Log.i(TAG, "Setting mute:" + isGroupMuted + " to " + device);
+ if (isGroupMuted) {
+ mNativeInterface.mute(device);
+ } else {
+ mNativeInterface.unmute(device);
+ }
}
}
@@ -769,14 +920,22 @@ public class VolumeControlService extends ProfileService {
return;
}
- mGroupVolumeCache.put(groupId, volume);
- mGroupMuteCache.put(groupId, mute);
+ synchronized (mDeviceVolumeCache) {
+ mGroupVolumeCache.put(groupId, volume);
+ mGroupMuteCache.put(groupId, mute);
+ if (vcpDeviceVolumeApiImprovements()) {
+ for (BluetoothDevice dev : getGroupDevices(groupId)) {
+ mDeviceVolumeCache.put(dev, volume);
+ mDeviceMuteCache.put(dev, mute);
+ }
+ }
+ }
LeAudioService leAudioService = mFactory.getLeAudioService();
if (leAudioService != null) {
int currentlyActiveGroupId = leAudioService.getActiveGroupId();
if (currentlyActiveGroupId == GROUP_ID_INVALID || groupId != currentlyActiveGroupId) {
- if (!Flags.leaudioBroadcastVolumeControlPrimaryGroupOnly()) {
+ if (!leaudioBroadcastVolumeControlPrimaryGroupOnly()) {
Log.i(
TAG,
"Skip updating to audio system if not updating volume for current"
@@ -823,7 +982,7 @@ public class VolumeControlService extends ProfileService {
return (int) Math.round((double) streamVolume * LE_AUDIO_MAX_VOL / streamMaxVolume);
}
- void handleVolumeControlChanged(
+ synchronized void handleVolumeControlChanged(
BluetoothDevice device,
int groupId,
int volume,
@@ -838,9 +997,6 @@ public class VolumeControlService extends ProfileService {
return;
}
- int groupVolume = getGroupVolume(groupId);
- Boolean groupMute = getGroupMute(groupId);
-
if (isAutonomous && device != null) {
Log.i(
TAG,
@@ -851,25 +1007,59 @@ public class VolumeControlService extends ProfileService {
/* We are here, because system has just started and LeAudio device is connected. If
* remote device has User Persistent flag set, Android sets the volume to local cache
* and to the audio system if not already streaming to other devices.
- * If Reset Flag is set, then Android sets to remote devices either cached volume volume
- * taken from audio manager.
+ * If Reset Flag is set, then Android sets to remote devices either cached volume or
+ * volume taken from audio manager (AF always call setVolume via LeAudioService at first
+ * connected remote from group).
* Note, to match BR/EDR behavior, don't show volume change in UI here
*/
if (((flags & VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK) == 0x01)
&& (getConnectedDevices(groupId).size() == 1)) {
Log.i(TAG, "Setting device: " + device + " volume: " + volume + " to the system");
+ if (vcpDeviceVolumeApiImprovements()) {
+ // Ignore volume from AF because persisted volume was used
+ mIgnoreSetVolumeFromAF = true;
+ }
updateGroupCacheAndAudioSystem(groupId, volume, mute, false);
return;
}
// Reset flag is used
- if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
- Log.i(TAG, "Setting group volume: " + groupVolume + " to the device: " + device);
- setGroupVolume(groupId, groupVolume);
+ if (vcpDeviceVolumeApiImprovements()) {
+ int deviceVolume = getDeviceVolume(device);
+ if (deviceVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
+ Log.i(
+ TAG,
+ "Setting device/group volume: "
+ + deviceVolume
+ + " to the device: "
+ + device);
+ setDeviceVolume(device, deviceVolume, false);
+ Boolean isDeviceMuted = getMute(device);
+ Log.i(TAG, "Setting mute:" + isDeviceMuted + " to " + device);
+ if (isDeviceMuted) {
+ mute(device);
+ } else {
+ unmute(device);
+ }
+ if (getConnectedDevices(groupId).size() == 1) {
+ // Ignore volume from AF because cached volume was used
+ mIgnoreSetVolumeFromAF = true;
+ }
+ }
} else {
- int systemVolume = getBleVolumeFromCurrentStream();
- Log.i(TAG, "Setting system volume: " + systemVolume + " to the group: " + groupId);
- setGroupVolume(groupId, systemVolume);
+ int groupVolume = getGroupVolume(groupId);
+ if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
+ Log.i(
+ TAG,
+ "Setting group volume: " + groupVolume + " to the device: " + device);
+ setGroupVolume(groupId, groupVolume);
+ } else {
+ int systemVolume = getBleVolumeFromCurrentStream();
+ Log.i(
+ TAG,
+ "Setting system volume: " + systemVolume + " to the group: " + groupId);
+ setGroupVolume(groupId, systemVolume);
+ }
}
return;
@@ -894,13 +1084,16 @@ public class VolumeControlService extends ProfileService {
}
}
- if (!isAutonomous) {
+ if (!vcpDeviceVolumeApiImprovements() && !isAutonomous) {
/* If the change is triggered by Android device, the stream is already changed.
* However it might be called with isAutonomous, one the first read of after
* reconnection. Make sure device has group volume. Also it might happen that
* remote side send us wrong value - lets check it.
*/
+ int groupVolume = getGroupVolume(groupId);
+ Boolean groupMute = getGroupMute(groupId);
+
if ((groupVolume == volume) && (groupMute == mute)) {
Log.i(TAG, " Volume:" + volume + ", mute:" + mute + " confirmed by remote side.");
return;
@@ -936,7 +1129,9 @@ public class VolumeControlService extends ProfileService {
+ " expected volume: "
+ groupVolume);
}
- } else {
+ }
+
+ if (isAutonomous && device == null) {
/* Received group notification for autonomous change. Update cache and audio system. */
updateGroupCacheAndAudioSystem(groupId, volume, mute, true);
}
@@ -1249,7 +1444,7 @@ public class VolumeControlService extends ProfileService {
input.onGainSettingsPropertiesChanged(id, unit, min, max);
}
- void handleStackEvent(VolumeControlStackEvent stackEvent) {
+ synchronized void handleStackEvent(VolumeControlStackEvent stackEvent) {
if (!isAvailable()) {
Log.e(TAG, "Event ignored, service not available: " + stackEvent);
return;
@@ -1375,10 +1570,13 @@ public class VolumeControlService extends ProfileService {
int broadcastVolume = VOLUME_CONTROL_UNKNOWN_VOLUME;
if (volume.isPresent()) {
broadcastVolume = volume.get();
- mDeviceVolumeCache.put(dev, broadcastVolume);
+ if (!vcpDeviceVolumeApiImprovements()) {
+ mDeviceVolumeCache.put(dev, broadcastVolume);
+ }
} else {
broadcastVolume = getDeviceVolume(dev);
- if (broadcastVolume == VOLUME_CONTROL_UNKNOWN_VOLUME) {
+ if (!vcpDeviceVolumeApiImprovements()
+ && broadcastVolume == VOLUME_CONTROL_UNKNOWN_VOLUME) {
broadcastVolume = getGroupVolume(getGroupId(dev));
}
}
@@ -1474,7 +1672,7 @@ public class VolumeControlService extends ProfileService {
Log.d(TAG, device + " is unbond. Remove state machine");
removeStateMachine(device);
}
- } else if (toState == STATE_CONNECTED) {
+ } else if (!vcpDeviceVolumeApiImprovements() && toState == STATE_CONNECTED) {
// Restore the group volume if it was changed while the device was not yet connected.
Integer groupId = getGroupId(device);
if (groupId != GROUP_ID_INVALID) {
@@ -2025,5 +2223,18 @@ public class VolumeControlService extends ProfileService {
+ ", mute: "
+ getGroupMute(entry.getKey()));
}
+
+ if (vcpDeviceVolumeApiImprovements()) {
+ for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceVolumeCache.entrySet()) {
+ ProfileService.println(
+ sb,
+ " Device: "
+ + entry.getKey()
+ + " volume: "
+ + entry.getValue()
+ + ", mute: "
+ + getMute(entry.getKey()));
+ }
+ }
}
}
diff --git a/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java b/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java
new file mode 100644
index 0000000000..4fd324dcf9
--- /dev/null
+++ b/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.le_scan;
+
+import static com.android.bluetooth.le_scan.ScanController.DEFAULT_REPORT_DELAY_FLOOR;
+
+import android.provider.DeviceConfig;
+
+import com.android.bluetooth.Utils.TimeProvider;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Set;
+
+/**
+ * Throttler to reduce the number of times the Bluetooth process wakes up to check for pending batch
+ * scan results. The wake-up intervals are increased when no matching results are found and are
+ * longer when the screen is off.
+ */
+class BatchScanThrottler {
+ // Minimum batch trigger interval to check for batched results when the screen is off
+ @VisibleForTesting static final long SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS = 20000L;
+ // Adjusted minimum report delay for unfiltered batch scan clients
+ @VisibleForTesting static final long UNFILTERED_DELAY_FLOOR_MS = 20000L;
+ // Adjusted minimum report delay for unfiltered batch scan clients when the screen is off
+ @VisibleForTesting static final long UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS = 60000L;
+ // Backoff stages used as multipliers for the minimum delay floor (standard or screen-off)
+ @VisibleForTesting static final int[] BACKOFF_MULTIPLIERS = {1, 1, 2, 2, 4};
+ // Start screen-off trigger interval throttling after the screen has been off for this period
+ // of time. This allows the screen-on intervals to be used for a short period of time after the
+ // screen has gone off, and avoids too much flipping between screen-off and screen-on backoffs
+ // when the screen is off for a short period of time
+ @VisibleForTesting static final long SCREEN_OFF_DELAY_MS = 60000L;
+ private final TimeProvider mTimeProvider;
+ private final long mDelayFloor;
+ private final long mScreenOffDelayFloor;
+ private int mBackoffStage = 0;
+ private long mScreenOffTriggerTime = 0L;
+ private boolean mScreenOffThrottling = false;
+
+ BatchScanThrottler(TimeProvider timeProvider, boolean screenOn) {
+ mTimeProvider = timeProvider;
+ mDelayFloor =
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_BLUETOOTH,
+ "report_delay",
+ DEFAULT_REPORT_DELAY_FLOOR);
+ mScreenOffDelayFloor = Math.max(mDelayFloor, SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS);
+ onScreenOn(screenOn);
+ }
+
+ void resetBackoff() {
+ mBackoffStage = 0;
+ }
+
+ void onScreenOn(boolean screenOn) {
+ if (screenOn) {
+ mScreenOffTriggerTime = 0L;
+ mScreenOffThrottling = false;
+ resetBackoff();
+ } else {
+ // Screen-off intervals to be used after the trigger time
+ mScreenOffTriggerTime = mTimeProvider.elapsedRealtime() + SCREEN_OFF_DELAY_MS;
+ }
+ }
+
+ long getBatchTriggerIntervalMillis(Set<ScanClient> batchClients) {
+ // Check if we're past the screen-off time and should be using screen-off backoff values
+ if (!mScreenOffThrottling
+ && mScreenOffTriggerTime != 0
+ && mTimeProvider.elapsedRealtime() >= mScreenOffTriggerTime) {
+ mScreenOffThrottling = true;
+ resetBackoff();
+ }
+ long unfilteredFloor =
+ mScreenOffThrottling
+ ? UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS
+ : UNFILTERED_DELAY_FLOOR_MS;
+ long intervalMillis = Long.MAX_VALUE;
+ for (ScanClient client : batchClients) {
+ if (client.settings.getReportDelayMillis() > 0) {
+ long clientIntervalMillis = client.settings.getReportDelayMillis();
+ if ((client.filters == null || client.filters.isEmpty())
+ && clientIntervalMillis < unfilteredFloor) {
+ clientIntervalMillis = unfilteredFloor;
+ }
+ intervalMillis = Math.min(intervalMillis, clientIntervalMillis);
+ }
+ }
+ int backoffIndex =
+ mBackoffStage >= BACKOFF_MULTIPLIERS.length
+ ? BACKOFF_MULTIPLIERS.length - 1
+ : mBackoffStage++;
+ return Math.max(
+ intervalMillis,
+ (mScreenOffThrottling ? mScreenOffDelayFloor : mDelayFloor)
+ * BACKOFF_MULTIPLIERS[backoffIndex]);
+ }
+}
diff --git a/android/app/tests/unit/Android.bp b/android/app/tests/unit/Android.bp
index 346c57fffa..eae8e4a89f 100644
--- a/android/app/tests/unit/Android.bp
+++ b/android/app/tests/unit/Android.bp
@@ -22,6 +22,7 @@ java_defaults {
static_libs: [
"PlatformProperties",
+ "TestParameterInjector",
"android.media.audio-aconfig-exported-java",
"androidx.media_media",
"androidx.room_room-migration",
diff --git a/android/app/tests/unit/AndroidTest.xml b/android/app/tests/unit/AndroidTest.xml
index 212b245d6e..5ac9bd5608 100644
--- a/android/app/tests/unit/AndroidTest.xml
+++ b/android/app/tests/unit/AndroidTest.xml
@@ -55,6 +55,6 @@
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="enable" value="true" />
- <option name="mainline-module-package-name" value="com.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
</object>
</configuration>
diff --git a/android/app/tests/unit/GoogleAndroidTest.xml b/android/app/tests/unit/GoogleAndroidTest.xml
index 61a5011c8f..bcda5eacce 100644
--- a/android/app/tests/unit/GoogleAndroidTest.xml
+++ b/android/app/tests/unit/GoogleAndroidTest.xml
@@ -51,6 +51,6 @@
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="enable" value="true" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
index 877df75ef2..78d1e66db1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
@@ -41,7 +41,6 @@ import androidx.test.uiautomator.UiDevice;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
import com.android.bluetooth.btservice.AdapterService;
-import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -112,11 +111,11 @@ public class TestUtils {
* TestUtils#setAdapterService(AdapterService)}
*/
public static void clearAdapterService(AdapterService adapterService) {
- Assert.assertSame(
- "AdapterService.getAdapterService() must return the same object as the"
- + " supplied adapterService in this method",
- adapterService,
- AdapterService.getAdapterService());
+ assertWithMessage(
+ "AdapterService.getAdapterService() must return the same object as the"
+ + " supplied adapterService in this method")
+ .that(adapterService)
+ .isSameInstanceAs(AdapterService.getAdapterService());
assertThat(adapterService).isNotNull();
AdapterService.clearAdapterService(adapterService);
}
@@ -157,10 +156,7 @@ public class TestUtils {
return context.getPackageManager()
.getResourcesForApplication("com.android.bluetooth.tests");
} catch (PackageManager.NameNotFoundException e) {
- assertWithMessage(
- "Setup Failure: Unable to get test application resources"
- + e.toString())
- .fail();
+ assertWithMessage("Unable to get test application resources: " + e.toString()).fail();
return null;
}
}
@@ -178,7 +174,7 @@ public class TestUtils {
assertThat(intent).isNotNull();
return intent;
} catch (InterruptedException e) {
- Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage());
+ assertWithMessage("Cannot obtain an Intent from the queue: " + e.toString()).fail();
}
return null;
}
@@ -194,7 +190,7 @@ public class TestUtils {
Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
assertThat(intent).isNull();
} catch (InterruptedException e) {
- Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage());
+ assertWithMessage("Cannot obtain an Intent from the queue: " + e.toString()).fail();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
index 04527233f0..a481cef2e0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpCodecConfigTest.java
@@ -16,6 +16,9 @@
package com.android.bluetooth.a2dp;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.mockito.Mockito.*;
import android.bluetooth.BluetoothAdapter;
@@ -31,7 +34,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.R;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -243,22 +245,22 @@ public class A2dpCodecConfigTest {
for (BluetoothCodecConfig config : codecConfigs) {
switch (config.getCodecType()) {
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
- Assert.assertEquals(config.getCodecPriority(), SBC_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(SBC_PRIORITY_DEFAULT);
break;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
- Assert.assertEquals(config.getCodecPriority(), AAC_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(AAC_PRIORITY_DEFAULT);
break;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
- Assert.assertEquals(config.getCodecPriority(), APTX_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(APTX_PRIORITY_DEFAULT);
break;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
- Assert.assertEquals(config.getCodecPriority(), APTX_HD_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(APTX_HD_PRIORITY_DEFAULT);
break;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
- Assert.assertEquals(config.getCodecPriority(), LDAC_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(LDAC_PRIORITY_DEFAULT);
break;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
- Assert.assertEquals(config.getCodecPriority(), OPUS_PRIORITY_DEFAULT);
+ assertThat(config.getCodecPriority()).isEqualTo(OPUS_PRIORITY_DEFAULT);
break;
}
}
@@ -785,10 +787,12 @@ public class A2dpCodecConfigTest {
codecConfig.getCodecSpecific3(),
codecConfig.getCodecSpecific4());
}
- Assert.fail(
- "getDefaultCodecConfigByType: No such codecType="
- + codecType
- + " in sDefaultCodecConfigs");
+ assertWithMessage(
+ "Default codec ("
+ + Arrays.toString(sDefaultCodecConfigs)
+ + ") does not contains "
+ + codecType)
+ .fail();
return null;
}
@@ -808,10 +812,12 @@ public class A2dpCodecConfigTest {
codecCapabilities.getCodecSpecific3(),
codecCapabilities.getCodecSpecific4());
}
- Assert.fail(
- "getCodecCapabilitiesByType: No such codecType="
- + codecType
- + " in sCodecCapabilities");
+ assertWithMessage(
+ "Codec capabilities ("
+ + Arrays.toString(sCodecCapabilities)
+ + ") does not contains "
+ + codecType)
+ .fail();
return null;
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java
index 4b67658962..cd9ad4dd9a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java
@@ -44,7 +44,6 @@ import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -274,7 +273,7 @@ public class BrowserPlayerWrapperTest {
MediaBrowser.ConnectionCallback browserConnCb = mBrowserConnCb.getValue();
browserConnCb.onConnected();
- Assert.assertEquals("root_folder", wrapper.getRootId());
+ assertThat(wrapper.getRootId()).isEqualTo("root_folder");
verify(mMockBrowser).disconnect();
}
@@ -392,22 +391,22 @@ public class BrowserPlayerWrapperTest {
for (int i = 0; i < item_list.size(); i++) {
MediaItem expected = items.get(i);
ListItem item = item_list.get(i);
- Assert.assertEquals(expected.isBrowsable(), item.isFolder);
+ assertThat(item.isFolder).isEqualTo(expected.isBrowsable());
if (item.isFolder) {
Folder folder = item.folder;
assertThat(folder).isNotNull();
assertThat(folder.isPlayable).isFalse();
- Assert.assertEquals(expected.getDescription().getMediaId(), folder.mediaId);
- Assert.assertEquals(expected.getDescription().getTitle().toString(), folder.title);
+ assertThat(folder.mediaId).isEqualTo(expected.getDescription().getMediaId());
+ assertThat(folder.title).isEqualTo(expected.getDescription().getTitle().toString());
} else {
Metadata song = item.song;
assertThat(song).isNotNull();
- Assert.assertEquals(expected.getDescription().getMediaId(), song.mediaId);
- Assert.assertEquals(expected.getDescription().getTitle().toString(), song.title);
- Assert.assertEquals(
- expected.getDescription().getSubtitle().toString(), song.artist);
- Assert.assertEquals(
- expected.getDescription().getDescription().toString(), song.album);
+ assertThat(song.mediaId).isEqualTo(expected.getDescription().getMediaId());
+ assertThat(song.title).isEqualTo(expected.getDescription().getTitle().toString());
+ assertThat(song.artist)
+ .isEqualTo(expected.getDescription().getSubtitle().toString());
+ assertThat(song.album)
+ .isEqualTo(expected.getDescription().getDescription().toString());
if (expected.getDescription().getIconBitmap() != null) {
assertThat(song.image).isNotNull();
Bitmap expectedBitmap = expected.getDescription().getIconBitmap();
@@ -415,7 +414,7 @@ public class BrowserPlayerWrapperTest {
} else if (expected.getDescription().getIconUri() != null) {
assertThat(mTestBitmap.sameAs(song.image.getImage())).isTrue();
} else {
- Assert.assertEquals(null, song.image);
+ assertThat(song.image).isNull();
}
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java
index ee066309b7..c8b03d632f 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java
@@ -16,6 +16,8 @@
package com.android.bluetooth.audio_util;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.*;
import android.content.Context;
@@ -31,7 +33,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -138,7 +139,7 @@ public class MediaPlayerListTest {
}
@Test
- public void testUpdateMeidaDataForAudioPlaybackWhenAcitvePlayNotPlaying() {
+ public void testUpdateMediaDataForAudioPlaybackWhenActivePlayNotPlaying() {
// Verify update media data with playing state
doReturn(prepareMediaData(PlaybackState.STATE_PAUSED))
.when(mMockPlayerWrapper)
@@ -146,7 +147,7 @@ public class MediaPlayerListTest {
mMediaPlayerList.injectAudioPlaybacActive(true);
verify(mMediaUpdateCallback).run(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(data.state.getState(), PlaybackState.STATE_PLAYING);
+ assertThat(data.state.getState()).isEqualTo(PlaybackState.STATE_PLAYING);
// verify update media data with current media player media data
MediaData currentMediaData = prepareMediaData(PlaybackState.STATE_PAUSED);
@@ -154,9 +155,9 @@ public class MediaPlayerListTest {
mMediaPlayerList.injectAudioPlaybacActive(false);
verify(mMediaUpdateCallback, times(2)).run(mMediaUpdateData.capture());
data = mMediaUpdateData.getValue();
- Assert.assertEquals(data.metadata, currentMediaData.metadata);
- Assert.assertEquals(data.state.toString(), currentMediaData.state.toString());
- Assert.assertEquals(data.queue, currentMediaData.queue);
+ assertThat(data.metadata).isEqualTo(currentMediaData.metadata);
+ assertThat(data.state.toString()).isEqualTo(currentMediaData.state.toString());
+ assertThat(data.queue).isEqualTo(currentMediaData.queue);
}
@Test
@@ -171,7 +172,7 @@ public class MediaPlayerListTest {
}
@Test
- public void testNotUdpateMediaDataForAudioPlaybackWhenActivePlayerIsPlaying() {
+ public void testNotUpdateMediaDataForAudioPlaybackWhenActivePlayerIsPlaying() {
// Verify not update media data for Audio Playback when active player is playing
doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
.when(mMockPlayerWrapper)
@@ -182,7 +183,7 @@ public class MediaPlayerListTest {
}
@Test
- public void testNotUdpateMediaDataForActivePlayerWhenAudioPlaybackIsActive() {
+ public void testNotUpdateMediaDataForActivePlayerWhenAudioPlaybackIsActive() {
doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
.when(mMockPlayerWrapper)
.getCurrentMediaData();
@@ -225,7 +226,7 @@ public class MediaPlayerListTest {
MediaPlayerWrapper newActiveMediaPlayer = mMediaPlayerList.getActivePlayer();
// Should be the same as before.
- Assert.assertEquals(activeMediaPlayer, newActiveMediaPlayer);
+ assertThat(activeMediaPlayer).isEqualTo(newActiveMediaPlayer);
session.release();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
index 2ffe3589d5..2b323d9810 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
@@ -39,7 +39,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -257,7 +256,7 @@ public class MediaPlayerWrapperTest {
verify(mMockController).registerCallback(mControllerCbs.capture(), any());
MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
- // Update Metdata returned by controller
+ // Update Metadata returned by controller
mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title");
doReturn(mTestMetadata.build()).when(mMockController).getMetadata();
controllerCallbacks.onMetadataChanged(mTestMetadata.build());
@@ -265,15 +264,9 @@ public class MediaPlayerWrapperTest {
// Assert that the metadata was updated and playback state wasn't
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to original PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0);
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.queue).isEmpty();
// Update PlaybackState returned by controller
mTestState.setActiveQueueItemId(103);
@@ -283,15 +276,9 @@ public class MediaPlayerWrapperTest {
// Assert that the PlaybackState was changed but metadata stayed the same
verify(mTestCbs, times(2)).mediaUpdatedCallback(mMediaUpdateData.capture());
data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0);
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.queue).isEmpty();
// Verify that there are no timeout messages pending and there were no timeouts
assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse();
@@ -332,15 +319,9 @@ public class MediaPlayerWrapperTest {
// Assert that both metadata and playback state are there.
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0);
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.queue).isEmpty();
// Verify that there are no timeout messages pending and there were no timeouts
assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse();
@@ -371,10 +352,7 @@ public class MediaPlayerWrapperTest {
// Assert that the metadata returned by getMetadata() is used instead of null
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned metadata is incorrect",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
}
@Test
@@ -402,10 +380,7 @@ public class MediaPlayerWrapperTest {
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned PlaybackState is incorrect",
- data.state.toString(),
- mTestState.build().toString());
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
}
@Test
@@ -428,7 +403,7 @@ public class MediaPlayerWrapperTest {
// Assert that both metadata and playback state are there.
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals("Returned Queue isn't null", data.queue.size(), 0);
+ assertThat(data.queue).isEmpty();
}
/*
@@ -446,9 +421,10 @@ public class MediaPlayerWrapperTest {
// Call getCurrentQueue() multiple times.
for (int i = 0; i < 3; i++) {
- Assert.assertEquals(
- Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)),
- wrapper.getCurrentQueue());
+ assertThat(wrapper.getCurrentQueue())
+ .isEqualTo(
+ Util.toMetadataList(
+ mMockContext, getQueueFromDescriptions(mTestQueue)));
}
doReturn(mTestMetadata.build()).when(mMockController).getMetadata();
@@ -471,9 +447,8 @@ public class MediaPlayerWrapperTest {
assertThat(Util.toMetadata(mMockContext, mTestMetadata.build()).duration)
.isNotEqualTo(wrapper.getCurrentQueue().get(0).duration);
doReturn(mTestMetadata.build()).when(mMockController).getMetadata();
- Assert.assertEquals(
- Util.toMetadata(mMockContext, mTestMetadata.build()).duration,
- wrapper.getCurrentQueue().get(0).duration);
+ assertThat(wrapper.getCurrentQueue().get(0).duration)
+ .isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()).duration);
// The MediaController Metadata should still not be equal to the queue
// as the track count is different and should not be overridden.
assertThat(Util.toMetadata(mMockContext, mTestMetadata.build()))
@@ -506,15 +481,9 @@ public class MediaPlayerWrapperTest {
// Assert that both metadata and only the first playback state is there.
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0);
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.queue).isEmpty();
// Update PlaybackState returned by controller (Shouldn't trigger update)
mTestState.setState(PlaybackState.STATE_PLAYING, 1020, 1.0f);
@@ -623,18 +592,10 @@ public class MediaPlayerWrapperTest {
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals(
- "Returned Queue isn't equal to given Queue",
- data.queue,
- Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)));
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.queue)
+ .isEqualTo(Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)));
// Verify that there are no timeout messages pending and there were no timeouts
assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse();
@@ -673,18 +634,10 @@ public class MediaPlayerWrapperTest {
// Assert that the callback was called with the mismatch data
verify(mTestCbs).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, mTestMetadata.build()));
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- mTestState.build().toString());
- Assert.assertEquals(
- "Returned Queue isn't equal to given Queue",
- data.queue,
- Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)));
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, mTestMetadata.build()));
+ assertThat(data.state.toString()).isEqualTo(mTestState.build().toString());
+ assertThat(data.queue)
+ .isEqualTo(Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)));
}
/*
@@ -711,7 +664,7 @@ public class MediaPlayerWrapperTest {
s.setState(PlaybackState.STATE_PAUSED, 0, 1.0f);
MediaDescription.Builder d = new MediaDescription.Builder();
for (int i = 1; i <= numTestLoops; i++) {
- // Setup Media Info for current itteration
+ // Setup Media Info for current iteration
m.putString(MediaMetadata.METADATA_KEY_TITLE, "BT Fuzz Song " + i);
m.putString(MediaMetadata.METADATA_KEY_ARTIST, "BT Fuzz Artist " + i);
m.putString(MediaMetadata.METADATA_KEY_ALBUM, "BT Fuzz Album " + i);
@@ -751,18 +704,9 @@ public class MediaPlayerWrapperTest {
// that all the Media info matches what was given
verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture());
MediaData data = mMediaUpdateData.getValue();
- Assert.assertEquals(
- "Returned Metadata isn't equal to given Metadata",
- data.metadata,
- Util.toMetadata(mMockContext, m.build()));
- Assert.assertEquals(
- "Returned PlaybackState isn't equal to given PlaybackState",
- data.state.toString(),
- s.build().toString());
- Assert.assertEquals(
- "Returned Queue isn't equal to given Queue",
- data.queue,
- Util.toMetadataList(mMockContext, q));
+ assertThat(data.metadata).isEqualTo(Util.toMetadata(mMockContext, m.build()));
+ assertThat(data.state.toString()).isEqualTo(s.build().toString());
+ assertThat(data.queue).isEqualTo(Util.toMetadataList(mMockContext, q));
}
// Verify that there are no timeout messages pending and there were no timeouts
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index 03a05875b2..24006678fc 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -50,9 +50,7 @@ import com.android.bluetooth.a2dpsink.A2dpSinkService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.flags.Flags;
-import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -173,7 +171,7 @@ public class AvrcpControllerStateMachineTest {
TestUtils.waitForLooperToBeIdle(sm.getHandler().getLooper());
// is disconnected
- Assert.assertEquals(sm.getState(), BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(sm.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
// told mAvrcpControllerService to remove it
verify(mAvrcpControllerService).removeStateMachine(eq(sm));
@@ -203,19 +201,17 @@ public class AvrcpControllerStateMachineTest {
*/
private int setUpConnectedState(boolean control, boolean browsing) {
- Assert.assertThat(
- mAvrcpStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
+ assertThat(mAvrcpStateMachine.getCurrentState())
+ .isInstanceOf(AvrcpControllerStateMachine.Disconnected.class);
mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(control, browsing));
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
.sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
- Assert.assertThat(
- mAvrcpStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Connected.class));
- Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_CONNECTED);
+ assertThat(mAvrcpStateMachine.getCurrentState())
+ .isInstanceOf(AvrcpControllerStateMachine.Connected.class);
+ assertThat(mAvrcpStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
return BluetoothProfile.STATE_CONNECTED;
}
@@ -273,7 +269,7 @@ public class AvrcpControllerStateMachineTest {
mAvrcpStateMachine.sendMessage(
AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, track);
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
- Assert.assertEquals(mAvrcpStateMachine.getCurrentTrack(), track);
+ assertThat(mAvrcpStateMachine.getCurrentTrack()).isEqualTo(track);
}
/** Set the current play status (Play, Pause, etc.) of the device */
@@ -328,35 +324,7 @@ public class AvrcpControllerStateMachineTest {
// Make sure its set by re grabbing the node and checking its contents are cached
nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
assertThat(nowPlaying.isCached()).isTrue();
- assertNowPlayingList(nowPlayingList);
- }
-
- private String avrcpItemListToString(List<AvrcpItem> items) {
- StringBuilder s = new StringBuilder();
- s.append("[");
- if (items != null) {
- for (int i = 0; i < items.size(); i++) {
- AvrcpItem item = items.get(i);
- s.append((item != null ? Long.toString(item.getUid()) : "null"));
- if (i != items.size() - 1) s.append(", ");
- }
- }
- s.append("]");
- return s.toString();
- }
-
- /** Assert that the Now Playing list is a particular value */
- private void assertNowPlayingList(List<AvrcpItem> expected) {
- List<AvrcpItem> current = getNowPlayingList();
- String err =
- "Now playing list incorrect, expected="
- + avrcpItemListToString(expected)
- + ", actual="
- + avrcpItemListToString(current);
- Assert.assertEquals(err, expected.size(), current.size());
- for (int i = 0; i < expected.size(); i++) {
- Assert.assertEquals(err, expected.get(i), current.get(i));
- }
+ assertThat(getNowPlayingList()).containsExactlyElementsIn(nowPlayingList).inOrder();
}
/**
@@ -387,19 +355,19 @@ public class AvrcpControllerStateMachineTest {
numBroadcastsSent += 2;
verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
- Assert.assertEquals(
- mTestDevice,
- mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(
- BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
- mIntentArgument.getValue().getAction());
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertThat(
- mAvrcpStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
- Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(
+ mIntentArgument
+ .getValue()
+ .getParcelableExtra(
+ BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(mTestDevice);
+ assertThat(mIntentArgument.getValue().getAction())
+ .isEqualTo(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
+ assertThat(mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mAvrcpStateMachine.getCurrentState())
+ .isInstanceOf(AvrcpControllerStateMachine.Disconnected.class);
+ assertThat(mAvrcpStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
}
@@ -410,26 +378,25 @@ public class AvrcpControllerStateMachineTest {
MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
assertThat(transportControls).isNotNull();
- Assert.assertEquals(
- PlaybackStateCompat.STATE_NONE,
- BluetoothMediaBrowserService.getPlaybackState().getState());
+ assertThat(BluetoothMediaBrowserService.getPlaybackState().getState())
+ .isEqualTo(PlaybackStateCompat.STATE_NONE);
mAvrcpStateMachine.disconnect();
numBroadcastsSent += 2;
verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
- Assert.assertEquals(
- mTestDevice,
- mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(
- BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
- mIntentArgument.getValue().getAction());
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertThat(
- mAvrcpStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
- Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(
+ mIntentArgument
+ .getValue()
+ .getParcelableExtra(
+ BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(mTestDevice);
+ assertThat(mIntentArgument.getValue().getAction())
+ .isEqualTo(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
+ assertThat(mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mAvrcpStateMachine.getCurrentState())
+ .isInstanceOf(AvrcpControllerStateMachine.Disconnected.class);
+ assertThat(mAvrcpStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
}
@@ -437,29 +404,28 @@ public class AvrcpControllerStateMachineTest {
@Test
@FlakyTest
public void testBrowsingOnly() {
- Assert.assertEquals(0, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
+ assertThat(mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount()).isEqualTo(0);
int numBroadcastsSent = setUpConnectedState(false, true);
- Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
- Assert.assertEquals(
- PlaybackStateCompat.STATE_NONE,
- BluetoothMediaBrowserService.getPlaybackState().getState());
+ assertThat(mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount()).isEqualTo(1);
+ assertThat(BluetoothMediaBrowserService.getPlaybackState().getState())
+ .isEqualTo(PlaybackStateCompat.STATE_NONE);
mAvrcpStateMachine.disconnect();
numBroadcastsSent += 2;
verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
- Assert.assertEquals(
- mTestDevice,
- mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(
- BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
- mIntentArgument.getValue().getAction());
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertThat(
- mAvrcpStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
- Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(
+ mIntentArgument
+ .getValue()
+ .getParcelableExtra(
+ BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(mTestDevice);
+ assertThat(mIntentArgument.getValue().getAction())
+ .isEqualTo(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
+ assertThat(mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mAvrcpStateMachine.getCurrentState())
+ .isInstanceOf(AvrcpControllerStateMachine.Disconnected.class);
+ assertThat(mAvrcpStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
}
@@ -478,7 +444,7 @@ public class AvrcpControllerStateMachineTest {
/** Test to make sure the state machine is tracking the correct device */
@Test
public void testGetDevice() {
- Assert.assertEquals(mAvrcpStateMachine.getDevice(), mTestDevice);
+ assertThat(mAvrcpStateMachine.getDevice()).isEqualTo(mTestDevice);
}
/** Test that dumpsys will generate information about connected devices */
@@ -724,10 +690,9 @@ public class AvrcpControllerStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
// Verify that the player object is available.
- Assert.assertEquals(true, results.isCached());
- Assert.assertEquals(
- "MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}",
- results.getChildren().get(0).getMediaItem().toString());
+ assertThat(results.isCached()).isTrue();
+ assertThat(results.getChildren().get(0).getMediaItem().toString())
+ .isEqualTo("MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}");
// Fetch contents of that player object
BrowseTree.BrowseNode playerOneNode =
@@ -806,13 +771,13 @@ public class AvrcpControllerStateMachineTest {
assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isTrue();
SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue();
- Assert.assertEquals(testPlayers.size(), players.size());
+ assertThat(players.size()).isEqualTo(testPlayers.size());
for (AvrcpPlayer player : testPlayers) {
assertThat(players.contains(player.getId())).isTrue();
}
// Verify we request metadata, playback state and now playing list
- assertNowPlayingList(new ArrayList<AvrcpItem>());
+ assertThat(getNowPlayingList()).isEmpty();
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
.getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -858,7 +823,7 @@ public class AvrcpControllerStateMachineTest {
assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isTrue();
SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue();
- Assert.assertEquals(testPlayers.size() + 1, players.size());
+ assertThat(players.size()).isEqualTo(testPlayers.size() + 1);
for (AvrcpPlayer player : testPlayers) {
assertThat(players.contains(player.getId())).isTrue();
}
@@ -913,12 +878,12 @@ public class AvrcpControllerStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
// The addressed player should always be in the available player set
- Assert.assertEquals(2, mAvrcpStateMachine.getAddressedPlayerId());
+ assertThat(mAvrcpStateMachine.getAddressedPlayerId()).isEqualTo(2);
SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue();
// Make sure the Now Playing list is now cleared
- assertNowPlayingList(new ArrayList<AvrcpItem>());
+ assertThat(getNowPlayingList()).isEmpty();
// Verify that a player change to a player with Now Playing support causes a refresh.
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -967,7 +932,7 @@ public class AvrcpControllerStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
// Make sure the Now Playing list is now cleared and we requested metadata
- assertNowPlayingList(new ArrayList<AvrcpItem>());
+ assertThat(getNowPlayingList()).isEmpty();
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
.getCurrentMetadata(eq(mTestAddress));
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -1256,24 +1221,24 @@ public class AvrcpControllerStateMachineTest {
MediaMetadataCompat metadata = controller.getMetadata();
assertThat(metadata).isNotNull();
- Assert.assertEquals("title", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
- Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
- Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
- Assert.assertEquals(1, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
- Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
- Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
- Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)).isEqualTo("title");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)).isEqualTo("artist");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)).isEqualTo("album");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)).isEqualTo(1);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)).isEqualTo(10);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)).isEqualTo("none");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)).isEqualTo(10);
PlaybackStateCompat playbackState = controller.getPlaybackState();
assertThat(playbackState).isNotNull();
- Assert.assertEquals(PlaybackStateCompat.STATE_PAUSED, playbackState.getState());
- Assert.assertEquals(7, playbackState.getPosition());
+ assertThat(playbackState.getState()).isEqualTo(PlaybackStateCompat.STATE_PAUSED);
+ assertThat(playbackState.getPosition()).isEqualTo(7);
List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
assertThat(queue).isNotNull();
- Assert.assertEquals(2, queue.size());
- Assert.assertEquals("title", queue.get(0).getDescription().getTitle().toString());
- Assert.assertEquals("title 2", queue.get(1).getDescription().getTitle().toString());
+ assertThat(queue).hasSize(2);
+ assertThat(queue.get(0).getDescription().getTitle().toString()).isEqualTo("title");
+ assertThat(queue.get(1).getDescription().getTitle().toString()).isEqualTo("title 2");
}
/** Test becoming inactive from the active state */
@@ -1323,18 +1288,18 @@ public class AvrcpControllerStateMachineTest {
MediaMetadataCompat metadata = controller.getMetadata();
assertThat(metadata).isNotNull();
- Assert.assertEquals("Song 1", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
- Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
- Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
- Assert.assertEquals(1, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
- Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
- Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
- Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)).isEqualTo("Song 1");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)).isEqualTo("artist");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)).isEqualTo("album");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)).isEqualTo(1);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)).isEqualTo(2);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)).isEqualTo("none");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)).isEqualTo(10);
PlaybackStateCompat playbackState = controller.getPlaybackState();
assertThat(playbackState).isNotNull();
- Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
- Assert.assertEquals(0, playbackState.getActiveQueueItemId());
+ assertThat(playbackState.getState()).isEqualTo(PlaybackStateCompat.STATE_PLAYING);
+ assertThat(playbackState.getActiveQueueItemId()).isEqualTo(0);
// Track changes, with new metadata and new track number
track = makeTrack("Song 2", "artist", "album", 2, 2, "none", 10, null);
@@ -1344,18 +1309,18 @@ public class AvrcpControllerStateMachineTest {
// Assert new track metadata and active queue item
metadata = controller.getMetadata();
assertThat(metadata).isNotNull();
- Assert.assertEquals("Song 2", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
- Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
- Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
- Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
- Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
- Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
- Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)).isEqualTo("Song 2");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)).isEqualTo("artist");
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)).isEqualTo("album");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)).isEqualTo(2);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS)).isEqualTo(2);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)).isEqualTo("none");
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)).isEqualTo(10);
playbackState = controller.getPlaybackState();
assertThat(playbackState).isNotNull();
- Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
- Assert.assertEquals(1, playbackState.getActiveQueueItemId());
+ assertThat(playbackState.getState()).isEqualTo(PlaybackStateCompat.STATE_PLAYING);
+ assertThat(playbackState.getActiveQueueItemId()).isEqualTo(1);
}
/** Test receiving a track change update when we're not the active device */
@@ -1405,9 +1370,8 @@ public class AvrcpControllerStateMachineTest {
eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
eq(KEY_DOWN));
verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
- Assert.assertEquals(
- PlaybackStateCompat.STATE_ERROR,
- BluetoothMediaBrowserService.getPlaybackState().getState());
+ assertThat(BluetoothMediaBrowserService.getPlaybackState().getState())
+ .isEqualTo(PlaybackStateCompat.STATE_ERROR);
}
/** Test receiving a play position update when we're not the active device */
@@ -1433,7 +1397,7 @@ public class AvrcpControllerStateMachineTest {
PlaybackStateCompat playbackState = controller.getPlaybackState();
assertThat(playbackState).isNotNull();
- Assert.assertEquals(0, playbackState.getPosition());
+ assertThat(playbackState.getPosition()).isEqualTo(0);
}
/** Test receiving a now playing list update when we're not the active device */
@@ -1771,7 +1735,7 @@ public class AvrcpControllerStateMachineTest {
// Make sure its set by re grabbing the node and checking its contents are cached
nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
assertThat(nowPlaying.isCached()).isTrue();
- assertNowPlayingList(updatedNowPlayingList);
+ assertThat(getNowPlayingList()).containsExactlyElementsIn(updatedNowPlayingList).inOrder();
}
/**
@@ -1831,7 +1795,7 @@ public class AvrcpControllerStateMachineTest {
// Make sure its set by re grabbing the node and checking its contents are cached
nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
assertThat(nowPlaying.isCached()).isTrue();
- assertNowPlayingList(updatedNowPlayingList);
+ assertThat(getNowPlayingList()).containsExactlyElementsIn(updatedNowPlayingList).inOrder();
}
/**
@@ -1899,7 +1863,7 @@ public class AvrcpControllerStateMachineTest {
// Make sure its set by re grabbing the node and checking its contents are cached
nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
assertThat(nowPlaying.isCached()).isTrue();
- assertNowPlayingList(updatedNowPlayingList);
+ assertThat(getNowPlayingList()).containsExactlyElementsIn(updatedNowPlayingList).inOrder();
}
/**
@@ -1964,7 +1928,7 @@ public class AvrcpControllerStateMachineTest {
// Make sure its set by re grabbing the node and checking its contents are cached
nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
assertThat(nowPlaying.isCached()).isTrue();
- assertNowPlayingList(updatedNowPlayingList);
+ assertThat(getNowPlayingList()).containsExactlyElementsIn(updatedNowPlayingList).inOrder();
}
/**
@@ -1997,7 +1961,8 @@ public class AvrcpControllerStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
// Node should be set to cached and notified on
- assertNowPlayingList(new ArrayList<AvrcpItem>());
+ assertThat(getNowPlayingList()).isEmpty();
+
assertThat(nowPlaying.isCached()).isTrue();
// See that state from BluetoothMediaBrowserService is updated to null (i.e. empty)
@@ -2089,7 +2054,7 @@ public class AvrcpControllerStateMachineTest {
playerNodes = mAvrcpStateMachine.findNode(results.getID());
assertThat(playerNodes.isCached()).isTrue();
assertThat(playerNodes.getChildren()).isNotNull();
- assertThat(playerNodes.getChildren().size()).isEqualTo(2);
+ assertThat(playerNodes.getChildren()).hasSize(2);
assertThat(playerNodes.getChildren().get(0).getMediaItem().toString())
.isEqualTo("MediaItem{mFlags=1, mDescription=player 1, null, null}");
assertThat(playerNodes.getChildren().get(1).getMediaItem().toString())
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java
index 6cf6bb466b..37db2a87bb 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java
@@ -32,7 +32,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,7 +94,7 @@ public final class AvrcpCoverArtStorageTest {
Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1);
- Assert.assertEquals(expectedUri, uri);
+ assertThat(uri).isEqualTo(expectedUri);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
}
@@ -106,12 +105,12 @@ public final class AvrcpCoverArtStorageTest {
Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
- Assert.assertEquals(expectedUri, uri);
+ assertThat(uri).isEqualTo(expectedUri);
assertImageSame(mImage1, mDevice1, mHandle1);
uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage2);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
- Assert.assertEquals(expectedUri, uri);
+ assertThat(uri).isEqualTo(expectedUri);
assertImageSame(mImage2, mDevice1, mHandle1);
}
@@ -126,10 +125,10 @@ public final class AvrcpCoverArtStorageTest {
Uri uri2 = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle2, mImage2);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
- Assert.assertEquals(expectedUri1, uri1);
+ assertThat(uri1).isEqualTo(expectedUri1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isTrue();
- Assert.assertEquals(expectedUri2, uri2);
+ assertThat(uri2).isEqualTo(expectedUri2);
}
@Test
@@ -143,38 +142,38 @@ public final class AvrcpCoverArtStorageTest {
Uri uri2 = mAvrcpCoverArtStorage.addImage(mDevice2, mHandle1, mImage1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
- Assert.assertEquals(expectedUri1, uri1);
+ assertThat(uri1).isEqualTo(expectedUri1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue();
- Assert.assertEquals(expectedUri2, uri2);
+ assertThat(uri2).isEqualTo(expectedUri2);
}
@Test
public void addNullImage_imageNotAdded() {
Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, null);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
- Assert.assertEquals(null, uri);
+ assertThat(uri).isNull();
}
@Test
public void addImageNullDevice_imageNotAdded() {
Uri uri = mAvrcpCoverArtStorage.addImage(null, mHandle1, mImage1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
- Assert.assertEquals(null, uri);
+ assertThat(uri).isNull();
}
@Test
public void addImageNullHandle_imageNotAdded() {
Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, null, mImage1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
- Assert.assertEquals(null, uri);
+ assertThat(uri).isNull();
}
@Test
public void addImageEmptyHandle_imageNotAdded() {
Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, "", mImage1);
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
- Assert.assertEquals(null, uri);
+ assertThat(uri).isNull();
}
@Test
@@ -198,28 +197,28 @@ public final class AvrcpCoverArtStorageTest {
public void getImageThatDoesntExist_returnsNull() {
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, mHandle1);
- Assert.assertEquals(null, image);
+ assertThat(image).isNull();
}
@Test
public void getImageNullDevice_returnsNull() {
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
Bitmap image = mAvrcpCoverArtStorage.getImage(null, mHandle1);
- Assert.assertEquals(null, image);
+ assertThat(image).isNull();
}
@Test
public void getImageNullHandle_returnsNull() {
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, null);
- Assert.assertEquals(null, image);
+ assertThat(image).isNull();
}
@Test
public void getImageEmptyHandle_returnsNull() {
assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse();
Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, "");
- Assert.assertEquals(null, image);
+ assertThat(image).isNull();
}
@Test
@@ -344,6 +343,6 @@ public final class AvrcpCoverArtStorageTest {
mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1);
- Assert.assertEquals(expectedString, mAvrcpCoverArtStorage.toString());
+ assertThat(mAvrcpCoverArtStorage.toString()).isEqualTo(expectedString);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java
index 7a296cd075..9e77c640d2 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java
@@ -29,7 +29,6 @@ import android.support.v4.media.MediaMetadataCompat;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -92,19 +91,19 @@ public final class AvrcpItemTest {
AvrcpItem item = builder.build();
- Assert.assertEquals(mDevice, item.getDevice());
- Assert.assertEquals(true, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(UUID, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(trackNumber, item.getTrackNumber());
- Assert.assertEquals(totalTracks, item.getTotalNumberOfTracks());
- Assert.assertEquals(artHandle, item.getCoverArtHandle());
- Assert.assertEquals(uri, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isEqualTo(mDevice);
+ assertThat(item.isPlayable()).isTrue();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isEqualTo(UUID);
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(trackNumber);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(totalTracks);
+ assertThat(item.getCoverArtHandle()).isEqualTo(artHandle);
+ assertThat(item.getCoverArtLocation()).isEqualTo(uri);
}
@Test
@@ -139,19 +138,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(artHandle, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isEqualTo(artHandle);
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -201,19 +200,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(artHandle, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isEqualTo(artHandle);
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -248,19 +247,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(null, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isNull();
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -295,19 +294,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(null, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isNull();
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -342,19 +341,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(null, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isNull();
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -389,19 +388,19 @@ public final class AvrcpItemTest {
builder.fromAvrcpAttributeArray(attrIds, attrMap);
AvrcpItem item = builder.build();
- Assert.assertEquals(null, item.getDevice());
- Assert.assertEquals(false, item.isPlayable());
- Assert.assertEquals(false, item.isBrowsable());
- Assert.assertEquals(0, item.getUid());
- Assert.assertEquals(null, item.getUuid());
- Assert.assertEquals(null, item.getDisplayableName());
- Assert.assertEquals(title, item.getTitle());
- Assert.assertEquals(artist, item.getArtistName());
- Assert.assertEquals(album, item.getAlbumName());
- Assert.assertEquals(1, item.getTrackNumber());
- Assert.assertEquals(12, item.getTotalNumberOfTracks());
- Assert.assertEquals(null, item.getCoverArtHandle());
- Assert.assertEquals(null, item.getCoverArtLocation());
+ assertThat(item.getDevice()).isNull();
+ assertThat(item.isPlayable()).isFalse();
+ assertThat(item.isBrowsable()).isFalse();
+ assertThat(item.getUid()).isEqualTo(0);
+ assertThat(item.getUuid()).isNull();
+ assertThat(item.getDisplayableName()).isNull();
+ assertThat(item.getTitle()).isEqualTo(title);
+ assertThat(item.getArtistName()).isEqualTo(artist);
+ assertThat(item.getAlbumName()).isEqualTo(album);
+ assertThat(item.getTrackNumber()).isEqualTo(1);
+ assertThat(item.getTotalNumberOfTracks()).isEqualTo(12);
+ assertThat(item.getCoverArtHandle()).isNull();
+ assertThat(item.getCoverArtLocation()).isNull();
}
@Test
@@ -413,10 +412,10 @@ public final class AvrcpItemTest {
builder.setCoverArtLocation(uri);
AvrcpItem item = builder.build();
- Assert.assertEquals(uri, item.getCoverArtLocation());
+ assertThat(item.getCoverArtLocation()).isEqualTo(uri);
item.setCoverArtLocation(uri2);
- Assert.assertEquals(uri2, item.getCoverArtLocation());
+ assertThat(item.getCoverArtLocation()).isEqualTo(uri2);
}
@Test
@@ -452,30 +451,28 @@ public final class AvrcpItemTest {
AvrcpItem item = builder.build();
MediaMetadataCompat metadata = item.toMediaMetadata();
- Assert.assertEquals(UUID, metadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID));
- Assert.assertEquals(
- title, metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE));
- Assert.assertEquals(title, metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
- Assert.assertEquals(artist, metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
- Assert.assertEquals(album, metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
- Assert.assertEquals(
- trackNumber, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
- Assert.assertEquals(
- totalTracks, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
- Assert.assertEquals(genre, metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
- Assert.assertEquals(
- playingTime, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
- Assert.assertEquals(
- uri,
- Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI)));
- Assert.assertEquals(
- uri, Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ART_URI)));
- Assert.assertEquals(
- uri, Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)));
- Assert.assertEquals(
- null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON));
- Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART));
- Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART));
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)).isEqualTo(UUID);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
+ .isEqualTo(title);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)).isEqualTo(title);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)).isEqualTo(artist);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)).isEqualTo(album);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER))
+ .isEqualTo(trackNumber);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS))
+ .isEqualTo(totalTracks);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)).isEqualTo(genre);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION))
+ .isEqualTo(playingTime);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI)))
+ .isEqualTo(uri);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ART_URI)))
+ .isEqualTo(uri);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)))
+ .isEqualTo(uri);
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON)).isNull();
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART)).isNull();
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)).isNull();
assertThat(metadata.containsKey(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE)).isFalse();
}
@@ -507,30 +504,28 @@ public final class AvrcpItemTest {
AvrcpItem item = builder.build();
MediaMetadataCompat metadata = item.toMediaMetadata();
- Assert.assertEquals(UUID, metadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID));
- Assert.assertEquals(
- title, metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE));
- Assert.assertEquals(title, metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
- Assert.assertEquals(artist, metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
- Assert.assertEquals(null, metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
- Assert.assertEquals(
- totalTracks, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
- Assert.assertEquals(genre, metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
- Assert.assertEquals(
- playingTime, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
- Assert.assertEquals(
- uri,
- Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI)));
- Assert.assertEquals(
- uri, Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ART_URI)));
- Assert.assertEquals(
- uri, Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)));
- Assert.assertEquals(
- null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON));
- Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART));
- Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART));
- Assert.assertEquals(
- type, metadata.getLong(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE));
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID)).isEqualTo(UUID);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
+ .isEqualTo(title);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)).isEqualTo(title);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)).isEqualTo(artist);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)).isNull();
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS))
+ .isEqualTo(totalTracks);
+ assertThat(metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)).isEqualTo(genre);
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION))
+ .isEqualTo(playingTime);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI)))
+ .isEqualTo(uri);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ART_URI)))
+ .isEqualTo(uri);
+ assertThat(Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI)))
+ .isEqualTo(uri);
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON)).isNull();
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART)).isNull();
+ assertThat(metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)).isNull();
+ assertThat(metadata.getLong(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE))
+ .isEqualTo(type);
}
@Test
@@ -550,14 +545,14 @@ public final class AvrcpItemTest {
assertThat(mediaItem.isPlayable()).isTrue();
assertThat(mediaItem.isBrowsable()).isFalse();
- Assert.assertEquals(UUID, mediaItem.getMediaId());
+ assertThat(mediaItem.getMediaId()).isEqualTo(UUID);
- Assert.assertEquals(UUID, desc.getMediaId());
- Assert.assertEquals(null, desc.getMediaUri());
- Assert.assertEquals(title, desc.getTitle().toString());
+ assertThat(desc.getMediaId()).isEqualTo(UUID);
+ assertThat(desc.getMediaUri()).isNull();
+ assertThat(desc.getTitle().toString()).isEqualTo(title);
assertThat(desc.getSubtitle()).isNull();
- Assert.assertEquals(uri, desc.getIconUri());
- Assert.assertEquals(null, desc.getIconBitmap());
+ assertThat(desc.getIconUri()).isEqualTo(uri);
+ assertThat(desc.getIconBitmap()).isNull();
}
@Test
@@ -579,14 +574,14 @@ public final class AvrcpItemTest {
assertThat(mediaItem.isPlayable()).isTrue();
assertThat(mediaItem.isBrowsable()).isFalse();
- Assert.assertEquals(UUID, mediaItem.getMediaId());
+ assertThat(mediaItem.getMediaId()).isEqualTo(UUID);
- Assert.assertEquals(UUID, desc.getMediaId());
- Assert.assertEquals(null, desc.getMediaUri());
- Assert.assertEquals(displayName, desc.getTitle().toString());
+ assertThat(desc.getMediaId()).isEqualTo(UUID);
+ assertThat(desc.getMediaUri()).isNull();
+ assertThat(desc.getTitle().toString()).isEqualTo(displayName);
assertThat(desc.getSubtitle()).isNull();
- Assert.assertEquals(uri, desc.getIconUri());
- Assert.assertEquals(null, desc.getIconBitmap());
+ assertThat(desc.getIconUri()).isEqualTo(uri);
+ assertThat(desc.getIconBitmap()).isNull();
}
@Test
@@ -606,14 +601,14 @@ public final class AvrcpItemTest {
assertThat(mediaItem.isPlayable()).isFalse();
assertThat(mediaItem.isBrowsable()).isTrue();
- Assert.assertEquals(UUID, mediaItem.getMediaId());
+ assertThat(mediaItem.getMediaId()).isEqualTo(UUID);
- Assert.assertEquals(UUID, desc.getMediaId());
- Assert.assertEquals(null, desc.getMediaUri());
- Assert.assertEquals(title, desc.getTitle().toString());
+ assertThat(desc.getMediaId()).isEqualTo(UUID);
+ assertThat(desc.getMediaUri()).isNull();
+ assertThat(desc.getTitle().toString()).isEqualTo(title);
assertThat(desc.getSubtitle()).isNull();
- Assert.assertEquals(uri, desc.getIconUri());
- Assert.assertEquals(null, desc.getIconBitmap());
+ assertThat(desc.getIconUri()).isEqualTo(uri);
+ assertThat(desc.getIconBitmap()).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
index 6241e65d0a..4649eb4cf3 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
@@ -155,7 +155,7 @@ public class BrowseNodeTest {
mRootNode.addChild(browseNode);
- assertThat(mRootNode.getContents().size()).isEqualTo(1);
+ assertThat(mRootNode.getContents()).hasSize(1);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
index 275b39452d..16c43583e6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
@@ -47,7 +47,7 @@ public class BrowseTreeTest {
public void constructor_withoutDevice() {
BrowseTree browseTree = new BrowseTree(null);
- assertThat(browseTree.mRootNode.mItem.getDevice()).isEqualTo(null);
+ assertThat(browseTree.mRootNode.mItem.getDevice()).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java
index c4ae402379..3500d0e0c7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java
@@ -22,7 +22,6 @@ import android.annotation.SuppressLint;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,23 +62,23 @@ public class BipAttachmentFormatTest {
int expectedSize = (size != null ? Integer.parseInt(size) : -1);
BipAttachmentFormat attachment =
new BipAttachmentFormat(contentType, charset, name, size, created, modified);
- Assert.assertEquals(contentType, attachment.getContentType());
- Assert.assertEquals(charset, attachment.getCharset());
- Assert.assertEquals(name, attachment.getName());
- Assert.assertEquals(expectedSize, attachment.getSize());
+ assertThat(attachment.getContentType()).isEqualTo(contentType);
+ assertThat(attachment.getCharset()).isEqualTo(charset);
+ assertThat(attachment.getName()).isEqualTo(name);
+ assertThat(attachment.getSize()).isEqualTo(expectedSize);
if (expectedCreated != null) {
- Assert.assertEquals(expectedCreated, attachment.getCreatedDate().getTime());
- Assert.assertEquals(isCreatedUtc, attachment.getCreatedDate().isUtc());
+ assertThat(attachment.getCreatedDate().getTime()).isEqualTo(expectedCreated);
+ assertThat(attachment.getCreatedDate().isUtc()).isEqualTo(isCreatedUtc);
} else {
- Assert.assertEquals(null, attachment.getCreatedDate());
+ assertThat(attachment.getCreatedDate()).isNull();
}
if (expectedModified != null) {
- Assert.assertEquals(expectedModified, attachment.getModifiedDate().getTime());
- Assert.assertEquals(isModifiedUtc, attachment.getModifiedDate().isUtc());
+ assertThat(attachment.getModifiedDate().getTime()).isEqualTo(expectedModified);
+ assertThat(attachment.getModifiedDate().isUtc()).isEqualTo(isModifiedUtc);
} else {
- Assert.assertEquals(null, attachment.getModifiedDate());
+ assertThat(attachment.getModifiedDate()).isNull();
}
}
@@ -93,23 +92,23 @@ public class BipAttachmentFormatTest {
Date modified) {
BipAttachmentFormat attachment =
new BipAttachmentFormat(contentType, charset, name, size, created, modified);
- Assert.assertEquals(contentType, attachment.getContentType());
- Assert.assertEquals(charset, attachment.getCharset());
- Assert.assertEquals(name, attachment.getName());
- Assert.assertEquals(size, attachment.getSize());
+ assertThat(attachment.getContentType()).isEqualTo(contentType);
+ assertThat(attachment.getCharset()).isEqualTo(charset);
+ assertThat(attachment.getName()).isEqualTo(name);
+ assertThat(attachment.getSize()).isEqualTo(size);
if (created != null) {
- Assert.assertEquals(created, attachment.getCreatedDate().getTime());
+ assertThat(attachment.getCreatedDate().getTime()).isEqualTo(created);
assertThat(attachment.getCreatedDate().isUtc()).isTrue();
} else {
- Assert.assertEquals(null, attachment.getCreatedDate());
+ assertThat(attachment.getCreatedDate()).isNull();
}
if (modified != null) {
- Assert.assertEquals(modified, attachment.getModifiedDate().getTime());
+ assertThat(attachment.getModifiedDate().getTime()).isEqualTo(modified);
assertThat(attachment.getModifiedDate().isUtc()).isTrue();
} else {
- Assert.assertEquals(null, attachment.getModifiedDate());
+ assertThat(attachment.getModifiedDate()).isNull();
}
}
@@ -326,7 +325,7 @@ public class BipAttachmentFormatTest {
"2048",
"19900101T123456",
"19900101T123456");
- Assert.assertEquals(expected, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expected);
// Create by parsing, all fields with utc dates
attachment =
@@ -337,31 +336,31 @@ public class BipAttachmentFormatTest {
"2048",
"19900101T123456Z",
"19900101T123456Z");
- Assert.assertEquals(expectedUtc, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedUtc);
// Create by parsing, no timestamps
attachment =
new BipAttachmentFormat(
"text/plain", "ISO-8859-1", "thisisatextfile.txt", "2048", null, null);
- Assert.assertEquals(expectedNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoDates);
// Create by parsing, no size, no dates
attachment =
new BipAttachmentFormat(
"text/plain", "ISO-8859-1", "thisisatextfile.txt", null, null, null);
- Assert.assertEquals(expectedNoSizeNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoSizeNoDates);
// Create by parsing, no charset, no dates
attachment =
new BipAttachmentFormat(
"text/plain", null, "thisisatextfile.txt", "2048", null, null);
- Assert.assertEquals(expectedNoCharsetNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoCharsetNoDates);
// Create by parsing, content type only
attachment =
new BipAttachmentFormat(
"text/plain", null, "thisisatextfile.txt", null, null, null);
- Assert.assertEquals(expectedRequiredOnly, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedRequiredOnly);
}
@Test
@@ -395,30 +394,30 @@ public class BipAttachmentFormatTest {
BipAttachmentFormat attachment =
new BipAttachmentFormat(
"text/plain", "ISO-8859-1", "thisisatextfile.txt", 2048, date, date);
- Assert.assertEquals(expected, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expected);
// Create with objects, no dates
attachment =
new BipAttachmentFormat(
"text/plain", "ISO-8859-1", "thisisatextfile.txt", 2048, null, null);
- Assert.assertEquals(expectedNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoDates);
// Create with objects, no size and no dates
attachment =
new BipAttachmentFormat(
"text/plain", "ISO-8859-1", "thisisatextfile.txt", -1, null, null);
- Assert.assertEquals(expectedNoSizeNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoSizeNoDates);
// Create with objects, no charset, no dates
attachment =
new BipAttachmentFormat(
"text/plain", null, "thisisatextfile.txt", 2048, null, null);
- Assert.assertEquals(expectedNoCharsetNoDates, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedNoCharsetNoDates);
// Create with objects, content type only
attachment =
new BipAttachmentFormat("text/plain", null, "thisisatextfile.txt", -1, null, null);
- Assert.assertEquals(expectedRequiredOnly, attachment.toString());
+ assertThat(attachment.toString()).isEqualTo(expectedRequiredOnly);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java
index 33937d8741..9b3032c657 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java
@@ -22,7 +22,6 @@ import android.annotation.SuppressLint;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,17 +66,17 @@ public class BipDatetimeTest {
@SuppressLint("UndefinedEquals")
private void testParse(String date, Date expectedDate, boolean isUtc, String expectedStr) {
BipDateTime bipDateTime = new BipDateTime(date);
- Assert.assertEquals(expectedDate, bipDateTime.getTime());
- Assert.assertEquals(isUtc, bipDateTime.isUtc());
- Assert.assertEquals(expectedStr, bipDateTime.toString());
+ assertThat(bipDateTime.getTime()).isEqualTo(expectedDate);
+ assertThat(bipDateTime.isUtc()).isEqualTo(isUtc);
+ assertThat(bipDateTime.toString()).isEqualTo(expectedStr);
}
@SuppressLint("UndefinedEquals")
private void testCreate(Date date, String dateStr) {
BipDateTime bipDate = new BipDateTime(date);
- Assert.assertEquals(date, bipDate.getTime());
+ assertThat(bipDate.getTime()).isEqualTo(date);
assertThat(bipDate.isUtc()).isTrue();
- Assert.assertEquals(dateStr, bipDate.toString());
+ assertThat(bipDate.toString()).isEqualTo(dateStr);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java
index 2ffb5a9870..99f4a5281a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,10 +34,10 @@ public class BipEncodingTest {
String propId,
boolean isAndroidSupported) {
BipEncoding encoding = new BipEncoding(input);
- Assert.assertEquals(encodingType, encoding.getType());
- Assert.assertEquals(encodingStr, encoding.toString());
- Assert.assertEquals(propId, encoding.getProprietaryEncodingId());
- Assert.assertEquals(isAndroidSupported, encoding.isAndroidSupported());
+ assertThat(encoding.getType()).isEqualTo(encodingType);
+ assertThat(encoding.toString()).isEqualTo(encodingStr);
+ assertThat(encoding.getProprietaryEncodingId()).isEqualTo(propId);
+ assertThat(encoding.isAndroidSupported()).isEqualTo(isAndroidSupported);
}
private void testParseMany(
@@ -111,26 +110,26 @@ public class BipEncodingTest {
};
for (int encodingType : inputs) {
BipEncoding encoding = new BipEncoding(encodingType, null);
- Assert.assertEquals(encodingType, encoding.getType());
- Assert.assertEquals(null, encoding.getProprietaryEncodingId());
+ assertThat(encoding.getType()).isEqualTo(encodingType);
+ assertThat(encoding.getProprietaryEncodingId()).isNull();
}
}
@Test
public void testCreateProprietaryEncoding() {
BipEncoding encoding = new BipEncoding(BipEncoding.USR_XXX, "test-encoding");
- Assert.assertEquals(BipEncoding.USR_XXX, encoding.getType());
- Assert.assertEquals("TEST-ENCODING", encoding.getProprietaryEncodingId());
- Assert.assertEquals("USR-TEST-ENCODING", encoding.toString());
+ assertThat(encoding.getType()).isEqualTo(BipEncoding.USR_XXX);
+ assertThat(encoding.getProprietaryEncodingId()).isEqualTo("TEST-ENCODING");
+ assertThat(encoding.toString()).isEqualTo("USR-TEST-ENCODING");
assertThat(encoding.isAndroidSupported()).isFalse();
}
@Test
public void testCreateProprietaryEncoding_emptyId() {
BipEncoding encoding = new BipEncoding(BipEncoding.USR_XXX, "");
- Assert.assertEquals(BipEncoding.USR_XXX, encoding.getType());
- Assert.assertEquals("", encoding.getProprietaryEncodingId());
- Assert.assertEquals("USR-", encoding.toString());
+ assertThat(encoding.getType()).isEqualTo(BipEncoding.USR_XXX);
+ assertThat(encoding.getProprietaryEncodingId()).isEqualTo("");
+ assertThat(encoding.toString()).isEqualTo("USR-");
assertThat(encoding.isAndroidSupported()).isFalse();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java
index 971ec50064..58f89753cd 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java
@@ -22,7 +22,6 @@ import android.annotation.SuppressLint;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +46,7 @@ public class BipImageDescriptorTest {
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -65,7 +64,7 @@ public class BipImageDescriptorTest {
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -78,12 +77,12 @@ public class BipImageDescriptorTest {
+ "</image-descriptor>";
BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
- builder.setPropietaryEncoding("NOKIA-1");
+ builder.setProprietaryEncoding("NOKIA-1");
builder.setFixedDimensions(1280, 960);
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -96,12 +95,12 @@ public class BipImageDescriptorTest {
+ "</image-descriptor>";
BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
- builder.setPropietaryEncoding("NOKIA-1");
+ builder.setProprietaryEncoding("NOKIA-1");
builder.setFixedDimensions(1280, 960);
builder.setTransformation(BipTransformation.STRETCH);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -114,12 +113,12 @@ public class BipImageDescriptorTest {
+ "</image-descriptor>";
BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
- builder.setPropietaryEncoding("NOKIA-1");
+ builder.setProprietaryEncoding("NOKIA-1");
builder.setFixedDimensions(1280, 960);
builder.setTransformation(BipTransformation.CROP);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -132,12 +131,12 @@ public class BipImageDescriptorTest {
+ "</image-descriptor>";
BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
- builder.setPropietaryEncoding("NOKIA-1");
+ builder.setProprietaryEncoding("NOKIA-1");
builder.setFixedDimensions(1280, 960);
builder.setTransformation(BipTransformation.FILL);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -150,13 +149,13 @@ public class BipImageDescriptorTest {
+ "</image-descriptor>";
BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder();
- builder.setPropietaryEncoding("NOKIA-1");
+ builder.setProprietaryEncoding("NOKIA-1");
builder.setFixedDimensions(1280, 960);
builder.setTransformation(BipTransformation.CROP);
builder.setTransformation(BipTransformation.FILL);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -172,7 +171,7 @@ public class BipImageDescriptorTest {
builder.setFixedDimensions(1280, 960);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -189,7 +188,7 @@ public class BipImageDescriptorTest {
builder.setMaxFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -208,7 +207,7 @@ public class BipImageDescriptorTest {
builder.setTransformation(BipTransformation.FILL);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(expected, descriptor.toString());
+ assertThat(descriptor.toString()).isEqualTo(expected);
}
@Test
@@ -218,7 +217,7 @@ public class BipImageDescriptorTest {
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(null, descriptor.toString());
+ assertThat(descriptor.toString()).isNull();
}
@Test
@@ -228,7 +227,7 @@ public class BipImageDescriptorTest {
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(null, descriptor.toString());
+ assertThat(descriptor.toString()).isNull();
}
@Test
@@ -237,7 +236,7 @@ public class BipImageDescriptorTest {
builder.setFileSize(500000);
BipImageDescriptor descriptor = builder.build();
- Assert.assertEquals(null, descriptor.toString());
+ assertThat(descriptor.toString()).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java
index 9c8aec1b85..2f1e60cf34 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java
@@ -22,7 +22,6 @@ import android.annotation.SuppressLint;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,52 +32,52 @@ public class BipImageFormatTest {
public void testParseNative_requiredOnly() {
String expected = "<native encoding=\"JPEG\" pixel=\"1280*1024\" />";
BipImageFormat format = BipImageFormat.parseNative("JPEG", "1280*1024", null);
- Assert.assertEquals(BipImageFormat.FORMAT_NATIVE, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_NATIVE);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(new BipTransformation());
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
public void testParseNative_withSize() {
String expected = "<native encoding=\"JPEG\" pixel=\"1280*1024\" size=\"1048576\" />";
BipImageFormat format = BipImageFormat.parseNative("JPEG", "1280*1024", "1048576");
- Assert.assertEquals(BipImageFormat.FORMAT_NATIVE, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(1048576, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_NATIVE);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(new BipTransformation());
+ assertThat(format.getSize()).isEqualTo(1048576);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
public void testParseVariant_requiredOnly() {
String expected = "<variant encoding=\"JPEG\" pixel=\"1280*1024\" />";
BipImageFormat format = BipImageFormat.parseVariant("JPEG", "1280*1024", null, null);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(new BipTransformation());
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
public void testParseVariant_withMaxSize() {
String expected = "<variant encoding=\"JPEG\" pixel=\"1280*1024\" maxsize=\"1048576\" />";
BipImageFormat format = BipImageFormat.parseVariant("JPEG", "1280*1024", "1048576", null);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(1048576, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(new BipTransformation());
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(1048576);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -93,16 +92,16 @@ public class BipImageFormatTest {
BipImageFormat format =
BipImageFormat.parseVariant("JPEG", "1280*1024", null, "stretch fill crop");
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.toString()).isEqualTo(expected);
format = BipImageFormat.parseVariant("JPEG", "1280*1024", null, "stretch crop fill");
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.toString()).isEqualTo(expected);
format = BipImageFormat.parseVariant("JPEG", "1280*1024", null, "crop stretch fill");
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -117,31 +116,31 @@ public class BipImageFormatTest {
BipImageFormat format =
BipImageFormat.parseVariant("JPEG", "1280*1024", "1048576", "stretch fill crop");
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(1048576, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(1048576);
+ assertThat(format.toString()).isEqualTo(expected);
format = BipImageFormat.parseVariant("JPEG", "1280*1024", "1048576", "stretch crop fill");
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(1048576, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(1048576);
+ assertThat(format.toString()).isEqualTo(expected);
format = BipImageFormat.parseVariant("JPEG", "1280*1024", "1048576", "crop stretch fill");
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(1048576, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(1048576);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -152,13 +151,13 @@ public class BipImageFormatTest {
new BipEncoding(BipEncoding.JPEG, null),
BipPixel.createFixed(1280, 1024),
-1);
- Assert.assertEquals(BipImageFormat.FORMAT_NATIVE, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_NATIVE);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isNull();
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -169,13 +168,13 @@ public class BipImageFormatTest {
new BipEncoding(BipEncoding.JPEG, null),
BipPixel.createFixed(1280, 1024),
1048576);
- Assert.assertEquals(BipImageFormat.FORMAT_NATIVE, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(1280, 1024), format.getPixel());
- Assert.assertEquals(new BipTransformation(), format.getTransformation());
- Assert.assertEquals(1048576, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_NATIVE);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(1280, 1024));
+ assertThat(format.getTransformation()).isNull();
+ assertThat(format.getSize()).isEqualTo(1048576);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -187,13 +186,13 @@ public class BipImageFormatTest {
BipPixel.createFixed(32, 32),
-1,
null);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(32, 32), format.getPixel());
- Assert.assertEquals(null, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(32, 32));
+ assertThat(format.getTransformation()).isNull();
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -214,13 +213,13 @@ public class BipImageFormatTest {
BipPixel.createFixed(32, 32),
-1,
trans);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(32, 32), format.getPixel());
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(-1, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(32, 32));
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(-1);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -232,13 +231,13 @@ public class BipImageFormatTest {
BipPixel.createFixed(32, 32),
123,
null);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(32, 32), format.getPixel());
- Assert.assertEquals(null, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(123, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(32, 32));
+ assertThat(format.getTransformation()).isNull();
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(123);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test
@@ -259,13 +258,13 @@ public class BipImageFormatTest {
BipPixel.createFixed(32, 32),
123,
trans);
- Assert.assertEquals(BipImageFormat.FORMAT_VARIANT, format.getType());
- Assert.assertEquals(new BipEncoding(BipEncoding.JPEG, null), format.getEncoding());
- Assert.assertEquals(BipPixel.createFixed(32, 32), format.getPixel());
- Assert.assertEquals(trans, format.getTransformation());
- Assert.assertEquals(-1, format.getSize());
- Assert.assertEquals(123, format.getMaxSize());
- Assert.assertEquals(expected, format.toString());
+ assertThat(format.getType()).isEqualTo(BipImageFormat.FORMAT_VARIANT);
+ assertThat(format.getEncoding()).isEqualTo(new BipEncoding(BipEncoding.JPEG, null));
+ assertThat(format.getPixel()).isEqualTo(BipPixel.createFixed(32, 32));
+ assertThat(format.getTransformation()).isEqualTo(trans);
+ assertThat(format.getSize()).isEqualTo(-1);
+ assertThat(format.getMaxSize()).isEqualTo(123);
+ assertThat(format.toString()).isEqualTo(expected);
}
@Test(expected = ParseException.class)
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java
index c238f914d9..6e767249e9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -131,11 +130,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(null, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isNull();
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -166,11 +165,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -192,12 +191,12 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(null, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isNull();
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isFalse();
- Assert.assertEquals(xmlString, properties.toString());
- Assert.assertEquals(null, properties.serialize());
+ assertThat(properties.toString()).isEqualTo(xmlString);
+ assertThat(properties.serialize()).isNull();
}
/**
@@ -219,12 +218,12 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(null, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isNull();
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isFalse();
- Assert.assertEquals(xmlString, properties.toString());
- Assert.assertEquals(null, properties.serialize());
+ assertThat(properties.toString()).isEqualTo(xmlString);
+ assertThat(properties.serialize()).isNull();
}
/**
@@ -245,11 +244,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(null, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isNull();
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -268,11 +267,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -291,11 +290,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -314,11 +313,11 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
/**
@@ -342,12 +341,12 @@ public class BipImagePropertiesTest {
+ IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isFalse();
- Assert.assertEquals(xmlString, properties.toString());
- Assert.assertEquals(null, properties.serialize());
+ assertThat(properties.toString()).isEqualTo(xmlString);
+ assertThat(properties.serialize()).isNull();
}
/**
@@ -361,11 +360,11 @@ public class BipImagePropertiesTest {
String xmlString = XML_DOC_DECL + IMAGE_PROPERTIES + IMAGE_PROPERTIES_END;
InputStream stream = toUtf8Stream(xmlString);
BipImageProperties properties = new BipImageProperties(stream);
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isFalse();
- Assert.assertEquals(null, properties.serialize());
+ assertThat(properties.serialize()).isNull();
}
/** Test parsing an image-properties with no open tag */
@@ -447,10 +446,10 @@ public class BipImagePropertiesTest {
new BipAttachmentFormat("audio/basic", null, "ABCD1234.wav", 102400, null, null));
BipImageProperties properties = builder.build();
- Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle());
- Assert.assertEquals(VERSION, properties.getVersion());
- Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName());
+ assertThat(properties.getImageHandle()).isEqualTo(IMAGE_HANDLE);
+ assertThat(properties.getVersion()).isEqualTo(VERSION);
+ assertThat(properties.getFriendlyName()).isEqualTo(FRIENDLY_NAME);
assertThat(properties.isValid()).isTrue();
- Assert.assertEquals(xmlString, properties.toString());
+ assertThat(properties.toString()).isEqualTo(xmlString);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java
index 87ffdc5387..e3ee39dee5 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java
@@ -27,7 +27,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +55,7 @@ public class BipImageTest {
mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.image_200_200);
Bitmap bitmap = BitmapFactory.decodeStream(expectedInputStream);
- Assert.assertEquals(sImageHandle, image.getImageHandle());
+ assertThat(image.getImageHandle()).isEqualTo(sImageHandle);
assertThat(bitmap.sameAs(image.getImage())).isTrue();
}
@@ -70,7 +69,7 @@ public class BipImageTest {
mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.image_600_600);
Bitmap bitmap = BitmapFactory.decodeStream(expectedInputStream);
- Assert.assertEquals(sImageHandle, image.getImageHandle());
+ assertThat(image.getImageHandle()).isEqualTo(sImageHandle);
assertThat(bitmap.sameAs(image.getImage())).isTrue();
}
@@ -80,7 +79,7 @@ public class BipImageTest {
mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.image_200_200);
Bitmap bitmap = BitmapFactory.decodeStream(imageInputStream);
BipImage image = new BipImage(sImageHandle, bitmap);
- Assert.assertEquals(sImageHandle, image.getImageHandle());
+ assertThat(image.getImageHandle()).isEqualTo(sImageHandle);
assertThat(bitmap.sameAs(image.getImage())).isTrue();
}
@@ -90,7 +89,7 @@ public class BipImageTest {
mTestResources.openRawResource(com.android.bluetooth.tests.R.raw.image_600_600);
Bitmap bitmap = BitmapFactory.decodeStream(imageInputStream);
BipImage image = new BipImage(sImageHandle, bitmap);
- Assert.assertEquals(sImageHandle, image.getImageHandle());
+ assertThat(image.getImageHandle()).isEqualTo(sImageHandle);
assertThat(bitmap.sameAs(image.getImage())).isTrue();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipPixelTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipPixelTest.java
index e40ef9cefc..88f093966a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipPixelTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipPixelTest.java
@@ -16,9 +16,10 @@
package com.android.bluetooth.avrcpcontroller;
+import static com.google.common.truth.Truth.assertThat;
+
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,44 +36,44 @@ public class BipPixelTest {
int maxHeight,
String pixelStr) {
BipPixel pixel = new BipPixel(input);
- Assert.assertEquals(pixelType, pixel.getType());
- Assert.assertEquals(minWidth, pixel.getMinWidth());
- Assert.assertEquals(minHeight, pixel.getMinHeight());
- Assert.assertEquals(maxWidth, pixel.getMaxWidth());
- Assert.assertEquals(maxHeight, pixel.getMaxHeight());
- Assert.assertEquals(pixelStr, pixel.toString());
+ assertThat(pixel.getType()).isEqualTo(pixelType);
+ assertThat(pixel.getMinWidth()).isEqualTo(minWidth);
+ assertThat(pixel.getMinHeight()).isEqualTo(minHeight);
+ assertThat(pixel.getMaxWidth()).isEqualTo(maxWidth);
+ assertThat(pixel.getMaxHeight()).isEqualTo(maxHeight);
+ assertThat(pixel.toString()).isEqualTo(pixelStr);
}
private void testFixed(int width, int height, String pixelStr) {
BipPixel pixel = BipPixel.createFixed(width, height);
- Assert.assertEquals(BipPixel.TYPE_FIXED, pixel.getType());
- Assert.assertEquals(width, pixel.getMinWidth());
- Assert.assertEquals(height, pixel.getMinHeight());
- Assert.assertEquals(width, pixel.getMaxWidth());
- Assert.assertEquals(height, pixel.getMaxHeight());
- Assert.assertEquals(pixelStr, pixel.toString());
+ assertThat(pixel.getType()).isEqualTo(BipPixel.TYPE_FIXED);
+ assertThat(pixel.getMinWidth()).isEqualTo(width);
+ assertThat(pixel.getMinHeight()).isEqualTo(height);
+ assertThat(pixel.getMaxWidth()).isEqualTo(width);
+ assertThat(pixel.getMaxHeight()).isEqualTo(height);
+ assertThat(pixel.toString()).isEqualTo(pixelStr);
}
private void testResizableModified(
int minWidth, int minHeight, int maxWidth, int maxHeight, String pixelStr) {
BipPixel pixel = BipPixel.createResizableModified(minWidth, minHeight, maxWidth, maxHeight);
- Assert.assertEquals(BipPixel.TYPE_RESIZE_MODIFIED_ASPECT_RATIO, pixel.getType());
- Assert.assertEquals(minWidth, pixel.getMinWidth());
- Assert.assertEquals(minHeight, pixel.getMinHeight());
- Assert.assertEquals(maxWidth, pixel.getMaxWidth());
- Assert.assertEquals(maxHeight, pixel.getMaxHeight());
- Assert.assertEquals(pixelStr, pixel.toString());
+ assertThat(pixel.getType()).isEqualTo(BipPixel.TYPE_RESIZE_MODIFIED_ASPECT_RATIO);
+ assertThat(pixel.getMinWidth()).isEqualTo(minWidth);
+ assertThat(pixel.getMinHeight()).isEqualTo(minHeight);
+ assertThat(pixel.getMaxWidth()).isEqualTo(maxWidth);
+ assertThat(pixel.getMaxHeight()).isEqualTo(maxHeight);
+ assertThat(pixel.toString()).isEqualTo(pixelStr);
}
private void testResizableFixed(int minWidth, int maxWidth, int maxHeight, String pixelStr) {
int minHeight = (minWidth * maxHeight) / maxWidth; // spec defined
BipPixel pixel = BipPixel.createResizableFixed(minWidth, maxWidth, maxHeight);
- Assert.assertEquals(BipPixel.TYPE_RESIZE_FIXED_ASPECT_RATIO, pixel.getType());
- Assert.assertEquals(minWidth, pixel.getMinWidth());
- Assert.assertEquals(minHeight, pixel.getMinHeight());
- Assert.assertEquals(maxWidth, pixel.getMaxWidth());
- Assert.assertEquals(maxHeight, pixel.getMaxHeight());
- Assert.assertEquals(pixelStr, pixel.toString());
+ assertThat(pixel.getType()).isEqualTo(BipPixel.TYPE_RESIZE_FIXED_ASPECT_RATIO);
+ assertThat(pixel.getMinWidth()).isEqualTo(minWidth);
+ assertThat(pixel.getMinHeight()).isEqualTo(minHeight);
+ assertThat(pixel.getMaxWidth()).isEqualTo(maxWidth);
+ assertThat(pixel.getMaxHeight()).isEqualTo(maxHeight);
+ assertThat(pixel.toString()).isEqualTo(pixelStr);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java
index 3b5893c1f1..031a71c81e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,7 +31,7 @@ public class BipTransformationTest {
public void testCreateEmpty() {
BipTransformation trans = new BipTransformation();
assertThat(trans.supportsAny()).isFalse();
- Assert.assertEquals(null, trans.toString());
+ assertThat(trans.toString()).isNull();
}
@Test
@@ -42,13 +41,13 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
trans.addTransformation(BipTransformation.STRETCH);
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch crop");
}
@Test
@@ -58,13 +57,13 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
trans.addTransformation(BipTransformation.CROP);
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
}
@Test(expected = IllegalArgumentException.class)
@@ -87,14 +86,14 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
trans.removeTransformation(BipTransformation.CROP);
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.supportsAny()).isFalse();
- Assert.assertEquals(null, trans.toString());
+ assertThat(trans.toString()).isNull();
}
@Test
@@ -105,13 +104,13 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch crop");
trans.removeTransformation(BipTransformation.CROP);
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch");
}
@Test(expected = IllegalArgumentException.class)
@@ -123,7 +122,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch crop");
}
@Test
@@ -135,7 +134,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch crop");
}
@Test
@@ -144,7 +143,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("stretch", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch");
}
@Test
@@ -153,7 +152,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
}
@Test
@@ -162,7 +161,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("fill", trans.toString());
+ assertThat(trans.toString()).isEqualTo("fill");
}
@Test
@@ -171,7 +170,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("stretch fill", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill");
}
@Test
@@ -180,7 +179,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
- Assert.assertEquals("stretch crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch crop");
}
@Test
@@ -189,7 +188,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
- Assert.assertEquals("fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("fill crop");
}
@Test
@@ -198,7 +197,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test
@@ -207,7 +206,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
- Assert.assertEquals("fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("fill crop");
}
@Test
@@ -216,7 +215,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test
@@ -225,7 +224,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test
@@ -234,7 +233,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("stretch", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch");
}
@Test
@@ -243,7 +242,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("fill", trans.toString());
+ assertThat(trans.toString()).isEqualTo("fill");
}
@Test
@@ -252,7 +251,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
}
@Test
@@ -261,7 +260,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("crop");
}
@Test
@@ -272,7 +271,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("stretch fill", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill");
}
@Test
@@ -287,7 +286,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test
@@ -302,7 +301,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test(expected = IllegalArgumentException.class)
@@ -321,7 +320,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals(null, trans.toString());
+ assertThat(trans.toString()).isNull();
}
@Test
@@ -330,7 +329,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("stretch fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch fill crop");
}
@Test
@@ -339,7 +338,7 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse();
assertThat(trans.isSupported(BipTransformation.FILL)).isTrue();
assertThat(trans.isSupported(BipTransformation.CROP)).isTrue();
- Assert.assertEquals("fill crop", trans.toString());
+ assertThat(trans.toString()).isEqualTo("fill crop");
}
@Test
@@ -348,6 +347,6 @@ public class BipTransformationTest {
assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue();
assertThat(trans.isSupported(BipTransformation.FILL)).isFalse();
assertThat(trans.isSupported(BipTransformation.CROP)).isFalse();
- Assert.assertEquals("stretch", trans.toString());
+ assertThat(trans.toString()).isEqualTo("stretch");
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BaseDataTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BaseDataTest.java
index 93082e863a..bf0d75a60a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BaseDataTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BaseDataTest.java
@@ -105,7 +105,7 @@ public class BaseDataTest {
assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
assertThat(level.numSubGroups).isEqualTo(1);
- assertThat(data.getLevelTwo().size()).isEqualTo(1);
+ assertThat(data.getLevelTwo()).hasSize(1);
level = data.getLevelTwo().get(0);
assertThat(level.numSubGroups).isEqualTo(1);
@@ -113,7 +113,7 @@ public class BaseDataTest {
assertThat(level.codecConfigLength).isEqualTo(2);
assertThat(level.metaDataLength).isEqualTo(3);
- assertThat(data.getLevelThree().size()).isEqualTo(1);
+ assertThat(data.getLevelThree()).hasSize(1);
level = data.getLevelThree().get(0);
assertThat(level.index).isEqualTo(4);
assertThat(level.codecConfigLength).isEqualTo(3);
@@ -142,8 +142,7 @@ public class BaseDataTest {
(byte) 'A', // codecConfigInfo
};
- BaseData data = BaseData.parseBaseData(serviceData);
- assertThat(data).isEqualTo(null);
+ assertThat(BaseData.parseBaseData(serviceData)).isNull();
}
@Test
@@ -173,8 +172,7 @@ public class BaseDataTest {
(byte) 0x08, // metaData
};
- BaseData data = BaseData.parseBaseData(serviceData);
- assertThat(data).isEqualTo(null);
+ assertThat(BaseData.parseBaseData(serviceData)).isNull();
}
@Test
@@ -210,8 +208,7 @@ public class BaseDataTest {
(byte) 'C' // codecConfigInfo
};
- BaseData data = BaseData.parseBaseData(serviceData);
- assertThat(data).isEqualTo(null);
+ assertThat(BaseData.parseBaseData(serviceData)).isNull();
}
@Test
@@ -252,7 +249,7 @@ public class BaseDataTest {
assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
assertThat(level.numSubGroups).isEqualTo(1);
- assertThat(data.getLevelTwo().size()).isEqualTo(1);
+ assertThat(data.getLevelTwo()).hasSize(1);
level = data.getLevelTwo().get(0);
assertThat(level.numSubGroups).isEqualTo(1);
@@ -260,7 +257,7 @@ public class BaseDataTest {
assertThat(level.codecConfigLength).isEqualTo(2);
assertThat(level.metaDataLength).isEqualTo(3);
- assertThat(data.getLevelThree().size()).isEqualTo(1);
+ assertThat(data.getLevelThree()).hasSize(1);
level = data.getLevelThree().get(0);
assertThat(level.index).isEqualTo(4);
@@ -308,7 +305,7 @@ public class BaseDataTest {
assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
assertThat(level.numSubGroups).isEqualTo(1);
- assertThat(data.getLevelTwo().size()).isEqualTo(1);
+ assertThat(data.getLevelTwo()).hasSize(1);
level = data.getLevelTwo().get(0);
assertThat(level.numSubGroups).isEqualTo(1);
@@ -320,7 +317,7 @@ public class BaseDataTest {
assertThat(level.codecConfigLength).isEqualTo(4);
assertThat(level.metaDataLength).isEqualTo(3);
- assertThat(data.getLevelThree().size()).isEqualTo(1);
+ assertThat(data.getLevelThree()).hasSize(1);
level = data.getLevelThree().get(0);
assertThat(level.index).isEqualTo(4);
assertThat(level.codecConfigLength).isEqualTo(3);
@@ -356,7 +353,7 @@ public class BaseDataTest {
assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
assertThat(level.numSubGroups).isEqualTo(1);
- assertThat(data.getLevelTwo().size()).isEqualTo(1);
+ assertThat(data.getLevelTwo()).hasSize(1);
level = data.getLevelTwo().get(0);
assertThat(level.numSubGroups).isEqualTo(1);
@@ -368,7 +365,7 @@ public class BaseDataTest {
assertThat(level.codecConfigLength).isEqualTo(0);
assertThat(level.metaDataLength).isEqualTo(0);
- assertThat(data.getLevelThree().size()).isEqualTo(1);
+ assertThat(data.getLevelThree()).hasSize(1);
level = data.getLevelThree().get(0);
assertThat(level.index).isEqualTo(4);
assertThat(level.codecConfigLength).isEqualTo(0);
@@ -392,8 +389,7 @@ public class BaseDataTest {
(byte) 0x00, // metaDataLength
};
- BaseData data = BaseData.parseBaseData(serviceData);
- assertThat(data).isEqualTo(null);
+ assertThat(BaseData.parseBaseData(serviceData)).isNull();
}
@Test
@@ -455,7 +451,7 @@ public class BaseDataTest {
assertThat(level.presentationDelay).isEqualTo(new byte[] {0x01, 0x02, 0x03});
assertThat(level.numSubGroups).isEqualTo(1);
- assertThat(data.getLevelTwo().size()).isEqualTo(1);
+ assertThat(data.getLevelTwo()).hasSize(1);
level = data.getLevelTwo().get(0);
assertThat(level.numSubGroups).isEqualTo(1);
@@ -464,7 +460,7 @@ public class BaseDataTest {
assertThat(level.metaDataLength).isEqualTo(metaDataLength);
assertThat(level.metaData).isEqualTo(Bytes.concat(metadataHeader, metadataPayload));
- assertThat(data.getLevelThree().size()).isEqualTo(1);
+ assertThat(data.getLevelThree()).hasSize(1);
level = data.getLevelThree().get(0);
assertThat(level.index).isEqualTo(4);
assertThat(level.codecConfigLength).isEqualTo(3);
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 8f3dd8aa7c..c1c7734f2e 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
@@ -86,7 +86,6 @@ import com.android.bluetooth.le_audio.LeAudioService;
import com.google.common.truth.Expect;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -105,6 +104,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
@@ -143,7 +143,7 @@ public class BassClientServiceTest {
private static final int TEST_NUM_SOURCES = 1;
private final HashMap<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>();
- private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue;
+ private final BlockingQueue<Intent> mIntentQueue = new LinkedBlockingQueue<>();
private Context mTargetContext;
private BassClientService mBassClientService;
@@ -291,6 +291,7 @@ public class BassClientServiceTest {
doReturn((BluetoothDevice) invocation.getArgument(0))
.when(stateMachine)
.getDevice();
+ doReturn(true).when(stateMachine).isBassStateReady();
mStateMachines.put(
(BluetoothDevice) invocation.getArgument(0), stateMachine);
return stateMachine;
@@ -312,10 +313,6 @@ public class BassClientServiceTest {
when(mCallback.asBinder()).thenReturn(mBinder);
mBassClientService.registerCallback(mCallback);
- mIntentQueue = new HashMap<>();
- mIntentQueue.put(mCurrentDevice, new LinkedBlockingQueue<>());
- mIntentQueue.put(mCurrentDevice1, new LinkedBlockingQueue<>());
-
// Set up the Connection State Changed receiver
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -339,6 +336,7 @@ public class BassClientServiceTest {
if (mBassClientService == null) {
return;
}
+ mTargetContext.unregisterReceiver(mBassIntentReceiver);
mBassClientService.unregisterCallback(mCallback);
mBassClientService.stop();
@@ -349,7 +347,6 @@ public class BassClientServiceTest {
mCurrentDevice1 = null;
mSourceDevice = null;
mSourceDevice2 = null;
- mTargetContext.unregisterReceiver(mBassIntentReceiver);
mIntentQueue.clear();
BassObjectsFactory.setInstanceForTesting(null);
TestUtils.clearAdapterService(mAdapterService);
@@ -368,11 +365,7 @@ public class BassClientServiceTest {
}
try {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- assertThat(device).isNotNull();
- LinkedBlockingQueue<Intent> queue = mIntentQueue.get(device);
- assertThat(queue).isNotNull();
- queue.put(intent);
+ mIntentQueue.put(intent);
} catch (InterruptedException e) {
throw new AssertionError("Cannot add Intent to the queue: " + e.getMessage());
}
@@ -396,10 +389,8 @@ public class BassClientServiceTest {
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));
+ assertThat(mBassClientService.getConnectionPolicy(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
}
/**
@@ -459,7 +450,7 @@ public class BassClientServiceTest {
prepareConnectedDeviceGroup();
List<ScanFilter> scanFilters = new ArrayList<>();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
Mockito.clearInvocations(sm);
}
@@ -494,10 +485,6 @@ public class BassClientServiceTest {
mCurrentDevice = TestUtils.getTestDevice(mBluetoothAdapter, 0);
mCurrentDevice1 = TestUtils.getTestDevice(mBluetoothAdapter, 1);
- // Prepare intent queues
- mIntentQueue.put(mCurrentDevice, new LinkedBlockingQueue<>());
- mIntentQueue.put(mCurrentDevice1, new LinkedBlockingQueue<>());
-
// Mock the CSIP group
List<BluetoothDevice> groupDevices = new ArrayList<>();
groupDevices.add(mCurrentDevice);
@@ -513,7 +500,7 @@ public class BassClientServiceTest {
assertThat(mBassClientService.connect(mCurrentDevice)).isTrue();
assertThat(mBassClientService.connect(mCurrentDevice1)).isTrue();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
// Verify the call
verify(sm).sendMessage(eq(BassClientStateMachine.CONNECT));
@@ -567,7 +554,7 @@ public class BassClientServiceTest {
private void startSearchingForSources() {
List<ScanFilter> scanFilters = new ArrayList<>();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
Mockito.clearInvocations(sm);
}
@@ -598,7 +585,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
@@ -617,7 +604,7 @@ public class BassClientServiceTest {
.verify(mMethodProxy)
.periodicAdvertisingManagerUnregisterSync(any(), any());
expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
- expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
}
@@ -635,7 +622,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
@@ -651,7 +638,7 @@ public class BassClientServiceTest {
.verify(mMethodProxy)
.periodicAdvertisingManagerUnregisterSync(any(), any());
expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
- expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
}
@@ -755,7 +742,7 @@ public class BassClientServiceTest {
any(), any(), anyInt(), anyInt(), any(), any());
// Verify not getting ADD_BCAST_SOURCE message before source sync
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
verify(sm, never()).sendMessage(any());
}
@@ -987,7 +974,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
- // Error in syncEstablished causes soureLost, sourceAddFailed notification
+ // Error in syncEstablished causes sourceLost, sourceAddFailed notification
// and removing cache because scanning is active
onSyncEstablishedFailed(device1, handle1);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
@@ -1042,7 +1029,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
- // Error in syncEstablished causes soureLost, sourceAddFailed notification
+ // Error in syncEstablished causes sourceLost, sourceAddFailed notification
// and not removing cache because scanning is inactice
onSyncEstablishedFailed(device1, handle1);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
@@ -1159,7 +1146,7 @@ public class BassClientServiceTest {
mBassClientService.addSource(mCurrentDevice1, meta, false);
handleHandoverSupport();
- // Error in syncEstablished causes soureLost, sourceAddFailed notification for both sinks
+ // Error in syncEstablished causes sourceLost, sourceAddFailed notification for both sinks
onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
InOrder inOrderCallback = inOrder(mCallback);
@@ -1239,7 +1226,7 @@ public class BassClientServiceTest {
.isFalse();
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
@@ -1288,7 +1275,7 @@ public class BassClientServiceTest {
assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
.isTrue();
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
@@ -1477,7 +1464,7 @@ public class BassClientServiceTest {
private void verifyConnectionStateIntent(
int timeoutMs, BluetoothDevice device, int newState, int prevState) {
- Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue.get(device));
+ Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue);
assertThat(intent.getAction())
.isEqualTo(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
@@ -1500,7 +1487,7 @@ public class BassClientServiceTest {
handleHandoverSupport();
// Verify all group members getting ADD_BCAST_SOURCE message
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -1924,7 +1911,7 @@ public class BassClientServiceTest {
// Verify all group members getting UPDATE_BCAST_SOURCE message
// because PA state is synced
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -1958,7 +1945,7 @@ public class BassClientServiceTest {
// Verify all group members getting UPDATE_BCAST_SOURCE message if
// bis sync state is non-zero and pa sync state is not synced
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -2044,7 +2031,7 @@ public class BassClientServiceTest {
assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
assertThat(msg.get().arg2).isEqualTo(BassConstants.PA_SYNC_DO_NOT_SYNC);
// Verify metadata is null
- assertThat(msg.get().obj).isEqualTo(null);
+ assertThat(msg.get().obj).isNull();
}
for (BassClientStateMachine sm : mStateMachines.values()) {
@@ -2073,7 +2060,7 @@ public class BassClientServiceTest {
null);
doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
- .isEqualTo(null);
+ .isNull();
doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
@@ -2248,7 +2235,7 @@ public class BassClientServiceTest {
onScanResult(mSourceDevice, TEST_BROADCAST_ID);
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
verifyAddSourceForGroup(meta);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
prepareRemoteSourceState(meta, true, true);
// Add another broadcast source
@@ -2259,7 +2246,7 @@ public class BassClientServiceTest {
onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
verifyAddSourceForGroup(meta1);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
if (sm.getDevice().equals(mCurrentDevice)) {
injectRemoteSourceStateSourceAdded(
@@ -2290,13 +2277,13 @@ public class BassClientServiceTest {
// Remove the first broadcast source
mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
verifyRemoveMessageAndInjectSourceRemoval();
// Modify the second one and verify all group members getting UPDATE_BCAST_SOURCE
BluetoothLeBroadcastMetadata metaUpdate = createBroadcastMetadata(TEST_BROADCAST_ID + 3);
mBassClientService.modifySource(mCurrentDevice1, TEST_SOURCE_ID + 3, metaUpdate);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -2321,7 +2308,7 @@ public class BassClientServiceTest {
// Remove the second broadcast source and verify all group members getting
// REMOVE_BCAST_SOURCE message for the second source
mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID + 2);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -2390,7 +2377,7 @@ public class BassClientServiceTest {
.UPDATE_BCAST_SOURCE)
&& (m.arg1 == TEST_SOURCE_ID + 20))
.collect(Collectors.toList());
- assertThat(msgs.size()).isEqualTo(1);
+ assertThat(msgs).hasSize(1);
} else {
throw new AssertionError("Unexpected device");
}
@@ -2404,7 +2391,7 @@ public class BassClientServiceTest {
// Verify errors are reported for the entire group
mBassClientService.addSource(mCurrentDevice1, null, true);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
verify(sm, never()).sendMessage(any());
}
@@ -2419,7 +2406,7 @@ public class BassClientServiceTest {
// Verify errors are reported for the entire group
mBassClientService.modifySource(mCurrentDevice, TEST_SOURCE_ID, null);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
BluetoothDevice dev = sm.getDevice();
try {
@@ -2433,7 +2420,7 @@ public class BassClientServiceTest {
}
}
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
doReturn(BluetoothProfile.STATE_DISCONNECTED).when(sm).getConnectionState();
}
@@ -2441,7 +2428,7 @@ public class BassClientServiceTest {
// Verify errors are reported for the entire group
mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
BluetoothDevice dev = sm.getDevice();
try {
@@ -2491,30 +2478,30 @@ public class BassClientServiceTest {
// Check adding first handle
mBassClientService.addActiveSyncedSource(handle1);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
// Check if cannot add duplicate element
mBassClientService.addActiveSyncedSource(handle1);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
// Check adding second element
mBassClientService.addActiveSyncedSource(handle2);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(2);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2)
.inOrder();
// Check removing non existing element
mBassClientService.removeActiveSyncedSource(handle3);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(2);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2)
.inOrder();
// Check removing second element
mBassClientService.removeActiveSyncedSource(handle1);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle2);
// Check removing first element
@@ -2524,7 +2511,7 @@ public class BassClientServiceTest {
// Add 2 elements
mBassClientService.addActiveSyncedSource(handle1);
mBassClientService.addActiveSyncedSource(handle2);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(2);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
// Check removing all at once
mBassClientService.removeActiveSyncedSource(null);
@@ -2532,10 +2519,10 @@ public class BassClientServiceTest {
} else {
final int testSyncHandle = 1;
prepareConnectedDeviceGroup();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNull();
// Verify add active synced source
mBassClientService.addActiveSyncedSource(mCurrentDevice, testSyncHandle);
@@ -2543,10 +2530,8 @@ public class BassClientServiceTest {
// Verify duplicated source won't be added
mBassClientService.addActiveSyncedSource(mCurrentDevice, testSyncHandle);
mBassClientService.addActiveSyncedSource(mCurrentDevice1, testSyncHandle);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice))
- .isNotEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1))
- .isNotEqualTo(null);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotNull();
assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size())
.isEqualTo(1);
assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size())
@@ -2555,8 +2540,8 @@ public class BassClientServiceTest {
// Verify remove active synced source
mBassClientService.removeActiveSyncedSource(mCurrentDevice, testSyncHandle);
mBassClientService.removeActiveSyncedSource(mCurrentDevice1, testSyncHandle);
- expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
- expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+ expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNull();
+ expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNull();
}
}
@@ -2833,7 +2818,7 @@ public class BassClientServiceTest {
0x56,
0x18,
0x07,
- 0x04, // WRONG PUBLIC_BROADCAST data (metada size)
+ 0x04, // WRONG PUBLIC_BROADCAST data (metadata size)
0x06,
0x07,
0x08,
@@ -2913,11 +2898,11 @@ public class BassClientServiceTest {
// Two SyncRequest queued but not synced yet
assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
- assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2))
@@ -2935,13 +2920,13 @@ public class BassClientServiceTest {
.verify(mMethodProxy)
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
@@ -2954,15 +2939,15 @@ public class BassClientServiceTest {
// Sync 2
onSyncEstablished(device2, handle2);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(2);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2)
.inOrder();
assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3))
@@ -2979,14 +2964,14 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(device3, handle3);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(3);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(3);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2, handle3)
.inOrder();
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(null);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
@@ -3002,7 +2987,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(device4, handle4);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2, handle3, handle4)
.inOrder();
@@ -3010,7 +2995,7 @@ public class BassClientServiceTest {
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
@@ -3027,15 +3012,15 @@ public class BassClientServiceTest {
.verify(mMethodProxy)
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(3);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(3);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle2, handle3, handle4)
.inOrder();
- assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
@@ -3050,7 +3035,7 @@ public class BassClientServiceTest {
expect.that(mBassClientService.getActiveSyncedSources())
.containsExactly(handle2, handle3, handle4, handle5)
.inOrder();
- expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
@@ -3111,7 +3096,7 @@ public class BassClientServiceTest {
onSyncEstablished(device3, handle3);
onScanResult(device4, broadcastId4);
onSyncEstablished(device4, handle4);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2, handle3, handle4)
.inOrder();
@@ -3119,7 +3104,7 @@ public class BassClientServiceTest {
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
@@ -3146,10 +3131,10 @@ public class BassClientServiceTest {
.containsExactly(handle1, handle3, handle4)
.inOrder();
expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
- expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
.isEqualTo(broadcastId1);
expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
@@ -3206,7 +3191,7 @@ public class BassClientServiceTest {
onSyncEstablished(device3, handle3);
onScanResult(device4, broadcastId4);
onSyncEstablished(device4, handle4);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle1, handle2, handle3, handle4)
.inOrder();
@@ -3214,7 +3199,7 @@ public class BassClientServiceTest {
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
@@ -3279,11 +3264,11 @@ public class BassClientServiceTest {
expect.that(mBassClientService.getActiveSyncedSources())
.containsExactly(handle2, handle3, handle4)
.inOrder();
- expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
- expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
@@ -3350,11 +3335,11 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(device5, handle5);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
assertThat(mBassClientService.getActiveSyncedSources())
.containsExactly(handle2, handle3, handle4, handle5)
.inOrder();
- assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
@@ -3397,7 +3382,7 @@ public class BassClientServiceTest {
.isEqualTo(broadcastId1);
// Verify not getting ADD_BCAST_SOURCE message before source sync
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
verify(sm, never()).sendMessage(any());
}
@@ -3410,7 +3395,7 @@ public class BassClientServiceTest {
.containsExactly(handle3, handle4, handle5, handle1)
.inOrder();
expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
- expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(device5);
@@ -3465,7 +3450,7 @@ public class BassClientServiceTest {
.when(mLeAudioService)
.getActiveDevices();
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
@@ -3487,7 +3472,7 @@ public class BassClientServiceTest {
handleHandoverSupport();
// Verify all group members getting ADD_BCAST_SOURCE message
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -3642,7 +3627,7 @@ public class BassClientServiceTest {
prepareConnectedDeviceGroup();
startSearchingForSources();
- // Added and executed immidiatelly as no other in queue
+ // Added and executed immediately as no other in queue
mCallbackCaptor
.getValue()
.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
@@ -3840,7 +3825,7 @@ public class BassClientServiceTest {
// Test using onSyncEstablishedFailed
- // Added and executed immidiatelly as no other in queue, high rssi
+ // Added and executed immediately as no other in queue, high rssi
mCallbackCaptor
.getValue()
.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
@@ -3923,7 +3908,7 @@ public class BassClientServiceTest {
// Test using onSyncLost
- // Added and executed immidiatelly as no other in queue, high rssi
+ // Added and executed immediately as no other in queue, high rssi
mCallbackCaptor
.getValue()
.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1);
@@ -3995,15 +3980,15 @@ public class BassClientServiceTest {
ScanRecord record = ScanRecord.parseFromBytes(scanRecord);
prepareConnectedDeviceGroup();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
// Verify add active synced source
mBassClientService.addActiveSyncedSource(mCurrentDevice, testSyncHandle);
mBassClientService.addActiveSyncedSource(mCurrentDevice1, testSyncHandle);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size()).isEqualTo(1);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).hasSize(1);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).hasSize(1);
// Verify selectSource with max synced device should not proceed
mBassClientService.addActiveSyncedSource(mCurrentDevice, testSyncHandle1);
@@ -4013,10 +3998,10 @@ public class BassClientServiceTest {
mBassClientService.addActiveSyncedSource(mCurrentDevice, testSyncHandle3);
mBassClientService.addActiveSyncedSource(mCurrentDevice1, testSyncHandle3);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotEqualTo(null);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size()).isEqualTo(4);
- assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size()).isEqualTo(4);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotNull();
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).hasSize(4);
+ assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).hasSize(4);
BluetoothDevice testDevice4 =
mBluetoothAdapter.getRemoteLeDevice(
@@ -4038,8 +4023,8 @@ public class BassClientServiceTest {
// Verify remove all active synced source
mBassClientService.removeActiveSyncedSource(mCurrentDevice, null);
mBassClientService.removeActiveSyncedSource(mCurrentDevice1, null);
- expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
- expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+ expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNull();
+ expect.that(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNull();
}
@Test
@@ -4078,7 +4063,7 @@ public class BassClientServiceTest {
assertThat(
mBassClientService.getPeriodicAdvertisementResult(
mSourceDevice, testBroadcastIdInvalid))
- .isEqualTo(null);
+ .isNull();
PeriodicAdvertisementResult paResult =
mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId);
assertThat(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
@@ -4134,7 +4119,7 @@ public class BassClientServiceTest {
assertThat(
mBassClientService.getPeriodicAdvertisementResult(
mSourceDevice, testBroadcastId2))
- .isEqualTo(null);
+ .isNull();
PeriodicAdvertisementResult paResult =
mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId1);
assertThat(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
@@ -4170,7 +4155,7 @@ public class BassClientServiceTest {
expect.that(
mBassClientService.getPeriodicAdvertisementResult(
mSourceDevice, testBroadcastId1))
- .isEqualTo(null);
+ .isNull();
paResult =
mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId2);
expect.that(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
@@ -4244,7 +4229,7 @@ public class BassClientServiceTest {
}
private void verifyAllGroupMembersGettingUpdateOrAddSource(BluetoothLeBroadcastMetadata meta) {
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
@@ -4400,7 +4385,7 @@ public class BassClientServiceTest {
/* Imitate broadcast source stop, sink notify about loosing BIS sync */
verifyRemoveMessageAndInjectSourceRemoval();
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
Mockito.clearInvocations(sm);
}
@@ -4414,7 +4399,7 @@ public class BassClientServiceTest {
mBassClientService.handleUnicastSourceStreamStatusChange(
0 /* STATUS_LOCAL_STREAM_REQUESTED */);
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
verify(sm, never()).sendMessage(any());
}
@@ -4487,6 +4472,8 @@ public class BassClientServiceTest {
// Verify getSyncedBroadcastSinks returns empty device list if no broadcst ID
assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue();
+ assertThat(mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID).isEmpty())
+ .isTrue();
// Update receiver state with broadcast ID
injectRemoteSourceStateChanged(meta, true, false);
@@ -4494,7 +4481,7 @@ public class BassClientServiceTest {
List<BluetoothDevice> activeSinks = mBassClientService.getSyncedBroadcastSinks();
if (Flags.leaudioBigDependsOnAudioState()) {
// Verify getSyncedBroadcastSinks returns correct device list if no BIS synced
- assertThat(activeSinks.size()).isEqualTo(2);
+ assertThat(activeSinks).hasSize(2);
assertThat(activeSinks.contains(mCurrentDevice)).isTrue();
assertThat(activeSinks.contains(mCurrentDevice1)).isTrue();
} else {
@@ -4502,6 +4489,16 @@ public class BassClientServiceTest {
assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue();
}
+ activeSinks.clear();
+ // Verify getSyncedBroadcastSinks by broadcast id
+ activeSinks = mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID);
+ if (Flags.leaudioBigDependsOnAudioState()) {
+ // Verify getSyncedBroadcastSinks returns correct device list if no BIS synced
+ assertThat(activeSinks.size()).isEqualTo(2);
+ assertThat(activeSinks.contains(mCurrentDevice)).isTrue();
+ assertThat(activeSinks.contains(mCurrentDevice1)).isTrue();
+ }
+
// Update receiver state with BIS sync
injectRemoteSourceStateChanged(meta, true, true);
@@ -4814,13 +4811,13 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
byte[] scanRecord =
new byte[] {
@@ -4870,13 +4867,13 @@ public class BassClientServiceTest {
callback.onPeriodicAdvertisingReport(report);
// Not canceled, not updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
mInOrderMethodProxy
.verify(mMethodProxy, never())
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -4885,10 +4882,10 @@ public class BassClientServiceTest {
// Canceled, not updated base
expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
- expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
- expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
mInOrderMethodProxy
.verify(mMethodProxy)
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -4905,13 +4902,13 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
byte[] scanRecord =
new byte[] {
@@ -4970,13 +4967,13 @@ public class BassClientServiceTest {
callback.onPeriodicAdvertisingReport(report);
// Not canceled, not updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
mInOrderMethodProxy
.verify(mMethodProxy, never())
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -4985,10 +4982,10 @@ public class BassClientServiceTest {
// Canceled, not updated base
expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
- expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
- expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
mInOrderMethodProxy
.verify(mMethodProxy)
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -5005,13 +5002,13 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
onPeriodicAdvertisingReport();
@@ -5022,7 +5019,7 @@ public class BassClientServiceTest {
.isEqualTo(mSourceDevice);
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
mInOrderMethodProxy
.verify(mMethodProxy, never())
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -5039,13 +5036,13 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
byte[] scanRecordNoBaseData =
new byte[] {
@@ -5146,13 +5143,13 @@ public class BassClientServiceTest {
callback.onPeriodicAdvertisingReport(report);
// Not canceled, not updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
mInOrderMethodProxy
.verify(mMethodProxy, never())
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -5166,7 +5163,7 @@ public class BassClientServiceTest {
.isEqualTo(mSourceDevice);
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
mInOrderMethodProxy
.verify(mMethodProxy, never())
.periodicAdvertisingManagerUnregisterSync(any(), any());
@@ -5179,24 +5176,24 @@ public class BassClientServiceTest {
startSearchingForSources();
onScanResult(mSourceDevice, TEST_BROADCAST_ID);
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
onPeriodicAdvertisingReport();
// Not canceled, updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
if (!Flags.leaudioBigDependsOnAudioState()) {
onBigInfoAdvertisingReport();
@@ -5212,7 +5209,7 @@ public class BassClientServiceTest {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- Assert.assertEquals(TEST_RSSI, metaData.getValue().getRssi());
+ assertThat(metaData.getValue().getRssi()).isEqualTo(TEST_RSSI);
// Any of them should not notified second time
onPeriodicAdvertisingReport();
@@ -5294,25 +5291,25 @@ public class BassClientServiceTest {
mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
// No public announcement so it will not notify
onPeriodicAdvertisingReport();
// Not canceled, updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
// Not notified
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
@@ -5342,13 +5339,13 @@ public class BassClientServiceTest {
startSearchingForSources();
onScanResult(mSourceDevice, TEST_BROADCAST_ID);
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
// Big report before periodic so before base update
onBigInfoAdvertisingReport();
@@ -5365,13 +5362,13 @@ public class BassClientServiceTest {
onPeriodicAdvertisingReport();
// Not canceled, updated base
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
if (!Flags.leaudioBigDependsOnAudioState()) {
// onBigInfoAdvertisingReport causes notification
@@ -5397,13 +5394,13 @@ public class BassClientServiceTest {
startSearchingForSources();
onScanResult(mSourceDevice, TEST_BROADCAST_ID);
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
byte[] scanRecordNoBaseData =
new byte[] {
@@ -5469,7 +5466,7 @@ public class BassClientServiceTest {
.isEqualTo(mSourceDevice);
expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(TEST_BROADCAST_ID);
- expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotEqualTo(null);
+ expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
// Notified
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
@@ -5560,7 +5557,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
@@ -5578,7 +5575,7 @@ public class BassClientServiceTest {
// Cleaned all
assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
- assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
@@ -5604,7 +5601,7 @@ public class BassClientServiceTest {
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
- assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
+ assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(mSourceDevice);
@@ -5623,7 +5620,7 @@ public class BassClientServiceTest {
// Cleaned all
assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
- assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isEqualTo(null);
+ assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
.isEqualTo(BassConstants.INVALID_BROADCAST_ID);
@@ -6363,7 +6360,8 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
- Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
})
public void sinkUnintentional_handleUnicastSourceStreamStatusChange_withoutScanning() {
sinkUnintentionalWithoutScanning();
@@ -6372,7 +6370,6 @@ public class BassClientServiceTest {
mBassClientService.handleUnicastSourceStreamStatusChange(
0 /* STATUS_LOCAL_STREAM_REQUESTED */);
verifyStopBigMonitoringWithUnsync();
- verifyRemoveMessageAndInjectSourceRemoval();
checkNoResumeSynchronizationByBig();
/* Unicast finished streaming */
@@ -6385,7 +6382,8 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
- Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
})
public void sinkUnintentional_handleUnicastSourceStreamStatusChange_duringScanning() {
sinkUnintentionalDuringScanning();
@@ -6394,7 +6392,6 @@ public class BassClientServiceTest {
mBassClientService.handleUnicastSourceStreamStatusChange(
0 /* STATUS_LOCAL_STREAM_REQUESTED */);
verifyStopBigMonitoringWithoutUnsync();
- verifyRemoveMessageAndInjectSourceRemoval();
checkNoResumeSynchronizationByBig();
/* Unicast finished streaming */
@@ -6657,6 +6654,44 @@ public class BassClientServiceTest {
@Test
@EnableFlags({
Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE,
+ Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT,
+ Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
+ })
+ public void hostIntentional_handleUnicastSourceStreamStatusChange_beforeResumeCompleted() {
+ prepareSynchronizedPairAndStopSearching();
+
+ /* Unicast would like to stream */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
+ checkNoSinkPause();
+
+ /* Unicast finished streaming */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
+ mInOrderMethodProxy
+ .verify(mMethodProxy)
+ .periodicAdvertisingManagerRegisterSync(
+ any(), any(), anyInt(), anyInt(), any(), any());
+
+ /* Unicast would like to stream again before previous resume was complete*/
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
+
+ /* Unicast finished streaming */
+ mBassClientService.handleUnicastSourceStreamStatusChange(
+ 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
+ mInOrderMethodProxy
+ .verify(mMethodProxy)
+ .periodicAdvertisingManagerRegisterSync(
+ any(), any(), anyInt(), anyInt(), any(), any());
+ onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
+ verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
+ }
+
+ @Test
+ @EnableFlags({
+ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
})
public void hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning() {
@@ -6839,7 +6874,7 @@ public class BassClientServiceTest {
mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
- assertThat(mStateMachines.size()).isEqualTo(2);
+ assertThat(mStateMachines).hasSize(2);
for (BassClientStateMachine sm : mStateMachines.values()) {
// No adding source if device remain synced
verify(sm, never()).sendMessage(any());
@@ -6876,6 +6911,83 @@ public class BassClientServiceTest {
}
}
+ /** Test add pending source when BASS state get ready */
+ @Test
+ @EnableFlags({
+ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
+ Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE
+ })
+ public void sinkBassStateReady_addPendingSource() {
+ prepareConnectedDeviceGroup();
+ BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
+ // Verify adding source when Bass state not ready
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ doReturn(false).when(sm).isBassStateReady();
+ }
+ doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
+ doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
+ .when(mLeAudioService)
+ .getAllBroadcastMetadata();
+ // Add broadcast source and got queued due to BASS not ready
+ mBassClientService.addSource(mCurrentDevice, meta, false);
+
+ mBassClientService.getCallbacks().notifyBassStateSetupFailed(mCurrentDevice);
+ TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
+
+ // Verify adding source callback is triggered if BASS state initiate failed
+ try {
+ verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onSourceAddFailed(
+ eq(mCurrentDevice),
+ eq(meta),
+ eq(BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ // Verify not getting ADD_BCAST_SOURCE message if no pending source to add
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ doReturn(true).when(sm).isBassStateReady();
+ }
+ mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
+ TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
+
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ if (sm.getDevice().equals(mCurrentDevice)) {
+ verify(sm, never()).sendMessage(any());
+ clearInvocations(sm);
+ }
+ }
+
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ doReturn(false).when(sm).isBassStateReady();
+ }
+ // Add broadcast source and got queued due to BASS not ready
+ mBassClientService.addSource(mCurrentDevice, meta, false);
+
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ doReturn(true).when(sm).isBassStateReady();
+ }
+ mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
+ TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
+
+ // Verify adding source is resumed once BASS state ready
+ for (BassClientStateMachine sm : mStateMachines.values()) {
+ if (sm.getDevice().equals(mCurrentDevice)) {
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
+
+ Message msg =
+ messageCaptor.getAllValues().stream()
+ .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
+ .findFirst()
+ .orElse(null);
+ assertThat(msg).isNotNull();
+ clearInvocations(sm);
+ }
+ }
+ }
+
@Test
public void testIsLocalBroadacst() {
int broadcastId = 12345;
@@ -7020,7 +7132,7 @@ public class BassClientServiceTest {
mBassClientService.syncRequestForPast(
mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
- // Sync will send INITIATE_PA_SYNC_TRANSFER and remove pending soure to add
+ // Sync will send INITIATE_PA_SYNC_TRANSFER and remove pending source to add
onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
verifyInitiatePaSyncTransferAndNoOthers();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
index 919326767f..e6b2d5f1d6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
@@ -48,6 +48,7 @@ import static com.android.bluetooth.bass_client.BassClientStateMachine.UPDATE_BC
import static com.android.bluetooth.bass_client.BassConstants.CLIENT_CHARACTERISTIC_CONFIG;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -110,9 +111,7 @@ import com.google.common.primitives.Bytes;
import org.hamcrest.Matcher;
import org.hamcrest.core.AllOf;
-import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -145,8 +144,11 @@ public class BassClientStateMachineTest {
private static final int WAIT_MS = 1_200;
private static final int TEST_BROADCAST_ID = 42;
private static final int TEST_SOURCE_ID = 1;
+ private static final int TEST_CHANNEL_INDEX = 1;
private static final String TEST_BROADCAST_NAME = "Test";
private static final String EMPTY_BLUETOOTH_DEVICE_ADDRESS = "00:00:00:00:00:00";
+ private static final byte OPCODE_UPDATE_SOURCE = 0x03;
+ private static final int UPDATE_SOURCE_FIXED_LENGTH = 6;
private Context mTargetContext;
private BluetoothAdapter mAdapter;
private HandlerThread mHandlerThread;
@@ -208,7 +210,7 @@ public class BassClientStateMachineTest {
|| type == BassClientStateMachine.ConnectedProcessing.class) {
return BluetoothProfile.STATE_CONNECTED;
} else {
- Assert.fail("Invalid class type given: " + type);
+ assertWithMessage("Invalid class type given: " + type).fail();
return 0;
}
}
@@ -228,8 +230,8 @@ public class BassClientStateMachineTest {
/** Test that default state is disconnected */
@Test
public void testDefaultDisconnectedState() {
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED, mBassClientStateMachine.getConnectionState());
+ assertThat(mBassClientStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
}
/**
@@ -259,9 +261,8 @@ public class BassClientStateMachineTest {
.sendBroadcast(any(Intent.class), anyString());
// Check that we are in Disconnected state
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Disconnected.class);
}
@Test
@@ -277,9 +278,8 @@ public class BassClientStateMachineTest {
.sendBroadcast(any(Intent.class), anyString());
// Check that we are in Disconnected state
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Disconnected.class);
assertThat(mBassClientStateMachine.mBluetoothGatt).isNull();
}
@@ -298,13 +298,11 @@ public class BassClientStateMachineTest {
intentArgument1.capture(),
any(String[].class),
any(BroadcastOptions.class));
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING,
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Connecting.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Connecting.class);
assertThat(mBassClientStateMachine.mGattCallback).isNotNull();
mBassClientStateMachine.notifyConnectionStateChanged(
@@ -319,9 +317,8 @@ public class BassClientStateMachineTest {
any(String[].class),
any(BroadcastOptions.class));
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Connected.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Connected.class);
}
@Test
@@ -339,13 +336,11 @@ public class BassClientStateMachineTest {
intentArgument1.capture(),
any(String[].class),
any(BroadcastOptions.class));
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING,
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Connecting.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Connecting.class);
// Verify that one connection state broadcast is executed
ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
@@ -354,13 +349,11 @@ public class BassClientStateMachineTest {
intentArgument2.capture(),
any(String[].class),
any(BroadcastOptions.class));
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
- Assert.assertThat(
- mBassClientStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class));
+ assertThat(mBassClientStateMachine.getCurrentState())
+ .isInstanceOf(BassClientStateMachine.Disconnected.class);
}
@Test
@@ -734,6 +727,8 @@ public class BassClientStateMachineTest {
BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
mBassClientStateMachine.mBluetoothGatt = btGatt;
+ BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
+ when(mBassClientService.getCallbacks()).thenReturn(callbacks);
// Do nothing if mDiscoveryInitiated is false.
mBassClientStateMachine.mDiscoveryInitiated = false;
@@ -748,6 +743,8 @@ public class BassClientStateMachineTest {
cb.onServicesDiscovered(null, status);
verify(btGatt, never()).requestMtu(anyInt());
+ verify(callbacks).notifyBassStateSetupFailed(eq(mBassClientStateMachine.getDevice()));
+ assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(false);
// call requestMtu() if status is GATT_SUCCESS.
mBassClientStateMachine.mDiscoveryInitiated = true;
@@ -812,7 +809,7 @@ public class BassClientStateMachineTest {
ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class);
verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
mBassClientStateMachine.mPendingOperation = 0;
mBassClientStateMachine.mPendingSourceId = 0;
@@ -824,7 +821,7 @@ public class BassClientStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata();
sourceId = 1;
@@ -881,7 +878,7 @@ public class BassClientStateMachineTest {
verify(callbacks).notifySourceAdded(any(), any(), anyInt());
verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// set some values for covering more lines of processPASyncState()
mBassClientStateMachine.mPendingMetadata = null;
@@ -915,7 +912,7 @@ public class BassClientStateMachineTest {
verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
assertThat(mBassClientStateMachine.mMsgWhats).contains(REMOVE_BCAST_SOURCE);
mBassClientStateMachine.mIsPendingRemove = null;
@@ -939,7 +936,7 @@ public class BassClientStateMachineTest {
any(), anyInt(), eq(BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST));
verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
}
@Test
@@ -972,7 +969,7 @@ public class BassClientStateMachineTest {
ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor =
ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class);
verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
}
@Test
@@ -1104,7 +1101,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Read first time second (last) characteristic
int sourceId2 = 2;
@@ -1121,7 +1118,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId2), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
}
/** This also tests BassClientStateMachine#processBroadcastReceiverState. */
@@ -1219,7 +1216,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Empty value to indicates removing source from device by remote
when(characteristic.getValue()).thenReturn(new byte[] {});
@@ -1234,7 +1231,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
// Sync value again
mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE;
@@ -1249,7 +1246,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Empty value to indicates removing source from device by local app
mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE;
@@ -1265,7 +1262,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
// Sync value again
mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE;
@@ -1280,7 +1277,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Empty value to indicates removing source from device by stack (source switch)
BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
@@ -1297,7 +1294,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice);
assertThat(mBassClientStateMachine.mMsgWhats).contains(ADD_BCAST_SOURCE);
assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(metadata);
@@ -1314,7 +1311,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Update value - PA SyncInfo Request
value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] =
@@ -1342,7 +1339,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Update value - PA SyncInfo Request, local broadcast
mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata();
@@ -1364,7 +1361,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Update value - Broadcast Code
value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] =
@@ -1384,7 +1381,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
// Update value - Pending Remove
value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] =
@@ -1401,7 +1398,7 @@ public class BassClientStateMachineTest {
inOrderCallbacks
.verify(callbacks)
.notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
- Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
+ assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice);
}
@Test
@@ -1436,6 +1433,15 @@ public class BassClientStateMachineTest {
BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback;
mBassClientStateMachine.mMTUChangeRequested = true;
+ BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
+ when(mBassClientService.getCallbacks()).thenReturn(callbacks);
+
+ // Verify notifyBassStateSetupFailed is called
+ cb.onMtuChanged(null, 10, GATT_FAILURE);
+ verify(callbacks).notifyBassStateSetupFailed(eq(mBassClientStateMachine.getDevice()));
+ assertThat(mBassClientStateMachine.mMTUChangeRequested).isTrue();
+ assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(false);
+
cb.onMtuChanged(null, 10, GATT_SUCCESS);
assertThat(mBassClientStateMachine.mMTUChangeRequested).isTrue();
@@ -1853,7 +1859,7 @@ public class BassClientStateMachineTest {
BassClientStateMachine.ConnectedProcessing.class);
verify(scanControlPoint).setValue(any(byte[].class));
verify(btGatt).writeCharacteristic(any());
- assertThat(mBassClientStateMachine.mPendingSourceToSwitch).isEqualTo(null);
+ assertThat(mBassClientStateMachine.mPendingSourceToSwitch).isNull();
}
@Test
@@ -2651,7 +2657,7 @@ public class BassClientStateMachineTest {
verify(mMethodProxy, timeout(TIMEOUT_MS))
.periodicAdvertisingManagerRegisterSync(
any(), any(), anyInt(), anyInt(), any(), any());
- Assert.assertEquals(mBassClientStateMachine.mPendingSourceToAdd, metadata);
+ assertThat(mBassClientStateMachine.mPendingSourceToAdd).isEqualTo(metadata);
verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
}
@@ -2778,7 +2784,7 @@ public class BassClientStateMachineTest {
ArgumentCaptor.forClass(BluetoothLeBroadcastMetadata.class);
verify(callbacks).notifySourceFound(metaData.capture());
- Assert.assertEquals(testRssi, metaData.getValue().getRssi());
+ assertThat(metaData.getValue().getRssi()).isEqualTo(testRssi);
}
@Test
@@ -2978,7 +2984,7 @@ public class BassClientStateMachineTest {
TEST_SOURCE_ID,
BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING,
- BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG);
+ BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG);
// Verify broadcast audio session is logged when bis sync failed
verify(mMetricsLogger)
.logLeAudioBroadcastAudioSync(
@@ -3088,6 +3094,7 @@ public class BassClientStateMachineTest {
0x0L);
// Verify notifyBassStateReady is called
verify(callbacks).notifyBassStateReady(eq(mTestDevice));
+ assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(true);
}
@Test
@@ -3179,6 +3186,69 @@ public class BassClientStateMachineTest {
assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
}
+ @Test
+ public void updateBroadcastSource_withMetadataChanged() {
+ prepareInitialReceiveStateForGatt();
+
+ generateBroadcastReceiveStatesAndVerify(
+ mSourceTestDevice,
+ TEST_SOURCE_ID,
+ BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+ BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING,
+ 0x1L);
+
+ BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
+ Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
+ mBassClientStateMachine.mBluetoothGatt = btGatt;
+ BluetoothGattCharacteristic scanControlPoint =
+ Mockito.mock(BluetoothGattCharacteristic.class);
+ mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;
+
+ BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
+ mBassClientStateMachine.mPendingMetadata = metadata;
+
+ // Verify pausing broadcast stream with updated metadata
+ BluetoothLeBroadcastMetadata updatedMetadataPaused = getMetadataToPauseStream(metadata);
+ byte[] valueBisPaused = convertMetadataToUpdateSourceByteArray(updatedMetadataPaused);
+
+ sendMessageAndVerifyTransition(
+ mBassClientStateMachine.obtainMessage(
+ UPDATE_BCAST_SOURCE,
+ TEST_SOURCE_ID,
+ BassConstants.INVALID_PA_SYNC_VALUE,
+ updatedMetadataPaused),
+ BassClientStateMachine.ConnectedProcessing.class);
+ assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE);
+ assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
+ verify(scanControlPoint).setValue(eq(valueBisPaused));
+
+ sendMessageAndVerifyTransition(
+ mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED),
+ BassClientStateMachine.Connected.class);
+ TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+ Mockito.clearInvocations(scanControlPoint);
+
+ // Verify resuming broadcast stream with the original metadata
+ byte[] valueBisResumed = convertMetadataToUpdateSourceByteArray(metadata);
+ sendMessageAndVerifyTransition(
+ mBassClientStateMachine.obtainMessage(
+ UPDATE_BCAST_SOURCE,
+ TEST_SOURCE_ID,
+ BassConstants.INVALID_PA_SYNC_VALUE,
+ metadata),
+ BassClientStateMachine.ConnectedProcessing.class);
+ assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE);
+ assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID);
+ verify(scanControlPoint).setValue(eq(valueBisResumed));
+
+ sendMessageAndVerifyTransition(
+ mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED),
+ BassClientStateMachine.Connected.class);
+
+ TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+ Mockito.clearInvocations(scanControlPoint);
+ }
+
private void initToConnectingState() {
allowConnection(true);
allowConnectGatt(true);
@@ -3258,7 +3328,7 @@ public class BassClientStateMachineTest {
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND));
}
- Assert.assertThat(mBassClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type));
+ assertThat(mBassClientStateMachine.getCurrentState()).isInstanceOf(type);
}
private BluetoothLeBroadcastMetadata createBroadcastMetadata() {
@@ -3284,6 +3354,78 @@ public class BassClientStateMachineTest {
return builder.build();
}
+ private byte[] convertMetadataToUpdateSourceByteArray(BluetoothLeBroadcastMetadata metaData) {
+ int numSubGroups = metaData.getSubgroups().size();
+
+ byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
+ int offset = 0;
+ // Opcode
+ res[offset++] = OPCODE_UPDATE_SOURCE;
+ // Source_ID
+ res[offset++] = (byte) TEST_SOURCE_ID;
+ // PA_Sync
+ res[offset++] = (byte) (0x01);
+ // PA_Interval
+ res[offset++] = (byte) 0xFF;
+ res[offset++] = (byte) 0xFF;
+ // Num_Subgroups
+ res[offset++] = (byte) numSubGroups;
+
+ for (int i = 0; i < numSubGroups; i++) {
+ int bisIndexValue = 0;
+ for (BluetoothLeBroadcastChannel channel :
+ metaData.getSubgroups().get(i).getChannels()) {
+ if (channel.isSelected()) {
+ if (channel.getChannelIndex() == 0) {
+ continue;
+ }
+ bisIndexValue |= 1 << (channel.getChannelIndex() - 1);
+ }
+ }
+ // BIS_Sync
+ res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
+ res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
+ res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
+ res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
+ // Metadata_Length; On Modify source, don't update any Metadata
+ res[offset++] = 0;
+ }
+ return res;
+ }
+
+ private BluetoothLeBroadcastMetadata getMetadataToPauseStream(
+ BluetoothLeBroadcastMetadata metadata) {
+ BluetoothLeBroadcastMetadata.Builder metadataToUpdateBuilder =
+ new BluetoothLeBroadcastMetadata.Builder(metadata);
+
+ List<BluetoothLeBroadcastSubgroup> updatedSubgroups = new ArrayList<>();
+ for (BluetoothLeBroadcastSubgroup subgroup : metadata.getSubgroups()) {
+ BluetoothLeBroadcastSubgroup.Builder subgroupBuilder =
+ new BluetoothLeBroadcastSubgroup.Builder(subgroup);
+
+ List<BluetoothLeBroadcastChannel> updatedChannels = new ArrayList<>();
+ for (BluetoothLeBroadcastChannel channel : subgroup.getChannels()) {
+ BluetoothLeBroadcastChannel updatedChannel =
+ new BluetoothLeBroadcastChannel.Builder(channel).setSelected(false).build();
+ updatedChannels.add(updatedChannel);
+ }
+
+ subgroupBuilder.clearChannel();
+ for (BluetoothLeBroadcastChannel channel : updatedChannels) {
+ subgroupBuilder.addChannel(channel);
+ }
+
+ updatedSubgroups.add(subgroupBuilder.build());
+ }
+
+ metadataToUpdateBuilder.clearSubgroup();
+ for (BluetoothLeBroadcastSubgroup subgroup : updatedSubgroups) {
+ metadataToUpdateBuilder.addSubgroup(subgroup);
+ }
+
+ return metadataToUpdateBuilder.build();
+ }
+
private void prepareInitialReceiveStateForGatt() {
initToConnectedState();
mBassClientStateMachine.connectGatt(true);
@@ -3386,7 +3528,7 @@ public class BassClientStateMachineTest {
null, characteristic, GATT_SUCCESS);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- assertThat(mBassClientStateMachine.getAllSources().size()).isEqualTo(1);
+ assertThat(mBassClientStateMachine.getAllSources()).hasSize(1);
BluetoothLeBroadcastReceiveState recvState = mBassClientStateMachine.getAllSources().get(0);
assertThat(recvState.getSourceId()).isEqualTo(sourceId);
@@ -3398,10 +3540,10 @@ public class BassClientStateMachineTest {
assertThat(recvState.getBigEncryptionState()).isEqualTo(bigEncryptState);
assertThat(recvState.getNumSubgroups()).isEqualTo(numOfSubgroups);
- assertThat(recvState.getBisSyncState().size()).isEqualTo(numOfSubgroups);
+ assertThat(recvState.getBisSyncState()).hasSize(numOfSubgroups);
assertThat(recvState.getBisSyncState().get(0)).isEqualTo(bisSyncState);
- assertThat(recvState.getSubgroupMetadata().size()).isEqualTo(numOfSubgroups);
+ assertThat(recvState.getSubgroupMetadata()).hasSize(numOfSubgroups);
BluetoothLeAudioContentMetadata metaData = recvState.getSubgroupMetadata().get(0);
assertThat(metaData.getRawMetadata().length).isEqualTo(metaDataLength);
assertThat(metaData.getRawMetadata())
@@ -3416,7 +3558,6 @@ public class BassClientStateMachineTest {
// German language code in ISO 639-3
final String testLanguage = "deu";
final int testCodecId = 42;
- final int testChannelIndex = 56;
BluetoothLeAudioCodecConfigMetadata codecMetadata =
new BluetoothLeAudioCodecConfigMetadata.Builder()
@@ -3442,7 +3583,7 @@ public class BassClientStateMachineTest {
BluetoothLeBroadcastChannel channel =
new BluetoothLeBroadcastChannel.Builder()
.setSelected(true)
- .setChannelIndex(testChannelIndex)
+ .setChannelIndex(TEST_CHANNEL_INDEX)
.setCodecMetadata(channelCodecMetadata)
.build();
builder.addChannel(channel);
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 1d7e3a2c2d..c81c611336 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
@@ -63,7 +63,6 @@ import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
@@ -288,7 +287,7 @@ public class ActiveDeviceManagerTest {
// Don't call mA2dpService.setActiveDevice()
mTestLooper.dispatchAll();
verify(mA2dpService).setActiveDevice(mA2dpDevice);
- Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mA2dpDevice);
}
/**
@@ -358,7 +357,7 @@ public class ActiveDeviceManagerTest {
// Don't call mHeadsetService.setActiveDevice()
mTestLooper.dispatchAll();
verify(mHeadsetService).setActiveDevice(mHeadsetDevice);
- Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
+ assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mHeadsetDevice);
}
/**
@@ -589,7 +588,6 @@ public class ActiveDeviceManagerTest {
}
@Test
- @EnableFlags(Flags.FLAG_ADM_ALWAYS_FALLBACK_TO_AVAILABLE_DEVICE)
public void a2dpHeadsetActivated_checkFallbackMeachanismOneA2dpOneHeadset() {
// Active call
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
@@ -621,7 +619,7 @@ public class ActiveDeviceManagerTest {
a2dpActiveDeviceChanged(null);
mTestLooper.dispatchAll();
- assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(null);
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isNull();
assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mA2dpHeadsetDevice);
// Connect 2nd device
@@ -807,7 +805,7 @@ public class ActiveDeviceManagerTest {
verify(mHearingAidService).removeActiveDevice(false);
// Don't call mA2dpService.setActiveDevice()
verify(mA2dpService, never()).setActiveDevice(mA2dpDevice);
- Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mA2dpDevice);
assertThat(mActiveDeviceManager.getHearingAidActiveDevices()).isEmpty();
}
@@ -826,7 +824,7 @@ public class ActiveDeviceManagerTest {
verify(mHearingAidService).removeActiveDevice(false);
// Don't call mHeadsetService.setActiveDevice()
verify(mHeadsetService, never()).setActiveDevice(mHeadsetDevice);
- Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
+ assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mHeadsetDevice);
assertThat(mActiveDeviceManager.getHearingAidActiveDevices()).isEmpty();
}
@@ -975,7 +973,7 @@ public class ActiveDeviceManagerTest {
// Don't call mLeAudioService.setActiveDevice()
mTestLooper.dispatchAll();
verify(mLeAudioService, never()).setActiveDevice(any(BluetoothDevice.class));
- Assert.assertEquals(mLeAudioDevice, mActiveDeviceManager.getLeAudioActiveDevice());
+ assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isEqualTo(mLeAudioDevice);
}
/**
@@ -1203,7 +1201,7 @@ public class ActiveDeviceManagerTest {
mTestLooper.dispatchAll();
verify(mLeAudioService).removeActiveDevice(true);
verify(mA2dpService).setActiveDevice(mA2dpDevice);
- Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mA2dpDevice);
assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull();
}
@@ -1221,7 +1219,7 @@ public class ActiveDeviceManagerTest {
mTestLooper.dispatchAll();
verify(mLeAudioService).removeActiveDevice(true);
verify(mHeadsetService).setActiveDevice(mHeadsetDevice);
- Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
+ assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mHeadsetDevice);
assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull();
}
@@ -1585,8 +1583,8 @@ public class ActiveDeviceManagerTest {
verify(mA2dpService, atLeastOnce()).setActiveDevice(mDualModeAudioDevice);
verify(mHeadsetService, atLeastOnce()).setActiveDevice(mDualModeAudioDevice);
verify(mLeAudioService, atLeastOnce()).removeActiveDevice(true);
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getA2dpActiveDevice());
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getHfpActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mDualModeAudioDevice);
+ assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mDualModeAudioDevice);
// Ensure we make classic audio profiles inactive when LEA is made active
leAudioConnected(mDualModeAudioDevice);
@@ -1594,7 +1592,7 @@ public class ActiveDeviceManagerTest {
verify(mA2dpService).removeActiveDevice(false);
verify(mHeadsetService).setActiveDevice(isNull());
verify(mLeAudioService).setActiveDevice(mDualModeAudioDevice);
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getLeAudioActiveDevice());
+ assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isEqualTo(mDualModeAudioDevice);
}
/**
@@ -1638,15 +1636,15 @@ public class ActiveDeviceManagerTest {
verify(mLeAudioService, never()).removeActiveDevice(anyBoolean());
verify(mLeAudioService).setActiveDevice(mDualModeAudioDevice);
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getA2dpActiveDevice());
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getHfpActiveDevice());
- Assert.assertEquals(mDualModeAudioDevice, mActiveDeviceManager.getLeAudioActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mDualModeAudioDevice);
+ assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mDualModeAudioDevice);
+ assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isEqualTo(mDualModeAudioDevice);
// Verify LEA made inactive when a supported classic audio profile is made inactive
a2dpActiveDeviceChanged(null);
mTestLooper.dispatchAll();
- Assert.assertEquals(null, mActiveDeviceManager.getA2dpActiveDevice());
- Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
+ assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isNull();
+ assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull();
}
/**
@@ -1721,7 +1719,6 @@ public class ActiveDeviceManagerTest {
/** A wired audio device is disconnected. Check if falls back to connected A2DP. */
@Test
- @EnableFlags(Flags.FLAG_ADM_FALLBACK_WHEN_WIRED_AUDIO_DISCONNECTED)
public void wiredAudioDeviceDisconnected_setFallbackDevice() throws Exception {
AudioDeviceInfo[] testDevices = createAudioDeviceInfoTestDevices();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 44956086f3..f3dab9d9be 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -81,6 +81,7 @@ import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.gatt.AdvertiseManagerNativeInterface;
import com.android.bluetooth.gatt.DistanceMeasurementNativeInterface;
import com.android.bluetooth.gatt.GattNativeInterface;
+import com.android.bluetooth.le_audio.LeAudioService;
import com.android.bluetooth.le_scan.PeriodicScanNativeInterface;
import com.android.bluetooth.le_scan.ScanNativeInterface;
import com.android.bluetooth.sdp.SdpManagerNativeInterface;
@@ -93,6 +94,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -144,6 +146,7 @@ public class AdapterServiceTest {
private @Mock Context mMockContext;
private @Mock ApplicationInfo mMockApplicationInfo;
+ private @Mock LeAudioService mMockLeAudioService;
private @Mock Resources mMockResources;
private @Mock ProfileService mMockGattService;
private @Mock ProfileService mMockService;
@@ -185,6 +188,10 @@ public class AdapterServiceTest {
private int mForegroundUserId;
private TestLooper mLooper;
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 0);
+ private final BluetoothDevice mDeviceTwo = TestUtils.getTestDevice(mAdapter, 2);
+
static void configureEnabledProfiles() {
Log.e(TAG, "configureEnabledProfiles");
@@ -224,6 +231,12 @@ public class AdapterServiceTest {
doReturn(mJniCallbacks).when(mNativeInterface).getCallbacks();
+ doReturn(true).when(mMockLeAudioService).isAvailable();
+ LeAudioService.setLeAudioService(mMockLeAudioService);
+ doReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+ .when(mMockLeAudioService)
+ .getConnectionPolicy(any());
+
AdapterNativeInterface.setInstance(mNativeInterface);
BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface);
BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface);
@@ -351,6 +364,7 @@ public class AdapterServiceTest {
// Restores the foregroundUserId to the ID prior to the test setup
Utils.setForegroundUserId(mForegroundUserId);
+ LeAudioService.setLeAudioService(null);
mAdapterService.cleanup();
mAdapterService.unregisterRemoteCallback(mIBluetoothCallback);
AdapterNativeInterface.setInstance(null);
@@ -1121,4 +1135,333 @@ public class AdapterServiceTest {
}
assertThat(mLooper.nextMessage()).isNull();
}
+
+ InOrder prepareLeAudioWithConnectedDevices(
+ List<BluetoothDevice> devices,
+ int groupId,
+ boolean returnOnSetAutoActiveModeState,
+ int returnOnGetConnectionStateLeAudio,
+ int returnOnGetConnectionStateAdapter) {
+ doEnable(false);
+
+ doReturn(groupId).when(mMockLeAudioService).getGroupId(any());
+
+ doReturn(returnOnGetConnectionStateLeAudio)
+ .when(mMockLeAudioService)
+ .getConnectionState(any());
+ doReturn(returnOnGetConnectionStateAdapter)
+ .when(mNativeInterface)
+ .getConnectionState(any());
+
+ doReturn(returnOnSetAutoActiveModeState)
+ .when(mMockLeAudioService)
+ .setAutoActiveModeState(groupId, false);
+ doReturn(devices).when(mMockLeAudioService).getGroupDevices(groupId);
+
+ return inOrder(mMockLeAudioService);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_whenDeviceIsNotConnected_success() {
+ int groupId = 1;
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ mAdapterService.notifyDirectLeGattClientConnect(1, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_whenDeviceIsConnected_ignore() {
+ int groupId = 1;
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ false,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ mAdapterService.notifyDirectLeGattClientConnect(1, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_whenLeAudioIsNotAllowed_ignore() {
+ int groupId = 1;
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_DISCONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ false,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ doReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)
+ .when(mMockLeAudioService)
+ .getConnectionPolicy(any());
+ mAdapterService.notifyDirectLeGattClientConnect(1, mDevice);
+
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_failedToConnect() {
+ int groupId = 1;
+ int clientIf = 1;
+
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ mAdapterService.notifyGattClientConnectFailed(clientIf, mDevice);
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_triggerDisconnected() {
+ int groupId = 1;
+ int clientIf = 1;
+
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_DISCONNECTED;
+ int getConnectionState_AdapterService = BluetoothDevice.CONNECTION_STATE_DISCONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+ InOrder orderNative = inOrder(mNativeInterface);
+
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ mAdapterService.notifyGattClientDisconnect(clientIf, mDevice);
+ orderNative.verify(mNativeInterface, never()).disconnectAcl(any(), anyInt());
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_triggerDisconnecting() {
+ int groupId = 1;
+ int clientIf = 1;
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ InOrder orderNative = inOrder(mNativeInterface);
+
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ mAdapterService.notifyGattClientDisconnect(clientIf, mDevice);
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ orderNative.verify(mNativeInterface).disconnectAcl(any(), eq(BluetoothDevice.TRANSPORT_LE));
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_connectingMultipleClients() {
+ int groupId = 1;
+ int clientIf = 1;
+ int clientIfTwo = 2;
+
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ InOrder orderNative = inOrder(mNativeInterface);
+
+ // Connect first client to device
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Connect second client to device
+ mAdapterService.notifyDirectLeGattClientConnect(clientIfTwo, mDevice);
+
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(2);
+
+ // Disconnect first client to device
+ mAdapterService.notifyGattClientDisconnect(clientIf, mDevice);
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, true);
+ orderNative.verify(mNativeInterface, never()).disconnectAcl(any(), anyInt());
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Disconnect second client to device
+ mAdapterService.notifyGattClientDisconnect(clientIfTwo, mDevice);
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ orderNative
+ .verify(mNativeInterface, times(1))
+ .disconnectAcl(any(), eq(BluetoothDevice.TRANSPORT_LE));
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_connectingMultipleDevicesInSameGroup() {
+ int groupId = 1;
+ int clientIf = 1;
+ int clientIfTwo = 2;
+
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice, mDeviceTwo),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ InOrder orderNative = inOrder(mNativeInterface);
+
+ // Connecting device one
+ when(mMockLeAudioService.setAutoActiveModeState(groupId, false)).thenReturn(true);
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Connecting device two
+ mAdapterService.notifyDirectLeGattClientConnect(clientIfTwo, mDeviceTwo);
+
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(2);
+
+ // Disconnect first device
+ mAdapterService.notifyGattClientDisconnect(clientIf, mDevice);
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, true);
+ orderNative.verify(mNativeInterface, never()).disconnectAcl(any(), anyInt());
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Disconnect second device
+ mAdapterService.notifyGattClientDisconnect(clientIfTwo, mDeviceTwo);
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ orderNative
+ .verify(mNativeInterface, times(2))
+ .disconnectAcl(any(), eq(BluetoothDevice.TRANSPORT_LE));
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_GATT_CONNECT_FROM_THE_APPS_WITHOUT_MAKING_LEAUDIO_DEVICE_ACTIVE)
+ public void testGattConnectionToLeAudioDevice_remoteSwitchesToActiveBeforeDisconnect() {
+ int groupId = 1;
+ int clientIf = 1;
+ int clientIfTwo = 2;
+
+ int getConnectionState_LeAudioService = BluetoothProfile.STATE_CONNECTED;
+ int getConnectionState_AdapterService =
+ BluetoothDevice.CONNECTION_STATE_ENCRYPTED_LE
+ | BluetoothDevice.CONNECTION_STATE_CONNECTED;
+ InOrder order =
+ prepareLeAudioWithConnectedDevices(
+ List.of(mDevice, mDeviceTwo),
+ groupId,
+ true,
+ getConnectionState_LeAudioService,
+ getConnectionState_AdapterService);
+
+ InOrder orderNative = inOrder(mNativeInterface);
+
+ // Connecting device one
+ when(mMockLeAudioService.setAutoActiveModeState(groupId, false)).thenReturn(true);
+ mAdapterService.notifyDirectLeGattClientConnect(clientIf, mDevice);
+
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Connecting device two
+ mAdapterService.notifyDirectLeGattClientConnect(clientIfTwo, mDeviceTwo);
+
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, false);
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(2);
+
+ // Remote switches to Active
+ when(mMockLeAudioService.isAutoActiveModeEnabled(groupId)).thenReturn(true);
+
+ // Disconnect first device
+ mAdapterService.notifyGattClientDisconnect(clientIf, mDevice);
+ order.verify(mMockLeAudioService, never()).setAutoActiveModeState(groupId, true);
+ orderNative.verify(mNativeInterface, never()).disconnectAcl(any(), anyInt());
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode.size()).isEqualTo(1);
+
+ // Disconnect second device
+ mAdapterService.notifyGattClientDisconnect(clientIfTwo, mDeviceTwo);
+
+ // Verify devices will not be disconnected
+ order.verify(mMockLeAudioService).setAutoActiveModeState(groupId, true);
+ orderNative.verify(mNativeInterface, never()).disconnectAcl(any(), anyInt());
+ assertThat(mAdapterService.mLeGattClientsControllingAutoActiveMode).isEmpty();
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
index f24c1ff2aa..e274ac6eed 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
@@ -40,7 +40,6 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.Utils;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -219,7 +218,7 @@ public class BondStateMachineTest {
RemoteDevices.DeviceProperties testDeviceProperties =
mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES);
- testDeviceProperties.mUuids = TEST_UUIDS;
+ testDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
BluetoothDevice testDevice = testDeviceProperties.getDevice();
assertThat(testDevice).isNotNull();
@@ -229,7 +228,7 @@ public class BondStateMachineTest {
bondingMsg.arg2 = AbstractionLayer.BT_STATUS_RMT_DEV_DOWN;
mBondStateMachine.sendMessage(bondingMsg);
- pendingDeviceProperties.mUuids = TEST_UUIDS;
+ pendingDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
Message uuidUpdateMsg = mBondStateMachine.obtainMessage(BondStateMachine.UUID_UPDATE);
uuidUpdateMsg.obj = pendingDevice;
@@ -587,7 +586,7 @@ public class BondStateMachineTest {
// Properties are removed when bond is removed
if (newState != BluetoothDevice.BOND_NONE) {
- Assert.assertEquals(expectedNewState, mDeviceProperties.getBondState());
+ assertThat(mDeviceProperties.getBondState()).isEqualTo(expectedNewState);
}
// Check for bond state Intent status.
@@ -635,7 +634,7 @@ public class BondStateMachineTest {
}
if (uuids != null) {
// Add dummy UUID for the device.
- mDeviceProperties.mUuids = TEST_UUIDS;
+ mDeviceProperties.mUuidsBrEdr = TEST_UUIDS;
}
testSendIntentCase(
oldState,
@@ -739,16 +738,17 @@ public class BondStateMachineTest {
private void verifyBondStateChangeIntent(int oldState, int newState, Intent intent) {
assertThat(intent).isNotNull();
- Assert.assertEquals(BluetoothDevice.ACTION_BOND_STATE_CHANGED, intent.getAction());
- Assert.assertEquals(mDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(newState, intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1));
- Assert.assertEquals(
- oldState, intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1));
+ assertThat(intent.getAction()).isEqualTo(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(mDevice);
+ assertThat(intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)).isEqualTo(newState);
+ assertThat(intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1))
+ .isEqualTo(oldState);
if (newState == BOND_NONE) {
- Assert.assertEquals(
- TEST_BOND_REASON, intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, -1));
+ assertThat(intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, -1))
+ .isEqualTo(TEST_BOND_REASON);
} else {
- Assert.assertEquals(-1, intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, -1));
+ assertThat(intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, -1)).isEqualTo(-1);
}
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
index aa49e0d0a7..a46a805941 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java
@@ -34,7 +34,6 @@ import com.google.common.hash.Funnels;
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -132,15 +131,15 @@ public class MetricsLoggerTest {
BluetoothLog.Builder metricsBuilder = BluetoothLog.newBuilder();
MetricsLogger.dumpProto(metricsBuilder);
BluetoothLog metricsProto = metricsBuilder.build();
- Assert.assertEquals(1, metricsProto.getProfileConnectionStatsCount());
+ assertThat(metricsProto.getProfileConnectionStatsCount()).isEqualTo(1);
ProfileConnectionStats profileUsageStatsAvrcp = metricsProto.getProfileConnectionStats(0);
- Assert.assertEquals(ProfileId.AVRCP, profileUsageStatsAvrcp.getProfileId());
- Assert.assertEquals(1, profileUsageStatsAvrcp.getNumTimesConnected());
+ assertThat(profileUsageStatsAvrcp.getProfileId()).isEqualTo(ProfileId.AVRCP);
+ assertThat(profileUsageStatsAvrcp.getNumTimesConnected()).isEqualTo(1);
// Verify that MetricsLogger's internal state is cleared after a dump
BluetoothLog.Builder metricsBuilderAfterDump = BluetoothLog.newBuilder();
MetricsLogger.dumpProto(metricsBuilderAfterDump);
BluetoothLog metricsProtoAfterDump = metricsBuilderAfterDump.build();
- Assert.assertEquals(0, metricsProtoAfterDump.getProfileConnectionStatsCount());
+ assertThat(metricsProtoAfterDump.getProfileConnectionStatsCount()).isEqualTo(0);
}
/** Test whether multiple profile's connection events can be logged interleaving */
@@ -152,20 +151,20 @@ public class MetricsLoggerTest {
BluetoothLog.Builder metricsBuilder = BluetoothLog.newBuilder();
MetricsLogger.dumpProto(metricsBuilder);
BluetoothLog metricsProto = metricsBuilder.build();
- Assert.assertEquals(2, metricsProto.getProfileConnectionStatsCount());
+ assertThat(metricsProto.getProfileConnectionStatsCount()).isEqualTo(2);
Map<ProfileId, ProfileConnectionStats> profileConnectionCountMap =
getProfileUsageStatsMap(metricsProto.getProfileConnectionStatsList());
assertThat(profileConnectionCountMap).containsKey(ProfileId.AVRCP);
- Assert.assertEquals(
- 2, profileConnectionCountMap.get(ProfileId.AVRCP).getNumTimesConnected());
+ assertThat(profileConnectionCountMap.get(ProfileId.AVRCP).getNumTimesConnected())
+ .isEqualTo(2);
assertThat(profileConnectionCountMap).containsKey(ProfileId.HEADSET);
- Assert.assertEquals(
- 1, profileConnectionCountMap.get(ProfileId.HEADSET).getNumTimesConnected());
+ assertThat(profileConnectionCountMap.get(ProfileId.HEADSET).getNumTimesConnected())
+ .isEqualTo(1);
// Verify that MetricsLogger's internal state is cleared after a dump
BluetoothLog.Builder metricsBuilderAfterDump = BluetoothLog.newBuilder();
MetricsLogger.dumpProto(metricsBuilderAfterDump);
BluetoothLog metricsProtoAfterDump = metricsBuilderAfterDump.build();
- Assert.assertEquals(0, metricsProtoAfterDump.getProfileConnectionStatsCount());
+ assertThat(metricsProtoAfterDump.getProfileConnectionStatsCount()).isEqualTo(0);
}
private static Map<ProfileId, ProfileConnectionStats> getProfileUsageStatsMap(
@@ -183,17 +182,17 @@ public class MetricsLoggerTest {
mTestableMetricsLogger.cacheCount(2, 5);
mTestableMetricsLogger.drainBufferedCounters();
- Assert.assertEquals(20L, mTestableMetricsLogger.mTestableCounters.get(1).longValue());
- Assert.assertEquals(5L, mTestableMetricsLogger.mTestableCounters.get(2).longValue());
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(1).longValue()).isEqualTo(20L);
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(2).longValue()).isEqualTo(5L);
mTestableMetricsLogger.cacheCount(1, 3);
mTestableMetricsLogger.cacheCount(2, 5);
mTestableMetricsLogger.cacheCount(2, 5);
mTestableMetricsLogger.cacheCount(3, 1);
mTestableMetricsLogger.drainBufferedCounters();
- Assert.assertEquals(3L, mTestableMetricsLogger.mTestableCounters.get(1).longValue());
- Assert.assertEquals(10L, mTestableMetricsLogger.mTestableCounters.get(2).longValue());
- Assert.assertEquals(1L, mTestableMetricsLogger.mTestableCounters.get(3).longValue());
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(1).longValue()).isEqualTo(3L);
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(2).longValue()).isEqualTo(10L);
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(3).longValue()).isEqualTo(1L);
}
@Test
@@ -207,8 +206,8 @@ public class MetricsLoggerTest {
assertThat(mTestableMetricsLogger.mTestableCounters).doesNotContainKey(1);
assertThat(mTestableMetricsLogger.mTestableCounters).doesNotContainKey(3);
- Assert.assertEquals(
- Long.MAX_VALUE, mTestableMetricsLogger.mTestableCounters.get(2).longValue());
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(2).longValue())
+ .isEqualTo(Long.MAX_VALUE);
}
@Test
@@ -218,9 +217,9 @@ public class MetricsLoggerTest {
mTestableMetricsLogger.cacheCount(2, Long.MAX_VALUE);
mTestableMetricsLogger.close();
- Assert.assertEquals(1, mTestableMetricsLogger.mTestableCounters.get(1).longValue());
- Assert.assertEquals(
- Long.MAX_VALUE, mTestableMetricsLogger.mTestableCounters.get(2).longValue());
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(1).longValue()).isEqualTo(1);
+ assertThat(mTestableMetricsLogger.mTestableCounters.get(2).longValue())
+ .isEqualTo(Long.MAX_VALUE);
}
@Test
@@ -244,10 +243,8 @@ public class MetricsLoggerTest {
for (Map.Entry<String, String> entry : SANITIZED_DEVICE_NAME_MAP.entrySet()) {
String deviceName = entry.getKey();
String sha256 = MetricsLogger.getSha256String(entry.getValue());
- Assert.assertEquals(
- deviceName,
- sha256,
- mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, deviceName));
+ assertThat(mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, deviceName))
+ .isEqualTo(sha256);
}
}
@@ -263,7 +260,7 @@ public class MetricsLoggerTest {
BluetoothRemoteDeviceInformation bluetoothRemoteDeviceInformation =
BluetoothRemoteDeviceInformation.parseFrom(remoteDeviceInformationBytes);
int oui = (0 << 16) | (1 << 8) | 2; // OUI from the above mac address
- Assert.assertEquals(bluetoothRemoteDeviceInformation.getOui(), oui);
+ assertThat(bluetoothRemoteDeviceInformation.getOui()).isEqualTo(oui);
} catch (InvalidProtocolBufferException e) {
assertThat(e.getMessage()).isNull(); // test failure here
@@ -278,7 +275,7 @@ public class MetricsLoggerTest {
String actualMedicalDeviceSha256 =
mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, true);
- Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256);
+ assertThat(actualMedicalDeviceSha256).isEqualTo(expectMedicalDeviceSha256);
}
@Test
@@ -289,13 +286,13 @@ public class MetricsLoggerTest {
String actualMedicalDeviceSha256 =
mTestableMetricsLogger.getAllowlistedDeviceNameHash(deviceName, false);
- Assert.assertEquals(expectMedicalDeviceSha256, actualMedicalDeviceSha256);
+ assertThat(actualMedicalDeviceSha256).isEqualTo(expectMedicalDeviceSha256);
}
@Test
public void uploadEmptyDeviceName() throws IOException {
initTestingBloomfilter();
- Assert.assertEquals("", mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, ""));
+ assertThat(mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, "")).isEmpty();
}
private void initTestingBloomfilter() throws IOException {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
index 2e7225e74b..f02ed4098d 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
@@ -44,10 +44,8 @@ import com.android.bluetooth.hfpclient.NativeInterface;
import com.android.bluetooth.hid.HidDeviceNativeInterface;
import com.android.bluetooth.hid.HidHostNativeInterface;
import com.android.bluetooth.le_audio.LeAudioNativeInterface;
-import com.android.bluetooth.pan.PanNativeInterface;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -89,7 +87,6 @@ public class ProfileServiceTest {
@Mock private HearingAidNativeInterface mHearingAidNativeInterface;
@Mock private HidDeviceNativeInterface mHidDeviceNativeInterface;
@Mock private HidHostNativeInterface mHidHostNativeInterface;
- @Mock private PanNativeInterface mPanNativeInterface;
@Mock private LeAudioNativeInterface mLeAudioInterface;
private void setProfileState(int profile, int state) {
@@ -121,9 +118,7 @@ public class ProfileServiceTest {
.collect(Collectors.groupingBy(Object::getClass, Collectors.counting()));
counts.forEach(
- (clazz, count) ->
- Assert.assertEquals(
- clazz.getSimpleName(), (long) invocationNumber, count.longValue()));
+ (clazz, count) -> assertThat((long) invocationNumber).isEqualTo(count.longValue()));
}
@Before
@@ -155,6 +150,7 @@ public class ProfileServiceTest {
&& profile != BluetoothProfile.VOLUME_CONTROL
&& profile != BluetoothProfile.CSIP_SET_COORDINATOR
&& profile != BluetoothProfile.GATT
+ && profile != BluetoothProfile.PAN
&& profile != BluetoothProfile.A2DP)
.toArray();
TestUtils.setAdapterService(mAdapterService);
@@ -169,7 +165,6 @@ public class ProfileServiceTest {
HearingAidNativeInterface.setInstance(mHearingAidNativeInterface);
HidDeviceNativeInterface.setInstance(mHidDeviceNativeInterface);
HidHostNativeInterface.setInstance(mHidHostNativeInterface);
- PanNativeInterface.setInstance(mPanNativeInterface);
LeAudioNativeInterface.setInstance(mLeAudioInterface);
}
@@ -187,7 +182,6 @@ public class ProfileServiceTest {
HearingAidNativeInterface.setInstance(null);
HidDeviceNativeInterface.setInstance(null);
HidHostNativeInterface.setInstance(null);
- PanNativeInterface.setInstance(null);
LeAudioNativeInterface.setInstance(null);
}
@@ -236,7 +230,7 @@ public class ProfileServiceTest {
List<ProfileService> startedArguments = starts.getAllValues();
List<ProfileService> stoppedArguments = stops.getAllValues();
- Assert.assertEquals(startedArguments.size(), stoppedArguments.size());
+ assertThat(startedArguments).hasSize(stoppedArguments.size());
for (ProfileService service : startedArguments) {
assertThat(stoppedArguments).contains(service);
stoppedArguments.remove(service);
@@ -263,7 +257,7 @@ public class ProfileServiceTest {
verify(mAdapterService, times(NUM_REPEATS * profileNumber + i + 1))
.onProfileServiceStateChanged(
stop.capture(), eq(BluetoothAdapter.STATE_OFF));
- Assert.assertEquals(start.getValue(), stop.getValue());
+ assertThat(start.getValue()).isEqualTo(stop.getValue());
}
profileNumber += 1;
}
@@ -287,7 +281,7 @@ public class ProfileServiceTest {
ArgumentCaptor<ProfileService> stop = ArgumentCaptor.forClass(ProfileService.class);
verify(mAdapterService, times(NUM_REPEATS * profileNumber + i + 1))
.removeProfile(stop.capture());
- Assert.assertEquals(start.getValue(), stop.getValue());
+ assertThat(start.getValue()).isEqualTo(stop.getValue());
}
profileNumber += 1;
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
index 28747da680..f9d4d0f045 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
@@ -33,7 +33,6 @@ import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.hfp.HeadsetHalConstants;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -123,12 +122,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that update same battery level for the same device does not trigger intent
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false);
@@ -140,12 +139,12 @@ public class RemoteDevicesTest {
verify(mAdapterService, times(2))
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
// Verify that user can get battery level after the update
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
verifyNoMoreInteractions(mAdapterService);
}
@@ -209,12 +208,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that resetting battery level changes it back to BluetoothDevice
// .BATTERY_LEVEL_UNKNOWN
@@ -225,12 +224,11 @@ public class RemoteDevicesTest {
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(
mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify value is reset in properties
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
// Verify no intent is sent after second reset
mRemoteDevices.resetBatteryLevel(mDevice1, /* fromBas= */ false);
@@ -242,7 +240,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
verifyNoMoreInteractions(mAdapterService);
}
@@ -260,12 +258,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that resetting battery level changes it back to BluetoothDevice
// .BATTERY_LEVEL_UNKNOWN
@@ -279,12 +277,11 @@ public class RemoteDevicesTest {
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(
mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify value is reset in properties
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
// Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false);
@@ -292,7 +289,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
verifyNoMoreInteractions(mAdapterService);
}
@@ -313,12 +310,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that battery level is not reset
mRemoteDevices.onHeadsetConnectionStateChanged(
@@ -327,8 +324,8 @@ public class RemoteDevicesTest {
BluetoothProfile.STATE_DISCONNECTED);
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Recover the previous battery service if exists
clearBatteryServiceForTesting(oldBatteryService);
@@ -350,12 +347,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that when device is completely disconnected, RemoteDevices reset battery level to
// BluetoothDevice.BATTERY_LEVEL_UNKNOWN
@@ -376,17 +373,15 @@ public class RemoteDevicesTest {
mDevice1,
BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 2));
- Assert.assertEquals(
- BLUETOOTH_CONNECT,
- mStringArgument.getAllValues().get(mStringArgument.getAllValues().size() - 2));
- Assert.assertEquals(
- BluetoothDevice.ACTION_ACL_DISCONNECTED, mIntentArgument.getValue().getAction());
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getAllValues().get(mStringArgument.getAllValues().size() - 2))
+ .isEqualTo(BLUETOOTH_CONNECT);
+ assertThat(mIntentArgument.getValue().getAction())
+ .isEqualTo(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify value is reset in properties
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
// Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false);
@@ -394,7 +389,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
}
@Test
@@ -411,7 +406,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
}
@Test
@@ -444,7 +439,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, 42, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
}
@Test
@@ -471,107 +466,102 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, 60, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
}
@Test
public void testGetBatteryLevelFromXEventVsc() {
- Assert.assertEquals(42, RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 8)));
- Assert.assertEquals(
- 100, RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(10, 11)));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(1, 1)));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 1)));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, 1)));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, -1)));
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 8))).isEqualTo(42);
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(10, 11)))
+ .isEqualTo(100);
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(1, 1)))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(3, 1)))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, 1)))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(RemoteDevices.getBatteryLevelFromXEventVsc(getXEventArray(-1, -1)))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
}
@Test
public void testGetBatteryLevelFromAppleBatteryVsc() {
- Assert.assertEquals(
- 10,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 1,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- 0
- }));
- Assert.assertEquals(
- 100,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 1,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- 9
- }));
- Assert.assertEquals(
- 60,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 3,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- 5,
- 2,
- 1,
- 3,
- 10
- }));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 3,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- 5,
- 2,
- 1,
- 3
- }));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 1,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- 10
- }));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 1,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- -1
- }));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {
- 1,
- BluetoothHeadset
- .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
- "5"
- }));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[] {1, 35, 37}));
- Assert.assertEquals(
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
- RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
- new Object[] {1, "WRONG", "WRONG"}));
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 1,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ 0
+ }))
+ .isEqualTo(10);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 1,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ 9
+ }))
+ .isEqualTo(100);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 3,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ 5,
+ 2,
+ 1,
+ 3,
+ 10
+ }))
+ .isEqualTo(60);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 3,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ 5,
+ 2,
+ 1,
+ 3
+ }))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 1,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ 10
+ }))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 1,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ -1
+ }))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {
+ 1,
+ BluetoothHeadset
+ .VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+ "5"
+ }))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(RemoteDevices.getBatteryLevelFromAppleBatteryVsc(new Object[] {1, 35, 37}))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(
+ RemoteDevices.getBatteryLevelFromAppleBatteryVsc(
+ new Object[] {1, "WRONG", "WRONG"}))
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
}
@Test
@@ -587,12 +577,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that resetting battery level changes it back to BluetoothDevice
// .BATTERY_LEVEL_UNKNOWN
@@ -607,13 +597,12 @@ public class RemoteDevicesTest {
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(
mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify value is reset in properties
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(),
- BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
// Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent again
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false);
@@ -621,7 +610,7 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
verifyNoMoreInteractions(mAdapterService);
}
@@ -642,12 +631,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that battery level is not reset.
mRemoteDevices.onHeadsetClientConnectionStateChanged(
@@ -656,8 +645,8 @@ public class RemoteDevicesTest {
BluetoothProfile.STATE_DISCONNECTED);
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
clearBatteryServiceForTesting(oldBatteryService);
@@ -681,12 +670,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that updating battery service overrides hfp battery level
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel2, /* fromBas= */ true);
@@ -697,8 +686,8 @@ public class RemoteDevicesTest {
// Verify that the battery level isn't reset
mRemoteDevices.resetBatteryLevel(mDevice1, /* fromBas= */ true);
- Assert.assertEquals(
- batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
verify(mAdapterService, times(3))
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
@@ -725,12 +714,12 @@ public class RemoteDevicesTest {
.sendBroadcast(
mIntentArgument.capture(), mStringArgument.capture(), any(Bundle.class));
verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
// Verify that user can get battery level after the update
assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull();
- Assert.assertEquals(
- mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel);
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
// Verify that updating battery service doesn't send broadcast
mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ true);
@@ -738,8 +727,8 @@ public class RemoteDevicesTest {
// Verify that the battery level isn't reset
mRemoteDevices.resetBatteryLevel(mDevice1, /* fromBas= */ true);
- Assert.assertEquals(
- batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel())
+ .isEqualTo(batteryLevel);
verifyNoMoreInteractions(mAdapterService);
clearBatteryServiceForTesting(oldBatteryService);
@@ -763,7 +752,7 @@ public class RemoteDevicesTest {
mDevice1,
RemoteDevices.batteryChargeIndicatorToPercentge(batteryLevel),
mIntentArgument);
- Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue());
+ assertThat(mStringArgument.getValue()).isEqualTo(BLUETOOTH_CONNECT);
}
@Test
@@ -775,7 +764,7 @@ public class RemoteDevicesTest {
mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1));
DeviceProperties prop2 =
mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1));
- Assert.assertEquals(prop2, prop1);
+ assertThat(prop1).isEqualTo(prop2);
}
@Test
@@ -796,9 +785,8 @@ public class RemoteDevicesTest {
deviceProp.setHfAudioPolicyForRemoteAg(policies);
// Verify that the audio policy properties are set and get properly
- Assert.assertEquals(
- policies,
- mRemoteDevices.getDeviceProperties(mDevice1).getHfAudioPolicyForRemoteAg());
+ assertThat(mRemoteDevices.getDeviceProperties(mDevice1).getHfAudioPolicyForRemoteAg())
+ .isEqualTo(policies);
}
@Test
@@ -845,14 +833,15 @@ public class RemoteDevicesTest {
private static void verifyBatteryLevelChangedIntent(
BluetoothDevice device, int batteryLevel, Intent intent) {
- Assert.assertEquals(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, intent.getAction());
- Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(
- batteryLevel, intent.getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, -15));
- Assert.assertEquals(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- intent.getFlags());
+ assertThat(intent.getAction()).isEqualTo(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
+ assertThat(intent.getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, -15))
+ .isEqualTo(batteryLevel);
+ assertThat(intent.getFlags())
+ .isEqualTo(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
private static Object[] getXEventArray(int batteryLevel, int numLevels) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
index c616c90aa7..593e422776 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
@@ -39,7 +39,6 @@ import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hfp.HeadsetService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -122,7 +121,7 @@ public class SilenceDeviceManagerTest {
// Set silence state and check whether state changed successfully
assertThat(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)).isTrue();
TestUtils.waitForLooperToFinishScheduledTask(mLooper);
- Assert.assertEquals(enableSilence, mSilenceDeviceManager.getSilenceMode(mTestDevice));
+ assertThat(mSilenceDeviceManager.getSilenceMode(mTestDevice)).isEqualTo(enableSilence);
// Check for silence state changed intent
if (wasSilenced != enableSilence) {
@@ -163,8 +162,9 @@ public class SilenceDeviceManagerTest {
}
void verifySilenceStateIntent(Intent intent) {
- Assert.assertEquals(BluetoothDevice.ACTION_SILENCE_MODE_CHANGED, intent.getAction());
- Assert.assertEquals(mTestDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
+ assertThat(intent.getAction()).isEqualTo(BluetoothDevice.ACTION_SILENCE_MODE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(mTestDevice);
}
/** Helper to indicate A2dp connected for a device. */
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java
index 668743ad31..93ce02664c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java
@@ -155,14 +155,6 @@ public final class BluetoothKeystoreServiceTest {
"aec555555555555555555555555555555555555555555555");
}
- private boolean doCompareKeySet(Map<String, String> map1, Map<String, String> map2) {
- return map1.keySet().equals(map2.keySet());
- }
-
- private boolean doCompareMap(Map<String, String> map1, Map<String, String> map2) {
- return map1.equals(map2);
- }
-
private boolean parseConfigFile(String filePathString) {
try {
mBluetoothKeystoreService.parseConfigFile(filePathString);
@@ -205,11 +197,7 @@ public final class BluetoothKeystoreServiceTest {
// load config file.
assertThat(parseConfigFile(CONFIG_FILE_PATH)).isTrue();
// make sure it is same with createNameDecryptKeyResult
- assertThat(
- doCompareMap(
- mNameDecryptKeyResult,
- mBluetoothKeystoreService.getNameDecryptKey()))
- .isTrue();
+ assertThat(mBluetoothKeystoreService.getNameDecryptKey()).isEqualTo(mNameDecryptKeyResult);
}
@Test
@@ -219,11 +207,8 @@ public final class BluetoothKeystoreServiceTest {
// Wait for encryption to complete
mBluetoothKeystoreService.stopThread();
- assertThat(
- doCompareKeySet(
- mNameDecryptKeyResult,
- mBluetoothKeystoreService.getNameEncryptKey()))
- .isTrue();
+ assertThat(mBluetoothKeystoreService.getNameDecryptKey().keySet())
+ .containsExactlyElementsIn(mNameDecryptKeyResult.keySet());
}
@Test
@@ -238,11 +223,7 @@ public final class BluetoothKeystoreServiceTest {
// Wait for encryption to complete
mBluetoothKeystoreService.stopThread();
- assertThat(
- doCompareMap(
- mNameDecryptKeyResult,
- mBluetoothKeystoreService.getNameDecryptKey()))
- .isTrue();
+ assertThat(mBluetoothKeystoreService.getNameDecryptKey()).isEqualTo(mNameDecryptKeyResult);
}
@Test
@@ -279,11 +260,7 @@ public final class BluetoothKeystoreServiceTest {
// remove hash data avoid interfering result.
mBluetoothKeystoreService.getNameDecryptKey().remove(CONFIG_FILE_PREFIX);
- assertThat(
- doCompareMap(
- mNameDecryptKeyResult,
- mBluetoothKeystoreService.getNameDecryptKey()))
- .isTrue();
+ assertThat(mBluetoothKeystoreService.getNameDecryptKey()).isEqualTo(mNameDecryptKeyResult);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java
index bcaccfd94f..2753ee7b17 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java
@@ -18,6 +18,7 @@ package com.android.bluetooth.btservice.storage;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
@@ -152,18 +153,15 @@ public final class DatabaseManagerTest {
restartDatabaseManagerHelper();
for (int id = 0; id < BluetoothProfile.MAX_PROFILE_ID; id++) {
- Assert.assertEquals(
- BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
- mDatabaseManager.getProfileConnectionPolicy(mTestDevice, id));
+ assertThat(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, id))
+ .isEqualTo(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
}
- Assert.assertEquals(
- BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN,
- mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice))
+ .isEqualTo(BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
- Assert.assertEquals(
- BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN,
- mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice))
+ .isEqualTo(BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
for (int id = 0; id < MAX_META_ID; id++) {
assertThat(mDatabaseManager.getCustomMeta(mTestDevice, id)).isNull();
@@ -340,11 +338,11 @@ public final class DatabaseManagerTest {
List<Metadata> list = mDatabase.load();
// Check number of metadata in the database
- Assert.assertEquals(1, list.size());
+ assertThat(list).hasSize(1);
// Check whether the device is in database
Metadata checkData = list.get(0);
- Assert.assertEquals(TEST_BT_ADDR, checkData.getAddress());
+ assertThat(checkData.getAddress()).isEqualTo(TEST_BT_ADDR);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -388,13 +386,13 @@ public final class DatabaseManagerTest {
// Check number of metadata in the database
List<Metadata> list = mDatabase.load();
// OTHER_BT_ADDR1 and OTHER_BT_ADDR2 should still in database
- Assert.assertEquals(2, list.size());
+ assertThat(list).hasSize(2);
// Check whether the devices are in the database
Metadata checkData1 = list.get(0);
- Assert.assertEquals(OTHER_BT_ADDR2, checkData1.getAddress());
+ assertThat(checkData1.getAddress()).isEqualTo(OTHER_BT_ADDR2);
Metadata checkData2 = list.get(1);
- Assert.assertEquals(OTHER_BT_ADDR1, checkData2.getAddress());
+ assertThat(checkData2.getAddress()).isEqualTo(OTHER_BT_ADDR1);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -522,7 +520,7 @@ public final class DatabaseManagerTest {
public void testSetConnectionHeadset() {
mSetFlagsRule.disableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE);
// Verify pre-conditions to ensure a fresh test
- Assert.assertEquals(0, mDatabaseManager.mMetadataCache.size());
+ assertThat(mDatabaseManager.mMetadataCache).isEmpty();
assertThat(mTestDevice).isNotNull();
assertThat(mTestDevice2).isNotNull();
assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isNull();
@@ -535,9 +533,8 @@ public final class DatabaseManagerTest {
.isTrue();
List<BluetoothDevice> mostRecentlyConnectedDevicesOrdered =
mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyActiveHfpDevice());
- Assert.assertEquals(1, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0));
+ assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isEqualTo(mTestDevice);
+ assertThat(mostRecentlyConnectedDevicesOrdered).containsExactly(mTestDevice);
// Setting the second device's connection
mDatabaseManager.setConnection(mTestDevice2, BluetoothProfile.HEADSET);
@@ -547,11 +544,11 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()).isActiveHfpDevice)
.isTrue();
- Assert.assertEquals(mTestDevice2, mDatabaseManager.getMostRecentlyActiveHfpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isEqualTo(mTestDevice2);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice2, mTestDevice)
+ .inOrder();
// Disconnect first test device's connection
mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.HEADSET);
@@ -561,9 +558,9 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isNotNull();
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(0));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice2, mTestDevice)
+ .inOrder();
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -575,7 +572,7 @@ public final class DatabaseManagerTest {
public void testSetConnection() {
mSetFlagsRule.disableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE);
// Verify pre-conditions to ensure a fresh test
- Assert.assertEquals(0, mDatabaseManager.mMetadataCache.size());
+ assertThat(mDatabaseManager.mMetadataCache).isEmpty();
assertThat(mTestDevice).isNotNull();
assertThat(mTestDevice2).isNotNull();
assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull();
@@ -590,9 +587,8 @@ public final class DatabaseManagerTest {
.isTrue();
List<BluetoothDevice> mostRecentlyConnectedDevicesOrdered =
mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
- Assert.assertEquals(1, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0));
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice);
+ assertThat(mostRecentlyConnectedDevicesOrdered).containsExactly(mTestDevice);
// Setting the second device's connection
mDatabaseManager.setConnection(mTestDevice2, BluetoothProfile.A2DP);
@@ -606,11 +602,11 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress())
.is_active_a2dp_device)
.isTrue();
- Assert.assertEquals(mTestDevice2, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice2);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice2, mTestDevice)
+ .inOrder();
// Connect first test device again
mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.A2DP);
@@ -624,11 +620,11 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress())
.is_active_a2dp_device)
.isFalse();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(1));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice, mTestDevice2)
+ .inOrder();
// Disconnect first test device's connection
mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.A2DP);
@@ -644,9 +640,9 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull();
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(1));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice, mTestDevice2)
+ .inOrder();
// Connect third test device (non-a2dp device)
mDatabaseManager.setConnection(mTestDevice3, BluetoothProfile.HEADSET);
@@ -666,10 +662,9 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull();
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice3, mTestDevice, mTestDevice2)
+ .inOrder();
// Connect first test device again
mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.A2DP);
@@ -687,12 +682,11 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress())
.is_active_a2dp_device)
.isFalse();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice, mTestDevice3, mTestDevice2)
+ .inOrder();
// Connect third test device again and ensure it doesn't reset active a2dp device
mDatabaseManager.setConnection(mTestDevice3, BluetoothProfile.HEADSET);
@@ -710,12 +704,11 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress())
.is_active_a2dp_device)
.isFalse();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice3, mTestDevice, mTestDevice2)
+ .inOrder();
// Disconnect second test device
mDatabaseManager.setDisconnection(mTestDevice2, BluetoothProfile.A2DP);
@@ -733,12 +726,11 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress())
.is_active_a2dp_device)
.isFalse();
- Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice());
+ assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isEqualTo(mTestDevice);
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice3, mTestDevice, mTestDevice2)
+ .inOrder();
// Disconnect first test device
mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.A2DP);
@@ -758,10 +750,9 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull();
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice3, mTestDevice, mTestDevice2)
+ .inOrder();
// Disconnect third test device
mDatabaseManager.setDisconnection(mTestDevice3, BluetoothProfile.A2DP);
@@ -781,10 +772,9 @@ public final class DatabaseManagerTest {
.isFalse();
assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull();
mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices();
- Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size());
- Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0));
- Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1));
- Assert.assertEquals(mTestDevice2, mostRecentlyConnectedDevicesOrdered.get(2));
+ assertThat(mostRecentlyConnectedDevicesOrdered)
+ .containsExactly(mTestDevice3, mTestDevice, mTestDevice2)
+ .inOrder();
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -799,16 +789,16 @@ public final class DatabaseManagerTest {
preferences.putInt(BluetoothAdapter.AUDIO_MODE_DUPLEX, BluetoothProfile.LE_AUDIO);
// TEST 1: If input is invalid, throws the right Exception
- Assert.assertThrows(
+ assertThrows(
NullPointerException.class,
() -> mDatabaseManager.setPreferredAudioProfiles(null, preferences));
- Assert.assertThrows(
+ assertThrows(
NullPointerException.class,
() -> mDatabaseManager.setPreferredAudioProfiles(new ArrayList<>(), null));
- Assert.assertThrows(
+ assertThrows(
IllegalArgumentException.class,
() -> mDatabaseManager.setPreferredAudioProfiles(new ArrayList<>(), preferences));
- Assert.assertThrows(
+ assertThrows(
IllegalArgumentException.class,
() -> mDatabaseManager.getPreferredAudioProfiles(null));
@@ -1651,13 +1641,14 @@ public final class DatabaseManagerTest {
mDatabaseManager.mMetadataCache.put(TEST_BT_ADDR, data);
mDatabase.insert(data);
}
- Assert.assertEquals(
- expectedSetResult,
- mDatabaseManager.setProfileConnectionPolicy(
- mTestDevice, BluetoothProfile.HEADSET, connectionPolicy));
- Assert.assertEquals(
- expectedConnectionPolicy,
- mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.HEADSET));
+ assertThat(
+ mDatabaseManager.setProfileConnectionPolicy(
+ mTestDevice, BluetoothProfile.HEADSET, connectionPolicy))
+ .isEqualTo(expectedSetResult);
+ assertThat(
+ mDatabaseManager.getProfileConnectionPolicy(
+ mTestDevice, BluetoothProfile.HEADSET))
+ .isEqualTo(expectedConnectionPolicy);
// Wait for database update
TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper());
@@ -1668,17 +1659,18 @@ public final class DatabaseManagerTest {
if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
// Database won't be updated
- Assert.assertEquals(0, list.size());
+ assertThat(list).isEmpty();
return;
}
}
- Assert.assertEquals(1, list.size());
+ assertThat(list).hasSize(1);
// Check whether the device is in database
restartDatabaseManagerHelper();
- Assert.assertEquals(
- expectedConnectionPolicy,
- mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.HEADSET));
+ assertThat(
+ mDatabaseManager.getProfileConnectionPolicy(
+ mTestDevice, BluetoothProfile.HEADSET))
+ .isEqualTo(expectedConnectionPolicy);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -1694,12 +1686,12 @@ public final class DatabaseManagerTest {
}
if (test == A2DP_SUPPORT_OP_CODEC_TEST) {
mDatabaseManager.setA2dpSupportsOptionalCodecs(mTestDevice, value);
- Assert.assertEquals(
- expectedValue, mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice))
+ .isEqualTo(expectedValue);
} else {
mDatabaseManager.setA2dpOptionalCodecsEnabled(mTestDevice, value);
- Assert.assertEquals(
- expectedValue, mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice))
+ .isEqualTo(expectedValue);
}
// Wait for database update
TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper());
@@ -1709,19 +1701,19 @@ public final class DatabaseManagerTest {
// Check number of metadata in the database
if (!stored) {
// Database won't be updated
- Assert.assertEquals(0, list.size());
+ assertThat(list).isEmpty();
return;
}
- Assert.assertEquals(1, list.size());
+ assertThat(list).hasSize(1);
// Check whether the device is in database
restartDatabaseManagerHelper();
if (test == A2DP_SUPPORT_OP_CODEC_TEST) {
- Assert.assertEquals(
- expectedValue, mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpSupportsOptionalCodecs(mTestDevice))
+ .isEqualTo(expectedValue);
} else {
- Assert.assertEquals(
- expectedValue, mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice));
+ assertThat(mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice))
+ .isEqualTo(expectedValue);
}
mDatabaseManager.factoryReset();
@@ -1737,17 +1729,17 @@ public final class DatabaseManagerTest {
Metadata data = new Metadata(TEST_BT_ADDR);
mDatabaseManager.mMetadataCache.put(TEST_BT_ADDR, data);
mDatabase.insert(data);
- Assert.assertEquals(
- expectedResult, mDatabaseManager.setCustomMeta(mTestDevice, key, testValue));
+ assertThat(mDatabaseManager.setCustomMeta(mTestDevice, key, testValue))
+ .isEqualTo(expectedResult);
verify(mAdapterService).onMetadataChanged(mTestDevice, key, testValue);
verifyTime++;
}
- Assert.assertEquals(
- expectedResult, mDatabaseManager.setCustomMeta(mTestDevice, key, value));
+ assertThat(mDatabaseManager.setCustomMeta(mTestDevice, key, value))
+ .isEqualTo(expectedResult);
if (expectedResult) {
// Check for callback and get value
verify(mAdapterService, times(verifyTime)).onMetadataChanged(mTestDevice, key, value);
- Assert.assertEquals(value, mDatabaseManager.getCustomMeta(mTestDevice, key));
+ assertThat(mDatabaseManager.getCustomMeta(mTestDevice, key)).isEqualTo(value);
} else {
assertThat(mDatabaseManager.getCustomMeta(mTestDevice, key)).isNull();
return;
@@ -1757,7 +1749,7 @@ public final class DatabaseManagerTest {
// Check whether the value is saved in database
restartDatabaseManagerHelper();
- Assert.assertArrayEquals(value, mDatabaseManager.getCustomMeta(mTestDevice, key));
+ assertThat(mDatabaseManager.getCustomMeta(mTestDevice, key)).isEqualTo(value);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -1772,15 +1764,14 @@ public final class DatabaseManagerTest {
Metadata data = new Metadata(TEST_BT_ADDR);
mDatabaseManager.mMetadataCache.put(TEST_BT_ADDR, data);
mDatabase.insert(data);
- Assert.assertEquals(
- expectedResult,
- mDatabaseManager.setAudioPolicyMetadata(mTestDevice, testPolicy));
+ assertThat(mDatabaseManager.setAudioPolicyMetadata(mTestDevice, testPolicy))
+ .isEqualTo(expectedResult);
}
- Assert.assertEquals(
- expectedResult, mDatabaseManager.setAudioPolicyMetadata(mTestDevice, policy));
+ assertThat(mDatabaseManager.setAudioPolicyMetadata(mTestDevice, policy))
+ .isEqualTo(expectedResult);
if (expectedResult) {
// Check for callback and get value
- Assert.assertEquals(policy, mDatabaseManager.getAudioPolicyMetadata(mTestDevice));
+ assertThat(mDatabaseManager.getAudioPolicyMetadata(mTestDevice)).isEqualTo(policy);
} else {
assertThat(mDatabaseManager.getAudioPolicyMetadata(mTestDevice)).isNull();
return;
@@ -1790,7 +1781,7 @@ public final class DatabaseManagerTest {
// Check whether the value is saved in database
restartDatabaseManagerHelper();
- Assert.assertEquals(policy, mDatabaseManager.getAudioPolicyMetadata(mTestDevice));
+ assertThat(mDatabaseManager.getAudioPolicyMetadata(mTestDevice)).isEqualTo(policy);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
@@ -1815,23 +1806,20 @@ public final class DatabaseManagerTest {
groupDevices.add(mTestDevice);
groupDevices.add(mTestDevice2);
- Assert.assertEquals(
- expectedSetResult,
- mDatabaseManager.setPreferredAudioProfiles(groupDevices, preferencesToSet));
+ assertThat(mDatabaseManager.setPreferredAudioProfiles(groupDevices, preferencesToSet))
+ .isEqualTo(expectedSetResult);
Bundle testDevicePreferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice);
Bundle testDevice2Preferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice2);
assertThat(testDevicePreferences).isNotNull();
assertThat(testDevice2Preferences).isNotNull();
- Assert.assertEquals(
- expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY),
- testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
- Assert.assertEquals(
- expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX),
- testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
- Assert.assertEquals(
- 0, testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
- Assert.assertEquals(0, testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ assertThat(testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY))
+ .isEqualTo(expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
+ assertThat(testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX))
+ .isEqualTo(expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ assertThat(testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY))
+ .isEqualTo(0);
+ assertThat(testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)).isEqualTo(0);
// Wait for database update
TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper());
@@ -1840,10 +1828,10 @@ public final class DatabaseManagerTest {
// Check number of metadata in the database
if (!stored) {
- Assert.assertEquals(0, list.size());
+ assertThat(list).isEmpty();
return;
}
- Assert.assertEquals(2, list.size());
+ assertThat(list).hasSize(2);
// Check whether the device is in database
restartDatabaseManagerHelper();
@@ -1852,15 +1840,13 @@ public final class DatabaseManagerTest {
assertThat(testDevicePreferences).isNotNull();
assertThat(testDevice2Preferences).isNotNull();
- Assert.assertEquals(
- expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY),
- testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
- Assert.assertEquals(
- expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX),
- testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
- Assert.assertEquals(
- 0, testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
- Assert.assertEquals(0, testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ assertThat(testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY))
+ .isEqualTo(expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
+ assertThat(testDevicePreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX))
+ .isEqualTo(expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ assertThat(testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY))
+ .isEqualTo(0);
+ assertThat(testDevice2Preferences.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)).isEqualTo(0);
mDatabaseManager.factoryReset();
mDatabaseManager.mMetadataCache.clear();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
index 8502c240d1..a2b46c9aa2 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
@@ -43,7 +43,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
-import org.hamcrest.core.IsInstanceOf;
import org.junit.*;
import org.junit.Rule;
import org.junit.runner.RunWith;
@@ -112,7 +111,7 @@ public class CsipSetCoordinatorStateMachineTest {
/** Test that default state is disconnected */
@Test
public void testDefaultDisconnectedState() {
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
}
/**
@@ -140,9 +139,8 @@ public class CsipSetCoordinatorStateMachineTest {
// Verify that no connection state broadcast is executed
verify(mService, after(TIMEOUT_MS).never()).sendBroadcast(any(Intent.class), anyString());
// Check that we are in Disconnected state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Disconnected.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Disconnected.class);
}
/** Test that an incoming connection with policy allowing connection is accepted */
@@ -162,14 +160,11 @@ public class CsipSetCoordinatorStateMachineTest {
ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(TIMEOUT_MS).times(1))
.sendBroadcast(intentArgument1.capture(), anyString());
- Assert.assertEquals(
- STATE_CONNECTING,
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(STATE_CONNECTING);
- // Check that we are in Connecting state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Connecting.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Connecting.class);
// Send a message to trigger connection completed
CsipSetCoordinatorStackEvent connCompletedEvent =
@@ -185,10 +180,9 @@ public class CsipSetCoordinatorStateMachineTest {
ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(TIMEOUT_MS).times(2))
.sendBroadcast(intentArgument2.capture(), anyString());
- // Check that we are in Connected state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Connected.class));
+
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Connected.class);
}
/** Test that an outgoing connection times out */
@@ -205,27 +199,21 @@ public class CsipSetCoordinatorStateMachineTest {
ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(TIMEOUT_MS).times(1))
.sendBroadcast(intentArgument1.capture(), anyString());
- Assert.assertEquals(
- STATE_CONNECTING,
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(STATE_CONNECTING);
- // Check that we are in Connecting state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Connecting.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Connecting.class);
// Verify that one connection state broadcast is executed
ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(CsipSetCoordinatorStateMachine.sConnectTimeoutMs * 2L).times(2))
.sendBroadcast(intentArgument2.capture(), anyString());
- Assert.assertEquals(
- STATE_DISCONNECTED,
- intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(STATE_DISCONNECTED);
- // Check that we are in Disconnected state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Disconnected.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Disconnected.class);
verify(mNativeInterface).disconnect(eq(mTestDevice));
}
@@ -248,33 +236,27 @@ public class CsipSetCoordinatorStateMachineTest {
ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(TIMEOUT_MS).times(1))
.sendBroadcast(intentArgument1.capture(), anyString());
- Assert.assertEquals(
- STATE_CONNECTING,
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(STATE_CONNECTING);
- // Check that we are in Connecting state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Connecting.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Connecting.class);
// Verify that one connection state broadcast is executed
ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
verify(mService, timeout(CsipSetCoordinatorStateMachine.sConnectTimeoutMs * 2L).times(2))
.sendBroadcast(intentArgument2.capture(), anyString());
- Assert.assertEquals(
- STATE_DISCONNECTED,
- intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ assertThat(intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
+ .isEqualTo(STATE_DISCONNECTED);
- // Check that we are in Disconnected state
- Assert.assertThat(
- mStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Disconnected.class));
+ assertThat(mStateMachine.getCurrentState())
+ .isInstanceOf(CsipSetCoordinatorStateMachine.Disconnected.class);
verify(mNativeInterface).disconnect(eq(mTestDevice));
}
@Test
public void testGetDevice() {
- Assert.assertEquals(mTestDevice, mStateMachine.getDevice());
+ assertThat(mStateMachine.getDevice()).isEqualTo(mTestDevice);
}
@Test
@@ -294,7 +276,7 @@ public class CsipSetCoordinatorStateMachineTest {
public void testProcessDisconnectMessage_onDisconnectedState() {
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.DISCONNECT);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
}
@Test
@@ -302,12 +284,12 @@ public class CsipSetCoordinatorStateMachineTest {
allowConnection(false);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
allowConnection(false);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
allowConnection(true);
doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class));
@@ -323,7 +305,7 @@ public class CsipSetCoordinatorStateMachineTest {
CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
event =
new CsipSetCoordinatorStackEvent(
@@ -331,7 +313,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTED;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
event =
new CsipSetCoordinatorStackEvent(
@@ -339,7 +321,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
verify(mNativeInterface).disconnect(mTestDevice);
Mockito.clearInvocations(mNativeInterface);
@@ -349,7 +331,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
verify(mNativeInterface).disconnect(mTestDevice);
event =
@@ -358,7 +340,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
event =
new CsipSetCoordinatorStackEvent(
@@ -366,7 +348,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = -1;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
}
@Test
@@ -424,7 +406,7 @@ public class CsipSetCoordinatorStateMachineTest {
CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTING);
event =
new CsipSetCoordinatorStackEvent(
@@ -432,7 +414,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTING);
event =
new CsipSetCoordinatorStackEvent(
@@ -440,7 +422,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = 10000;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTING);
}
@Test
@@ -484,7 +466,7 @@ public class CsipSetCoordinatorStateMachineTest {
initToConnectedState();
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTED);
}
@Test
@@ -511,7 +493,7 @@ public class CsipSetCoordinatorStateMachineTest {
CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTED);
event =
new CsipSetCoordinatorStackEvent(
@@ -519,7 +501,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTED);
}
@Test
@@ -577,7 +559,7 @@ public class CsipSetCoordinatorStateMachineTest {
CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTING);
allowConnection(false);
event =
@@ -603,7 +585,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTING);
event =
new CsipSetCoordinatorStackEvent(
@@ -611,7 +593,7 @@ public class CsipSetCoordinatorStateMachineTest {
event.valueInt1 = 10000;
mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+ assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTING);
}
@Test
@@ -706,7 +688,7 @@ public class CsipSetCoordinatorStateMachineTest {
mStateMachine.sendMessage(msg);
// Verify that one connection state broadcast is executed
verify(mService, timeout(TIMEOUT_MS)).sendBroadcast(any(Intent.class), anyString());
- Assert.assertThat(mStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type));
+ assertThat(mStateMachine.getCurrentState()).isInstanceOf(type);
}
public static class CsipSetCoordinatorStateMachineWrapper
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java
new file mode 100644
index 0000000000..1eca1e2483
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.content.AttributionSource;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.btservice.AdapterService;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test cases for {@link AdvertiseBinder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdvertiseBinderTest {
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock private AdapterService mAdapterService;
+ @Mock private AdvertiseManager mAdvertiseManager;
+
+ private final AttributionSource mAttributionSource =
+ BluetoothAdapter.getDefaultAdapter().getAttributionSource();
+ private AdvertiseBinder mBinder;
+
+ @Before
+ public void setUp() {
+ doAnswer(
+ invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ })
+ .when(mAdvertiseManager)
+ .doOnAdvertiseThread(any());
+ mBinder = new AdvertiseBinder(mAdapterService, mAdvertiseManager);
+ }
+
+ @Test
+ public void startAdvertisingSet() {
+ AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
+ AdvertiseData advertiseData = new AdvertiseData.Builder().build();
+ AdvertiseData scanResponse = new AdvertiseData.Builder().build();
+ PeriodicAdvertisingParameters periodicParameters =
+ new PeriodicAdvertisingParameters.Builder().build();
+ AdvertiseData periodicData = new AdvertiseData.Builder().build();
+ int duration = 1;
+ int maxExtAdvEvents = 2;
+ int serverIf = 3;
+ IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class);
+
+ mBinder.startAdvertisingSet(
+ parameters,
+ advertiseData,
+ scanResponse,
+ periodicParameters,
+ periodicData,
+ duration,
+ maxExtAdvEvents,
+ serverIf,
+ callback,
+ mAttributionSource);
+
+ verify(mAdvertiseManager)
+ .startAdvertisingSet(
+ parameters,
+ advertiseData,
+ scanResponse,
+ periodicParameters,
+ periodicData,
+ duration,
+ maxExtAdvEvents,
+ serverIf,
+ callback,
+ mAttributionSource);
+ }
+
+ @Test
+ public void stopAdvertisingSet() {
+ IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class);
+
+ mBinder.stopAdvertisingSet(callback, mAttributionSource);
+
+ verify(mAdvertiseManager).stopAdvertisingSet(callback);
+ }
+
+ @Test
+ public void setAdvertisingData() {
+ int advertiserId = 1;
+ AdvertiseData data = new AdvertiseData.Builder().build();
+
+ mBinder.setAdvertisingData(advertiserId, data, mAttributionSource);
+ verify(mAdvertiseManager).setAdvertisingData(advertiserId, data);
+ }
+
+ @Test
+ public void setAdvertisingParameters() {
+ int advertiserId = 1;
+ AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
+
+ mBinder.setAdvertisingParameters(advertiserId, parameters, mAttributionSource);
+ verify(mAdvertiseManager).setAdvertisingParameters(advertiserId, parameters);
+ }
+
+ @Test
+ public void setPeriodicAdvertisingData() {
+ int advertiserId = 1;
+ AdvertiseData data = new AdvertiseData.Builder().build();
+
+ mBinder.setPeriodicAdvertisingData(advertiserId, data, mAttributionSource);
+ verify(mAdvertiseManager).setPeriodicAdvertisingData(advertiserId, data);
+ }
+
+ @Test
+ public void setPeriodicAdvertisingEnable() {
+ int advertiserId = 1;
+ boolean enable = true;
+
+ mBinder.setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource);
+ verify(mAdvertiseManager).setPeriodicAdvertisingEnable(advertiserId, enable);
+ }
+
+ @Test
+ public void setPeriodicAdvertisingParameters() {
+ int advertiserId = 1;
+ PeriodicAdvertisingParameters parameters =
+ new PeriodicAdvertisingParameters.Builder().build();
+
+ mBinder.setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource);
+ verify(mAdvertiseManager).setPeriodicAdvertisingParameters(advertiserId, parameters);
+ }
+
+ @Test
+ public void setScanResponseData() {
+ int advertiserId = 1;
+ AdvertiseData data = new AdvertiseData.Builder().build();
+
+ mBinder.setScanResponseData(advertiserId, data, mAttributionSource);
+ verify(mAdvertiseManager).setScanResponseData(advertiserId, data);
+ }
+
+ @Test
+ public void getOwnAddress() {
+ int advertiserId = 1;
+
+ mBinder.getOwnAddress(advertiserId, mAttributionSource);
+ verify(mAdvertiseManager).getOwnAddress(advertiserId);
+ }
+
+ @Test
+ public void enableAdvertisingSet() {
+ int advertiserId = 1;
+ boolean enable = true;
+ int duration = 3;
+ int maxExtAdvEvents = 4;
+
+ mBinder.enableAdvertisingSet(
+ advertiserId, enable, duration, maxExtAdvEvents, mAttributionSource);
+ verify(mAdvertiseManager)
+ .enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents);
+ }
+
+ @Test
+ public void cleanUp_doesNotCrash() {
+ mBinder.cleanup();
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
index 6621a43331..4cd5423ce9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java
@@ -27,15 +27,16 @@ import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.IAdvertisingSetCallback;
import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.os.IBinder;
+import android.os.test.TestLooper;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.flags.Flags;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -44,17 +45,21 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.util.List;
+
/** Test cases for {@link AdvertiseManager}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class AdvertiseManagerTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule;
@Mock private AdapterService mAdapterService;
- @Mock private GattService mService;
-
@Mock private AdvertiserMap mAdvertiserMap;
@Mock private AdvertiseManagerNativeInterface mNativeInterface;
@@ -66,10 +71,23 @@ public class AdvertiseManagerTest {
private AdvertiseManager mAdvertiseManager;
private int mAdvertiserId;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(Flags.FLAG_ADVERTISE_THREAD);
+ }
+
+ public AdvertiseManagerTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() throws Exception {
- TestUtils.setAdapterService(mAdapterService);
- mAdvertiseManager = new AdvertiseManager(mService, mNativeInterface, mAdvertiserMap);
+ mAdvertiseManager =
+ new AdvertiseManager(
+ mAdapterService,
+ new TestLooper().getLooper(),
+ mNativeInterface,
+ mAdvertiserMap);
AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
AdvertiseData advertiseData = new AdvertiseData.Builder().build();
@@ -95,12 +113,7 @@ public class AdvertiseManagerTest {
mCallback,
InstrumentationRegistry.getTargetContext().getAttributionSource());
- mAdvertiserId = AdvertiseManager.sTempRegistrationId;
- }
-
- @After
- public void tearDown() throws Exception {
- TestUtils.clearAdapterService(mAdapterService);
+ mAdvertiserId = mAdvertiseManager.mTempRegistrationId;
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java
index cb3aa0dc88..cef16ca6ca 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java
@@ -102,7 +102,7 @@ public class AppAdvertiseStatsTest {
AppAdvertiseStats appAdvertiseStats =
new AppAdvertiseStats(appUid, id, name, mAttributionSource);
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(0);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).isEmpty();
int duration = 1;
int maxExtAdvEvents = 2;
@@ -129,7 +129,7 @@ public class AppAdvertiseStatsTest {
int numOfExpectedRecords = 2;
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(numOfExpectedRecords);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).hasSize(numOfExpectedRecords);
}
@Test
@@ -145,7 +145,7 @@ public class AppAdvertiseStatsTest {
int maxExtAdvEvents = 2;
int instanceCount = 3;
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(0);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).isEmpty();
appAdvertiseStats.recordAdvertiseStart(duration, maxExtAdvEvents, instanceCount);
@@ -170,7 +170,7 @@ public class AppAdvertiseStatsTest {
int numOfExpectedRecords = 2;
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(numOfExpectedRecords);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).hasSize(numOfExpectedRecords);
}
@Test
@@ -186,14 +186,14 @@ public class AppAdvertiseStatsTest {
int maxExtAdvEvents = 2;
int instanceCount = 3;
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(0);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).isEmpty();
appAdvertiseStats.enableAdvertisingSet(true, duration, maxExtAdvEvents, instanceCount);
appAdvertiseStats.enableAdvertisingSet(false, duration, maxExtAdvEvents, instanceCount);
int numOfExpectedRecords = 1;
- assertThat(appAdvertiseStats.mAdvertiserRecords.size()).isEqualTo(numOfExpectedRecords);
+ assertThat(appAdvertiseStats.mAdvertiserRecords).hasSize(numOfExpectedRecords);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
index e0fb8671df..151f607c1e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
@@ -140,15 +140,15 @@ public class ContextMapTest {
@Test
public void removeMethods() {
ContextMap<IBluetoothGattCallback> contextMap = getMapWithAppAndConnection();
- contextMap.remove(APP_ID1);
+ contextMap.remove(APP_ID1, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
assertThat(contextMap.getAllAppsIds()).isNotEmpty();
- contextMap.remove(APP_ID2);
+ contextMap.remove(APP_ID2, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
assertThat(contextMap.getAllAppsIds()).isEmpty();
contextMap = getMapWithAppAndConnection();
- contextMap.remove(RANDOM_UUID1);
+ contextMap.remove(RANDOM_UUID1, ContextMap.RemoveReason.REASON_REGISTER_FAILED);
assertThat(contextMap.getAllAppsIds()).isNotEmpty();
- contextMap.remove(RANDOM_UUID2);
+ contextMap.remove(RANDOM_UUID2, ContextMap.RemoveReason.REASON_REGISTER_FAILED);
assertThat(contextMap.getAllAppsIds()).isEmpty();
contextMap = getMapWithAppAndConnection();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java
new file mode 100644
index 0000000000..3ed9b80092
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.DistanceMeasurementMethod;
+import android.bluetooth.le.DistanceMeasurementParams;
+import android.bluetooth.le.IDistanceMeasurementCallback;
+import android.content.AttributionSource;
+import android.os.ParcelUuid;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.btservice.AdapterService;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.UUID;
+
+/** Test cases for {@link DistanceMeasurementBinder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DistanceMeasurementBinderTest {
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock private DistanceMeasurementManager mDistanceMeasurementManager;
+ @Mock private AdapterService mAdapterService;
+
+ private final AttributionSource mAttributionSource =
+ BluetoothAdapter.getDefaultAdapter().getAttributionSource();
+
+ private DistanceMeasurementBinder mBinder;
+
+ @Before
+ public void setUp() {
+ mBinder = new DistanceMeasurementBinder(mAdapterService, mDistanceMeasurementManager);
+ when(mDistanceMeasurementManager.getSupportedDistanceMeasurementMethods())
+ .thenReturn(new DistanceMeasurementMethod[0]);
+ }
+
+ @Test
+ public void getSupportedDistanceMeasurementMethods() {
+ mBinder.getSupportedDistanceMeasurementMethods(mAttributionSource);
+ verify(mDistanceMeasurementManager).getSupportedDistanceMeasurementMethods();
+ }
+
+ @Test
+ public void startDistanceMeasurement() {
+ UUID uuid = UUID.randomUUID();
+ BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
+ DistanceMeasurementParams params =
+ new DistanceMeasurementParams.Builder(device)
+ .setDurationSeconds(123)
+ .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW)
+ .build();
+ IDistanceMeasurementCallback callback = mock(IDistanceMeasurementCallback.class);
+ mBinder.startDistanceMeasurement(
+ new ParcelUuid(uuid), params, callback, mAttributionSource);
+ verify(mDistanceMeasurementManager).startDistanceMeasurement(uuid, params, callback);
+ }
+
+ @Test
+ public void stopDistanceMeasurement() {
+ UUID uuid = UUID.randomUUID();
+ BluetoothDevice device =
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
+ int method = DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI;
+ mBinder.stopDistanceMeasurement(new ParcelUuid(uuid), device, method, mAttributionSource);
+ verify(mDistanceMeasurementManager).stopDistanceMeasurement(uuid, device, method, false);
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
index 2b34af7690..2fb7a5e8b6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
@@ -210,18 +210,29 @@ public class DistanceMeasurementManagerTest {
IDENTITY_ADDRESS,
100,
0,
+ 100,
0,
+ 45,
0,
- 0,
- 0,
- 0,
+ 10000,
1,
+ /* delayedSpreadMeters= */ 10.0,
+ /* detectedAttackLevel= */ DistanceMeasurementResult.NADM_ATTACK_IS_POSSIBLE,
+ /* velocityMetersPerSecond= */ 1.0,
DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING);
ArgumentCaptor<DistanceMeasurementResult> result =
ArgumentCaptor.forClass(DistanceMeasurementResult.class);
verify(mCallback).onResult(eq(mDevice), result.capture());
assertThat(result.getValue().getResultMeters()).isEqualTo(1.00);
+ assertThat(result.getValue().getAzimuthAngle()).isEqualTo(100);
+ assertThat(result.getValue().getAltitudeAngle()).isEqualTo(45);
+ assertThat(result.getValue().getMeasurementTimestampNanos()).isEqualTo(10000);
+ assertThat(result.getValue().getConfidenceLevel()).isEqualTo(0.01);
+ assertThat(result.getValue().getDelaySpreadMeters()).isEqualTo(10.0);
+ assertThat(result.getValue().getDetectedAttackLevel())
+ .isEqualTo(DistanceMeasurementResult.NADM_ATTACK_IS_POSSIBLE);
+ assertThat(result.getValue().getVelocityMetersPerSecond()).isEqualTo(1.0);
}
@Test
@@ -267,6 +278,9 @@ public class DistanceMeasurementManagerTest {
-1,
1000L,
-1,
+ /* delayedSpreadMeters= */ 10.0,
+ /* detectedAttackLevel= */ DistanceMeasurementResult.NADM_ATTACK_IS_POSSIBLE,
+ /* velocityMetersPerSecond= */ 0.0,
DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI);
ArgumentCaptor<DistanceMeasurementResult> result =
ArgumentCaptor.forClass(DistanceMeasurementResult.class);
@@ -278,6 +292,10 @@ public class DistanceMeasurementManagerTest {
assertThat(result.getValue().getAltitudeAngle()).isEqualTo(Double.NaN);
assertThat(result.getValue().getErrorAltitudeAngle()).isEqualTo(Double.NaN);
assertThat(result.getValue().getMeasurementTimestampNanos()).isEqualTo(1000L);
+ assertThat(result.getValue().getDelaySpreadMeters()).isEqualTo(Double.NaN);
+ assertThat(result.getValue().getDetectedAttackLevel())
+ .isEqualTo(DistanceMeasurementResult.NADM_UNKNOWN);
+ assertThat(result.getValue().getVelocityMetersPerSecond()).isEqualTo(Double.NaN);
}
@Test
@@ -306,6 +324,9 @@ public class DistanceMeasurementManagerTest {
-1,
1000L,
-1,
+ /* delayedSpreadMeters= */ 10.0,
+ /* detectedAttackLevel= */ DistanceMeasurementResult.NADM_ATTACK_IS_POSSIBLE,
+ /* velocityMetersPerSecond= */ 0.0,
DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI);
DistanceMeasurementResult result =
new DistanceMeasurementResult.Builder(1.00, 1.00).build();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
index 0e5bfaac94..3d749abb73 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java
@@ -25,10 +25,6 @@ import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertisingSetParameters;
-import android.bluetooth.le.IAdvertisingSetCallback;
-import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.content.AttributionSource;
import android.os.ParcelUuid;
@@ -92,7 +88,11 @@ public class GattServiceBinderTest {
mBinder.unregisterClient(clientIf, mAttributionSource);
- verify(mService).unregisterClient(clientIf, mAttributionSource);
+ verify(mService)
+ .unregisterClient(
+ clientIf,
+ mAttributionSource,
+ ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
}
@Test
@@ -515,140 +515,6 @@ public class GattServiceBinderTest {
}
@Test
- public void startAdvertisingSet() throws Exception {
- AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
- AdvertiseData advertiseData = new AdvertiseData.Builder().build();
- AdvertiseData scanResponse = new AdvertiseData.Builder().build();
- PeriodicAdvertisingParameters periodicParameters =
- new PeriodicAdvertisingParameters.Builder().build();
- AdvertiseData periodicData = new AdvertiseData.Builder().build();
- int duration = 1;
- int maxExtAdvEvents = 2;
- int serverIf = 3;
- IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class);
-
- mBinder.startAdvertisingSet(
- parameters,
- advertiseData,
- scanResponse,
- periodicParameters,
- periodicData,
- duration,
- maxExtAdvEvents,
- serverIf,
- callback,
- mAttributionSource);
-
- verify(mService)
- .startAdvertisingSet(
- parameters,
- advertiseData,
- scanResponse,
- periodicParameters,
- periodicData,
- duration,
- maxExtAdvEvents,
- serverIf,
- callback,
- mAttributionSource);
- }
-
- @Test
- public void stopAdvertisingSet() throws Exception {
- IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class);
-
- mBinder.stopAdvertisingSet(callback, mAttributionSource);
-
- verify(mService).stopAdvertisingSet(callback, mAttributionSource);
- }
-
- @Test
- public void getOwnAddress() throws Exception {
- int advertiserId = 1;
-
- mBinder.getOwnAddress(advertiserId, mAttributionSource);
-
- verify(mService).getOwnAddress(advertiserId, mAttributionSource);
- }
-
- @Test
- public void enableAdvertisingSet() throws Exception {
- int advertiserId = 1;
- boolean enable = true;
- int duration = 3;
- int maxExtAdvEvents = 4;
-
- mBinder.enableAdvertisingSet(
- advertiserId, enable, duration, maxExtAdvEvents, mAttributionSource);
-
- verify(mService)
- .enableAdvertisingSet(
- advertiserId, enable, duration, maxExtAdvEvents, mAttributionSource);
- }
-
- @Test
- public void setAdvertisingData() throws Exception {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
-
- mBinder.setAdvertisingData(advertiserId, data, mAttributionSource);
-
- verify(mService).setAdvertisingData(advertiserId, data, mAttributionSource);
- }
-
- @Test
- public void setScanResponseData() throws Exception {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
-
- mBinder.setScanResponseData(advertiserId, data, mAttributionSource);
-
- verify(mService).setScanResponseData(advertiserId, data, mAttributionSource);
- }
-
- @Test
- public void setAdvertisingParameters() throws Exception {
- int advertiserId = 1;
- AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
-
- mBinder.setAdvertisingParameters(advertiserId, parameters, mAttributionSource);
-
- verify(mService).setAdvertisingParameters(advertiserId, parameters, mAttributionSource);
- }
-
- @Test
- public void setPeriodicAdvertisingParameters() throws Exception {
- int advertiserId = 1;
- PeriodicAdvertisingParameters parameters =
- new PeriodicAdvertisingParameters.Builder().build();
-
- mBinder.setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource);
-
- verify(mService)
- .setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource);
- }
-
- @Test
- public void setPeriodicAdvertisingData() throws Exception {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
-
- mBinder.setPeriodicAdvertisingData(advertiserId, data, mAttributionSource);
-
- verify(mService).setPeriodicAdvertisingData(advertiserId, data, mAttributionSource);
- }
-
- @Test
- public void setPeriodicAdvertisingEnable() throws Exception {
- int advertiserId = 1;
- boolean enable = true;
-
- mBinder.setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource);
-
- verify(mService).setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource);
- }
-
- @Test
public void disconnectAll() throws Exception {
mBinder.disconnectAll(mAttributionSource);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
index 04a46a96b3..2ff148fa41 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
@@ -30,18 +30,13 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertisingSetParameters;
-import android.bluetooth.le.DistanceMeasurementMethod;
-import android.bluetooth.le.DistanceMeasurementParams;
-import android.bluetooth.le.IDistanceMeasurementCallback;
-import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
import android.content.Context;
import android.content.res.Resources;
import android.location.LocationManager;
import android.os.Bundle;
+import android.os.Process;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.test.mock.MockContentProvider;
@@ -55,6 +50,7 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.CompanionManager;
import com.android.bluetooth.flags.Flags;
+import com.android.bluetooth.le_scan.PeriodicScanManager;
import com.android.bluetooth.le_scan.ScanManager;
import com.android.bluetooth.le_scan.ScanObjectsFactory;
@@ -84,6 +80,7 @@ public class GattServiceTest {
@Mock private ContextMap<IBluetoothGattCallback> mClientMap;
@Mock private ScanManager mScanManager;
+ @Mock private PeriodicScanManager mPeriodicScanManager;
@Mock private Set<String> mReliableQueue;
@Mock private ContextMap<IBluetoothGattServerCallback> mServerMap;
@Mock private DistanceMeasurementManager mDistanceMeasurementManager;
@@ -130,6 +127,7 @@ public class GattServiceTest {
doReturn(mScanManager)
.when(mScanObjectsFactory)
.createScanManager(any(), any(), any(), any());
+ doReturn(mPeriodicScanManager).when(mScanObjectsFactory).createPeriodicScanManager(any());
doReturn(mContext.getPackageManager()).when(mAdapterService).getPackageManager();
doReturn(mContext.getSharedPreferences("GattServiceTestPrefs", Context.MODE_PRIVATE))
.when(mAdapterService)
@@ -272,66 +270,129 @@ public class GattServiceTest {
}
@Test
- public void disconnectAll() {
- Map<Integer, String> connMap = new HashMap<>();
+ public void clientConnectOverLeFailed() throws Exception {
int clientIf = 1;
- String address = "02:00:00:00:00:00";
- connMap.put(clientIf, address);
- doReturn(connMap).when(mClientMap).getConnectedMap();
- Integer connId = 1;
- doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+ String address = REMOTE_DEVICE_ADDRESS;
+ int addressType = BluetoothDevice.ADDRESS_TYPE_RANDOM;
+ boolean isDirect = true;
+ int transport = BluetoothDevice.TRANSPORT_LE;
+ boolean opportunistic = false;
+ int phy = 3;
- mService.disconnectAll(mAttributionSource);
- verify(mNativeInterface).gattClientDisconnect(clientIf, address, connId);
- }
+ AttributionSource testAttributeSource =
+ new AttributionSource.Builder(Process.SYSTEM_UID)
+ .setPid(Process.myPid())
+ .setDeviceId(Context.DEVICE_ID_DEFAULT)
+ .setPackageName("com.google.android.gms")
+ .setAttributionTag("com.google.android.gms.findmydevice")
+ .build();
- @Test
- public void setAdvertisingData() {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
+ mService.clientConnect(
+ clientIf,
+ address,
+ addressType,
+ isDirect,
+ transport,
+ opportunistic,
+ phy,
+ testAttributeSource);
- mService.setAdvertisingData(advertiserId, data, mAttributionSource);
+ verify(mAdapterService).notifyDirectLeGattClientConnect(anyInt(), any());
+ verify(mNativeInterface)
+ .gattClientConnect(
+ clientIf, address, addressType, isDirect, transport, opportunistic, phy, 0);
+ mService.onConnected(clientIf, 0, BluetoothGatt.GATT_CONNECTION_TIMEOUT, address);
+ verify(mAdapterService).notifyGattClientConnectFailed(anyInt(), any());
}
@Test
- public void setAdvertisingParameters() {
- int advertiserId = 1;
- AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
+ public void clientConnectDisconnectOverLe() throws Exception {
+ int clientIf = 1;
+ String address = REMOTE_DEVICE_ADDRESS;
+ int addressType = BluetoothDevice.ADDRESS_TYPE_RANDOM;
+ boolean isDirect = true;
+ int transport = BluetoothDevice.TRANSPORT_LE;
+ boolean opportunistic = false;
+ int phy = 3;
- mService.setAdvertisingParameters(advertiserId, parameters, mAttributionSource);
- }
+ AttributionSource testAttributeSource =
+ new AttributionSource.Builder(Process.SYSTEM_UID)
+ .setPid(Process.myPid())
+ .setDeviceId(Context.DEVICE_ID_DEFAULT)
+ .setPackageName("com.google.android.gms")
+ .setAttributionTag("com.google.android.gms.findmydevice")
+ .build();
- @Test
- public void setPeriodicAdvertisingData() {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
+ mService.clientConnect(
+ clientIf,
+ address,
+ addressType,
+ isDirect,
+ transport,
+ opportunistic,
+ phy,
+ testAttributeSource);
+
+ verify(mAdapterService).notifyDirectLeGattClientConnect(anyInt(), any());
+ verify(mNativeInterface)
+ .gattClientConnect(
+ clientIf, address, addressType, isDirect, transport, opportunistic, phy, 0);
+ mService.onConnected(clientIf, 15, BluetoothGatt.GATT_SUCCESS, address);
+ mService.clientDisconnect(clientIf, address, mAttributionSource);
- mService.setPeriodicAdvertisingData(advertiserId, data, mAttributionSource);
+ verify(mAdapterService).notifyGattClientDisconnect(anyInt(), any());
}
@Test
- public void setPeriodicAdvertisingEnable() {
- int advertiserId = 1;
- boolean enable = true;
+ public void clientConnectOverLeDisconnectedByRemote() throws Exception {
+ int clientIf = 1;
+ String address = REMOTE_DEVICE_ADDRESS;
+ int addressType = BluetoothDevice.ADDRESS_TYPE_RANDOM;
+ boolean isDirect = true;
+ int transport = BluetoothDevice.TRANSPORT_LE;
+ boolean opportunistic = false;
+ int phy = 3;
- mService.setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource);
- }
+ AttributionSource testAttributeSource =
+ new AttributionSource.Builder(Process.SYSTEM_UID)
+ .setPid(Process.myPid())
+ .setDeviceId(Context.DEVICE_ID_DEFAULT)
+ .setPackageName("com.google.android.gms")
+ .setAttributionTag("com.google.android.gms.findmydevice")
+ .build();
- @Test
- public void setPeriodicAdvertisingParameters() {
- int advertiserId = 1;
- PeriodicAdvertisingParameters parameters =
- new PeriodicAdvertisingParameters.Builder().build();
+ mService.clientConnect(
+ clientIf,
+ address,
+ addressType,
+ isDirect,
+ transport,
+ opportunistic,
+ phy,
+ testAttributeSource);
- mService.setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource);
+ verify(mAdapterService).notifyDirectLeGattClientConnect(anyInt(), any());
+ verify(mNativeInterface)
+ .gattClientConnect(
+ clientIf, address, addressType, isDirect, transport, opportunistic, phy, 0);
+ mService.onConnected(clientIf, 15, BluetoothGatt.GATT_SUCCESS, address);
+ mService.onDisconnected(clientIf, 15, 1, address);
+
+ verify(mAdapterService).notifyGattClientDisconnect(anyInt(), any());
}
@Test
- public void setScanResponseData() {
- int advertiserId = 1;
- AdvertiseData data = new AdvertiseData.Builder().build();
+ public void disconnectAll() {
+ Map<Integer, String> connMap = new HashMap<>();
+ int clientIf = 1;
+ String address = "02:00:00:00:00:00";
+ connMap.put(clientIf, address);
+ doReturn(connMap).when(mClientMap).getConnectedMap();
+ Integer connId = 1;
+ doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
- mService.setScanResponseData(advertiserId, data, mAttributionSource);
+ mService.disconnectAll(mAttributionSource);
+ verify(mNativeInterface).gattClientDisconnect(clientIf, address, connId);
}
@Test
@@ -351,7 +412,7 @@ public class GattServiceTest {
mService.getDevicesMatchingConnectionStates(states, mAttributionSource);
int expectedSize = 1;
- assertThat(deviceList.size()).isEqualTo(expectedSize);
+ assertThat(deviceList).hasSize(expectedSize);
BluetoothDevice bluetoothDevice = deviceList.get(0);
assertThat(bluetoothDevice.getAddress()).isEqualTo(address);
@@ -366,7 +427,10 @@ public class GattServiceTest {
mService.registerClient(uuid, callback, eattSupport, mAttributionSource);
verify(mNativeInterface)
.gattClientRegisterApp(
- uuid.getLeastSignificantBits(), uuid.getMostSignificantBits(), eattSupport);
+ uuid.getLeastSignificantBits(),
+ uuid.getMostSignificantBits(),
+ mAttributionSource.getPackageName(),
+ eattSupport);
}
@Test
@@ -378,15 +442,17 @@ public class GattServiceTest {
mService.registerClient(uuid, callback, /* eattSupport= */ true, mAttributionSource);
verify(mClientMap, never()).add(any(), any(), any(), any());
- verify(mNativeInterface, never()).gattClientRegisterApp(anyLong(), anyLong(), anyBoolean());
+ verify(mNativeInterface, never())
+ .gattClientRegisterApp(anyLong(), anyLong(), anyString(), anyBoolean());
}
@Test
public void unregisterClient() {
int clientIf = 3;
- mService.unregisterClient(clientIf, mAttributionSource);
- verify(mClientMap).remove(clientIf);
+ mService.unregisterClient(
+ clientIf, mAttributionSource, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
+ verify(mClientMap).remove(clientIf, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT);
verify(mNativeInterface).gattClientUnregisterApp(clientIf);
}
@@ -626,24 +692,6 @@ public class GattServiceTest {
}
@Test
- public void getOwnAddress() throws Exception {
- int advertiserId = 1;
-
- mService.getOwnAddress(advertiserId, mAttributionSource);
- }
-
- @Test
- public void enableAdvertisingSet() throws Exception {
- int advertiserId = 1;
- boolean enable = true;
- int duration = 3;
- int maxExtAdvEvents = 4;
-
- mService.enableAdvertisingSet(
- advertiserId, enable, duration, maxExtAdvEvents, mAttributionSource);
- }
-
- @Test
public void unregAll() throws Exception {
int appId = 1;
List<Integer> appIds = new ArrayList<>();
@@ -651,40 +699,11 @@ public class GattServiceTest {
doReturn(appIds).when(mClientMap).getAllAppsIds();
mService.unregAll(mAttributionSource);
- verify(mClientMap).remove(appId);
+ verify(mClientMap).remove(appId, ContextMap.RemoveReason.REASON_UNREGISTER_ALL);
verify(mNativeInterface).gattClientUnregisterApp(appId);
}
@Test
- public void getSupportedDistanceMeasurementMethods() {
- mService.getSupportedDistanceMeasurementMethods();
- verify(mDistanceMeasurementManager).getSupportedDistanceMeasurementMethods();
- }
-
- @Test
- public void startDistanceMeasurement() {
- UUID uuid = UUID.randomUUID();
- BluetoothDevice device = mAdapter.getRemoteDevice("00:01:02:03:04:05");
- DistanceMeasurementParams params =
- new DistanceMeasurementParams.Builder(device)
- .setDurationSeconds(123)
- .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW)
- .build();
- IDistanceMeasurementCallback callback = mock(IDistanceMeasurementCallback.class);
- mService.startDistanceMeasurement(uuid, params, callback);
- verify(mDistanceMeasurementManager).startDistanceMeasurement(uuid, params, callback);
- }
-
- @Test
- public void stopDistanceMeasurement() {
- UUID uuid = UUID.randomUUID();
- BluetoothDevice device = mAdapter.getRemoteDevice("00:01:02:03:04:05");
- int method = DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI;
- mService.stopDistanceMeasurement(uuid, device, method);
- verify(mDistanceMeasurementManager).stopDistanceMeasurement(uuid, device, method, false);
- }
-
- @Test
public void cleanUp_doesNotCrash() {
mService.cleanup();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
index 81c4865b84..815f4c7de9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
@@ -513,7 +513,7 @@ public class HapClientServiceTest {
eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST));
List<BluetoothHapPresetInfo> presets = presetsCaptor.getValue();
- assertThat(presets.size()).isEqualTo(3);
+ assertThat(presets).hasSize(3);
Optional<BluetoothHapPresetInfo> preset =
presetsCaptor.getValue().stream().filter(p -> 0x01 == p.getIndex()).findFirst();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
index a1095a6c59..f14884bcb8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
@@ -18,6 +18,7 @@ package com.android.bluetooth.hfp;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -34,6 +35,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
@@ -58,9 +60,7 @@ import com.android.bluetooth.btservice.SilenceDeviceManager;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
-import org.hamcrest.Matchers;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -71,8 +71,8 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Set;
/** Tests for {@link HeadsetService} */
@@ -192,15 +192,13 @@ public class HeadsetServiceTest {
/** Test to verify that HeadsetService can be successfully started */
@Test
public void testGetHeadsetService() {
- Assert.assertEquals(mHeadsetService, HeadsetService.getHeadsetService());
+ assertThat(HeadsetService.getHeadsetService()).isEqualTo(mHeadsetService);
// Verify default connection and audio states
mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(
- BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
- mHeadsetService.getAudioState(mCurrentDevice));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mHeadsetService.getAudioState(mCurrentDevice))
+ .isEqualTo(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
}
/** Test okToAcceptConnection method using various test cases */
@@ -300,16 +298,13 @@ public class HeadsetServiceTest {
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTING);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING,
- mHeadsetService.getConnectionState(mCurrentDevice));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(
- Collections.singletonList(mCurrentDevice), mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices()).isEqualTo(List.of(mCurrentDevice));
// 2nd connection attempt will fail
assertThat(mHeadsetService.connect(mCurrentDevice)).isFalse();
// Verify makeStateMachine is only called once
@@ -346,11 +341,9 @@ public class HeadsetServiceTest {
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(
- Collections.singletonList(mCurrentDevice), mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices()).isEqualTo(List.of(mCurrentDevice));
// Test disconnect from native
HeadsetStackEvent disconnectEvent =
new HeadsetStackEvent(
@@ -362,10 +355,9 @@ public class HeadsetServiceTest {
.sendMessage(HeadsetStateMachine.STACK_EVENT, disconnectEvent);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_DISCONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(Collections.EMPTY_LIST, mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices()).isEmpty();
}
/**
@@ -405,12 +397,9 @@ public class HeadsetServiceTest {
HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED,
mCurrentDevice);
- try {
- mHeadsetService.messageFromNative(connectingEvent);
- Assert.fail("Expect an IllegalStateException");
- } catch (IllegalStateException exception) {
- // Do nothing
- }
+ assertThrows(
+ IllegalStateException.class,
+ () -> mHeadsetService.messageFromNative(connectingEvent));
verifyNoMoreInteractions(mObjectsFactory);
}
@@ -450,20 +439,17 @@ public class HeadsetServiceTest {
// Put device to connecting
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
}
// Connect the next device will fail
mCurrentDevice = TestUtils.getTestDevice(mAdapter, MAX_HEADSET_CONNECTIONS);
@@ -477,12 +463,10 @@ public class HeadsetServiceTest {
eq(mAdapterService),
eq(mNativeInterface),
eq(mSystemInterface));
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
}
/**
@@ -512,40 +496,37 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(
- Collections.singletonList(mCurrentDevice), mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices()).isEqualTo(List.of(mCurrentDevice));
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED);
// Test connect audio - set the device first as the active device
assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue();
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTING);
// 2nd connection attempt for the same device will succeed as well
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
// Verify CONNECT_AUDIO is only sent once
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(eq(HeadsetStateMachine.CONNECT_AUDIO), any());
// Test disconnect audio
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.disconnectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.disconnectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
// Further disconnection requests will fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED,
- mHeadsetService.disconnectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.disconnectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(eq(HeadsetStateMachine.DISCONNECT_AUDIO), any(BluetoothDevice.class));
}
@@ -590,9 +571,8 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
@@ -600,33 +580,30 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_CONNECTED);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Try to connect audio
// Should fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
- mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE);
// Should succeed after setActiveDevice()
assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue();
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice);
// Put device to audio connecting state
when(mStateMachines.get(mCurrentDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTING);
// 2nd connection attempt will also succeed
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
// Verify CONNECT_AUDIO is only sent once
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(eq(HeadsetStateMachine.CONNECT_AUDIO), any());
@@ -634,16 +611,15 @@ public class HeadsetServiceTest {
when(mStateMachines.get(mCurrentDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
// Disconnect audio
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.disconnectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.disconnectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
// Further disconnection requests will fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED,
- mHeadsetService.disconnectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.disconnectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED);
verify(mStateMachines.get(mCurrentDevice))
.sendMessage(
eq(HeadsetStateMachine.DISCONNECT_AUDIO), any(BluetoothDevice.class));
@@ -691,9 +667,8 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
@@ -705,12 +680,10 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
}
if (MAX_HEADSET_CONNECTIONS >= 2) {
// Try to connect audio
@@ -718,29 +691,27 @@ public class HeadsetServiceTest {
BluetoothDevice secondDevice = connectedDevices.get(1);
// Set the first device as the active device
assertThat(mHeadsetService.setActiveDevice(firstDevice)).isTrue();
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(firstDevice));
+ assertThat(mHeadsetService.connectAudio(firstDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(firstDevice))
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice);
// Put device to audio connecting state
when(mStateMachines.get(firstDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTING);
// 2nd connection attempt will succeed for the same device
- Assert.assertEquals(
- BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(firstDevice));
+ assertThat(mHeadsetService.connectAudio(firstDevice))
+ .isEqualTo(BluetoothStatusCodes.SUCCESS);
// Connect to 2nd device will fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
- mHeadsetService.connectAudio(secondDevice));
+ assertThat(mHeadsetService.connectAudio(secondDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE);
verify(mStateMachines.get(secondDevice), never())
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, secondDevice);
// Put device to audio connected state
when(mStateMachines.get(firstDevice).getAudioState())
.thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
// Connect to 2nd device will fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
- mHeadsetService.connectAudio(secondDevice));
+ assertThat(mHeadsetService.connectAudio(secondDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE);
verify(mStateMachines.get(secondDevice), never())
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, secondDevice);
}
@@ -796,9 +767,8 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
@@ -806,12 +776,10 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_CONNECTING,
@@ -820,7 +788,7 @@ public class HeadsetServiceTest {
// Try to connect audio
BluetoothDevice firstDevice = connectedDevices.get(0);
assertThat(mHeadsetService.setActiveDevice(firstDevice)).isTrue();
- Assert.assertEquals(BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio());
+ assertThat(mHeadsetService.connectAudio()).isEqualTo(BluetoothStatusCodes.SUCCESS);
verify(mStateMachines.get(firstDevice))
.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice);
}
@@ -832,9 +800,8 @@ public class HeadsetServiceTest {
@Test
public void testConnectAudio_deviceNeverConnected() {
mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED,
- mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED);
}
/**
@@ -862,18 +829,16 @@ public class HeadsetServiceTest {
// Put device in disconnected state
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_DISCONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertEquals(Collections.EMPTY_LIST, mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices()).isEmpty();
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
// connectAudio should fail
- Assert.assertEquals(
- BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE,
- mHeadsetService.connectAudio(mCurrentDevice));
+ assertThat(mHeadsetService.connectAudio(mCurrentDevice))
+ .isEqualTo(BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE);
verify(mStateMachines.get(mCurrentDevice), never())
.sendMessage(eq(HeadsetStateMachine.CONNECT_AUDIO), any());
}
@@ -943,9 +908,8 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
@@ -953,12 +917,10 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_CONNECTING,
@@ -1055,9 +1017,8 @@ public class HeadsetServiceTest {
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
// Put device to connected
connectedDevices.add(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
@@ -1065,12 +1026,10 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
- Assert.assertThat(
- mHeadsetService.getConnectedDevices(),
- Matchers.containsInAnyOrder(connectedDevices.toArray()));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetService.getConnectedDevices())
+ .containsExactlyElementsIn(connectedDevices);
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_CONNECTING,
@@ -1118,9 +1077,8 @@ public class HeadsetServiceTest {
when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHeadsetService.getConnectionState(mCurrentDevice));
+ assertThat(mHeadsetService.getConnectionState(mCurrentDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
mHeadsetService.clccResponse(1, 0, 0, 0, false, "8225319000", 0);
// index 0 is the end mark of CLCC response.
mHeadsetService.clccResponse(0, 0, 0, 0, false, "8225319000", 0);
@@ -1185,23 +1143,23 @@ public class HeadsetServiceTest {
// Test whether active device been removed after enable silence mode.
assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue();
- Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isEqualTo(mCurrentDevice);
assertThat(mHeadsetService.setSilenceMode(mCurrentDevice, true)).isTrue();
assertThat(mHeadsetService.getActiveDevice()).isNull();
// Test whether active device been resumed after disable silence mode.
assertThat(mHeadsetService.setSilenceMode(mCurrentDevice, false)).isTrue();
- Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isEqualTo(mCurrentDevice);
// Test that active device should not be changed when silence a non-active device
assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue();
- Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isEqualTo(mCurrentDevice);
assertThat(mHeadsetService.setSilenceMode(otherDevice, true)).isTrue();
- Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isEqualTo(mCurrentDevice);
// Test that active device should not be changed when another device exits silence mode
assertThat(mHeadsetService.setSilenceMode(otherDevice, false)).isTrue();
- Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isEqualTo(mCurrentDevice);
}
/** Test that whether active device been removed after enable silence mode */
@@ -1224,7 +1182,7 @@ public class HeadsetServiceTest {
// Test that active device should not be changed if audio is not allowed
assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isFalse();
- Assert.assertEquals(null, mHeadsetService.getActiveDevice());
+ assertThat(mHeadsetService.getActiveDevice()).isNull();
}
@Test
@@ -1277,6 +1235,27 @@ public class HeadsetServiceTest {
}
@Test
+ public void testGetFallbackCandidates_HasWatchDeviceWithCod() {
+ BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0);
+ BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1);
+
+ // Make deviceWatch as watch with COD
+ when(mAdapterService.getRemoteClass(deviceWatch))
+ .thenReturn(BluetoothClass.Device.WEARABLE_WRIST_WATCH);
+ when(mAdapterService.getRemoteClass(deviceRegular))
+ .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+
+ // Has a connected watch device
+ addConnectedDeviceHelper(deviceWatch);
+ assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)).isEmpty();
+
+ // Two connected devices with one watch
+ addConnectedDeviceHelper(deviceRegular);
+ assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager))
+ .containsExactly(deviceRegular);
+ }
+
+ @Test
public void testConnectDeviceNotAllowedInbandRingPolicy_InbandRingStatus() {
when(mDatabaseManager.getProfileConnectionPolicy(
any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET)))
@@ -1288,8 +1267,7 @@ public class HeadsetServiceTest {
.thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs())
.thenReturn(SystemClock.uptimeMillis());
- Assert.assertEquals(
- Collections.singletonList(mCurrentDevice), mHeadsetService.getConnectedDevices());
+ assertThat(mHeadsetService.getConnectedDevices()).isEqualTo(List.of(mCurrentDevice));
mHeadsetService.onConnectionStateChangedFromStateMachine(
mCurrentDevice,
BluetoothProfile.STATE_DISCONNECTED,
@@ -1304,7 +1282,7 @@ public class HeadsetServiceTest {
BluetoothSinkAudioPolicy.POLICY_ALLOWED)
.setInBandRingtonePolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED)
.build());
- Assert.assertEquals(true, mHeadsetService.isInbandRingingEnabled());
+ assertThat(mHeadsetService.isInbandRingingEnabled()).isTrue();
when(mStateMachines.get(mCurrentDevice).getHfpCallAudioPolicy())
.thenReturn(
@@ -1315,7 +1293,7 @@ public class HeadsetServiceTest {
.setInBandRingtonePolicy(
BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED)
.build());
- Assert.assertEquals(false, mHeadsetService.isInbandRingingEnabled());
+ assertThat(mHeadsetService.isInbandRingingEnabled()).isFalse();
}
private void addConnectedDeviceHelper(BluetoothDevice device) {
@@ -1327,12 +1305,12 @@ public class HeadsetServiceTest {
when(mStateMachines.get(device).getDevice()).thenReturn(device);
when(mStateMachines.get(device).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTING);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING, mHeadsetService.getConnectionState(device));
+ assertThat(mHeadsetService.getConnectionState(device))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
when(mStateMachines.get(mCurrentDevice).getConnectionState())
.thenReturn(BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED, mHeadsetService.getConnectionState(device));
+ assertThat(mHeadsetService.getConnectionState(device))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
assertThat(mHeadsetService.getConnectedDevices()).contains(device);
}
@@ -1349,6 +1327,6 @@ public class HeadsetServiceTest {
doReturn(bondState).when(mAdapterService).getBondState(device);
when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
.thenReturn(priority);
- Assert.assertEquals(expected, mHeadsetService.okToAcceptConnection(device, false));
+ assertThat(mHeadsetService.okToAcceptConnection(device, false)).isEqualTo(expected);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
index 75dfc971a1..0b42a50d92 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
@@ -66,9 +66,7 @@ import com.android.bluetooth.btservice.SilenceDeviceManager;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
-import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -186,22 +184,20 @@ public class HeadsetStateMachineTest {
/** Test that default state is Disconnected */
@Test
public void testDefaultDisconnectedState() {
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED, mHeadsetStateMachine.getConnectionState());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/** Test that state is Connected after calling setUpConnectedState() */
@Test
public void testSetupConnectedState() {
setUpConnectedState();
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED, mHeadsetStateMachine.getConnectionState());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/** Test state transition from Disconnected to Connecting state via CONNECT message */
@@ -219,9 +215,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
}
/**
@@ -246,9 +241,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
}
/**
@@ -273,9 +267,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
}
/**
@@ -296,9 +289,8 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcastAsUser(
any(Intent.class), any(UserHandle.class), anyString(), any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
// Indicate connection failed to test state machine
mHeadsetStateMachine.sendMessage(
@@ -320,9 +312,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/** Test state transition from Connecting to Disconnected state via CONNECT_TIMEOUT message */
@@ -342,9 +333,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/**
@@ -364,9 +354,8 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcastAsUser(
any(Intent.class), any(UserHandle.class), anyString(), any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
// Indicate RFCOMM connection is successful to test state machine
mHeadsetStateMachine.sendMessage(
@@ -379,9 +368,8 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, after(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
.sendBroadcastAsUser(
any(Intent.class), any(UserHandle.class), anyString(), any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
// Indicate SLC connection is successful to test state machine
numBroadcastsSent++;
@@ -402,9 +390,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/**
@@ -433,9 +420,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/**
@@ -457,9 +443,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/**
@@ -488,9 +473,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_DISCONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/** Test state transition from Connected to Disconnecting state via DISCONNECT message */
@@ -511,9 +495,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
/**
@@ -542,9 +525,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
/**
@@ -573,9 +555,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/** Test state transition from Connected to AudioConnecting state via CONNECT_AUDIO message */
@@ -596,9 +577,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTING,
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioConnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioConnecting.class);
}
/**
@@ -616,9 +596,8 @@ public class HeadsetStateMachineTest {
// verify no native connect audio
verify(mNativeInterface, never()).connectAudio(mTestDevice);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioConnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioConnecting.class);
Utils.setIsScoManagedByAudioEnabled(false);
}
@@ -648,9 +627,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTING,
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioConnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioConnecting.class);
}
/**
@@ -678,9 +656,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTED,
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioOn.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioOn.class);
}
/** Test state transition from AudioConnecting to Connected state via CONNECT_TIMEOUT message */
@@ -700,9 +677,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/**
@@ -731,9 +707,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/**
@@ -767,9 +742,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/**
@@ -803,9 +777,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
/**
@@ -834,9 +807,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioOn.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioOn.class);
}
/**
@@ -859,9 +831,8 @@ public class HeadsetStateMachineTest {
eq(UserHandle.ALL),
eq(BLUETOOTH_CONNECT),
any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioDisconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioDisconnecting.class);
}
/**
@@ -879,9 +850,8 @@ public class HeadsetStateMachineTest {
eq(UserHandle.ALL),
eq(BLUETOOTH_CONNECT),
any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioDisconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioDisconnecting.class);
}
/**
@@ -910,9 +880,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/** Test state transition from AudioOn to Disconnected state via Stack.DISCONNECTED message */
@@ -943,9 +912,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/** Test state transition from AudioOn to Disconnecting state via Stack.DISCONNECTING message */
@@ -976,9 +944,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
/**
@@ -1000,9 +967,8 @@ public class HeadsetStateMachineTest {
eq(UserHandle.ALL),
eq(BLUETOOTH_CONNECT),
any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioDisconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioDisconnecting.class);
if (i == MAX_RETRY_DISCONNECT_AUDIO) {
// Increment twice numBroadcastsSent as DISCONNECT message is added on max retry
numBroadcastsSent += 2;
@@ -1024,18 +990,16 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioOn.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioOn.class);
} else { // Max retry count reached, test Disconnecting state
HeadsetTestUtils.verifyConnectionStateBroadcast(
mTestDevice,
BluetoothHeadset.STATE_DISCONNECTING,
BluetoothHeadset.STATE_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
}
}
@@ -1066,9 +1030,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
}
/**
@@ -1097,9 +1060,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioOn.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioOn.class);
}
/**
@@ -1133,9 +1095,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
}
/**
@@ -1169,9 +1130,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getAllValues().get(mIntentArgument.getAllValues().size() - 1));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnected.class);
}
/**
@@ -1331,7 +1291,7 @@ public class HeadsetStateMachineTest {
verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(mTestDevice);
}
- /** A test to verfiy that we correctly handles AT+BIND event with driver safety case from HF */
+ /** A test to verify that we correctly handles AT+BIND event with driver safety case from HF */
@Test
public void testAtBindWithDriverSafetyEventWhenConnecting() {
setUpConnectingState();
@@ -1344,22 +1304,21 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
.sendBroadcast(intentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
verify(mHeadsetService).sendBroadcast(any(), any(), any());
- Assert.assertEquals(
- mTestDevice,
- intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null));
- Assert.assertEquals(
- HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1));
- Assert.assertEquals(
- -1,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2));
+ assertThat(intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null))
+ .isEqualTo(mTestDevice);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1))
+ .isEqualTo(HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2))
+ .isEqualTo(-1);
}
- /** A test to verfiy that we correctly handles AT+BIND event with battery level case from HF */
+ /** A test to verify that we correctly handles AT+BIND event with battery level case from HF */
@Test
public void testAtBindEventWithBatteryLevelEventWhenConnecting() {
setUpConnectingState();
@@ -1372,22 +1331,21 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
.sendBroadcast(intentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
verify(mHeadsetService).sendBroadcast(any(), any(), any());
- Assert.assertEquals(
- mTestDevice,
- intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null));
- Assert.assertEquals(
- HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1));
- Assert.assertEquals(
- -1,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2));
+ assertThat(intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null))
+ .isEqualTo(mTestDevice);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1))
+ .isEqualTo(HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2))
+ .isEqualTo(-1);
}
- /** A test to verfiy that we correctly handles AT+BIND event with error case from HF */
+ /** A test to verify that we correctly handles AT+BIND event with error case from HF */
@Test
public void testAtBindEventWithErrorEventWhenConnecting() {
setUpConnectingState();
@@ -1400,19 +1358,18 @@ public class HeadsetStateMachineTest {
verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
.sendBroadcast(intentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
verify(mHeadsetService).sendBroadcast(any(), any(), any());
- Assert.assertEquals(
- mTestDevice,
- intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null));
- Assert.assertEquals(
- HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1));
- Assert.assertEquals(
- -1,
- intentArgument
- .getValue()
- .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2));
+ assertThat(intentArgument.getValue().getExtra(BluetoothDevice.EXTRA_DEVICE, null))
+ .isEqualTo(mTestDevice);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1))
+ .isEqualTo(HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY);
+ assertThat(
+ intentArgument
+ .getValue()
+ .getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -2))
+ .isEqualTo(-1);
}
/** A test to verify that we correctly set AG indicator mask when enter/exit silence mode */
@@ -1442,7 +1399,7 @@ public class HeadsetStateMachineTest {
String input = "test";
int fromIndex = 0;
- Assert.assertEquals(HeadsetStateMachine.findChar(ch, input, fromIndex), 2);
+ assertThat(HeadsetStateMachine.findChar(ch, input, fromIndex)).isEqualTo(2);
}
@Test
@@ -1451,7 +1408,7 @@ public class HeadsetStateMachineTest {
String input = "test";
int fromIndex = 0;
- Assert.assertEquals(HeadsetStateMachine.findChar(ch, input, fromIndex), input.length());
+ assertThat(HeadsetStateMachine.findChar(ch, input, fromIndex)).isEqualTo(input.length());
}
@Test
@@ -1460,7 +1417,7 @@ public class HeadsetStateMachineTest {
String input = "te\"st";
int fromIndex = 0;
- Assert.assertEquals(HeadsetStateMachine.findChar(ch, input, fromIndex), input.length());
+ assertThat(HeadsetStateMachine.findChar(ch, input, fromIndex)).isEqualTo(input.length());
}
@Test
@@ -1470,46 +1427,47 @@ public class HeadsetStateMachineTest {
expected.add(11);
expected.add("notint");
- Assert.assertEquals(HeadsetStateMachine.generateArgs(input), expected.toArray());
+ assertThat(HeadsetStateMachine.generateArgs(input)).isEqualTo(expected.toArray());
}
@Test
public void testGetAtCommandType() {
String atCommand = "start?";
- Assert.assertEquals(
- mHeadsetStateMachine.getAtCommandType(atCommand), AtPhonebook.TYPE_READ);
+ assertThat(mHeadsetStateMachine.getAtCommandType(atCommand))
+ .isEqualTo(AtPhonebook.TYPE_READ);
atCommand = "start=?";
- Assert.assertEquals(
- mHeadsetStateMachine.getAtCommandType(atCommand), AtPhonebook.TYPE_TEST);
+ assertThat(mHeadsetStateMachine.getAtCommandType(atCommand))
+ .isEqualTo(AtPhonebook.TYPE_TEST);
atCommand = "start=comm";
- Assert.assertEquals(mHeadsetStateMachine.getAtCommandType(atCommand), AtPhonebook.TYPE_SET);
+ assertThat(mHeadsetStateMachine.getAtCommandType(atCommand))
+ .isEqualTo(AtPhonebook.TYPE_SET);
atCommand = "start!";
- Assert.assertEquals(
- mHeadsetStateMachine.getAtCommandType(atCommand), AtPhonebook.TYPE_UNKNOWN);
+ assertThat(mHeadsetStateMachine.getAtCommandType(atCommand))
+ .isEqualTo(AtPhonebook.TYPE_UNKNOWN);
}
@Test
public void testParseUnknownAt() {
String atString = "\"command\"";
- Assert.assertEquals(mHeadsetStateMachine.parseUnknownAt(atString), "\"command\"");
+ assertThat(mHeadsetStateMachine.parseUnknownAt(atString)).isEqualTo("\"command\"");
}
@Test
public void testParseUnknownAt_withUnmatchingQuotes() {
String atString = "\"command";
- Assert.assertEquals(mHeadsetStateMachine.parseUnknownAt(atString), "\"command\"");
+ assertThat(mHeadsetStateMachine.parseUnknownAt(atString)).isEqualTo("\"command\"");
}
@Test
public void testParseUnknownAt_withCharOutsideQuotes() {
String atString = "a\"command\"";
- Assert.assertEquals(mHeadsetStateMachine.parseUnknownAt(atString), "A\"command\"");
+ assertThat(mHeadsetStateMachine.parseUnknownAt(atString)).isEqualTo("A\"command\"");
}
@Ignore("b/265556073")
@@ -1803,7 +1761,7 @@ public class HeadsetStateMachineTest {
mHeadsetStateMachine.processVolumeEvent(HeadsetHalConstants.VOLUME_TYPE_MIC, 1);
- Assert.assertEquals(mHeadsetStateMachine.mMicVolume, 1);
+ assertThat(mHeadsetStateMachine.mMicVolume).isEqualTo(1);
}
@RequiresFlagsDisabled(FLAG_DEPRECATE_STREAM_BT_SCO)
@@ -1816,7 +1774,7 @@ public class HeadsetStateMachineTest {
mHeadsetStateMachine.processVolumeEvent(HeadsetHalConstants.VOLUME_TYPE_SPK, 2);
- Assert.assertEquals(mHeadsetStateMachine.mSpeakerVolume, 2);
+ assertThat(mHeadsetStateMachine.mSpeakerVolume).isEqualTo(2);
verify(mockAudioManager).setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, 2, 0);
}
@@ -1830,33 +1788,11 @@ public class HeadsetStateMachineTest {
mHeadsetStateMachine.processVolumeEvent(HeadsetHalConstants.VOLUME_TYPE_SPK, 2);
- Assert.assertEquals(mHeadsetStateMachine.mSpeakerVolume, 2);
+ assertThat(mHeadsetStateMachine.mSpeakerVolume).isEqualTo(2);
verify(mockAudioManager).setStreamVolume(AudioManager.STREAM_VOICE_CALL, 2, 0);
}
@Test
- @EnableFlags({Flags.FLAG_HFP_ALLOW_VOLUME_CHANGE_WITHOUT_SCO})
- public void testVolumeChangeEvent_fromIntentWhenConnected() {
- setUpConnectedState();
- int originalVolume = mHeadsetStateMachine.mSpeakerVolume;
- mHeadsetStateMachine.mSpeakerVolume = 0;
- int vol = 10;
-
- // Send INTENT_SCO_VOLUME_CHANGED message
- Intent volumeChange = new Intent(AudioManager.ACTION_VOLUME_CHANGED);
- volumeChange.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, vol);
-
- mHeadsetStateMachine.sendMessage(
- HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, volumeChange);
- TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
-
- // verify volume processed
- verify(mNativeInterface).setVolume(mTestDevice, HeadsetHalConstants.VOLUME_TYPE_SPK, vol);
-
- mHeadsetStateMachine.mSpeakerVolume = originalVolume;
- }
-
- @Test
public void testVolumeChangeEvent_fromIntentWhenAudioOn() {
setUpAudioOnState();
int originalVolume = mHeadsetStateMachine.mSpeakerVolume;
@@ -2169,9 +2105,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
return 1;
}
@@ -2199,9 +2134,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connecting.class);
mHeadsetStateMachine.sendMessage(
HeadsetStateMachine.STACK_EVENT,
new HeadsetStackEvent(
@@ -2219,9 +2153,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Connected.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Connected.class);
return 2;
}
@@ -2241,9 +2174,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTING,
BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioConnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioConnecting.class);
return numBroadcastsSent;
}
@@ -2268,9 +2200,8 @@ public class HeadsetStateMachineTest {
BluetoothHeadset.STATE_AUDIO_CONNECTED,
BluetoothHeadset.STATE_AUDIO_CONNECTING,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioOn.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioOn.class);
return numBroadcastsSent;
}
@@ -2284,9 +2215,8 @@ public class HeadsetStateMachineTest {
eq(UserHandle.ALL),
eq(BLUETOOTH_CONNECT),
any(Bundle.class));
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.AudioDisconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.AudioDisconnecting.class);
return numBroadcastsSent;
}
@@ -2306,9 +2236,8 @@ public class HeadsetStateMachineTest {
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED,
mIntentArgument.getValue());
- Assert.assertThat(
- mHeadsetStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(HeadsetStateMachine.Disconnecting.class));
+ assertThat(mHeadsetStateMachine.getCurrentState())
+ .isInstanceOf(HeadsetStateMachine.Disconnecting.class);
return numBroadcastsSent;
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java
index e80048dafa..4b2d0b291c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java
@@ -25,8 +25,6 @@ import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
-import org.junit.Assert;
-
/** Helper functions for HFP related tests */
public class HeadsetTestUtils {
@@ -41,11 +39,12 @@ public class HeadsetTestUtils {
public static void verifyAudioStateBroadcast(
BluetoothDevice device, int toState, int fromState, Intent intent) {
assertThat(intent).isNotNull();
- Assert.assertEquals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED, intent.getAction());
- Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(toState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertEquals(
- fromState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1));
+ assertThat(intent.getAction()).isEqualTo(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(toState);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1))
+ .isEqualTo(fromState);
}
/**
@@ -61,14 +60,15 @@ public class HeadsetTestUtils {
public static void verifyConnectionStateBroadcast(
BluetoothDevice device, int toState, int fromState, Intent intent, boolean checkFlag) {
assertThat(intent).isNotNull();
- Assert.assertEquals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, intent.getAction());
+ assertThat(intent.getAction()).isEqualTo(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
if (checkFlag) {
- Assert.assertEquals(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, intent.getFlags());
+ assertThat(intent.getFlags()).isEqualTo(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
- Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(toState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertEquals(
- fromState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1));
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(toState);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1))
+ .isEqualTo(fromState);
}
/**
@@ -97,13 +97,14 @@ public class HeadsetTestUtils {
public static void verifyActiveDeviceChangedBroadcast(
BluetoothDevice device, Intent intent, boolean checkFlag) {
assertThat(intent).isNotNull();
- Assert.assertEquals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, intent.getAction());
- Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
+ assertThat(intent.getAction()).isEqualTo(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
if (checkFlag) {
- Assert.assertEquals(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- intent.getFlags());
+ assertThat(intent.getFlags())
+ .isEqualTo(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
index 3541670116..543aca2578 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
@@ -39,8 +39,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.BatteryManager;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -49,10 +47,8 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.RemoteDevices;
import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.flags.Flags;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -71,7 +67,6 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class HeadsetClientServiceTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private AdapterService mAdapterService;
@Mock private HeadsetClientStateMachine mStateMachine;
@@ -211,7 +206,6 @@ public class HeadsetClientServiceTest {
* back HF values and checks if they match AM. This proves that the conversion is symmetric.
*/
@Test
- @EnableFlags(Flags.FLAG_HEADSET_CLIENT_AM_HF_VOLUME_SYMMETRIC)
public void testAmHfVolumeSymmetric_AmLowerRange() {
int amMin = 1;
int amMax = 10;
@@ -231,7 +225,7 @@ public class HeadsetClientServiceTest {
for (Map.Entry entry : amToHfMap.entrySet()) {
// Convert back from collected HF to AM and check if equal the saved AM value
- Assert.assertEquals(service.hfToAmVol((int) entry.getValue()), entry.getKey());
+ assertThat(service.hfToAmVol((int) entry.getValue())).isEqualTo(entry.getKey());
}
}
@@ -241,7 +235,6 @@ public class HeadsetClientServiceTest {
* back AM values and checks if they match HF. This proves that the conversion is symmetric.
*/
@Test
- @EnableFlags(Flags.FLAG_HEADSET_CLIENT_AM_HF_VOLUME_SYMMETRIC)
public void testAmHfVolumeSymmetric_HfLowerRange() {
int amMin = 1;
int amMax = 20;
@@ -261,7 +254,7 @@ public class HeadsetClientServiceTest {
for (Map.Entry entry : hfToAmMap.entrySet()) {
// Convert back from collected AM to HF and check if equal the saved HF value
- Assert.assertEquals(service.amToHfVol((int) entry.getValue()), entry.getKey());
+ assertThat(service.amToHfVol((int) entry.getValue())).isEqualTo(entry.getKey());
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
index 2ee7fa089b..0a74cdd777 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
@@ -1029,7 +1029,7 @@ public class HeadsetClientStateMachineTest {
mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(true);
mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy);
verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,2,1");
- assertThat(mHeadsetClientStateMachine.mQueuedActions.size()).isEqualTo(1);
+ assertThat(mHeadsetClientStateMachine.mQueuedActions).hasSize(1);
mHeadsetClientStateMachine.mQueuedActions.clear();
// Test if fail to sendAndroidAt
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java
index fc33b519d7..337fb1faa3 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java
@@ -41,7 +41,6 @@ import com.android.bluetooth.btservice.AdapterService;
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;
@@ -124,7 +123,7 @@ public class HidDeviceTest {
field.setAccessible(true);
HidDeviceNativeInterface nativeInterface =
(HidDeviceNativeInterface) field.get(mHidDeviceService);
- Assert.assertEquals(nativeInterface, mHidDeviceNativeInterface);
+ assertThat(nativeInterface).isEqualTo(mHidDeviceNativeInterface);
// Dummy SDP settings
mSettings =
@@ -184,11 +183,13 @@ public class HidDeviceTest {
int timeoutMs, BluetoothDevice device, int newState, int prevState) {
Intent intent = waitForIntent(timeoutMs, mConnectionStateChangedQueue);
assertThat(intent).isNotNull();
- Assert.assertEquals(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED, intent.getAction());
- Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
- Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
- Assert.assertEquals(
- prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1));
+ assertThat(intent.getAction())
+ .isEqualTo(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1))
+ .isEqualTo(prevState);
}
private void verifyCallback(int timeoutMs, int callbackType, BlockingQueue<Integer> queue) {
@@ -196,7 +197,7 @@ public class HidDeviceTest {
Integer lastCallback = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
assertThat(lastCallback).isNotNull();
int lastCallbackType = lastCallback;
- Assert.assertEquals(callbackType, lastCallbackType);
+ assertThat(callbackType).isEqualTo(lastCallbackType);
} catch (InterruptedException e) {
throw new AssertionError("Cannot obtain a callback from the queue", e);
}
@@ -261,7 +262,7 @@ public class HidDeviceTest {
/** Test getting HidDeviceService: getHidDeviceService(). */
@Test
public void testGetHidDeviceService() {
- Assert.assertEquals(mHidDeviceService, HidDeviceService.getHidDeviceService());
+ assertThat(HidDeviceService.getHidDeviceService()).isEqualTo(mHidDeviceService);
}
/**
@@ -311,7 +312,7 @@ public class HidDeviceTest {
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
verify(mHidDeviceNativeInterface).unregisterApp();
@@ -324,9 +325,8 @@ public class HidDeviceTest {
public void testSendReport() throws Exception {
doReturn(true).when(mHidDeviceNativeInterface).sendReport(anyInt(), any(byte[].class));
// sendReport() should fail without app registered
- Assert.assertEquals(
- false,
- mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
+ assertThat(mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT))
+ .isFalse();
// Register app
doReturn(true)
@@ -349,16 +349,15 @@ public class HidDeviceTest {
verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
// sendReport() should work when app is registered
- Assert.assertEquals(
- true,
- mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
+ assertThat(mHidDeviceService.sendReport(mTestDevice, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT))
+ .isTrue();
verify(mHidDeviceNativeInterface)
.sendReport(eq((int) SAMPLE_REPORT_ID), eq(SAMPLE_HID_REPORT));
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
}
/** Test the logic in replyReport(). This should fail when the app is not registered. */
@@ -368,10 +367,13 @@ public class HidDeviceTest {
.when(mHidDeviceNativeInterface)
.replyReport(anyByte(), anyByte(), any(byte[].class));
// replyReport() should fail without app registered
- Assert.assertEquals(
- false,
- mHidDeviceService.replyReport(
- mTestDevice, SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
+ assertThat(
+ mHidDeviceService.replyReport(
+ mTestDevice,
+ SAMPLE_REPORT_TYPE,
+ SAMPLE_REPORT_ID,
+ SAMPLE_HID_REPORT))
+ .isFalse();
// Register app
doReturn(true)
@@ -394,17 +396,20 @@ public class HidDeviceTest {
verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
// replyReport() should work when app is registered
- Assert.assertEquals(
- true,
- mHidDeviceService.replyReport(
- mTestDevice, SAMPLE_REPORT_TYPE, SAMPLE_REPORT_ID, SAMPLE_HID_REPORT));
+ assertThat(
+ mHidDeviceService.replyReport(
+ mTestDevice,
+ SAMPLE_REPORT_TYPE,
+ SAMPLE_REPORT_ID,
+ SAMPLE_HID_REPORT))
+ .isTrue();
verify(mHidDeviceNativeInterface)
.replyReport(eq(SAMPLE_REPORT_TYPE), eq(SAMPLE_REPORT_ID), eq(SAMPLE_HID_REPORT));
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
}
/** Test the logic in reportError(). This should fail when the app is not registered. */
@@ -412,7 +417,7 @@ public class HidDeviceTest {
public void testReportError() throws Exception {
doReturn(true).when(mHidDeviceNativeInterface).reportError(anyByte());
// reportError() should fail without app registered
- Assert.assertEquals(false, mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR));
+ assertThat(mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR)).isFalse();
// Register app
doReturn(true)
@@ -435,13 +440,13 @@ public class HidDeviceTest {
verifyCallback(TIMEOUT_MS, CALLBACK_APP_REGISTERED, mCallbackQueue);
// reportError() should work when app is registered
- Assert.assertEquals(true, mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR));
+ assertThat(mHidDeviceService.reportError(mTestDevice, SAMPLE_REPORT_ERROR)).isTrue();
verify(mHidDeviceNativeInterface).reportError(eq(SAMPLE_REPORT_ERROR));
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
}
/** Test that an outgoing connection/disconnection succeeds */
@@ -477,9 +482,8 @@ public class HidDeviceTest {
mTestDevice,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING,
- mHidDeviceService.getConnectionState(mTestDevice));
+ assertThat(mHidDeviceService.getConnectionState(mTestDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
mHidDeviceService.onConnectStateChangedFromNative(
mTestDevice, HidDeviceService.HAL_CONN_STATE_CONNECTED);
@@ -489,9 +493,8 @@ public class HidDeviceTest {
mTestDevice,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED,
- mHidDeviceService.getConnectionState(mTestDevice));
+ assertThat(mHidDeviceService.getConnectionState(mTestDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
// Verify the list of connected devices
assertThat(
@@ -510,9 +513,8 @@ public class HidDeviceTest {
mTestDevice,
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTING,
- mHidDeviceService.getConnectionState(mTestDevice));
+ assertThat(mHidDeviceService.getConnectionState(mTestDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
mHidDeviceService.onConnectStateChangedFromNative(
mTestDevice, HidDeviceService.HAL_CONN_STATE_DISCONNECTED);
@@ -522,9 +524,8 @@ public class HidDeviceTest {
mTestDevice,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTING);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED,
- mHidDeviceService.getConnectionState(mTestDevice));
+ assertThat(mHidDeviceService.getConnectionState(mTestDevice))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
// Verify the list of connected devices
assertThat(
@@ -534,7 +535,7 @@ public class HidDeviceTest {
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
}
/**
@@ -607,7 +608,7 @@ public class HidDeviceTest {
// Unregister app
doReturn(true).when(mHidDeviceNativeInterface).unregisterApp();
- Assert.assertEquals(true, mHidDeviceService.unregisterApp());
+ assertThat(mHidDeviceService.unregisterApp()).isTrue();
verify(mHidDeviceNativeInterface).unregisterApp();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
index 7cf714c142..878279dae0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
@@ -33,7 +33,6 @@ import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -176,10 +175,10 @@ public class HidHostServiceTest {
// Test when the AdapterService is in non-quiet mode.
doReturn(false).when(mAdapterService).isQuietModeEnabled();
- Assert.assertEquals(expected, mService.okToConnect(device));
+ assertThat(mService.okToConnect(device)).isEqualTo(expected);
// Test when the AdapterService is in quiet mode.
doReturn(true).when(mAdapterService).isQuietModeEnabled();
- Assert.assertEquals(false, mService.okToConnect(device));
+ assertThat(mService.okToConnect(device)).isFalse();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java
index fdd2aacad0..500e697502 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java
@@ -30,7 +30,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.btservice.ServiceFactory;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -64,15 +63,15 @@ public class ContentControlIdKeeperTest {
public int testCcidAcquire(ParcelUuid uuid, int context, int expectedListSize) {
int ccid = ContentControlIdKeeper.acquireCcid(uuid, context);
- Assert.assertNotEquals(ccid, ContentControlIdKeeper.CCID_INVALID);
+ assertThat(ccid).isNotEqualTo(ContentControlIdKeeper.CCID_INVALID);
verify(mLeAudioServiceMock).setCcidInformation(eq(uuid), eq(ccid), eq(context));
Map<ParcelUuid, Pair<Integer, Integer>> uuidToCcidContextPair =
ContentControlIdKeeper.getUuidToCcidContextPairMap();
- Assert.assertEquals(expectedListSize, uuidToCcidContextPair.size());
+ assertThat(uuidToCcidContextPair).hasSize(expectedListSize);
assertThat(uuidToCcidContextPair).containsKey(uuid);
- Assert.assertEquals(ccid, (long) uuidToCcidContextPair.get(uuid).first);
- Assert.assertEquals(context, (long) uuidToCcidContextPair.get(uuid).second);
+ assertThat(uuidToCcidContextPair.get(uuid).first).isEqualTo(ccid);
+ assertThat(uuidToCcidContextPair.get(uuid).second).isEqualTo(context);
return ccid;
}
@@ -88,7 +87,7 @@ public class ContentControlIdKeeperTest {
verify(mLeAudioServiceMock).setCcidInformation(eq(uuid), eq(ccid), eq(0));
- Assert.assertEquals(expectedListSize, uuidToCcidContextPair.size());
+ assertThat(uuidToCcidContextPair).hasSize(expectedListSize);
}
@Test
@@ -98,7 +97,7 @@ public class ContentControlIdKeeperTest {
int ccid_one = testCcidAcquire(uuid_one, BluetoothLeAudio.CONTEXT_TYPE_MEDIA, 1);
int ccid_two = testCcidAcquire(uuid_two, BluetoothLeAudio.CONTEXT_TYPE_RINGTONE, 2);
- Assert.assertNotEquals(ccid_one, ccid_two);
+ assertThat(ccid_one).isNotEqualTo(ccid_two);
testCcidRelease(uuid_one, ccid_one, 1);
testCcidRelease(uuid_two, ccid_two, 0);
@@ -120,14 +119,14 @@ public class ContentControlIdKeeperTest {
public void testAcquireInvalidContext() {
ParcelUuid uuid = new ParcelUuid(UUID.randomUUID());
- int ccid = ContentControlIdKeeper.acquireCcid(uuid, 0);
- Assert.assertEquals(ccid, ContentControlIdKeeper.CCID_INVALID);
+ assertThat(ContentControlIdKeeper.acquireCcid(uuid, 0))
+ .isEqualTo(ContentControlIdKeeper.CCID_INVALID);
verify(mLeAudioServiceMock, times(0))
.setCcidInformation(any(ParcelUuid.class), any(int.class), any(int.class));
Map<ParcelUuid, Pair<Integer, Integer>> uuidToCcidContextPair =
ContentControlIdKeeper.getUuidToCcidContextPairMap();
- Assert.assertEquals(0, uuidToCcidContextPair.size());
+ assertThat(uuidToCcidContextPair).isEmpty();
}
@Test
@@ -138,6 +137,6 @@ public class ContentControlIdKeeperTest {
int ccid_two = testCcidAcquire(uuid, BluetoothLeAudio.CONTEXT_TYPE_RINGTONE, 1);
// This is implementation specific but verifies that the previous CCID was recycled
- Assert.assertEquals(ccid_one, ccid_two);
+ assertThat(ccid_two).isEqualTo(ccid_one);
}
}
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 b57b9beb69..8433d1398c 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
@@ -22,6 +22,7 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static com.android.bluetooth.bass_client.BassConstants.INVALID_BROADCAST_ID;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.*;
@@ -57,7 +58,6 @@ import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.tbs.TbsService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -91,7 +91,6 @@ public class LeAudioBroadcastServiceTest {
private LeAudioService mService;
private LeAudioIntentReceiver mLeAudioIntentReceiver;
private LinkedBlockingQueue<Intent> mIntentQueue;
- private boolean onBroadcastToUnicastFallbackGroupChangedCallbackCalled = false;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private ActiveDeviceManager mActiveDeviceManager;
@@ -105,6 +104,7 @@ public class LeAudioBroadcastServiceTest {
@Mock private TbsService mTbsService;
@Mock private MetricsLogger mMetricsLogger;
@Mock private IBluetoothLeBroadcastCallback mCallbacks;
+ @Mock private IBluetoothLeAudioCallback mLeAudioCallbacks;
@Mock private IBinder mBinder;
@Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
@@ -155,6 +155,7 @@ public class LeAudioBroadcastServiceTest {
mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
doReturn(mBinder).when(mCallbacks).asBinder();
+ doReturn(mBinder).when(mLeAudioCallbacks).asBinder();
doNothing().when(mBinder).linkToDeath(any(), eq(0));
// Use spied objects factory
@@ -237,7 +238,7 @@ public class LeAudioBroadcastServiceTest {
/** Test getting LeAudio Service */
@Test
public void testGetLeAudioService() {
- Assert.assertEquals(mService, LeAudioService.getLeAudioService());
+ assertThat(LeAudioService.getLeAudioService()).isEqualTo(mService);
}
void verifyBroadcastStarted(int broadcastId, BluetoothLeBroadcastSettings settings) {
@@ -750,10 +751,8 @@ public class LeAudioBroadcastServiceTest {
TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
- List<BluetoothLeBroadcastMetadata> meta_list = mService.getAllBroadcastMetadata();
- assertThat(meta_list).isNotNull();
- Assert.assertNotEquals(meta_list.size(), 0);
- Assert.assertEquals(meta_list.get(0), state_event.broadcastMetadata);
+ assertThat(mService.getAllBroadcastMetadata())
+ .containsExactly(state_event.broadcastMetadata);
}
@Test
@@ -806,13 +805,13 @@ public class LeAudioBroadcastServiceTest {
int timeoutMs, BluetoothDevice device, int newState, int prevState) {
Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue);
assertThat(intent).isNotNull();
- Assert.assertEquals(
- BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED, intent.getAction());
- Assert.assertEquals(
- (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), device);
- Assert.assertEquals(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1), newState);
- Assert.assertEquals(
- intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1), prevState);
+ assertThat(intent.getAction())
+ .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
+ assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class))
+ .isEqualTo(device);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState);
+ assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1))
+ .isEqualTo(prevState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
// ActiveDeviceManager calls deviceConnected when connected.
@@ -854,7 +853,8 @@ public class LeAudioBroadcastServiceTest {
device,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
- Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(device));
+ assertThat(mService.getConnectionState(device))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
LeAudioStackEvent create_event =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
@@ -867,7 +867,7 @@ public class LeAudioBroadcastServiceTest {
device,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
- Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(device));
+ assertThat(mService.getConnectionState(device)).isEqualTo(BluetoothProfile.STATE_CONNECTED);
create_event =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
@@ -934,7 +934,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become inactive */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, LE_AUDIO_GROUP_ID_INVALID);
+ assertThat(activeGroup).isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
/* Imitate group inactivity to cause create broadcast */
stackEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
@@ -961,7 +961,7 @@ public class LeAudioBroadcastServiceTest {
eq(expectedDataArray));
activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(-1, activeGroup);
+ assertThat(activeGroup).isEqualTo(-1);
}
@Test
@@ -1048,7 +1048,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become inactive */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, LE_AUDIO_GROUP_ID_INVALID);
+ assertThat(activeGroup).isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
/* Imitate group inactivity to cause create broadcast */
create_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
@@ -1090,7 +1090,7 @@ public class LeAudioBroadcastServiceTest {
}
activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(LE_AUDIO_GROUP_ID_INVALID, activeGroup);
+ assertThat(activeGroup).isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
/* Check if broadcast is started automatically when created */
create_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
@@ -1160,7 +1160,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate setting device not in call */
mService.setInCall(false);
@@ -1237,7 +1237,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate group change request by Bluetooth Sink HAL suspend request */
create_event =
@@ -1332,7 +1332,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate group change request by Bluetooth Sink HAL suspend request */
create_event =
@@ -1425,7 +1425,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate setting device not in call */
mService.setInCall(false);
@@ -1503,7 +1503,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate group change request by Bluetooth Sink HAL suspend request */
create_event =
@@ -1593,7 +1593,7 @@ public class LeAudioBroadcastServiceTest {
/* Active group should become the one that was active before broadcasting */
int activeGroup = mService.getActiveGroupId();
- Assert.assertEquals(activeGroup, groupId);
+ assertThat(activeGroup).isEqualTo(groupId);
/* Imitate group change request by Bluetooth Sink HAL suspend request */
create_event =
@@ -1669,11 +1669,11 @@ public class LeAudioBroadcastServiceTest {
prepareConnectedUnicastDevice(groupId2, mDevice2);
prepareHandoverStreamingBroadcast(groupId, broadcastId, code);
- Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId);
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId);
reset(mAudioManager);
- Assert.assertTrue(mService.setActiveDevice(mDevice2));
+ assertThat(mService.setActiveDevice(mDevice2)).isTrue();
if (!Flags.leaudioUseAudioRecordingListener()) {
/* Update fallback active device (only input is active) */
@@ -1686,10 +1686,10 @@ public class LeAudioBroadcastServiceTest {
eq(mDevice2), eq(mDevice), connectionInfoArgumentCaptor.capture());
List<BluetoothProfileConnectionInfo> connInfos =
connectionInfoArgumentCaptor.getAllValues();
- Assert.assertEquals(connInfos.size(), 1);
+ assertThat(connInfos).hasSize(1);
assertThat(connInfos.get(0).isLeOutput()).isFalse();
}
- Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId2);
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId2);
}
@Test
@@ -1706,34 +1706,8 @@ public class LeAudioBroadcastServiceTest {
when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
- onBroadcastToUnicastFallbackGroupChangedCallbackCalled = false;
-
- IBluetoothLeAudioCallback leAudioCallbacks =
- new IBluetoothLeAudioCallback.Stub() {
- @Override
- public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {}
-
- @Override
- public void onGroupStatusChanged(int gid, int gStatus) {}
-
- @Override
- public void onGroupNodeAdded(BluetoothDevice device, int gid) {}
-
- @Override
- public void onGroupNodeRemoved(BluetoothDevice device, int gid) {}
-
- @Override
- public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) {}
-
- @Override
- public void onBroadcastToUnicastFallbackGroupChanged(int groupId) {
- onBroadcastToUnicastFallbackGroupChangedCallbackCalled = true;
- Assert.assertEquals(groupId2, groupId);
- }
- };
-
synchronized (mService.mLeAudioCallbacks) {
- mService.mLeAudioCallbacks.register(leAudioCallbacks);
+ mService.mLeAudioCallbacks.register(mLeAudioCallbacks);
}
initializeNative();
@@ -1743,22 +1717,50 @@ public class LeAudioBroadcastServiceTest {
prepareHandoverStreamingBroadcast(groupId1, broadcastId, code);
TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
- Assert.assertEquals(groupId2, mService.mUnicastGroupIdDeactivatedForBroadcastTransition);
- assertThat(onBroadcastToUnicastFallbackGroupChangedCallbackCalled).isTrue();
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId2);
+
+ try {
+ verify(mLeAudioCallbacks).onBroadcastToUnicastFallbackGroupChanged(groupId2);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
- onBroadcastToUnicastFallbackGroupChangedCallbackCalled = false;
synchronized (mService.mLeAudioCallbacks) {
- mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
+ mService.mLeAudioCallbacks.unregister(mLeAudioCallbacks);
}
}
+ @Test
+ public void testGetLocalBroadcastReceivers() {
+ int broadcastId = 243;
+ byte[] code = {0x00, 0x01, 0x00, 0x02};
+
+ synchronized (mService.mBroadcastCallbacks) {
+ mService.mBroadcastCallbacks.register(mCallbacks);
+ }
+
+ BluetoothLeAudioContentMetadata.Builder meta_builder =
+ new BluetoothLeAudioContentMetadata.Builder();
+ meta_builder.setLanguage("deu");
+ meta_builder.setProgramInfo("Subgroup broadcast info");
+ BluetoothLeAudioContentMetadata meta = meta_builder.build();
+
+ verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1));
+ when(mBassClientService.getSyncedBroadcastSinks(broadcastId)).thenReturn(List.of(mDevice));
+ assertThat(mService.getLocalBroadcastReceivers().size()).isEqualTo(1);
+ assertThat(mService.getLocalBroadcastReceivers()).containsExactly(mDevice);
+
+ verifyBroadcastStopped(broadcastId);
+ assertThat(mService.getLocalBroadcastReceivers()).isEmpty();
+ }
+
private class LeAudioIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
mIntentQueue.put(intent);
} catch (InterruptedException e) {
- Assert.fail("Cannot add Intent to the queue: " + e.getMessage());
+ assertWithMessage("Cannot add Intent to the queue: " + e.toString()).fail();
}
}
}
@@ -1785,7 +1787,7 @@ public class LeAudioBroadcastServiceTest {
devices.add(mDevice2);
prepareConnectedUnicastDevice(groupId2, mDevice2);
- Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId);
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId);
reset(mAudioManager);
@@ -1801,11 +1803,11 @@ public class LeAudioBroadcastServiceTest {
eq(mDevice2), eq(mDevice), connectionInfoArgumentCaptor.capture());
List<BluetoothProfileConnectionInfo> connInfos =
connectionInfoArgumentCaptor.getAllValues();
- Assert.assertEquals(connInfos.size(), 1);
+ assertThat(connInfos.size()).isEqualTo(1);
assertThat(connInfos.get(0).isLeOutput()).isFalse();
}
- Assert.assertEquals(mService.getBroadcastToUnicastFallbackGroup(), groupId2);
+ assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(groupId2);
}
private void disconnectDevice(BluetoothDevice device) {
@@ -1818,8 +1820,8 @@ public class LeAudioBroadcastServiceTest {
device,
BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_CONNECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTING, mService.getConnectionState(device));
+ assertThat(mService.getConnectionState(device))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
LeAudioStackEvent create_event =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
@@ -1832,8 +1834,8 @@ public class LeAudioBroadcastServiceTest {
device,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTING);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(device));
+ assertThat(mService.getConnectionState(device))
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
mService.deviceDisconnected(device, false);
TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
}
@@ -1847,18 +1849,17 @@ public class LeAudioBroadcastServiceTest {
public void testSetDefaultBroadcastToUnicastFallbackGroup() {
int groupId = 1;
int groupId2 = 2;
- int broadcastId = 243;
- byte[] code = {0x00, 0x01, 0x00, 0x02};
List<BluetoothDevice> devices = new ArrayList<>();
when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
- Assert.assertEquals(
- mService.getBroadcastToUnicastFallbackGroup(), LE_AUDIO_GROUP_ID_INVALID);
+ /* If no connected devices - no fallback device */
+ assertThat(mService.getBroadcastToUnicastFallbackGroup())
+ .isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
initializeNative();
devices.add(mDevice);
- prepareHandoverStreamingBroadcast(groupId, broadcastId, code);
+ prepareConnectedUnicastDevice(groupId, mDevice);
mService.deviceConnected(mDevice);
devices.add(mDevice2);
prepareConnectedUnicastDevice(groupId2, mDevice2);
@@ -1870,24 +1871,79 @@ public class LeAudioBroadcastServiceTest {
mService.messageFromNative(stackEvent);
/* First connected group become fallback group */
- Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId);
-
- reset(mAudioManager);
+ assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(groupId);
+ /* Force group as fallback 1 -> 2 */
mService.setBroadcastToUnicastFallbackGroup(groupId2);
-
- Assert.assertEquals(mService.getBroadcastToUnicastFallbackGroup(), groupId2);
+ assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(groupId2);
/* Disconnected last device from fallback should trigger set default group 2 -> 1 */
disconnectDevice(mDevice2);
- Assert.assertEquals(groupId, mService.getBroadcastToUnicastFallbackGroup());
+ assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(groupId);
stackEvent.valueInt1 = groupId;
mService.messageFromNative(stackEvent);
/* Disconnected last device from fallback should trigger set default group 1 -> -1 */
disconnectDevice(mDevice);
- Assert.assertEquals(
- LE_AUDIO_GROUP_ID_INVALID, mService.getBroadcastToUnicastFallbackGroup());
+ assertThat(mService.getBroadcastToUnicastFallbackGroup())
+ .isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
+ }
+
+ @Test
+ @EnableFlags({
+ Flags.FLAG_LEAUDIO_BROADCAST_API_MANAGE_PRIMARY_GROUP,
+ Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION
+ })
+ public void testUpdateFallbackDeviceWhileSettingActiveDevice() {
+ int groupId = 1;
+ int groupId2 = 2;
+ int broadcastId = 243;
+ byte[] code = {0x00, 0x01, 0x00, 0x02};
+ List<BluetoothDevice> devices = new ArrayList<>();
+
+ when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
+
+ synchronized (mService.mLeAudioCallbacks) {
+ mService.mLeAudioCallbacks.register(mLeAudioCallbacks);
+ }
+
+ initializeNative();
+ devices.add(mDevice2);
+ prepareConnectedUnicastDevice(groupId2, mDevice2);
+ devices.add(mDevice);
+ prepareHandoverStreamingBroadcast(groupId, broadcastId, code);
+
+ /* Earliest connected group (2) become fallback device */
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId2);
+ try {
+ verify(mLeAudioCallbacks).onBroadcastToUnicastFallbackGroupChanged(groupId2);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ Mockito.clearInvocations(mLeAudioCallbacks);
+ reset(mAudioManager);
+
+ /* Change active device while broadcasting - result in replacing fallback group 2->1 */
+ assertThat(mService.setActiveDevice(mDevice)).isTrue();
+ TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId);
+ try {
+ verify(mLeAudioCallbacks).onBroadcastToUnicastFallbackGroupChanged(groupId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ /* Verify that fallback device is not changed when there is no running broadcast */
+ Mockito.clearInvocations(mLeAudioCallbacks);
+ verifyBroadcastStopped(broadcastId);
+ assertThat(mService.setActiveDevice(mDevice2)).isTrue();
+ TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
+ try {
+ verify(mLeAudioCallbacks, times(0)).onBroadcastToUnicastFallbackGroupChanged(anyInt());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
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 e03a978949..312a031f82 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
@@ -46,6 +46,8 @@ import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothLeAudioCallback;
+import android.bluetooth.le.IScannerCallback;
+import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -56,6 +58,7 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
+import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -77,7 +80,6 @@ import com.android.bluetooth.gatt.GattService;
import com.android.bluetooth.hap.HapClientService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_scan.ScanController;
-import com.android.bluetooth.le_scan.TransitionalScanHelper;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.tbs.TbsService;
import com.android.bluetooth.vc.VolumeControlService;
@@ -139,7 +141,6 @@ public class LeAudioServiceTest {
@Mock private AdapterService mAdapterService;
@Mock private GattService mGattService;
@Mock private ScanController mScanController;
- @Mock private TransitionalScanHelper mTransitionalScanHelper;
@Mock private ActiveDeviceManager mActiveDeviceManager;
@Mock private AudioManager mAudioManager;
@Mock private DatabaseManager mDatabaseManager;
@@ -229,7 +230,6 @@ public class LeAudioServiceTest {
.getBondedDevices();
doReturn(mGattService).when(mAdapterService).getBluetoothGattService();
doReturn(mScanController).when(mAdapterService).getBluetoothScanController();
- doReturn(mTransitionalScanHelper).when(mScanController).getTransitionalScanHelper();
LeAudioBroadcasterNativeInterface.setInstance(mLeAudioBroadcasterNativeInterface);
LeAudioNativeInterface.setInstance(mNativeInterface);
@@ -285,6 +285,9 @@ public class LeAudioServiceTest {
.when(mAdapterService)
.getRemoteUuids(any(BluetoothDevice.class));
+ doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
+
verify(mNativeInterface, timeout(3000).times(1)).init(any());
}
@@ -535,8 +538,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Return No UUID
doReturn(new ParcelUuid[] {})
@@ -550,9 +551,6 @@ public class LeAudioServiceTest {
/** Test that an outgoing connection to device with PRIORITY_OFF is rejected */
@Test
public void testOutgoingConnectPriorityOff() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
-
// Set the device priority to PRIORITY_OFF so connect() should fail
when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
@@ -571,8 +569,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Send a connect request
assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
@@ -624,6 +620,13 @@ public class LeAudioServiceTest {
}
}
+ private void injectAndVerifyDeviceConnected(BluetoothDevice device) {
+ generateConnectionMessageFromNative(
+ device,
+ LeAudioStackEvent.CONNECTION_STATE_CONNECTED,
+ LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED);
+ }
+
private void injectNoVerifyDeviceConnected(BluetoothDevice device) {
generateUnexpectedConnectionMessageFromNative(
device, LeAudioStackEvent.CONNECTION_STATE_CONNECTED);
@@ -651,8 +654,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Send a connect request
assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
@@ -781,8 +782,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Create device descriptor with connect request
assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
@@ -858,8 +857,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Create device descriptor with connect request
assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
@@ -935,8 +932,6 @@ public class LeAudioServiceTest {
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
// Create device descriptor with connect request
assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
@@ -1076,8 +1071,6 @@ public class LeAudioServiceTest {
/** Test setting connection policy */
@Test
public void testSetConnectionPolicy() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
doReturn(true)
.when(mDatabaseManager)
.setProfileConnectionPolicy(any(BluetoothDevice.class), anyInt(), anyInt());
@@ -1190,6 +1183,13 @@ public class LeAudioServiceTest {
// Make device bonded
mBondedDevices.add(device);
+ LeAudioStackEvent nodeGroupAdded =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
+ nodeGroupAdded.device = device;
+ nodeGroupAdded.valueInt1 = GroupId;
+ nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED;
+ mService.messageFromNative(nodeGroupAdded);
+
// Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
// 250ms for processing two messages should be way more than enough. Anything that breaks
// this indicate some breakage in other part of Android OS
@@ -1217,13 +1217,6 @@ public class LeAudioServiceTest {
assertThat(mService.getConnectionState(device)).isEqualTo(BluetoothProfile.STATE_CONNECTED);
- LeAudioStackEvent nodeGroupAdded =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
- nodeGroupAdded.device = device;
- nodeGroupAdded.valueInt1 = GroupId;
- nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED;
- mService.messageFromNative(nodeGroupAdded);
-
// Verify that the device is in the list of connected devices
assertThat(mService.getConnectedDevices().contains(device)).isTrue();
// Verify the list of previously connected devices
@@ -1257,8 +1250,7 @@ public class LeAudioServiceTest {
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mSingleDevice, testGroupId);
// Add location support
@@ -1308,8 +1300,7 @@ public class LeAudioServiceTest {
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mSingleDevice, testGroupId);
// Add location support
@@ -1376,8 +1367,7 @@ public class LeAudioServiceTest {
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
@@ -1407,7 +1397,7 @@ public class LeAudioServiceTest {
eq(mLeftDevice), eq(null), connectionInfoArgumentCaptor.capture());
List<BluetoothProfileConnectionInfo> connInfos =
connectionInfoArgumentCaptor.getAllValues();
- assertThat(connInfos.size()).isEqualTo(2);
+ assertThat(connInfos).hasSize(2);
assertThat(connInfos.get(0).isLeOutput()).isEqualTo(true);
assertThat(connInfos.get(1).isLeOutput()).isEqualTo(false);
@@ -1418,7 +1408,7 @@ public class LeAudioServiceTest {
.handleBluetoothActiveDeviceChanged(
any(), any(), any(BluetoothProfileConnectionInfo.class));
connInfos = connectionInfoArgumentCaptor.getAllValues();
- assertThat(connInfos.size()).isEqualTo(2);
+ assertThat(connInfos).hasSize(2);
}
/** Test setting active device group with not available contexts */
@@ -1427,27 +1417,16 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 0;
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mSingleDevice, testGroupId);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
verify(mNativeInterface, times(0)).groupSetActive(groupId);
@@ -1461,8 +1440,6 @@ public class LeAudioServiceTest {
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
// Not connected device
@@ -1470,33 +1447,14 @@ public class LeAudioServiceTest {
// Define some return values needed in test
doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(anyInt());
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
// Connect both
connectTestDevice(mSingleDevice, groupId_1);
connectTestDevice(mSingleDevice_2, groupId_2);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId_1;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
-
- // Add location support
- audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice_2;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId_2;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId_1, availableContexts, direction);
+ injectAudioConfChanged(groupId_2, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId_1);
@@ -1559,8 +1517,6 @@ public class LeAudioServiceTest {
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
// Not connected device
@@ -1574,15 +1530,7 @@ public class LeAudioServiceTest {
connectTestDevice(mSingleDevice, groupId_1);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId_1;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId_1, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId_1);
@@ -1620,27 +1568,16 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5;
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mSingleDevice, testGroupId);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId);
@@ -1694,8 +1631,9 @@ public class LeAudioServiceTest {
@Test
@DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PRIMARY_GROUP_SELECTION)
public void testUpdateUnicastFallbackActiveDeviceGroupDuringBroadcast() {
+ List<BluetoothDevice> devices = new ArrayList<>();
int groupId = 1;
- int preGroupId = 2;
+ int groupId_2 = 2;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
int snkAudioLocation = 3;
@@ -1704,14 +1642,23 @@ public class LeAudioServiceTest {
int broadcastId = 243;
byte[] code = {0x00, 0x01, 0x00, 0x02};
+ when(mDatabaseManager.getMostRecentlyConnectedDevices()).thenReturn(devices);
+
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
+ // Connect devices
doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- connectTestDevice(mSingleDevice, testGroupId);
+ devices.add(mSingleDevice);
+ connectTestDevice(mSingleDevice, groupId);
+ devices.add(mSingleDevice_2);
+ connectTestDevice(mSingleDevice_2, groupId_2);
- mService.mUnicastGroupIdDeactivatedForBroadcastTransition = preGroupId;
+ // Default fallback group is LE_AUDIO_GROUP_ID_INVALID
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition)
+ .isEqualTo(LE_AUDIO_GROUP_ID_INVALID);
+
+ mService.mUnicastGroupIdDeactivatedForBroadcastTransition = groupId_2;
// mock create broadcast and currentlyActiveGroupId remains LE_AUDIO_GROUP_ID_INVALID
BluetoothLeAudioContentMetadata.Builder meta_builder =
new BluetoothLeAudioContentMetadata.Builder();
@@ -1723,24 +1670,31 @@ public class LeAudioServiceTest {
LeAudioStackEvent broadcastCreatedEvent =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
- broadcastCreatedEvent.device = mSingleDevice;
broadcastCreatedEvent.valueInt1 = broadcastId;
broadcastCreatedEvent.valueBool1 = true;
mService.messageFromNative(broadcastCreatedEvent);
+ LeAudioStackEvent broadcastStateStreamingEvent =
+ new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
+ broadcastStateStreamingEvent.valueInt1 = broadcastId;
+ broadcastStateStreamingEvent.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_STREAMING;
+ mService.messageFromNative(broadcastStateStreamingEvent);
+
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
LeAudioStackEvent audioConfChangedEvent =
new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
+ audioConfChangedEvent.device = mSingleDevice_2;
audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
+ audioConfChangedEvent.valueInt2 = groupId_2;
audioConfChangedEvent.valueInt3 = snkAudioLocation;
audioConfChangedEvent.valueInt4 = srcAudioLocation;
audioConfChangedEvent.valueInt5 = availableContexts;
mService.messageFromNative(audioConfChangedEvent);
// Verify only update the fallback group and not proceed to change active
- assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
- assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId);
+ assertThat(mService.setActiveDevice(mSingleDevice_2)).isTrue();
+ assertThat(mService.mUnicastGroupIdDeactivatedForBroadcastTransition).isEqualTo(groupId_2);
// Verify only update the fallback group to INVALID and not proceed to change active
assertThat(mService.setActiveDevice(null)).isTrue();
@@ -1756,14 +1710,11 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5;
int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED;
int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
// Single active device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
// Add device to group
@@ -1777,15 +1728,7 @@ public class LeAudioServiceTest {
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
@@ -1853,7 +1796,6 @@ public class LeAudioServiceTest {
doReturn(testVolume).when(mVolumeControlService).getAudioDeviceGroupVolume(testGroupId);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
injectAudioConfChanged(
testGroupId,
@@ -1871,7 +1813,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
/* Verify input and output has been connected to AF*/
List<BluetoothProfileConnectionInfo> connInfos = testConnectioInfoCapture.getAllValues();
- assertThat(connInfos.size()).isEqualTo(2);
+ assertThat(connInfos).hasSize(2);
assertThat(connInfos.get(0).isLeOutput()).isEqualTo(true);
assertThat(connInfos.get(1).isLeOutput()).isEqualTo(false);
@@ -1892,7 +1834,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
connInfos = testConnectioInfoCapture.getAllValues();
- assertThat(connInfos.size()).isEqualTo(3);
+ assertThat(connInfos).hasSize(3);
assertThat(connInfos.get(2).isLeOutput()).isEqualTo(false);
// remove Sink and add Source back
@@ -1916,7 +1858,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
connInfos = testConnectioInfoCapture.getAllValues();
- assertThat(connInfos.size()).isEqualTo(5);
+ assertThat(connInfos).hasSize(5);
assertThat(connInfos.get(3).isLeOutput()).isEqualTo(true);
assertThat(connInfos.get(4).isLeOutput()).isEqualTo(false);
}
@@ -1924,7 +1866,6 @@ public class LeAudioServiceTest {
/** Test native interface audio configuration changed message handling */
@Test
public void testMessageFromNativeAudioConfChangedActiveGroup() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
injectAudioConfChanged(
testGroupId,
@@ -1946,7 +1887,6 @@ public class LeAudioServiceTest {
/** Test native interface audio configuration changed message handling */
@Test
public void testMessageFromNativeAudioConfChangedInactiveGroup() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
Integer contexts =
@@ -1959,7 +1899,6 @@ public class LeAudioServiceTest {
/** Test native interface audio configuration changed message handling */
@Test
public void testMessageFromNativeAudioConfChangedNoGroupChanged() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
injectAudioConfChanged(testGroupId, 0, 3);
@@ -1972,7 +1911,6 @@ public class LeAudioServiceTest {
*/
@Test
public void testHealthBaseDeviceAction() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
LeAudioStackEvent healthBaseDevAction =
@@ -1985,7 +1923,6 @@ public class LeAudioServiceTest {
@Test
public void testHealthBasedGroupAction() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
LeAudioStackEvent healthBasedGroupAction =
@@ -2044,7 +1981,6 @@ public class LeAudioServiceTest {
/** Test native interface group status message handling */
@Test
public void testMessageFromNativeGroupStatusChanged() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
injectAudioConfChanged(
@@ -2105,7 +2041,6 @@ public class LeAudioServiceTest {
/** Test native interface group stream status message handling */
@Test
public void testMessageFromNativeGroupStreamStatusChanged() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
injectAudioConfChanged(
@@ -2167,7 +2102,6 @@ public class LeAudioServiceTest {
injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
testCodecStatus =
@@ -2254,7 +2188,6 @@ public class LeAudioServiceTest {
injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
testCodecStatus =
@@ -2350,7 +2283,6 @@ public class LeAudioServiceTest {
injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
testCodecStatus =
@@ -2421,15 +2353,12 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- ;
+
int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
BluetoothDevice leadDevice;
BluetoothDevice memberDevice = mLeftDevice;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
@@ -2440,15 +2369,7 @@ public class LeAudioServiceTest {
assertThat(mService.setActiveDevice(leadDevice)).isFalse();
- // Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(leadDevice)).isTrue();
@@ -2497,15 +2418,12 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- ;
+
int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
BluetoothDevice leadDevice;
BluetoothDevice memberDevice = mLeftDevice;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
@@ -2517,14 +2435,7 @@ public class LeAudioServiceTest {
assertThat(mService.setActiveDevice(leadDevice)).isFalse();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(leadDevice)).isTrue();
@@ -2576,7 +2487,6 @@ public class LeAudioServiceTest {
int direction = 1;
int availableContexts = 4;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
@@ -2635,7 +2545,6 @@ public class LeAudioServiceTest {
int direction = 1;
int availableContexts = 4;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
assertThat(mService.setActiveDevice(mLeftDevice)).isFalse();
@@ -2696,7 +2605,6 @@ public class LeAudioServiceTest {
@Test
public void testGetAudioLocation() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
assertThat(mService.getAudioLocation(null))
@@ -2714,7 +2622,6 @@ public class LeAudioServiceTest {
@Test
public void testGetConnectedPeerDevices() {
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, testGroupId);
connectTestDevice(mRightDevice, testGroupId);
@@ -2775,8 +2682,6 @@ public class LeAudioServiceTest {
doReturn(new BluetoothDevice[] {mSingleDevice}).when(mAdapterService).getBondedDevices();
when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
@@ -2790,7 +2695,7 @@ public class LeAudioServiceTest {
int groupId = 1;
mService.handleBluetoothEnabled();
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
verify(mMcpService).setDeviceAuthorized(mLeftDevice, true);
@@ -2802,7 +2707,6 @@ public class LeAudioServiceTest {
public void testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval() {
int groupId = 1;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
@@ -2835,8 +2739,6 @@ public class LeAudioServiceTest {
int groupId = 1;
mService.handleBluetoothEnabled();
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
- doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
doReturn(true)
.when(mDatabaseManager)
.setProfileConnectionPolicy(any(BluetoothDevice.class), anyInt(), anyInt());
@@ -2878,7 +2780,6 @@ public class LeAudioServiceTest {
int firstGroupId = 1;
int secondGroupId = 2;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, firstGroupId);
connectTestDevice(mRightDevice, firstGroupId);
connectTestDevice(mSingleDevice, secondGroupId);
@@ -2889,7 +2790,7 @@ public class LeAudioServiceTest {
List<BluetoothDevice> firstGroupDevicesByRightDevice =
mService.getGroupDevices(mRightDevice);
- assertThat(firstGroupDevicesById.size()).isEqualTo(2);
+ assertThat(firstGroupDevicesById).hasSize(2);
assertThat(firstGroupDevicesById.contains(mLeftDevice)).isTrue();
assertThat(firstGroupDevicesById.contains(mRightDevice)).isTrue();
assertThat(firstGroupDevicesById.contains(mSingleDevice)).isFalse();
@@ -2900,7 +2801,7 @@ public class LeAudioServiceTest {
List<BluetoothDevice> secondGroupDevicesById = mService.getGroupDevices(secondGroupId);
List<BluetoothDevice> secondGroupDevicesByDevice = mService.getGroupDevices(mSingleDevice);
- assertThat(secondGroupDevicesById.size()).isEqualTo(1);
+ assertThat(secondGroupDevicesById).hasSize(1);
assertThat(secondGroupDevicesById.contains(mSingleDevice)).isTrue();
assertThat(secondGroupDevicesById.contains(mLeftDevice)).isFalse();
assertThat(secondGroupDevicesById.contains(mRightDevice)).isFalse();
@@ -2926,14 +2827,11 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 | AUDIO_DIRECTION_INPUT_BIT = 0x02; */
int direction = 3;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5;
int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED;
int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
// Single active device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mSingleDevice, testGroupId);
// Add device to group
@@ -2947,15 +2845,7 @@ public class LeAudioServiceTest {
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
@@ -2976,30 +2866,20 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
// Checks group device lists for groupId 1
List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
- assertThat(groupDevicesById.size()).isEqualTo(2);
+ assertThat(groupDevicesById).hasSize(2);
assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
assertThat(groupDevicesById.contains(mRightDevice)).isTrue();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId);
@@ -3016,13 +2896,12 @@ public class LeAudioServiceTest {
reset(mNativeInterface);
/* Don't expect any change. */
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
verify(mNativeInterface, times(0)).groupSetActive(groupId);
reset(mNativeInterface);
/* Expect device to be incactive */
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
verify(mNativeInterface).groupSetActive(-1);
injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
@@ -3037,8 +2916,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
/* Expect device to be incactive */
- audioConfChangedEvent.valueInt5 = 1;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 1, direction);
verify(mNativeInterface).groupSetActive(groupId);
reset(mNativeInterface);
@@ -3052,16 +2930,189 @@ public class LeAudioServiceTest {
any(BluetoothProfileConnectionInfo.class));
}
+ @Test
+ public void testAutoActiveMode_verifyDefaultState() {
+ int groupId = 1;
+
+ /* Test scenario:
+ * 1. Connected two devices
+ * 2. Verify that Auto Active Mode is true be default.
+ */
+
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isTrue();
+ }
+
+ @Test
+ public void testAutoActiveMode_whenDeviceIsConnected_failToDisableIt() {
+ int groupId = 1;
+ /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
+ int direction = 1;
+ int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
+
+ /* Test scenario:
+ * 1. Connected two devices
+ * 2. Disconnect one device
+ * 3. Verify that Auto Active Mode cannot be set.
+ */
+
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ assertThat(mService.setAutoActiveModeState(groupId, false)).isFalse();
+
+ // Checks group device lists for groupId 1
+ List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
+
+ assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice);
+
+ injectAudioConfChanged(groupId, availableContexts, direction);
+ assertThat(mService.isGroupAvailableForStream(groupId)).isTrue();
+
+ injectAndVerifyDeviceDisconnected(mLeftDevice);
+
+ assertThat(mService.setAutoActiveModeState(groupId, false)).isFalse();
+ }
+
+ @Test
+ public void testAutoActiveMode_disabledWithSuccess() {
+ int groupId = 1;
+ /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
+ int direction = 1;
+ int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
+
+ /* Test scenario:
+ * 1. Connected two devices
+ * 2. Disconnect both devices
+ * 3. Verify that Auto Active Mode can be set.
+ */
+
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ // Checks group device lists for groupId 1
+ List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
+
+ assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice);
+
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
+ assertThat(mService.isGroupAvailableForStream(groupId)).isTrue();
+
+ injectAndVerifyDeviceDisconnected(mLeftDevice);
+ injectAndVerifyDeviceDisconnected(mRightDevice);
+
+ assertThat(mService.setAutoActiveModeState(groupId, false)).isTrue();
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isFalse();
+ }
+
+ @Test
+ public void testAutoActiveMode_whenUserSetsDeviceAsActive_resetToDefault() {
+ int groupId = 1;
+ /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
+ int direction = 1;
+ int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
+
+ /* Test scenario:
+ * 1. Connected two devices
+ * 2. Disconnect both devices
+ * 3. Disable Auto Active Mode
+ * 4. Connect at least one device
+ * 5. Set group as Active
+ * 6. Verify Auto Active Mode is back to default
+ */
+
+ mService.handleBluetoothEnabled();
+
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ // Checks group device lists for groupId 1
+ List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
+
+ assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice);
+
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
+ assertThat(mService.isGroupAvailableForStream(groupId)).isTrue();
+
+ injectAndVerifyDeviceDisconnected(mLeftDevice);
+ injectAndVerifyDeviceDisconnected(mRightDevice);
+
+ assertThat(mService.setAutoActiveModeState(groupId, false)).isTrue();
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isFalse();
+
+ injectAndVerifyDeviceConnected(mLeftDevice);
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
+ assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isTrue();
+ }
+
+ @Test
+ public void testAutoActiveMode_whenRemoteUsesTargetedAnnouncements_resetToDefault()
+ throws RemoteException {
+ int groupId = 1;
+ /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
+ int direction = 1;
+ int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
+
+ /* Test scenario:
+ * 1. Connected two devices
+ * 2. Disconnect both devices
+ * 3. Disable Auto Active Mode
+ * 4. Connect at least one device
+ * 5. Detect TA on remote device
+ * 6. Verify Auto Active Mode is back to default
+ */
+
+ mService.handleBluetoothEnabled();
+ ArgumentCaptor<IScannerCallback> scanCallbacks =
+ ArgumentCaptor.forClass(IScannerCallback.class);
+
+ connectTestDevice(mLeftDevice, groupId);
+ connectTestDevice(mRightDevice, groupId);
+
+ // Checks group device lists for groupId 1
+ List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
+
+ assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice);
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
+ assertThat(mService.isGroupAvailableForStream(groupId)).isTrue();
+
+ injectAndVerifyDeviceDisconnected(mLeftDevice);
+ injectAndVerifyDeviceDisconnected(mRightDevice);
+
+ assertThat(mService.setAutoActiveModeState(groupId, false)).isTrue();
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isFalse();
+
+ injectAndVerifyDeviceConnected(mLeftDevice);
+ injectAudioConfChanged(groupId, availableContexts, direction);
+
+ verify(mScanController).registerScannerInternal(scanCallbacks.capture(), any(), any());
+
+ ScanResult scanResult = new ScanResult(mRightDevice, null, 0, 0);
+
+ scanCallbacks.getValue().onScanResult(scanResult);
+
+ assertThat(mService.isAutoActiveModeEnabled(groupId)).isTrue();
+ }
+
/**
* Test the group is activated once the available contexts are back.
*
+ * <pre>
* Scenario:
- * 1. Have a group of 2 devices that initially does not expose any available contexts.
- * The group shall be inactive at this point.
- * 2. Once the available contexts are updated with non-zero value,
- * the group shall become active.
- * 3. The available contexts are changed to zero. Group becomes inactive.
- * 4. The available contexts are back again. Group becomes active.
+ * 1. Have a group of 2 devices that initially does not expose any available contexts. The
+ * group shall be inactive at this point.
+ * 2. Once the available contexts are updated with non-zero value, the group shall become
+ * active.
+ * 3. The available contexts are changed to zero. Group becomes inactive.
+ * 4. The available contexts are back again. Group becomes active.
+ * </pre>
*/
@Test
@EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS)
@@ -3069,37 +3120,26 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
// Checks group device lists for groupId 1
List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
- assertThat(groupDevicesById.size()).isEqualTo(2);
+ assertThat(groupDevicesById).hasSize(2);
assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
assertThat(groupDevicesById.contains(mRightDevice)).isTrue();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
assertThat(mService.setActiveDevice(mLeftDevice)).isFalse();
verify(mNativeInterface, times(0)).groupSetActive(groupId);
// Expect device to be active
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
verify(mNativeInterface).groupSetActive(groupId);
@@ -3115,8 +3155,7 @@ public class LeAudioServiceTest {
reset(mNativeInterface);
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
verify(mNativeInterface).groupSetActive(-1);
injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
@@ -3131,8 +3170,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
// Expect device to be active
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
verify(mNativeInterface).groupSetActive(groupId);
reset(mNativeInterface);
@@ -3149,14 +3187,16 @@ public class LeAudioServiceTest {
/**
* Test the group is activated once the available contexts are back.
*
+ * <pre>
* Scenario:
- * 1. Have a group of 2 devices. The available contexts are non-zero.
- * The group shall be active at this point.
- * 2. Once the available contexts are updated with zero value,
- * the group shall become inactive.
- * 3. All group devices are disconnected.
- * 4. Group devices are reconnected. The available contexts are still zero.
- * 4. The available contexts are updated with non-zero value. Group becomes active.
+ * 1. Have a group of 2 devices. The available contexts are non-zero. The group shall be
+ * active at this point.
+ * 2. Once the available contexts are updated with zero value, the group shall become
+ * inactive.
+ * 3. All group devices are disconnected.
+ * 4. Group devices are reconnected. The available contexts are still zero.
+ * 5. The available contexts are updated with non-zero value. Group becomes active.
+ * </pre>
*/
@Test
@EnableFlags(Flags.FLAG_LEAUDIO_UNICAST_NO_AVAILABLE_CONTEXTS)
@@ -3164,30 +3204,20 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
// Checks group device lists for groupId 1
List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
- assertThat(groupDevicesById.size()).isEqualTo(2);
+ assertThat(groupDevicesById).hasSize(2);
assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
assertThat(groupDevicesById.contains(mRightDevice)).isTrue();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId);
@@ -3204,8 +3234,7 @@ public class LeAudioServiceTest {
reset(mNativeInterface);
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
verify(mNativeInterface).groupSetActive(-1);
injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
@@ -3230,8 +3259,7 @@ public class LeAudioServiceTest {
assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse();
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
generateConnectionMessageFromNative(
mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
@@ -3240,18 +3268,18 @@ public class LeAudioServiceTest {
assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue();
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
generateConnectionMessageFromNative(
- mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+ mRightDevice,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
assertThat(mService.getConnectionState(mRightDevice))
.isEqualTo(BluetoothProfile.STATE_CONNECTED);
assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue();
// Expect device to be active
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
verify(mNativeInterface).groupSetActive(groupId);
@@ -3280,30 +3308,20 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
connectTestDevice(mLeftDevice, groupId);
connectTestDevice(mRightDevice, groupId);
// Checks group device lists for groupId 1
List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId);
- assertThat(groupDevicesById.size()).isEqualTo(2);
+ assertThat(groupDevicesById).hasSize(2);
assertThat(groupDevicesById.contains(mLeftDevice)).isTrue();
assertThat(groupDevicesById.contains(mRightDevice)).isTrue();
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId);
@@ -3329,8 +3347,7 @@ public class LeAudioServiceTest {
reset(mAudioManager);
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
generateConnectionMessageFromNative(
mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
@@ -3339,8 +3356,7 @@ public class LeAudioServiceTest {
assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue();
// Expect device to be inactive
- audioConfChangedEvent.valueInt5 = 0;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, 0, direction);
generateConnectionMessageFromNative(
mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
@@ -3349,8 +3365,7 @@ public class LeAudioServiceTest {
assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue();
// Expect device to be active
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
verify(mNativeInterface).groupSetActive(groupId);
@@ -3371,27 +3386,16 @@ public class LeAudioServiceTest {
int groupId = 1;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
// Not connected device
assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
connectTestDevice(mSingleDevice, testGroupId);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = groupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(groupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
verify(mNativeInterface).groupSetActive(groupId);
@@ -3439,8 +3443,6 @@ public class LeAudioServiceTest {
int secondGroupId = 2;
/* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
int direction = 1;
- int snkAudioLocation = 3;
- int srcAudioLocation = 4;
int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
List<BluetoothDevice> devices = new ArrayList<>();
@@ -3451,8 +3453,7 @@ public class LeAudioServiceTest {
assertThat(mService.getBroadcastToUnicastFallbackGroup())
.isEqualTo(BluetoothLeAudio.GROUP_ID_INVALID);
- // Connected device
- doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ // Connect device
devices.add(mSingleDevice);
connectTestDevice(mSingleDevice, testGroupId);
@@ -3460,15 +3461,7 @@ public class LeAudioServiceTest {
assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId);
// Add location support
- LeAudioStackEvent audioConfChangedEvent =
- new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
- audioConfChangedEvent.device = mSingleDevice;
- audioConfChangedEvent.valueInt1 = direction;
- audioConfChangedEvent.valueInt2 = firstGroupId;
- audioConfChangedEvent.valueInt3 = snkAudioLocation;
- audioConfChangedEvent.valueInt4 = srcAudioLocation;
- audioConfChangedEvent.valueInt5 = availableContexts;
- mService.messageFromNative(audioConfChangedEvent);
+ injectAudioConfChanged(firstGroupId, availableContexts, direction);
assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
verify(mNativeInterface).groupSetActive(firstGroupId);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
index 103b5b80f9..3c87186545 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java
@@ -17,6 +17,9 @@
package com.android.bluetooth.le_audio;
+import static com.android.bluetooth.le_audio.LeAudioStateMachine.CONNECT;
+import static com.android.bluetooth.le_audio.LeAudioStateMachine.DISCONNECT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.after;
@@ -34,12 +37,15 @@ import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.os.Bundle;
import android.os.HandlerThread;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -60,6 +66,7 @@ public class LeAudioStateMachineTest {
private static final int TIMEOUT_MS = 1000;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private AdapterService mAdapterService;
@Mock private LeAudioService mLeAudioService;
@@ -232,4 +239,41 @@ public class LeAudioStateMachineTest {
assertThat(mLeAudioStateMachine.getCurrentState())
.isInstanceOf(LeAudioStateMachine.Disconnected.class);
}
+
+ private void sendAndDispatchMessage(int what, Object obj) {
+ mLeAudioStateMachine.sendMessage(what, obj);
+ TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_LEAUDIO_SM_IGNORE_CONNECT_EVENTS_IN_CONNECTING_STATE)
+ public void connectEventNeglectedWhileInConnectingState() {
+ allowConnection(true);
+ doReturn(true).when(mLeAudioNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+ doReturn(true).when(mLeAudioNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
+
+ sendAndDispatchMessage(CONNECT, mTestDevice);
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(),
+ eq(BluetoothProfile.STATE_CONNECTING),
+ eq(BluetoothProfile.STATE_DISCONNECTED));
+ assertThat(mLeAudioStateMachine.getCurrentState())
+ .isInstanceOf(LeAudioStateMachine.Connecting.class);
+
+ // Dispatch CONNECT event twice more
+ sendAndDispatchMessage(CONNECT, mTestDevice);
+ sendAndDispatchMessage(CONNECT, mTestDevice);
+ sendAndDispatchMessage(DISCONNECT, mTestDevice);
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(),
+ eq(BluetoothProfile.STATE_DISCONNECTED),
+ eq(BluetoothProfile.STATE_CONNECTING));
+ assertThat(mLeAudioStateMachine.getCurrentState())
+ .isInstanceOf(LeAudioStateMachine.Disconnected.class);
+ TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
index 24d8e2c0b5..b442924dae 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
@@ -55,7 +55,7 @@ public class AppScanStatsTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private ScannerMap map;
- @Mock private TransitionalScanHelper mMockScanHelper;
+ @Mock private ScanController mMockScanController;
@Mock private AdapterService mAdapterService;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
@@ -79,10 +79,10 @@ public class AppScanStatsTest {
AppScanStats appScanStats =
new AppScanStats(
- name, source, map, mAdapterService, mMockScanHelper, getSystemClock());
+ name, source, map, mAdapterService, mMockScanController, getSystemClock());
assertThat(appScanStats.mScannerMap).isEqualTo(map);
- assertThat(appScanStats.mScanHelper).isEqualTo(mMockScanHelper);
+ assertThat(appScanStats.mScanController).isEqualTo(mMockScanController);
assertThat(appScanStats.isScanning()).isEqualTo(false);
}
@@ -94,7 +94,7 @@ public class AppScanStatsTest {
AppScanStats appScanStats =
new AppScanStats(
- name, source, map, mAdapterService, mMockScanHelper, getSystemClock());
+ name, source, map, mAdapterService, mMockScanController, getSystemClock());
ScanSettings settings = new ScanSettings.Builder().build();
List<ScanFilter> filters = new ArrayList<>();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java
new file mode 100644
index 0000000000..67f32c4f0c
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.le_scan;
+
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+
+import static com.android.bluetooth.le_scan.ScanController.DEFAULT_REPORT_DELAY_FLOOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.bluetooth.TestUtils.FakeTimeProvider;
+
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.LongStream;
+
+/** Test cases for {@link BatchScanThrottler}. */
+@SmallTest
+@RunWith(TestParameterInjector.class)
+public class BatchScanThrottlerTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ private FakeTimeProvider mTimeProvider;
+
+ @Before
+ public void setUp() {
+ mTimeProvider = new FakeTimeProvider();
+ }
+
+ private void advanceTime(long amountToAdvanceMillis) {
+ mTimeProvider.advanceTime(Duration.ofMillis(amountToAdvanceMillis));
+ }
+
+ @Test
+ public void basicThrottling(
+ @TestParameter boolean isFiltered, @TestParameter boolean isScreenOn) {
+ BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, isScreenOn);
+ if (!isScreenOn) {
+ advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS);
+ }
+ Set<ScanClient> clients =
+ Collections.singleton(
+ createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, isFiltered));
+ long[] backoffIntervals =
+ getBackoffIntervals(
+ isScreenOn
+ ? DEFAULT_REPORT_DELAY_FLOOR
+ : BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS);
+ for (long x : backoffIntervals) {
+ long expected = adjustExpectedInterval(x, isFiltered, isScreenOn);
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected);
+ }
+ long expected =
+ adjustExpectedInterval(
+ backoffIntervals[backoffIntervals.length - 1], isFiltered, isScreenOn);
+ // Ensure that subsequent calls continue to return the final throttled interval
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected);
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected);
+ }
+
+ @Test
+ public void screenOffDelayAndReset(@TestParameter boolean screenOnAtStart) {
+ BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, screenOnAtStart);
+ if (screenOnAtStart) {
+ throttler.onScreenOn(false);
+ }
+ Set<ScanClient> clients =
+ Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true));
+ long[] backoffIntervals = getBackoffIntervals(DEFAULT_REPORT_DELAY_FLOOR);
+ advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS - 1);
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+
+ backoffIntervals =
+ getBackoffIntervals(BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS);
+ advanceTime(1);
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients))
+ .isEqualTo(backoffIntervals[backoffIntervals.length - 1]);
+ }
+
+ @Test
+ public void testScreenOnReset() {
+ BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, false);
+ advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS);
+ Set<ScanClient> clients =
+ Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true));
+ long[] backoffIntervals =
+ getBackoffIntervals(BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS);
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+
+ throttler.onScreenOn(true);
+ backoffIntervals = getBackoffIntervals(DEFAULT_REPORT_DELAY_FLOOR);
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients))
+ .isEqualTo(backoffIntervals[backoffIntervals.length - 1]);
+ }
+
+ @Test
+ public void resetBackoff_restartsToFirstStage(@TestParameter boolean isScreenOn) {
+ BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, isScreenOn);
+ if (!isScreenOn) {
+ // Advance the time before we start the test to when the screen-off intervals should be
+ // used
+ advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS);
+ }
+ Set<ScanClient> clients =
+ Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true));
+ long[] backoffIntervals =
+ getBackoffIntervals(
+ isScreenOn
+ ? DEFAULT_REPORT_DELAY_FLOOR
+ : BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS);
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients))
+ .isEqualTo(backoffIntervals[backoffIntervals.length - 1]);
+
+ throttler.resetBackoff();
+ for (long x : backoffIntervals) {
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x);
+ }
+ assertThat(throttler.getBatchTriggerIntervalMillis(clients))
+ .isEqualTo(backoffIntervals[backoffIntervals.length - 1]);
+ }
+
+ private long adjustExpectedInterval(long interval, boolean isFiltered, boolean isScreenOn) {
+ if (isFiltered) {
+ return interval;
+ }
+ long threshold =
+ isScreenOn
+ ? BatchScanThrottler.UNFILTERED_DELAY_FLOOR_MS
+ : BatchScanThrottler.UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS;
+ return Math.max(interval, threshold);
+ }
+
+ private long[] getBackoffIntervals(long baseInterval) {
+ return LongStream.range(0, BatchScanThrottler.BACKOFF_MULTIPLIERS.length)
+ .map(x -> BatchScanThrottler.BACKOFF_MULTIPLIERS[(int) x] * baseInterval)
+ .toArray();
+ }
+
+ private ScanClient createBatchScanClient(long reportDelayMillis, boolean isFiltered) {
+ ScanSettings scanSettings =
+ new ScanSettings.Builder()
+ .setScanMode(SCAN_MODE_BALANCED)
+ .setReportDelay(reportDelayMillis)
+ .build();
+
+ return new ScanClient(1, scanSettings, createScanFilterList(isFiltered), 1);
+ }
+
+ private List<ScanFilter> createScanFilterList(boolean isFiltered) {
+ List<ScanFilter> scanFilterList = null;
+ if (isFiltered) {
+ scanFilterList = List.of(new ScanFilter.Builder().setDeviceName("TestName").build());
+ }
+ return scanFilterList;
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java
index e3db44b86b..ea051bc528 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
@@ -43,20 +44,20 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.WorkSource;
import android.os.test.TestLooper;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.CompanionManager;
-import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.gatt.GattNativeInterface;
import com.android.bluetooth.gatt.GattObjectsFactory;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -73,16 +74,16 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-/** Test cases for {@link TransitionalScanHelper}. */
+/** Test cases for {@link ScanController}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TransitionalScanHelperTest {
+@RunWith(TestParameterInjector.class)
+public class ScanControllerTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private ScannerMap mScannerMap;
@Mock private ScannerMap.ScannerApp mApp;
- @Mock private TransitionalScanHelper.PendingIntentInfo mPiInfo;
+ @Mock private ScanController.PendingIntentInfo mPiInfo;
@Mock private PeriodicScanManager mPeriodicScanManager;
@Mock private ScanManager mScanManager;
@Mock private Resources mResources;
@@ -96,7 +97,7 @@ public class TransitionalScanHelperTest {
private final AttributionSource mAttributionSource = mAdapter.getAttributionSource();
private final Context mContext = InstrumentationRegistry.getTargetContext();
- private TransitionalScanHelper mScanHelper;
+ private ScanController mScanController;
private CompanionManager mBtCompanionManager;
@Before
@@ -112,7 +113,7 @@ public class TransitionalScanHelperTest {
doReturn(mResources).when(mAdapterService).getResources();
doReturn(mContext.getPackageManager()).when(mAdapterService).getPackageManager();
- doReturn(mContext.getSharedPreferences("TransitionalScanHelperTest", Context.MODE_PRIVATE))
+ doReturn(mContext.getSharedPreferences("ScanControllerTest", Context.MODE_PRIVATE))
.when(mAdapterService)
.getSharedPreferences(anyString(), anyInt());
@@ -125,15 +126,15 @@ public class TransitionalScanHelperTest {
TestLooper testLooper = new TestLooper();
testLooper.startAutoDispatch();
- mScanHelper = new TransitionalScanHelper(mAdapterService, () -> false);
- mScanHelper.start(testLooper.getLooper());
+ mScanController = new ScanController(mAdapterService);
+ // mScanController.start(testLooper.getLooper());
- mScanHelper.setScannerMap(mScannerMap);
+ mScanController.setScannerMap(mScannerMap);
}
@After
public void tearDown() throws Exception {
- mScanHelper.stop();
+ mScanController.stop();
GattObjectsFactory.setInstanceForTesting(null);
ScanObjectsFactory.setInstanceForTesting(null);
@@ -141,7 +142,7 @@ public class TransitionalScanHelperTest {
@Test
public void testParseBatchTimestamp() {
- long timestampNanos = mScanHelper.parseTimestampNanos(new byte[] {-54, 7});
+ long timestampNanos = mScanController.parseTimestampNanos(new byte[] {-54, 7});
assertThat(timestampNanos).isEqualTo(99700000000L);
}
@@ -155,7 +156,7 @@ public class TransitionalScanHelperTest {
AppScanStats appScanStats = mock(AppScanStats.class);
doReturn(appScanStats).when(mScannerMap).getAppScanStatsById(scannerId);
- mScanHelper.continuePiStartScan(scannerId, mApp);
+ mScanController.continuePiStartScan(scannerId, mApp);
verify(appScanStats)
.recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId, null);
@@ -173,7 +174,7 @@ public class TransitionalScanHelperTest {
AppScanStats appScanStats = mock(AppScanStats.class);
doReturn(appScanStats).when(mScannerMap).getAppScanStatsById(scannerId);
- mScanHelper.continuePiStartScan(scannerId, mApp);
+ mScanController.continuePiStartScan(scannerId, mApp);
verify(appScanStats)
.recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId, null);
@@ -189,7 +190,8 @@ public class TransitionalScanHelperTest {
}
@Test
- public void onBatchScanReportsInternal_deliverBatchScan() throws RemoteException {
+ public void onBatchScanReportsInternal_deliverBatchScan_full(
+ @TestParameter boolean expectResults) throws RemoteException {
int status = 1;
int scannerId = 2;
int reportType = ScanManager.SCAN_RESULT_TYPE_FULL;
@@ -202,47 +204,79 @@ public class TransitionalScanHelperTest {
Set<ScanClient> scanClientSet = new HashSet<>();
ScanClient scanClient = new ScanClient(scannerId);
scanClient.associatedDevices = new ArrayList<>();
- scanClient.associatedDevices.add("02:00:00:00:00:00");
scanClient.scannerId = scannerId;
+ if (expectResults) {
+ scanClient.hasScanWithoutLocationPermission = true;
+ }
scanClientSet.add(scanClient);
doReturn(scanClientSet).when(mScanManager).getFullBatchScanQueue();
doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId);
+ IScannerCallback callback = mock(IScannerCallback.class);
+ mApp.mCallback = callback;
- mScanHelper.onBatchScanReportsInternal(
+ mScanController.onBatchScanReportsInternal(
status, scannerId, reportType, numRecords, recordData);
verify(mScanManager).callbackDone(scannerId, status);
+ if (expectResults) {
+ verify(callback).onBatchScanResults(any());
+ } else {
+ verify(callback, never()).onBatchScanResults(any());
+ }
+ }
- reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED;
- recordData =
+ @Test
+ public void onBatchScanReportsInternal_deliverBatchScan_truncated(
+ @TestParameter boolean expectResults) throws RemoteException {
+ int status = 1;
+ int scannerId = 2;
+ int reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED;
+ int numRecords = 1;
+ byte[] recordData =
new byte[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04, 0x02, 0x02, 0x00, 0x00, 0x02
};
+
+ Set<ScanClient> scanClientSet = new HashSet<>();
+ ScanClient scanClient = new ScanClient(scannerId);
+ scanClient.associatedDevices = new ArrayList<>();
+ if (expectResults) {
+ scanClient.associatedDevices.add("02:00:00:00:00:00");
+ }
+ scanClient.scannerId = scannerId;
+ scanClientSet.add(scanClient);
doReturn(scanClientSet).when(mScanManager).getBatchScanQueue();
+ doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId);
IScannerCallback callback = mock(IScannerCallback.class);
mApp.mCallback = callback;
- mScanHelper.onBatchScanReportsInternal(
+ mScanController.onBatchScanReportsInternal(
status, scannerId, reportType, numRecords, recordData);
- verify(callback).onBatchScanResults(any());
+ verify(mScanManager).callbackDone(scannerId, status);
+ if (expectResults) {
+ verify(callback).onBatchScanResults(any());
+ } else {
+ verify(callback, never()).onBatchScanResults(any());
+ }
}
@Test
public void enforceReportDelayFloor() {
- long reportDelayFloorHigher = TransitionalScanHelper.DEFAULT_REPORT_DELAY_FLOOR + 1;
+ long reportDelayFloorHigher = ScanController.DEFAULT_REPORT_DELAY_FLOOR + 1;
ScanSettings scanSettings =
new ScanSettings.Builder().setReportDelay(reportDelayFloorHigher).build();
- ScanSettings newScanSettings = mScanHelper.enforceReportDelayFloor(scanSettings);
+ ScanSettings newScanSettings = mScanController.enforceReportDelayFloor(scanSettings);
assertThat(newScanSettings.getReportDelayMillis())
.isEqualTo(scanSettings.getReportDelayMillis());
ScanSettings scanSettingsFloor = new ScanSettings.Builder().setReportDelay(1).build();
- ScanSettings newScanSettingsFloor = mScanHelper.enforceReportDelayFloor(scanSettingsFloor);
+ ScanSettings newScanSettingsFloor =
+ mScanController.enforceReportDelayFloor(scanSettingsFloor);
assertThat(newScanSettingsFloor.getReportDelayMillis())
- .isEqualTo(TransitionalScanHelper.DEFAULT_REPORT_DELAY_FLOOR);
+ .isEqualTo(ScanController.DEFAULT_REPORT_DELAY_FLOOR);
}
@Test
@@ -253,7 +287,7 @@ public class TransitionalScanHelperTest {
AppScanStats appScanStats = mock(AppScanStats.class);
doReturn(appScanStats).when(mScannerMap).getAppScanStatsByUid(Binder.getCallingUid());
- mScanHelper.registerScanner(callback, workSource, mAttributionSource);
+ mScanController.registerScanner(callback, workSource, mAttributionSource);
verify(mScannerMap)
.add(
any(),
@@ -261,7 +295,7 @@ public class TransitionalScanHelperTest {
eq(workSource),
eq(callback),
any(),
- eq(mScanHelper));
+ eq(mScanController));
verify(mScanManager).registerScanner(any());
}
@@ -269,7 +303,7 @@ public class TransitionalScanHelperTest {
public void flushPendingBatchResults() {
int scannerId = 3;
- mScanHelper.flushPendingBatchResults(scannerId, mAttributionSource);
+ mScanController.flushPendingBatchResults(scannerId, mAttributionSource);
verify(mScanManager).flushBatchScanResults(new ScanClient(scannerId));
}
@@ -313,7 +347,7 @@ public class TransitionalScanHelperTest {
// Simulate remote client crash
doThrow(new RemoteException()).when(callback).onScanResult(any());
- mScanHelper.onScanResult(
+ mScanController.onScanResult(
eventType,
addressType,
address,
@@ -337,7 +371,7 @@ public class TransitionalScanHelperTest {
int timeout = 2;
IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
- mScanHelper.registerSync(scanResult, skip, timeout, callback, mAttributionSource);
+ mScanController.registerSync(scanResult, skip, timeout, callback, mAttributionSource);
verify(mPeriodicScanManager).startSync(scanResult, skip, timeout, callback);
}
@@ -346,7 +380,7 @@ public class TransitionalScanHelperTest {
int serviceData = 1;
int syncHandle = 2;
- mScanHelper.transferSync(mDevice, serviceData, syncHandle, mAttributionSource);
+ mScanController.transferSync(mDevice, serviceData, syncHandle, mAttributionSource);
verify(mPeriodicScanManager).transferSync(mDevice, serviceData, syncHandle);
}
@@ -356,7 +390,8 @@ public class TransitionalScanHelperTest {
int advHandle = 2;
IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
- mScanHelper.transferSetInfo(mDevice, serviceData, advHandle, callback, mAttributionSource);
+ mScanController.transferSetInfo(
+ mDevice, serviceData, advHandle, callback, mAttributionSource);
verify(mPeriodicScanManager).transferSetInfo(mDevice, serviceData, advHandle, callback);
}
@@ -364,24 +399,13 @@ public class TransitionalScanHelperTest {
public void unregisterSync() {
IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
- mScanHelper.unregisterSync(callback, mAttributionSource);
+ mScanController.unregisterSync(callback, mAttributionSource);
verify(mPeriodicScanManager).stopSync(callback);
}
@Test
- public void getCurrentUsedTrackingAdvertisement() {
- mScanHelper.getCurrentUsedTrackingAdvertisement();
- verify(mScanManager).getCurrentUsedTrackingAdvertisement();
- }
-
- @Test
- public void cleanUp_doesNotCrash() {
- mScanHelper.cleanup();
- }
-
- @Test
public void profileConnectionStateChanged_notifyScanManager() {
- mScanHelper.notifyProfileConnectionStateChange(
+ mScanController.notifyProfileConnectionStateChange(
BluetoothProfile.A2DP,
BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_CONNECTED);
@@ -401,7 +425,7 @@ public class TransitionalScanHelperTest {
byte[] scanRsp = new byte[] {0x04};
int filtIndex = 5;
- int advState = TransitionalScanHelper.ADVT_STATE_ONFOUND;
+ int advState = ScanController.ADVT_STATE_ONFOUND;
int advInfoPresent = 7;
String address = "00:11:22:33:FF:EE";
int addrType = BluetoothDevice.ADDRESS_TYPE_RANDOM;
@@ -422,7 +446,7 @@ public class TransitionalScanHelperTest {
IScannerCallback callback = mock(IScannerCallback.class);
app.mCallback = callback;
- app.mInfo = mock(TransitionalScanHelper.PendingIntentInfo.class);
+ app.mInfo = mock(ScanController.PendingIntentInfo.class);
doReturn(app).when(mScannerMap).getById(scannerId);
doReturn(scanClientSet).when(mScanManager).getRegularScanQueue();
@@ -443,7 +467,7 @@ public class TransitionalScanHelperTest {
rssiValue,
timeStamp);
- mScanHelper.onTrackAdvFoundLost(advtFilterOnFoundOnLostInfo);
+ mScanController.onTrackAdvFoundLost(advtFilterOnFoundOnLostInfo);
ArgumentCaptor<ScanResult> result = ArgumentCaptor.forClass(ScanResult.class);
verify(callback).onFoundOrLost(eq(true), result.capture());
assertThat(result.getValue().getDevice()).isNotNull();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
index e0f908d70b..041a982a9b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
@@ -126,7 +126,7 @@ public class ScanManagerTest {
@Mock private LocationManager mLocationManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private ScanNativeInterface mScanNativeInterface;
- @Mock private TransitionalScanHelper mScanHelper;
+ @Mock private ScanController mScanController;
@Spy private GattObjectsFactory mGattObjectsFactory = GattObjectsFactory.getInstance();
@Spy private ScanObjectsFactory mScanObjectsFactory = ScanObjectsFactory.getInstance();
@@ -238,7 +238,7 @@ public class ScanManagerTest {
mScanManager =
new ScanManager(
mAdapterService,
- mScanHelper,
+ mScanController,
mBluetoothAdapterProxy,
mLooper.getLooper(),
mTimeProvider);
@@ -251,7 +251,7 @@ public class ScanManagerTest {
null,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
}
@@ -1045,7 +1045,7 @@ public class ScanManagerTest {
assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client);
assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client);
assertThat(mScanManager.getBatchScanQueue()).contains(client);
- assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
+ assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode);
}
}
@@ -1075,13 +1075,13 @@ public class ScanManagerTest {
sendMessageWaitForProcessed(createStartStopScanMessage(true, client));
assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client);
assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client);
- assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
+ assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode);
// Turn on screen
sendMessageWaitForProcessed(createScreenOnOffMessage(true));
assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client);
assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client);
assertThat(mScanManager.getBatchScanQueue()).contains(client);
- assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode);
+ assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode);
}
}
@@ -1152,7 +1152,7 @@ public class ScanManagerTest {
assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client);
assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client);
assertThat(mScanManager.getBatchScanQueue()).contains(client);
- assertThat(mScanManager.getBatchScanParams().scanMode)
+ assertThat(mScanManager.getBatchScanParams().mScanMode)
.isEqualTo(expectedScanMode);
// Turn on screen
sendMessageWaitForProcessed(createScreenOnOffMessage(true));
@@ -1166,7 +1166,7 @@ public class ScanManagerTest {
assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client);
assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client);
assertThat(mScanManager.getBatchScanQueue()).contains(client);
- assertThat(mScanManager.getBatchScanParams().scanMode)
+ assertThat(mScanManager.getBatchScanParams().mScanMode)
.isEqualTo(expectedScanMode);
});
}
@@ -1257,7 +1257,7 @@ public class ScanManagerTest {
source,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
// Create scan client for the app, which also records scan start
ScanClient client = createScanClient(isFiltered, scanMode, UID, appScanStats);
@@ -1328,7 +1328,7 @@ public class ScanManagerTest {
source1,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
// Create scan client for the first app
ScanClient client1 =
@@ -1350,7 +1350,7 @@ public class ScanManagerTest {
source2,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
// Create scan client for the second app
ScanClient client2 = createScanClient(isFiltered, SCAN_MODE_BALANCED, UID_2, appScanStats2);
@@ -1386,7 +1386,7 @@ public class ScanManagerTest {
source3,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
// Create scan client for the third app
ScanClient client3 =
@@ -1423,7 +1423,7 @@ public class ScanManagerTest {
source4,
null,
mAdapterService,
- mScanHelper,
+ mScanController,
mTimeProvider));
// Create scan client for the fourth app
ScanClient client4 =
@@ -1909,7 +1909,7 @@ public class ScanManagerTest {
mScanManager =
new ScanManager(
mAdapterService,
- mScanHelper,
+ mScanController,
mBluetoothAdapterProxy,
mLooper.getLooper(),
mTimeProvider);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScannerMapTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScannerMapTest.java
index 83cdcceae9..b8ad36f891 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScannerMapTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScannerMapTest.java
@@ -61,7 +61,7 @@ public class ScannerMapTest {
@Mock private AdapterService mAdapterService;
@Mock private PackageManager mMockPackageManager;
- @Mock private TransitionalScanHelper mMockTransitionalScanHelper;
+ @Mock private ScanController mMockScanController;
@Mock private IScannerCallback mMockScannerCallback;
private final AttributionSource mAttributionSource =
InstrumentationRegistry.getTargetContext().getAttributionSource();
@@ -85,8 +85,7 @@ public class ScannerMapTest {
@Test
public void getByMethodsWithPii() {
ScannerMap scannerMap = new ScannerMap();
- TransitionalScanHelper.PendingIntentInfo info =
- new TransitionalScanHelper.PendingIntentInfo();
+ ScanController.PendingIntentInfo info = new ScanController.PendingIntentInfo();
info.callingUid = UID;
info.callingPackage = APP_NAME;
info.intent =
@@ -98,11 +97,7 @@ public class ScannerMapTest {
UUID uuid = UUID.randomUUID();
ScannerMap.ScannerApp app =
scannerMap.add(
- uuid,
- mAttributionSource,
- info,
- mAdapterService,
- mMockTransitionalScanHelper);
+ uuid, mAttributionSource, info, mAdapterService, mMockScanController);
app.mId = SCANNER_ID;
ScannerMap.ScannerApp scannerMapById = scannerMap.getById(SCANNER_ID);
@@ -132,7 +127,7 @@ public class ScannerMapTest {
null,
mMockScannerCallback,
mAdapterService,
- mMockTransitionalScanHelper);
+ mMockScanController);
int appUid = Binder.getCallingUid();
app.mId = SCANNER_ID;
@@ -161,7 +156,7 @@ public class ScannerMapTest {
null,
mMockScannerCallback,
mAdapterService,
- mMockTransitionalScanHelper);
+ mMockScanController);
app.mId = SCANNER_ID;
ScannerMap.ScannerApp scannerMapById = scannerMap.getById(SCANNER_ID);
@@ -181,7 +176,7 @@ public class ScannerMapTest {
null,
mMockScannerCallback,
mAdapterService,
- mMockTransitionalScanHelper);
+ mMockScanController);
scannerMap.dump(sb);
scannerMap.dumpApps(sb, ProfileService::println);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java
index 4993b863a6..8e1be7da7f 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapAccountItemTest.java
@@ -202,7 +202,7 @@ public class BluetoothMapAccountItemTest {
TEST_UCI,
TEST_UCI_PREFIX);
- assertThat(accountItem).isNotEqualTo(null);
+ assertThat(accountItem).isNotNull();
}
@SuppressWarnings("EqualsIncompatibleType")
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
index 316dd5f621..44cea7dd97 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
@@ -55,7 +55,6 @@ import com.android.obex.ResponseCodes;
import com.google.android.mms.pdu.PduHeaders;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -233,9 +232,9 @@ public class BluetoothMapContentObserverTest {
() -> observer.pushMessage(message, folderElement, appParams, null));
// Validate that 3 addresses were inserted into the database with 2 being the recipients
- Assert.assertEquals(3, mProvider.mContents.size());
- assertThat(mProvider.mContents.contains(TEST_NUMBER_ONE)).isTrue();
- assertThat(mProvider.mContents.contains(TEST_NUMBER_TWO)).isTrue();
+ assertThat(mProvider.mContents).hasSize(3);
+ assertThat(mProvider.mContents).contains(TEST_NUMBER_ONE);
+ assertThat(mProvider.mContents).contains(TEST_NUMBER_TWO);
}
@Test
@@ -305,47 +304,40 @@ public class BluetoothMapContentObserverTest {
@Test
public void testSetContactList() {
- Map<String, BluetoothMapConvoContactElement> map = Map.of();
+ mObserver.setContactList(Map.of(), true);
- mObserver.setContactList(map, true);
-
- Assert.assertEquals(mObserver.getContactList(), map);
+ assertThat(mObserver.getContactList()).isEmpty();
}
@Test
public void testSetMsgListSms() {
- Map<Long, BluetoothMapContentObserver.Msg> map = Map.of();
-
- mObserver.setMsgListSms(map, true);
+ mObserver.setMsgListSms(Map.of(), true);
- Assert.assertEquals(mObserver.getMsgListSms(), map);
+ assertThat(mObserver.getMsgListSms()).isEmpty();
}
@Test
public void testSetMsgListMsg() {
- Map<Long, BluetoothMapContentObserver.Msg> map = Map.of();
+ mObserver.setMsgListMsg(Map.of(), true);
- mObserver.setMsgListMsg(map, true);
-
- Assert.assertEquals(mObserver.getMsgListMsg(), map);
+ assertThat(mObserver.getMsgListMsg()).isEmpty();
}
@Test
public void testSetMsgListMms() {
- Map<Long, BluetoothMapContentObserver.Msg> map = Map.of();
+ mObserver.setMsgListMms(Map.of(), true);
- mObserver.setMsgListMms(map, true);
-
- Assert.assertEquals(mObserver.getMsgListMms(), map);
+ assertThat(mObserver.getMsgListMms()).isEmpty();
}
@Test
public void testSetNotificationRegistration_withNullHandler() throws Exception {
when(mClient.getMessageHandler()).thenReturn(null);
- Assert.assertEquals(
- mObserver.setNotificationRegistration(BluetoothMapAppParams.NOTIFICATION_STATUS_NO),
- ResponseCodes.OBEX_HTTP_UNAVAILABLE);
+ assertThat(
+ mObserver.setNotificationRegistration(
+ BluetoothMapAppParams.NOTIFICATION_STATUS_NO))
+ .isEqualTo(ResponseCodes.OBEX_HTTP_UNAVAILABLE);
}
@Test
@@ -357,9 +349,10 @@ public class BluetoothMapContentObserverTest {
when(mClient.getMessageHandler()).thenReturn(handler);
when(mClient.isValidMnsRecord()).thenReturn(false);
- Assert.assertEquals(
- mObserver.setNotificationRegistration(BluetoothMapAppParams.NOTIFICATION_STATUS_NO),
- ResponseCodes.OBEX_HTTP_OK);
+ assertThat(
+ mObserver.setNotificationRegistration(
+ BluetoothMapAppParams.NOTIFICATION_STATUS_NO))
+ .isEqualTo(ResponseCodes.OBEX_HTTP_OK);
}
@Test
@@ -371,9 +364,10 @@ public class BluetoothMapContentObserverTest {
when(mClient.getMessageHandler()).thenReturn(handler);
when(mClient.isValidMnsRecord()).thenReturn(true);
- Assert.assertEquals(
- mObserver.setNotificationRegistration(BluetoothMapAppParams.NOTIFICATION_STATUS_NO),
- ResponseCodes.OBEX_HTTP_OK);
+ assertThat(
+ mObserver.setNotificationRegistration(
+ BluetoothMapAppParams.NOTIFICATION_STATUS_NO))
+ .isEqualTo(ResponseCodes.OBEX_HTTP_OK);
}
@Test
@@ -392,7 +386,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE))
.isTrue();
- Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
+ assertThat(msg.flagRead).isEqualTo(TEST_STATUS_VALUE);
}
@Test
@@ -411,7 +405,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE))
.isTrue();
- Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
+ assertThat(msg.flagRead).isEqualTo(TEST_STATUS_VALUE);
}
@Test
@@ -429,7 +423,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE))
.isTrue();
- Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
+ assertThat(msg.flagRead).isEqualTo(TEST_STATUS_VALUE);
}
@Test
@@ -439,7 +433,7 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListMms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID});
cursor.addRow(new Object[] {TEST_THREAD_ID});
@@ -452,7 +446,7 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.deleteMessageMms(TEST_HANDLE_ONE)).isTrue();
- Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID);
+ assertThat(msg.threadId).isEqualTo(BluetoothMapContentObserver.DELETED_THREAD_ID);
}
@Test
@@ -485,7 +479,7 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListSms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID});
cursor.addRow(new Object[] {TEST_THREAD_ID});
@@ -498,7 +492,7 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.deleteMessageSms(TEST_HANDLE_ONE)).isTrue();
- Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID);
+ assertThat(msg.threadId).isEqualTo(BluetoothMapContentObserver.DELETED_THREAD_ID);
}
@Test
@@ -531,8 +525,8 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListMms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_ALL);
MatrixCursor cursor =
new MatrixCursor(
@@ -556,8 +550,8 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue();
- Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX);
+ assertThat(msg.threadId).isEqualTo(TEST_OLD_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_INBOX);
}
@Test
@@ -567,8 +561,8 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListMms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_ALL);
MatrixCursor cursor =
new MatrixCursor(
@@ -592,8 +586,8 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue();
- Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX);
+ assertThat(msg.threadId).isEqualTo(TEST_OLD_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_INBOX);
}
@Test
@@ -603,8 +597,8 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListMms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_ALL);
MatrixCursor cursor =
new MatrixCursor(
@@ -622,8 +616,8 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue();
// Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Sms.MESSAGE_TYPE_ALL);
}
@Test
@@ -633,8 +627,8 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListSms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Sms.MESSAGE_TYPE_ALL);
MatrixCursor cursor = new MatrixCursor(new String[] {Sms.THREAD_ID, Sms.ADDRESS});
cursor.addRow(new Object[] {BluetoothMapContentObserver.DELETED_THREAD_ID, TEST_ADDRESS});
@@ -650,8 +644,8 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)).isTrue();
- Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
- Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_INBOX);
+ assertThat(msg.threadId).isEqualTo(TEST_OLD_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Sms.MESSAGE_TYPE_INBOX);
}
@Test
@@ -661,8 +655,8 @@ public class BluetoothMapContentObserverTest {
createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL, TEST_THREAD_ID);
map.put(TEST_HANDLE_ONE, msg);
mObserver.setMsgListSms(map, true);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Sms.MESSAGE_TYPE_ALL);
MatrixCursor cursor = new MatrixCursor(new String[] {Sms.THREAD_ID, Sms.ADDRESS});
cursor.addRow(new Object[] {TEST_THREAD_ID, TEST_ADDRESS});
@@ -676,8 +670,8 @@ public class BluetoothMapContentObserverTest {
assertThat(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)).isTrue();
// Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.type).isEqualTo(Sms.MESSAGE_TYPE_ALL);
}
@Test
@@ -691,11 +685,11 @@ public class BluetoothMapContentObserverTest {
BluetoothMapContentObserver.PushMsgInfo msgInfo =
new BluetoothMapContentObserver.PushMsgInfo(id, transparent, retry, phone, uri);
- Assert.assertEquals(msgInfo.id, id);
- Assert.assertEquals(msgInfo.transparent, transparent);
- Assert.assertEquals(msgInfo.retry, retry);
- Assert.assertEquals(msgInfo.phone, phone);
- Assert.assertEquals(msgInfo.uri, uri);
+ assertThat(msgInfo.id).isEqualTo(id);
+ assertThat(msgInfo.transparent).isEqualTo(transparent);
+ assertThat(msgInfo.retry).isEqualTo(retry);
+ assertThat(msgInfo.phone).isEqualTo(phone);
+ assertThat(msgInfo.uri).isEqualTo(uri);
}
@Test
@@ -719,7 +713,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE,
BluetoothMapAppParams.STATUS_VALUE_YES))
.isTrue();
- Assert.assertEquals(msg.folderId, TEST_DELETE_FOLDER_ID);
+ assertThat(msg.folderId).isEqualTo(TEST_DELETE_FOLDER_ID);
}
@Test
@@ -768,7 +762,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE,
BluetoothMapAppParams.STATUS_VALUE_NO))
.isTrue();
- Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
+ assertThat(msg.folderId).isEqualTo(TEST_INBOX_FOLDER_ID);
}
@Test
@@ -797,7 +791,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE,
BluetoothMapAppParams.STATUS_VALUE_NO))
.isTrue();
- Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
+ assertThat(msg.folderId).isEqualTo(TEST_INBOX_FOLDER_ID);
}
@Test
@@ -828,7 +822,7 @@ public class BluetoothMapContentObserverTest {
TEST_HANDLE_ONE,
BluetoothMapAppParams.STATUS_VALUE_NO))
.isTrue();
- Assert.assertEquals(msg.folderId, TEST_OLD_FOLDER_ID);
+ assertThat(msg.folderId).isEqualTo(TEST_OLD_FOLDER_ID);
}
@Test
@@ -949,10 +943,10 @@ public class BluetoothMapContentObserverTest {
mObserver.initMsgList();
BluetoothMapContentObserver.Msg msg = mObserver.getMsgListSms().get((long) TEST_ID);
- Assert.assertEquals(msg.id, TEST_ID);
- Assert.assertEquals(msg.type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ONE);
+ assertThat(msg.id).isEqualTo(TEST_ID);
+ assertThat(msg.type).isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.flagRead).isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -988,10 +982,10 @@ public class BluetoothMapContentObserverTest {
mObserver.initMsgList();
BluetoothMapContentObserver.Msg msg = mObserver.getMsgListMms().get((long) TEST_ID);
- Assert.assertEquals(msg.id, TEST_ID);
- Assert.assertEquals(msg.type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
- Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ZERO);
+ assertThat(msg.id).isEqualTo(TEST_ID);
+ assertThat(msg.type).isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(msg.threadId).isEqualTo(TEST_THREAD_ID);
+ assertThat(msg.flagRead).isEqualTo(TEST_READ_FLAG_ZERO);
}
@Test
@@ -1028,9 +1022,9 @@ public class BluetoothMapContentObserverTest {
mObserver.initMsgList();
BluetoothMapContentObserver.Msg msg = mObserver.getMsgListMsg().get((long) TEST_ID);
- Assert.assertEquals(msg.id, TEST_ID);
- Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
- Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ONE);
+ assertThat(msg.id).isEqualTo(TEST_ID);
+ assertThat(msg.folderId).isEqualTo(TEST_INBOX_FOLDER_ID);
+ assertThat(msg.flagRead).isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1077,16 +1071,16 @@ public class BluetoothMapContentObserverTest {
BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
- Assert.assertEquals(contactElement.getName(), TEST_NAME);
- Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
- Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
- Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
- Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
- Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
- Assert.assertEquals(
- contactElement.getLastActivityString(), format.format(TEST_LAST_ACTIVITY));
- Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
+ assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI);
+ assertThat(contactElement.getName()).isEqualTo(TEST_NAME);
+ assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME);
+ assertThat(contactElement.getBtUid()).isEqualTo(TEST_BT_UID);
+ assertThat(contactElement.getChatState()).isEqualTo(TEST_CHAT_STATE);
+ assertThat(contactElement.getPresenceStatus()).isEqualTo(TEST_STATUS_TEXT);
+ assertThat(contactElement.getPresenceAvailability()).isEqualTo(TEST_PRESENCE_STATE);
+ assertThat(contactElement.getLastActivityString())
+ .isEqualTo(format.format(TEST_LAST_ACTIVITY));
+ assertThat(contactElement.getPriority()).isEqualTo(TEST_PRIORITY);
}
@Test
@@ -1130,11 +1124,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type, TEST_INBOX_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_INBOX_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1180,11 +1174,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type, TEST_INBOX_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_INBOX_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1215,11 +1209,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type, TEST_INBOX_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_INBOX_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1250,11 +1244,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId, TEST_DELETE_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId)
+ .isEqualTo(TEST_DELETE_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1287,11 +1281,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId, TEST_SENT_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId)
+ .isEqualTo(TEST_SENT_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1328,11 +1322,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId, TEST_SENT_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId)
+ .isEqualTo(TEST_SENT_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1364,11 +1358,11 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMsg(TEST_URI);
- Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId, TEST_INBOX_FOLDER_ID);
- Assert.assertEquals(
- mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId)
+ .isEqualTo(TEST_INBOX_FOLDER_ID);
+ assertThat(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1414,12 +1408,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1465,12 +1460,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1521,7 +1517,7 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(null, mObserver.getMsgListMms().get(TEST_HANDLE_ONE));
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE)).isNull();
}
@Test
@@ -1555,12 +1551,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1595,12 +1592,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1635,12 +1633,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1675,13 +1674,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
- BluetoothMapContentObserver.DELETED_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(BluetoothMapContentObserver.DELETED_THREAD_ID);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1717,12 +1716,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesMms();
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId, undeletedThreadId);
- Assert.assertEquals(
- mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_MMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(undeletedThreadId);
+ assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1766,13 +1766,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_INBOX);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_INBOX);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1814,12 +1814,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1865,7 +1866,7 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(null, mObserver.getMsgListSms().get(TEST_HANDLE_ONE));
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE)).isNull();
}
@Test
@@ -1892,12 +1893,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1924,12 +1926,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId, TEST_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(TEST_THREAD_ID);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1959,13 +1962,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
- BluetoothMapContentObserver.DELETED_THREAD_ID);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(BluetoothMapContentObserver.DELETED_THREAD_ID);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -1993,12 +1996,13 @@ public class BluetoothMapContentObserverTest {
mObserver.handleMsgListChangesSms();
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
- Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId, undeletedThreadId);
- Assert.assertEquals(
- mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead, TEST_READ_FLAG_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id).isEqualTo(TEST_HANDLE_ONE);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type)
+ .isEqualTo(TEST_SMS_TYPE_ALL);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId)
+ .isEqualTo(undeletedThreadId);
+ assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead)
+ .isEqualTo(TEST_READ_FLAG_ONE);
}
@Test
@@ -2074,7 +2078,7 @@ public class BluetoothMapContentObserverTest {
mObserver.actionMessageSentDisconnected(mContext, mIntent, 1);
- assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue();
+ assertThat(mmsMsgList).containsKey(TEST_HANDLE_ONE);
}
@Test
@@ -2111,7 +2115,7 @@ public class BluetoothMapContentObserverTest {
mObserver.actionMmsSent(mContext, mIntent, 1, mmsMsgList);
- assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue();
+ assertThat(mmsMsgList).containsKey(TEST_HANDLE_ONE);
}
@Test
@@ -2158,7 +2162,7 @@ public class BluetoothMapContentObserverTest {
mObserver.actionMmsSent(mContext, mIntent, Activity.RESULT_OK, mmsMsgList);
- assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue();
+ assertThat(mmsMsgList).containsKey(TEST_HANDLE_ONE);
}
@Test
@@ -2176,7 +2180,7 @@ public class BluetoothMapContentObserverTest {
mObserver.actionMmsSent(mContext, mIntent, Activity.RESULT_FIRST_USER, mmsMsgList);
- Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_OUTBOX);
+ assertThat(msg.type).isEqualTo(Mms.MESSAGE_BOX_OUTBOX);
}
@Test
@@ -2315,16 +2319,16 @@ public class BluetoothMapContentObserverTest {
BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
- Assert.assertEquals(contactElement.getName(), TEST_NAME);
- Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
- Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
- Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
- Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
- Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
- Assert.assertEquals(
- contactElement.getLastActivityString(), format.format(TEST_LAST_ACTIVITY));
- Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
+ assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI);
+ assertThat(contactElement.getName()).isEqualTo(TEST_NAME);
+ assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME);
+ assertThat(contactElement.getBtUid()).isEqualTo(TEST_BT_UID);
+ assertThat(contactElement.getChatState()).isEqualTo(TEST_CHAT_STATE);
+ assertThat(contactElement.getPresenceStatus()).isEqualTo(TEST_STATUS_TEXT);
+ assertThat(contactElement.getPresenceAvailability()).isEqualTo(TEST_PRESENCE_STATE);
+ assertThat(contactElement.getLastActivityString())
+ .isEqualTo(format.format(TEST_LAST_ACTIVITY));
+ assertThat(contactElement.getPriority()).isEqualTo(TEST_PRIORITY);
}
@Test
@@ -2386,16 +2390,16 @@ public class BluetoothMapContentObserverTest {
BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
- Assert.assertEquals(contactElement.getName(), TEST_NAME);
- Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
- Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
- Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
- Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
- Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
- Assert.assertEquals(
- contactElement.getLastActivityString(), format.format(TEST_LAST_ACTIVITY));
- Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
+ assertThat(contactElement.getContactId()).isEqualTo(TEST_UCI);
+ assertThat(contactElement.getName()).isEqualTo(TEST_NAME);
+ assertThat(contactElement.getDisplayName()).isEqualTo(TEST_DISPLAY_NAME);
+ assertThat(contactElement.getBtUid()).isEqualTo(TEST_BT_UID);
+ assertThat(contactElement.getChatState()).isEqualTo(TEST_CHAT_STATE);
+ assertThat(contactElement.getPresenceStatus()).isEqualTo(TEST_STATUS_TEXT);
+ assertThat(contactElement.getPresenceAvailability()).isEqualTo(TEST_PRESENCE_STATE);
+ assertThat(contactElement.getLastActivityString())
+ .isEqualTo(format.format(TEST_LAST_ACTIVITY));
+ assertThat(contactElement.getPriority()).isEqualTo(TEST_PRIORITY);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
index c464035f44..99d69c8084 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
@@ -1106,8 +1106,8 @@ public class BluetoothMapContentTest {
assertThat(messageMimeParsed.getVersionString())
.isEqualTo("VERSION:" + mContent.mMessageVersion);
assertThat(messageMimeParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
- assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1);
- assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1);
+ assertThat(messageMimeParsed.getRecipients()).hasSize(1);
+ assertThat(messageMimeParsed.getOriginators()).hasSize(1);
assertThat(messageMimeParsed.getOriginators().get(0).getName()).isEmpty();
assertThat(messageMimeParsed.getRecipients().get(0).getName())
.isEqualTo(TEST_FORMATTED_NAME);
@@ -1166,8 +1166,8 @@ public class BluetoothMapContentTest {
assertThat(messageMimeParsed.getVersionString())
.isEqualTo("VERSION:" + mContent.mMessageVersion);
assertThat(messageMimeParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
- assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1);
- assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1);
+ assertThat(messageMimeParsed.getRecipients()).hasSize(1);
+ assertThat(messageMimeParsed.getOriginators()).hasSize(1);
assertThat(messageMimeParsed.getOriginators().get(0).getName())
.isEqualTo(TEST_FORMATTED_NAME);
assertThat(messageMimeParsed.getRecipients().get(0).getName()).isEmpty();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java
index 87739756e6..07dad9acb5 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingElementTest.java
@@ -124,13 +124,13 @@ public class BluetoothMapConvoListingElementTest {
@Test
public void removeContactWithObject() {
mListingElement.removeContact(TEST_CONTACT_ELEMENT_TWO);
- assertThat(mListingElement.getContacts().size()).isEqualTo(1);
+ assertThat(mListingElement.getContacts()).hasSize(1);
}
@Test
public void removeContactWithIndex() {
mListingElement.removeContact(1);
- assertThat(mListingElement.getContacts().size()).isEqualTo(1);
+ assertThat(mListingElement.getContacts()).hasSize(1);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java
index 4f8629f577..97ec95ea25 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapConvoListingTest.java
@@ -74,19 +74,19 @@ public class BluetoothMapConvoListingTest {
@Test
public void segment_whenCountIsLessThanOne_returnsOffsetToEnd() {
mListing.segment(0, 1);
- assertThat(mListing.getList().size()).isEqualTo(2);
+ assertThat(mListing.getList()).hasSize(2);
}
@Test
public void segment_whenOffsetIsBiggerThanSize_returnsEmptyList() {
mListing.segment(1, 4);
- assertThat(mListing.getList().size()).isEqualTo(0);
+ assertThat(mListing.getList()).isEmpty();
}
@Test
public void segment_whenOffsetCountCombinationIsValid_returnsCorrectly() {
mListing.segment(1, 1);
- assertThat(mListing.getList().size()).isEqualTo(1);
+ assertThat(mListing.getList()).hasSize(1);
}
@Test
@@ -173,7 +173,7 @@ public class BluetoothMapConvoListingTest {
BluetoothMapConvoListing listing = new BluetoothMapConvoListing();
listing.appendFromXml(listingStream);
- assertThat(listing.getList().size()).isEqualTo(2);
+ assertThat(listing.getList()).hasSize(2);
assertThat(listing.getList().get(0).getConvoId())
.isEqualTo(signedLongLongIdOne.toHexString());
assertThat(listing.getList().get(1).getConvoId())
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java
index 051b131773..ccc9170eee 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMessageListingTest.java
@@ -84,19 +84,19 @@ public class BluetoothMapMessageListingTest {
@Test
public void segment_whenCountIsLessThanOne_returnsOffsetToEnd() {
mListing.segment(0, 1);
- assertThat(mListing.getList().size()).isEqualTo(2);
+ assertThat(mListing.getList()).hasSize(2);
}
@Test
public void segment_whenOffsetIsBiggerThanSize_returnsEmptyList() {
mListing.segment(1, 4);
- assertThat(mListing.getList().size()).isEqualTo(0);
+ assertThat(mListing.getList()).isEmpty();
}
@Test
public void segment_whenOffsetCountCombinationIsValid_returnsCorrectly() {
mListing.segment(1, 1);
- assertThat(mListing.getList().size()).isEqualTo(1);
+ assertThat(mListing.getList()).hasSize(1);
}
@Test
@@ -122,14 +122,14 @@ public class BluetoothMapMessageListingTest {
listingToAppend.add(listingElementToAppendOne);
listingToAppend.add(listingElementToAppendTwo);
- assertThat(listingToAppend.getList().size()).isEqualTo(2);
+ assertThat(listingToAppend.getList()).hasSize(2);
final InputStream listingStream =
new ByteArrayInputStream(listingToAppend.encode(false, TEST_VERSION));
BluetoothMapMessageListing listing = new BluetoothMapMessageListing();
appendFromXml(listingStream, listing);
- assertThat(listing.getList().size()).isEqualTo(2);
+ assertThat(listing.getList()).hasSize(2);
assertThat(listing.getList().get(0).getDateTime()).isEqualTo(TEST_DATE_TIME_EARLIEST);
assertThat(listing.getList().get(1).getReadBool()).isTrue();
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSmsPduTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSmsPduTest.java
index 98c44f03c7..33bcd7b5b4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSmsPduTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSmsPduTest.java
@@ -118,7 +118,7 @@ public class BluetoothMapSmsPduTest {
BluetoothMapSmsPdu.getSubmitPdus(
mTargetContext, TEST_TEXT_WITH_TWO_SMS_PARTS, null);
- assertThat(pdus.size()).isEqualTo(2);
+ assertThat(pdus).hasSize(2);
assertThat(pdus.get(0).getType()).isEqualTo(BluetoothMapSmsPdu.SMS_TYPE_GSM);
BluetoothMapbMessageSms messageSmsToEncode = new BluetoothMapbMessageSms();
@@ -145,7 +145,7 @@ public class BluetoothMapSmsPduTest {
List<SmsPdu> pdus = BluetoothMapSmsPdu.getSubmitPdus(mTargetContext, TEST_TEXT, null);
- assertThat(pdus.size()).isEqualTo(1);
+ assertThat(pdus).hasSize(1);
assertThat(pdus.get(0).getType()).isEqualTo(BluetoothMapSmsPdu.SMS_TYPE_CDMA);
BluetoothMapbMessageSms messageSmsToEncode = new BluetoothMapbMessageSms();
@@ -172,7 +172,7 @@ public class BluetoothMapSmsPduTest {
BluetoothMapSmsPdu.getDeliverPdus(
mTargetContext, TEST_TEXT, TEST_DESTINATION_ADDRESS, TEST_DATE);
- assertThat(pdus.size()).isEqualTo(1);
+ assertThat(pdus).hasSize(1);
assertThat(pdus.get(0).getType()).isEqualTo(BluetoothMapSmsPdu.SMS_TYPE_GSM);
BluetoothMapbMessageSms messageSmsToEncode = new BluetoothMapbMessageSms();
@@ -201,7 +201,7 @@ public class BluetoothMapSmsPduTest {
BluetoothMapSmsPdu.getDeliverPdus(
mTargetContext, TEST_TEXT, TEST_DESTINATION_ADDRESS, TEST_DATE);
- assertThat(pdus.size()).isEqualTo(1);
+ assertThat(pdus).hasSize(1);
assertThat(pdus.get(0).getType()).isEqualTo(BluetoothMapSmsPdu.SMS_TYPE_CDMA);
BluetoothMapbMessageSms messageSmsToEncode = new BluetoothMapbMessageSms();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java
index 11f4bcf0d3..7aa1ad9b1c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageMimeTest.java
@@ -105,7 +105,7 @@ public class BluetoothMapbMessageMimeTest {
assertThat(mMime.getBcc()).isEqualTo(TEST_BCC);
assertThat(mMime.getReplyTo()).isEqualTo(TEST_REPLY_TO);
- assertThat(mMime.getMimeParts().size()).isEqualTo(1);
+ assertThat(mMime.getMimeParts()).hasSize(1);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java
index 299e2d4f46..1344d9b0f6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapbMessageTest.java
@@ -69,7 +69,7 @@ public class BluetoothMapbMessageTest {
assertThat(messageMime.getVersionString()).isEqualTo("VERSION:" + TEST_VERSION_STRING);
assertThat(messageMime.getType()).isEqualTo(TEST_TYPE);
assertThat(messageMime.getFolder()).isEqualTo("telecom/msg/" + TEST_FOLDER);
- assertThat(messageMime.getRecipients().size()).isEqualTo(1);
+ assertThat(messageMime.getRecipients()).hasSize(1);
assertThat(messageMime.getOriginators()).isNull();
}
@@ -189,7 +189,7 @@ public class BluetoothMapbMessageTest {
.isEqualTo("VERSION:" + TEST_VERSION_STRING);
assertThat(messageMimeParsed.getType()).isEqualTo(TEST_TYPE);
assertThat(messageMimeParsed.getFolder()).isEqualTo(TEST_FOLDER);
- assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1);
- assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1);
+ assertThat(messageMimeParsed.getRecipients()).hasSize(1);
+ assertThat(messageMimeParsed.getOriginators()).hasSize(1);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/SmsMmsContactsTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/SmsMmsContactsTest.java
index b929fa65bc..bcf9a266a8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/SmsMmsContactsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/SmsMmsContactsTest.java
@@ -137,7 +137,7 @@ public class SmsMmsContactsTest {
.when(mMapMethodProxy)
.contentResolverQuery(any(), any(), any(), any(), any(), any());
assertThat(mContacts.mNames).isEmpty();
- assertThat(mContacts.getPhoneNumber(mResolver, TEST_ID)).isEqualTo(null);
+ assertThat(mContacts.getPhoneNumber(mResolver, TEST_ID)).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java
index e6716b487c..2366add7d0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java
@@ -23,7 +23,6 @@ import static org.mockito.Mockito.*;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -96,7 +95,7 @@ public class BmessageTest {
message.setCharset("UTF-8");
- Assert.assertEquals(message.getCharset(), "UTF-8");
+ assertThat(message.getCharset()).isEqualTo("UTF-8");
}
@Test
@@ -105,7 +104,7 @@ public class BmessageTest {
message.setEncoding("test_encoding");
- Assert.assertEquals(message.getEncoding(), "test_encoding");
+ assertThat(message.getEncoding()).isEqualTo("test_encoding");
}
@Test
@@ -114,6 +113,6 @@ public class BmessageTest {
message.setStatus(Bmessage.Status.READ);
- Assert.assertEquals(message.getStatus(), Bmessage.Status.READ);
+ assertThat(message.getStatus()).isEqualTo(Bmessage.Status.READ);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientContentTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientContentTest.java
index 57a0e72941..37f3153718 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientContentTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientContentTest.java
@@ -51,7 +51,6 @@ import com.android.vcard.VCardEntry;
import com.android.vcard.VCardProperty;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -138,7 +137,7 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(0, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).isEmpty();
}
/** Test that a dirty database gets cleaned at startup. */
@@ -153,9 +152,9 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
- Assert.assertEquals(0, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).isEmpty();
}
/** Test inserting 2 SMS messages and then clearing out the database. */
@@ -170,16 +169,16 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
mMapClientContent.storeMessage(
mTestMessage1, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- Assert.assertEquals(2, mMockSmsContentProvider.mContentValues.size());
- Assert.assertEquals(0, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(2);
+ assertThat(mMockMmsContentProvider.mContentValues).isEmpty();
mMapClientContent.cleanUp();
- Assert.assertEquals(0, mMockSmsContentProvider.mContentValues.size());
- Assert.assertEquals(0, mMockThreadContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).isEmpty();
+ assertThat(mMockThreadContentProvider.mContentValues).isEmpty();
}
/** Test inserting 2 MMS messages and then clearing out the database. */
@@ -194,14 +193,14 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- Assert.assertEquals(2, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(2);
mMapClientContent.cleanUp();
- Assert.assertEquals(0, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).isEmpty();
}
/** Test that SMS and MMS messages end up in their respective databases. */
@@ -216,14 +215,14 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage2Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- Assert.assertEquals(2, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(2);
mMapClientContent.cleanUp();
- Assert.assertEquals(0, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).isEmpty();
}
/** Test read status changed */
@@ -238,16 +237,16 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- Assert.assertEquals(2, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(2);
mMapClientContent.markRead(mTestMessage1Handle);
mMapClientContent.cleanUp();
- Assert.assertEquals(0, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).isEmpty();
}
/**
@@ -262,7 +261,7 @@ public class MapClientContentTest {
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- Assert.assertEquals(1, mMockMmsContentProvider.mContentValues.size());
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
mMapClientContent.mContentObserver.onChange(false);
verify(mCallbacks)
.onMessageStatusChanged(eq(mTestMessage1Handle), eq(BluetoothMapClient.READ));
@@ -274,7 +273,7 @@ public class MapClientContentTest {
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
mMapClientContent.storeMessage(
mTestMessage1, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- assertThat(mMockSmsContentProvider.mContentValues.size()).isEqualTo(1);
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
ContentValues storedSMS =
(ContentValues) mMockSmsContentProvider.mContentValues.values().toArray()[0];
@@ -288,7 +287,7 @@ public class MapClientContentTest {
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
mMapClientContent.storeMessage(
mTestMessage1, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_NOT_SEEN);
- assertThat(mMockSmsContentProvider.mContentValues.size()).isEqualTo(1);
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
ContentValues storedSMS =
(ContentValues) mMockSmsContentProvider.mContentValues.values().toArray()[0];
@@ -302,7 +301,7 @@ public class MapClientContentTest {
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_SEEN);
- assertThat(mMockMmsContentProvider.mContentValues.size()).isEqualTo(1);
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
ContentValues storedMMS =
(ContentValues) mMockMmsContentProvider.mContentValues.values().toArray()[0];
@@ -316,7 +315,7 @@ public class MapClientContentTest {
mMapClientContent = new MapClientContent(mMockContext, mCallbacks, mTestDevice);
mMapClientContent.storeMessage(
mTestMessage2, mTestMessage1Handle, mTestMessage1Timestamp, MESSAGE_NOT_SEEN);
- assertThat(mMockMmsContentProvider.mContentValues.size()).isEqualTo(1);
+ assertThat(mMockMmsContentProvider.mContentValues).hasSize(1);
ContentValues storedMMS =
(ContentValues) mMockMmsContentProvider.mContentValues.values().toArray()[0];
@@ -341,14 +340,14 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
// attempt to delete an invalid handle, nothing should be removed.
mMapClientContent.deleteMessage(mTestMessage2Handle);
- Assert.assertEquals(1, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
// delete a valid handle
mMapClientContent.deleteMessage(mTestMessage1Handle);
- Assert.assertEquals(0, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).isEmpty();
}
/**
@@ -368,7 +367,7 @@ public class MapClientContentTest {
any(),
anyInt(),
eq(SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM));
- Assert.assertEquals(1, mMockSmsContentProvider.mContentValues.size());
+ assertThat(mMockSmsContentProvider.mContentValues).hasSize(1);
mMockSmsContentProvider.mContentValues.clear();
mMapClientContent.mContentObserver.onChange(false);
verify(mCallbacks)
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java
index 0a757e1d07..3ae4dd1077 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceTest.java
@@ -15,8 +15,14 @@
*/
package com.android.bluetooth.mapclient;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -30,10 +36,9 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.SdpMasRecord;
import android.content.Context;
-import android.os.Looper;
import android.os.test.TestLooper;
+import android.telephony.SubscriptionManager;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -50,48 +55,48 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
@MediumTest
@RunWith(AndroidJUnit4.class)
public class MapClientServiceTest {
- private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
-
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private AdapterService mAdapterService;
@Mock private DatabaseManager mDatabaseManager;
+ @Mock private MnsService mMnsService;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mRemoteDevice = TestUtils.getTestDevice(mAdapter, 0);
- private MapClientService mService = null;
- private BluetoothAdapter mAdapter = null;
- private BluetoothDevice mRemoteDevice;
+ private MapClientService mService;
private TestLooper mTestLooper;
@Before
public void setUp() throws Exception {
- Context targetContext = InstrumentationRegistry.getTargetContext();
- TestUtils.setAdapterService(mAdapterService);
+ doReturn(CONNECTION_POLICY_ALLOWED)
+ .when(mDatabaseManager)
+ .getProfileConnectionPolicy(any(), anyInt());
+
doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
mTestLooper = new TestLooper();
- MnsService mnsServer = null;
- mService = new MapClientService(targetContext, mTestLooper.getLooper(), mnsServer);
- mService.start();
+ mService = new MapClientService(mAdapterService, mTestLooper.getLooper(), mMnsService);
mService.setAvailable(true);
// Try getting the Bluetooth adapter
- mAdapter = BluetoothAdapter.getDefaultAdapter();
assertThat(mAdapter).isNotNull();
- mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
}
@After
public void tearDown() throws Exception {
mService.stop();
mService.cleanup();
- mService = MapClientService.getMapClientService();
- assertThat(mService).isNull();
- TestUtils.clearAdapterService(mAdapterService);
- mTestLooper.dispatchAll();
+ assertThat(MapClientService.getMapClientService()).isNull();
}
@Test
@@ -119,41 +124,37 @@ public class MapClientServiceTest {
@Test
public void setConnectionPolicy() {
- int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
- when(mDatabaseManager.setProfileConnectionPolicy(
- mRemoteDevice, BluetoothProfile.MAP_CLIENT, connectionPolicy))
- .thenReturn(true);
+ doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(), anyInt(), anyInt());
- assertThat(mService.setConnectionPolicy(mRemoteDevice, connectionPolicy)).isTrue();
+ assertThat(mService.setConnectionPolicy(mRemoteDevice, CONNECTION_POLICY_UNKNOWN)).isTrue();
+ verify(mDatabaseManager)
+ .setProfileConnectionPolicy(
+ mRemoteDevice, BluetoothProfile.MAP_CLIENT, CONNECTION_POLICY_UNKNOWN);
}
@Test
public void getConnectionPolicy() {
- int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
- when(mDatabaseManager.getProfileConnectionPolicy(
- mRemoteDevice, BluetoothProfile.MAP_CLIENT))
- .thenReturn(connectionPolicy);
-
- assertThat(mService.getConnectionPolicy(mRemoteDevice)).isEqualTo(connectionPolicy);
+ for (int policy :
+ List.of(
+ CONNECTION_POLICY_UNKNOWN,
+ CONNECTION_POLICY_FORBIDDEN,
+ CONNECTION_POLICY_ALLOWED)) {
+ doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt());
+ assertThat(mService.getConnectionPolicy(mRemoteDevice)).isEqualTo(policy);
+ }
}
@Test
public void connect_whenPolicyIsForbidden_returnsFalse() {
- int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- when(mDatabaseManager.getProfileConnectionPolicy(
- mRemoteDevice, BluetoothProfile.MAP_CLIENT))
- .thenReturn(connectionPolicy);
+ doReturn(CONNECTION_POLICY_FORBIDDEN)
+ .when(mDatabaseManager)
+ .getProfileConnectionPolicy(any(), anyInt());
assertThat(mService.connect(mRemoteDevice)).isFalse();
}
@Test
public void connect_whenPolicyIsAllowed_returnsTrue() {
- int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
- when(mDatabaseManager.getProfileConnectionPolicy(
- mRemoteDevice, BluetoothProfile.MAP_CLIENT))
- .thenReturn(connectionPolicy);
-
assertThat(mService.connect(mRemoteDevice)).isTrue();
}
@@ -277,7 +278,6 @@ public class MapClientServiceTest {
when(sm.getState()).thenReturn(connectionState);
mService.aclDisconnected(mRemoteDevice, BluetoothDevice.ERROR);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
mTestLooper.dispatchAll();
verify(sm, never()).disconnect();
@@ -291,7 +291,7 @@ public class MapClientServiceTest {
when(sm.getState()).thenReturn(connectionState);
mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_LE);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
verify(sm, never()).disconnect();
}
@@ -304,7 +304,7 @@ public class MapClientServiceTest {
when(sm.getState()).thenReturn(connectionState);
mService.aclDisconnected(mRemoteDevice, BluetoothDevice.TRANSPORT_BREDR);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
verify(sm).disconnect();
}
@@ -317,7 +317,7 @@ public class MapClientServiceTest {
mService.receiveSdpSearchRecord(
mRemoteDevice, MceStateMachine.SDP_SUCCESS, mockSdpRecord, BluetoothUuid.MAS);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
verify(sm).sendSdpResult(eq(MceStateMachine.SDP_SUCCESS), eq(mockSdpRecord));
}
@@ -329,7 +329,7 @@ public class MapClientServiceTest {
mService.receiveSdpSearchRecord(
mRemoteDevice, MceStateMachine.SDP_SUCCESS, null, BluetoothUuid.MAS);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
// Verify message: SDP was successfully complete, but no record was returned
verify(sm).sendSdpResult(eq(MceStateMachine.SDP_SUCCESS), eq(null));
@@ -342,7 +342,7 @@ public class MapClientServiceTest {
mService.receiveSdpSearchRecord(
mRemoteDevice, MceStateMachine.SDP_BUSY, null, BluetoothUuid.MAS);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
// Verify message: SDP was busy and no record was returned
verify(sm).sendSdpResult(eq(MceStateMachine.SDP_BUSY), eq(null));
@@ -355,9 +355,39 @@ public class MapClientServiceTest {
mService.receiveSdpSearchRecord(
mRemoteDevice, MceStateMachine.SDP_FAILED, null, BluetoothUuid.MAS);
- TestUtils.waitForLooperToBeIdle(Looper.getMainLooper());
+ mTestLooper.dispatchAll();
// Verify message: SDP was failed for some reason and no record was returned
verify(sm).sendSdpResult(eq(MceStateMachine.SDP_FAILED), eq(null));
}
+
+ @Test
+ public void connectOneDevice_whenAllowed_isConnected() {
+ assertThat(mService.getInstanceMap()).doesNotContainKey(mRemoteDevice);
+
+ assertThat(mService.connect(mRemoteDevice)).isTrue();
+ assertThat(mService.getInstanceMap().keySet()).containsExactly(mRemoteDevice);
+
+ mTestLooper.dispatchAll();
+ assertThat(mService.getConnectionState(mRemoteDevice))
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
+ }
+
+ @Test
+ public void connectDevice_whenMaxDevicesAreConnected_isRejected() {
+ List<BluetoothDevice> list = new ArrayList<>();
+ for (int i = 0; i < MapClientService.MAXIMUM_CONNECTED_DEVICES; ++i) {
+ BluetoothDevice testDevice = TestUtils.getTestDevice(mAdapter, i);
+ assertThat(mService.getInstanceMap().get(testDevice)).isNull();
+ assertThat(mService.connect(testDevice)).isTrue();
+
+ list.add(testDevice);
+ }
+
+ mTestLooper.dispatchAll();
+ assertThat(mService.getInstanceMap().keySet()).containsExactlyElementsIn(list);
+
+ // Try to connect one more device. Should fail.
+ assertThat(mService.connect(TestUtils.getTestDevice(mAdapter, 0xAF))).isFalse();
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
index 89a0cda5c6..109b7ab82a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java
@@ -16,10 +16,25 @@
package com.android.bluetooth.mapclient;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE;
+import static android.bluetooth.BluetoothProfile.EXTRA_STATE;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
+
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasPackage;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.inOrder;
import android.app.Activity;
import android.app.BroadcastOptions;
@@ -27,7 +42,6 @@ import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMapClient;
-import android.bluetooth.BluetoothProfile;
import android.bluetooth.SdpMasRecord;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -42,7 +56,6 @@ import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Telephony.Sms;
import android.telephony.SmsManager;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -53,9 +66,7 @@ import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import com.android.bluetooth.ObexAppParameters;
-import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.Flags;
import com.android.obex.HeaderSet;
import com.android.vcard.VCardConstants;
@@ -64,15 +75,18 @@ import com.android.vcard.VCardProperty;
import com.google.common.truth.Correspondence;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.AllOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.hamcrest.MockitoHamcrest;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -90,24 +104,24 @@ import java.util.concurrent.TimeUnit;
@MediumTest
@RunWith(ParameterizedAndroidJunit4.class)
public class MapClientStateMachineTest {
- private static final String TAG = "MapClientStateMachineTest";
-
@Rule public final SetFlagsRule mSetFlagsRule;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
- private static final long PENDING_INTENT_TIMEOUT_MS = 3_000;
-
- private static final int CONNECTION_STATE_UNDEFINED = -1;
+ @Mock private AdapterService mAdapterService;
+ @Mock private MapClientService mService;
+ @Mock private MapClientContent mDatabase;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private MasClient mMasClient;
+ @Mock private RequestPushMessage mRequestPushMessage;
+ @Mock private RequestGetMessagesListingForOwnNumber mRequestOwnNumberCompletedWithNumber;
+ @Mock private RequestGetMessagesListingForOwnNumber mRequestOwnNumberIncompleteSearch;
+ @Mock private RequestGetMessage mRequestGetMessage;
+ @Mock private RequestGetMessagesListing mRequestGetMessagesListing;
- private Bmessage mTestIncomingSmsBmessage;
- private Bmessage mTestIncomingMmsBmessage;
- private String mTestMessageSmsHandle = "0001";
- private String mTestMessageMmsHandle = "0002";
- private String mTestMessageUnknownHandle = "0003";
- boolean mIsAdapterServiceSet;
- boolean mIsMapClientServiceStarted;
+ private static final String TAG = "MapClientStateMachineTest";
+ private static final long PENDING_INTENT_TIMEOUT_MS = 3_000;
private static final boolean MESSAGE_SEEN = true;
private static final boolean MESSAGE_NOT_SEEN = false;
@@ -116,50 +130,29 @@ public class MapClientStateMachineTest {
private static final String SENT_PATH = "telecom/msg/sent";
private static final Uri[] TEST_CONTACTS_ONE_PHONENUM = new Uri[] {Uri.parse("tel://5551234")};
private static final String TEST_DATETIME = "19991231T235959";
-
- private VCardEntry mOriginator;
-
- private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
- private final BluetoothDevice mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
- private final Context mTargetContext = InstrumentationRegistry.getTargetContext();
-
- private MceStateMachine mMceStateMachine;
- private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
-
- @Mock private AdapterService mAdapterService;
- @Mock private DatabaseManager mDatabaseManager;
- @Mock private MapClientService mMockMapClientService;
- @Mock private MapClientContent mMockDatabase;
- private MockContentResolver mMockContentResolver;
- private MockSmsContentProvider mMockContentProvider;
-
- @Mock private TelephonyManager mMockTelephonyManager;
-
- @Mock private MasClient mMockMasClient;
-
- @Mock private RequestPushMessage mMockRequestPushMessage;
-
- @Mock private SubscriptionManager mMockSubscriptionManager;
-
private static final String TEST_OWN_PHONE_NUMBER = "555-1234";
- @Mock private RequestGetMessagesListingForOwnNumber mMockRequestOwnNumberCompletedWithNumber;
- @Mock private RequestGetMessagesListingForOwnNumber mMockRequestOwnNumberIncompleteSearch;
- @Mock private RequestGetMessage mMockRequestGetMessage;
- @Mock private RequestGetMessagesListing mMockRequestGetMessagesListing;
-
private static final Correspondence<Request, String> GET_FOLDER_NAME =
Correspondence.transforming(
MapClientStateMachineTest::getFolderNameFromRequestGetMessagesListing,
"has folder name of");
-
private static final String ACTION_MESSAGE_SENT =
"com.android.bluetooth.mapclient.MapClientStateMachineTest.action.MESSAGE_SENT";
private static final String ACTION_MESSAGE_DELIVERED =
"com.android.bluetooth.mapclient.MapClientStateMachineTest.action.MESSAGE_DELIVERED";
- private SentDeliveryReceiver mSentDeliveryReceiver;
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
+ private final Context mTargetContext = InstrumentationRegistry.getTargetContext();
+ private final String mTestMessageSmsHandle = "0001";
+ private final String mTestMessageMmsHandle = "0002";
+ private final String mTestMessageUnknownHandle = "0003";
+ private Bmessage mTestIncomingSmsBmessage;
+ private Bmessage mTestIncomingMmsBmessage;
+ private MceStateMachine mStateMachine;
+ private SentDeliveryReceiver mSentDeliveryReceiver;
private TestLooper mLooper;
+ private InOrder mInOrder;
private static class SentDeliveryReceiver extends BroadcastReceiver {
private CountDownLatch mActionReceivedLatch;
@@ -205,52 +198,43 @@ public class MapClientStateMachineTest {
public void setUp() throws Exception {
mLooper = new TestLooper();
- TestUtils.setAdapterService(mAdapterService);
- mIsAdapterServiceSet = true;
- mMockContentProvider = new MockSmsContentProvider();
- mMockContentResolver = new MockContentResolver();
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
- mIsMapClientServiceStarted = true;
- mMockContentResolver.addProvider("sms", mMockContentProvider);
- mMockContentResolver.addProvider("mms", mMockContentProvider);
- mMockContentResolver.addProvider("mms-sms", mMockContentProvider);
+ mInOrder = inOrder(mService);
- when(mMockMapClientService.getContentResolver()).thenReturn(mMockContentResolver);
- when(mMockMapClientService.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE))
- .thenReturn(mMockSubscriptionManager);
- when(mMockMapClientService.getSystemServiceName(SubscriptionManager.class))
- .thenReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ MockSmsContentProvider contentProvider = new MockSmsContentProvider();
+ MockContentResolver contentResolver = new MockContentResolver();
+ contentResolver.addProvider("sms", contentProvider);
+ contentResolver.addProvider("mms", contentProvider);
+ contentResolver.addProvider("mms-sms", contentProvider);
- doReturn(mTargetContext.getResources()).when(mMockMapClientService).getResources();
+ when(mService.getContentResolver()).thenReturn(contentResolver);
+ doReturn(mTargetContext.getResources()).when(mService).getResources();
- when(mMockMasClient.makeRequest(any(Request.class))).thenReturn(true);
- mMceStateMachine =
+ when(mMasClient.makeRequest(any(Request.class))).thenReturn(true);
+ mStateMachine =
new MceStateMachine(
- mMockMapClientService,
- mTestDevice,
- mMockMasClient,
- mMockDatabase,
- mLooper.getLooper());
+ mService,
+ mDevice,
+ mAdapterService,
+ mLooper.getLooper(),
+ mMasClient,
+ mDatabase);
mLooper.dispatchAll();
+ verifyStateTransitionAndIntent(
+ STATE_DISCONNECTED, STATE_CONNECTING);
- int initialExpectedState = BluetoothProfile.STATE_CONNECTING;
- assertThat(mMceStateMachine.getState()).isEqualTo(initialExpectedState);
-
- when(mMockRequestOwnNumberCompletedWithNumber.isSearchCompleted()).thenReturn(true);
- when(mMockRequestOwnNumberCompletedWithNumber.getOwnNumber())
- .thenReturn(TEST_OWN_PHONE_NUMBER);
- when(mMockRequestOwnNumberIncompleteSearch.isSearchCompleted()).thenReturn(false);
- when(mMockRequestOwnNumberIncompleteSearch.getOwnNumber()).thenReturn(null);
+ when(mRequestOwnNumberCompletedWithNumber.isSearchCompleted()).thenReturn(true);
+ when(mRequestOwnNumberCompletedWithNumber.getOwnNumber()).thenReturn(TEST_OWN_PHONE_NUMBER);
+ when(mRequestOwnNumberIncompleteSearch.isSearchCompleted()).thenReturn(false);
+ when(mRequestOwnNumberIncompleteSearch.getOwnNumber()).thenReturn(null);
createTestMessages();
- when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingSmsBmessage);
- when(mMockRequestGetMessage.getHandle()).thenReturn(mTestMessageSmsHandle);
+ when(mRequestGetMessage.getMessage()).thenReturn(mTestIncomingSmsBmessage);
+ when(mRequestGetMessage.getHandle()).thenReturn(mTestMessageSmsHandle);
- when(mMockMapClientService.getSystemService(Context.TELEPHONY_SERVICE))
- .thenReturn(mMockTelephonyManager);
- when(mMockTelephonyManager.isSmsCapable()).thenReturn(false);
+ when(mService.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.isSmsCapable()).thenReturn(false);
// Set up receiver for 'Sent' and 'Delivered' PendingIntents
IntentFilter filter = new IntentFilter();
@@ -263,20 +247,17 @@ public class MapClientStateMachineTest {
@After
public void tearDown() throws Exception {
- if (mMceStateMachine != null) {
- mMceStateMachine.doQuit();
+ if (mStateMachine != null) {
+ mStateMachine.doQuit();
}
- if (mIsAdapterServiceSet) {
- TestUtils.clearAdapterService(mAdapterService);
- }
mTargetContext.unregisterReceiver(mSentDeliveryReceiver);
}
/** Test that default state is STATE_CONNECTING */
@Test
public void testDefaultConnectingState() {
- Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState());
+ assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
}
/**
@@ -288,171 +269,111 @@ public class MapClientStateMachineTest {
setupSdpRecordReceipt();
sendAndDispatchMessage(MceStateMachine.MSG_MAS_DISCONNECTED);
- // Wait until the message is processed and a broadcast request is sent to
- // to MapClientService to change
- // state from STATE_CONNECTING to STATE_DISCONNECTED
- verify(mMockMapClientService, times(2))
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ verifyStateTransitionAndIntent(STATE_CONNECTING, STATE_DISCONNECTED);
}
- /** Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED */
@Test
- public void testStateTransitionFromConnectingToConnected() {
+ public void masConnected_whenConnecting_isConnected() {
setupSdpRecordReceipt();
-
- int expectedFromState = BluetoothProfile.STATE_CONNECTING;
- int expectedToState = BluetoothProfile.STATE_CONNECTED;
sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- verifyStateTransitionAndIntent(expectedFromState, expectedToState);
+ verifyStateTransitionAndIntent(STATE_CONNECTING, STATE_CONNECTED);
}
- /**
- * Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED -->
- * (receive MSG_MAS_DISCONNECTED) --> STATE_DISCONNECTING --> STATE_DISCONNECTED
- */
@Test
- public void testStateTransitionFromConnectedToDisconnected() {
+ public void masDisconnected_whenConnected_isDisconnected() {
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
- setupSdpRecordReceipt();
- // transition to the connected state
- testStateTransitionFromConnectingToConnected();
-
- int expectedFromState = BluetoothProfile.STATE_DISCONNECTING;
- int expectedToState = BluetoothProfile.STATE_DISCONNECTED;
sendAndDispatchMessage(MceStateMachine.MSG_MAS_DISCONNECTED);
- verifyStateTransitionAndIntent(expectedFromState, expectedToState);
+ verifyStateTransitionAndIntent(STATE_DISCONNECTING, STATE_DISCONNECTED);
}
/** Test receiving an empty event report */
@Test
public void testReceiveEmptyEvent() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
-
- // broadcast request is sent to change state from STATE_CONNECTING to STATE_CONNECTED
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
- // Send an empty notification event, verify the mMceStateMachine is still connected
+ // Send an empty notification event, verify the mStateMachine is still connected
sendAndDispatchMessage(MceStateMachine.MSG_NOTIFICATION);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
}
/** Test set message status */
@Test
public void testSetMessageStatus() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
// broadcast request is sent to change state from STATE_CONNECTING to STATE_CONNECTED
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
- assertThat(mMceStateMachine.setMessageStatus("123456789AB", BluetoothMapClient.READ))
- .isTrue();
+ assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
+ assertThat(mStateMachine.setMessageStatus("123456789AB", BluetoothMapClient.READ)).isTrue();
}
/** Test MceStateMachine#disconnect */
@Test
public void testDisconnect() {
- setupSdpRecordReceipt();
- doAnswer(
- invocation -> {
- mMceStateMachine.sendMessage(MceStateMachine.MSG_MAS_DISCONNECTED);
- return null;
- })
- .when(mMockMasClient)
- .shutdown();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
-
- // broadcast request is sent to change state from STATE_CONNECTING to STATE_CONNECTED
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
- mMceStateMachine.disconnect();
+ mStateMachine.disconnect();
mLooper.dispatchAll();
+ verifyStateTransitionAndIntent(STATE_CONNECTED, STATE_DISCONNECTING);
- verify(mMockMapClientService, times(4))
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
-
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ verify(mMasClient).shutdown();
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_DISCONNECTED);
+ verifyStateTransitionAndIntent(
+ STATE_DISCONNECTING, STATE_DISCONNECTED);
}
/** Test disconnect timeout */
@Test
public void testDisconnectTimeout() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
- // broadcast request is sent to change state from STATE_CONNECTING to STATE_CONNECTED
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
-
- mMceStateMachine.disconnect();
+ mStateMachine.disconnect();
mLooper.dispatchAll();
- verify(mMockMapClientService, times(3))
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
+ verifyStateTransitionAndIntent(STATE_CONNECTED, STATE_DISCONNECTING);
mLooper.moveTimeForward(MceStateMachine.DISCONNECT_TIMEOUT.toMillis());
mLooper.dispatchAll();
- verify(mMockMapClientService, times(4))
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ verifyStateTransitionAndIntent(STATE_DISCONNECTING, STATE_DISCONNECTED);
}
/** Test sending a message to a phone */
@Test
public void testSendSMSMessageToPhone() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
String testMessage = "Hello World!";
Uri[] contacts = new Uri[] {Uri.parse("tel://5551212")};
- verify(mMockMasClient, times(0)).makeRequest(any(RequestPushMessage.class));
- mMceStateMachine.sendMapMessage(contacts, testMessage, null, null);
+ verify(mMasClient, never()).makeRequest(any(RequestPushMessage.class));
+ mStateMachine.sendMapMessage(contacts, testMessage, null, null);
mLooper.dispatchAll();
- verify(mMockMasClient, times(1)).makeRequest(any(RequestPushMessage.class));
+ verify(mMasClient).makeRequest(any(RequestPushMessage.class));
}
/** Test sending a message to an email */
@Test
public void testSendSMSMessageToEmail() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
String testMessage = "Hello World!";
Uri[] contacts = new Uri[] {Uri.parse("mailto://sms-test@google.com")};
- verify(mMockMasClient, never()).makeRequest(any(RequestPushMessage.class));
- mMceStateMachine.sendMapMessage(contacts, testMessage, null, null);
+ verify(mMasClient, never()).makeRequest(any(RequestPushMessage.class));
+ mStateMachine.sendMapMessage(contacts, testMessage, null, null);
mLooper.dispatchAll();
- verify(mMockMasClient).makeRequest(any(RequestPushMessage.class));
+ verify(mMasClient).makeRequest(any(RequestPushMessage.class));
}
/** Test message sent successfully */
@Test
public void testSMSMessageSent() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
- when(mMockRequestPushMessage.getMsgHandle()).thenReturn(mTestMessageSmsHandle);
- when(mMockRequestPushMessage.getBMsg()).thenReturn(mTestIncomingSmsBmessage);
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestPushMessage);
+ when(mRequestPushMessage.getMsgHandle()).thenReturn(mTestMessageSmsHandle);
+ when(mRequestPushMessage.getBMsg()).thenReturn(mTestIncomingSmsBmessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestPushMessage);
- verify(mMockDatabase)
+ verify(mDatabase)
.storeMessage(
eq(mTestIncomingSmsBmessage),
eq(mTestMessageSmsHandle),
@@ -467,11 +388,11 @@ public class MapClientStateMachineTest {
* MessageListing of INBOX folder not sent
*/
private void testGetOwnNumber_setup() {
- testStateTransitionFromConnectingToConnected();
- verify(mMockMasClient, never()).makeRequest(any(RequestSetNotificationRegistration.class));
- verify(mMockMasClient, never()).makeRequest(any(RequestGetMessagesListing.class));
+ masConnected_whenConnecting_isConnected();
+ verify(mMasClient, never()).makeRequest(any(RequestSetNotificationRegistration.class));
+ verify(mMasClient, never()).makeRequest(any(RequestGetMessagesListing.class));
assertThat(
- mMceStateMachine
+ mStateMachine
.getHandler()
.hasMessages(MceStateMachine.MSG_SEARCH_OWN_NUMBER_TIMEOUT))
.isTrue();
@@ -485,11 +406,11 @@ public class MapClientStateMachineTest {
*/
private void testGetOwnNumber_assertNextStageStarted(boolean hasStarted) {
if (hasStarted) {
- verify(mMockMasClient).makeRequest(any(RequestSetNotificationRegistration.class));
- verify(mMockMasClient, times(2)).makeRequest(any(RequestGetMessagesListing.class));
+ verify(mMasClient).makeRequest(any(RequestSetNotificationRegistration.class));
+ verify(mMasClient, times(2)).makeRequest(any(RequestGetMessagesListing.class));
ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class);
- verify(mMockMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
+ verify(mMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
// There will be multiple calls to {@link MasClient#makeRequest} with different
// {@link Request} subtypes; not all of them will be {@link
// RequestGetMessagesListing}.
@@ -501,9 +422,8 @@ public class MapClientStateMachineTest {
.comparingElementsUsing(GET_FOLDER_NAME)
.contains(MceStateMachine.FOLDER_SENT);
} else {
- verify(mMockMasClient, never())
- .makeRequest(any(RequestSetNotificationRegistration.class));
- verify(mMockMasClient, never()).makeRequest(any(RequestGetMessagesListing.class));
+ verify(mMasClient, never()).makeRequest(any(RequestSetNotificationRegistration.class));
+ verify(mMasClient, never()).makeRequest(any(RequestGetMessagesListing.class));
}
}
@@ -523,12 +443,11 @@ public class MapClientStateMachineTest {
testGetOwnNumber_setup();
sendAndDispatchMessage(
- MceStateMachine.MSG_MAS_REQUEST_COMPLETED,
- mMockRequestOwnNumberCompletedWithNumber);
+ MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestOwnNumberCompletedWithNumber);
- verify(mMockMasClient, never()).makeRequest(eq(mMockRequestOwnNumberCompletedWithNumber));
+ verify(mMasClient, never()).makeRequest(eq(mRequestOwnNumberCompletedWithNumber));
assertThat(
- mMceStateMachine
+ mStateMachine
.getHandler()
.hasMessages(MceStateMachine.MSG_SEARCH_OWN_NUMBER_TIMEOUT))
.isFalse();
@@ -552,12 +471,11 @@ public class MapClientStateMachineTest {
testGetOwnNumber_setup();
sendAndDispatchMessage(
- MceStateMachine.MSG_SEARCH_OWN_NUMBER_TIMEOUT,
- mMockRequestOwnNumberIncompleteSearch);
+ MceStateMachine.MSG_SEARCH_OWN_NUMBER_TIMEOUT, mRequestOwnNumberIncompleteSearch);
- verify(mMockMasClient).abortRequest(mMockRequestOwnNumberIncompleteSearch);
+ verify(mMasClient).abortRequest(mRequestOwnNumberIncompleteSearch);
assertThat(
- mMceStateMachine
+ mStateMachine
.getHandler()
.hasMessages(MceStateMachine.MSG_MAS_REQUEST_COMPLETED))
.isFalse();
@@ -581,11 +499,11 @@ public class MapClientStateMachineTest {
testGetOwnNumber_setup();
sendAndDispatchMessage(
- MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestOwnNumberIncompleteSearch);
+ MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestOwnNumberIncompleteSearch);
- verify(mMockMasClient).makeRequest(eq(mMockRequestOwnNumberIncompleteSearch));
+ verify(mMasClient).makeRequest(eq(mRequestOwnNumberIncompleteSearch));
assertThat(
- mMceStateMachine
+ mStateMachine
.getHandler()
.hasMessages(MceStateMachine.MSG_SEARCH_OWN_NUMBER_TIMEOUT))
.isTrue();
@@ -595,9 +513,7 @@ public class MapClientStateMachineTest {
/** Test seen status set for new SMS */
@Test
public void testReceivedNewSms_messageStoredAsUnseen() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
String dateTime = new ObexTime(Instant.now()).toString();
EventReport event =
@@ -611,11 +527,11 @@ public class MapClientStateMachineTest {
sendAndDispatchEvent(event);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
- verify(mMockDatabase)
+ verify(mDatabase)
.storeMessage(
eq(mTestIncomingSmsBmessage),
eq(mTestMessageSmsHandle),
@@ -626,9 +542,7 @@ public class MapClientStateMachineTest {
/** Test seen status set for new MMS */
@Test
public void testReceivedNewMms_messageStoredAsUnseen() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
String dateTime = new ObexTime(Instant.now()).toString();
EventReport event =
@@ -640,16 +554,16 @@ public class MapClientStateMachineTest {
null,
"MMS");
- when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
- when(mMockRequestGetMessage.getHandle()).thenReturn(mTestMessageMmsHandle);
+ when(mRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
+ when(mRequestGetMessage.getHandle()).thenReturn(mTestMessageMmsHandle);
sendAndDispatchEvent(event);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
- verify(mMockDatabase)
+ verify(mDatabase)
.storeMessage(
eq(mTestIncomingMmsBmessage),
eq(mTestMessageMmsHandle),
@@ -659,9 +573,7 @@ public class MapClientStateMachineTest {
@Test
public void testReceiveNewMessage_handleNotRecognized_messageDropped() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
// Send new message event with handle A
String dateTime = new ObexTime(Instant.now()).toString();
@@ -675,85 +587,79 @@ public class MapClientStateMachineTest {
"MMS");
// Prepare to send back message content, but use handle B
- when(mMockRequestGetMessage.getHandle()).thenReturn(mTestMessageUnknownHandle);
- when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
+ when(mRequestGetMessage.getHandle()).thenReturn(mTestMessageUnknownHandle);
+ when(mRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
sendAndDispatchEvent(event);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
// We should drop the message and not store it, as it's not one we requested
- verify(mMockDatabase, never())
+ verify(mDatabase, never())
.storeMessage(any(Bmessage.class), anyString(), anyLong(), anyBoolean());
}
/** Test seen status set in database on initial download */
@Test
public void testDownloadExistingSms_messageStoredAsSeen() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
com.android.bluetooth.mapclient.Message testMessageListingSms =
createNewMessage("SMS_GSM", mTestMessageSmsHandle);
ArrayList<com.android.bluetooth.mapclient.Message> messageListSms = new ArrayList<>();
messageListSms.add(testMessageListingSms);
- when(mMockRequestGetMessagesListing.getList()).thenReturn(messageListSms);
+ when(mRequestGetMessagesListing.getList()).thenReturn(messageListSms);
sendAndDispatchMessage(
MceStateMachine.MSG_GET_MESSAGE_LISTING, MceStateMachine.FOLDER_INBOX);
- verify(mMockMasClient).makeRequest(any(RequestGetMessagesListing.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessagesListing.class));
sendAndDispatchMessage(
- MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessagesListing);
+ MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessagesListing);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
- verify(mMockDatabase).storeMessage(any(), any(), any(), eq(MESSAGE_SEEN));
+ verify(mDatabase).storeMessage(any(), any(), any(), eq(MESSAGE_SEEN));
}
/** Test seen status set in database on initial download */
@Test
public void testDownloadExistingMms_messageStoredAsSeen() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
com.android.bluetooth.mapclient.Message testMessageListingMms =
createNewMessage("MMS", mTestMessageMmsHandle);
ArrayList<com.android.bluetooth.mapclient.Message> messageListMms = new ArrayList<>();
messageListMms.add(testMessageListingMms);
- when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
- when(mMockRequestGetMessage.getHandle()).thenReturn(mTestMessageMmsHandle);
- when(mMockRequestGetMessagesListing.getList()).thenReturn(messageListMms);
+ when(mRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage);
+ when(mRequestGetMessage.getHandle()).thenReturn(mTestMessageMmsHandle);
+ when(mRequestGetMessagesListing.getList()).thenReturn(messageListMms);
sendAndDispatchMessage(
MceStateMachine.MSG_GET_MESSAGE_LISTING, MceStateMachine.FOLDER_INBOX);
- verify(mMockMasClient).makeRequest(any(RequestGetMessagesListing.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessagesListing.class));
sendAndDispatchMessage(
- MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessagesListing);
+ MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessagesListing);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
- verify(mMockDatabase).storeMessage(any(), any(), any(), eq(MESSAGE_SEEN));
+ verify(mDatabase).storeMessage(any(), any(), any(), eq(MESSAGE_SEEN));
}
/** Test receiving a new message notification. */
@Test
public void testReceiveNewMessageNotification() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
// Receive a new message notification.
String dateTime = new ObexTime(Instant.now()).toString();
@@ -768,14 +674,13 @@ public class MapClientStateMachineTest {
sendAndDispatchEvent(event);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
MceStateMachine.MessageMetadata messageMetadata =
- mMceStateMachine.mMessages.get(mTestMessageSmsHandle);
- Assert.assertEquals(messageMetadata.getHandle(), mTestMessageSmsHandle);
- Assert.assertEquals(
- new ObexTime(Instant.ofEpochMilli(messageMetadata.getTimestamp())).toString(),
- dateTime);
+ mStateMachine.mMessages.get(mTestMessageSmsHandle);
+ assertThat(messageMetadata.getHandle()).isEqualTo(mTestMessageSmsHandle);
+ assertThat(new ObexTime(Instant.ofEpochMilli(messageMetadata.getTimestamp())).toString())
+ .isEqualTo(dateTime);
}
/**
@@ -784,11 +689,10 @@ public class MapClientStateMachineTest {
*/
@Test
public void testMsgGetMessageListing_unsupportedMessageTypesNotRequested() {
- setupSdpRecordReceipt();
- clearInvocations(mMockMasClient);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
+
+ clearInvocations(mMasClient);
byte expectedFilter = MessagesFilter.MESSAGE_TYPE_EMAIL | MessagesFilter.MESSAGE_TYPE_IM;
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
sendAndDispatchMessage(
MceStateMachine.MSG_GET_MESSAGE_LISTING, MceStateMachine.FOLDER_INBOX);
@@ -796,7 +700,7 @@ public class MapClientStateMachineTest {
// using Request class as captor grabs all Request sub-classes even if
// RequestGetMessagesListing is specifically requested
ArgumentCaptor<Request> requestCaptor = ArgumentCaptor.forClass(Request.class);
- verify(mMockMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
+ verify(mMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
List<Request> requests = requestCaptor.getAllValues();
// iterating through captured values to grab RequestGetMessagesListing object
@@ -816,9 +720,7 @@ public class MapClientStateMachineTest {
@Test
public void testReceivedNewMmsNoSMSDefaultPackage_broadcastToSMSReplyPackage() {
- setupSdpRecordReceipt();
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ masConnected_whenConnecting_isConnected(); // transition to the connected state
String dateTime = new ObexTime(Instant.now()).toString();
EventReport event =
@@ -832,78 +734,70 @@ public class MapClientStateMachineTest {
sendAndDispatchEvent(event);
- verify(mMockMasClient).makeRequest(any(RequestGetMessage.class));
+ verify(mMasClient).makeRequest(any(RequestGetMessage.class));
- sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mMockRequestGetMessage);
+ sendAndDispatchMessage(MceStateMachine.MSG_MAS_REQUEST_COMPLETED, mRequestGetMessage);
- verify(mMockMapClientService, times(1))
- .sendBroadcast(
- mIntentArgument.capture(), eq(android.Manifest.permission.RECEIVE_SMS));
- assertThat(mIntentArgument.getValue().getPackage()).isNull();
+ verifyIntentSent(
+ android.Manifest.permission.RECEIVE_SMS, hasPackage(nullValue(String.class)));
}
@Test
public void testSdpBusyWhileConnecting_sdpRetried() {
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_CONNECTING);
// Send SDP Failed with status "busy"
// Note: There's no way to validate the BluetoothDevice#sdpSearch call
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_BUSY, null);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_BUSY, null);
// Send successful SDP record, then send MAS Client connected
SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "MasRecord");
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ verifyStateTransitionAndIntent(STATE_CONNECTING, STATE_CONNECTED);
}
@Test
public void testSdpBusyWhileConnectingAndRetryResultsReceivedAfterTimeout_resultsIgnored() {
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_CONNECTING);
// Send SDP Failed with status "busy"
// Note: There's no way to validate the BluetoothDevice#sdpSearch call
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_BUSY, null);
-
- // Timeout waiting for record
- sendAndDispatchMessage(MceStateMachine.MSG_CONNECTING_TIMEOUT);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_BUSY, null);
- // Verify we move into the disconnecting state
- verify(mMockMapClientService, times(2))
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
+ // Simulate timeout waiting for record
+ mLooper.moveTimeForward(MceStateMachine.CONNECT_TIMEOUT.toMillis());
+ mLooper.dispatchAll();
- assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
+ verifyStateTransitionAndIntent(STATE_CONNECTING, STATE_DISCONNECTING);
// Send successful SDP record, then send MAS Client connected
SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "MasRecord");
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
// Verify nothing happens
- verifyNoMoreInteractions(mMockMapClientService);
+ verifyNoMoreInteractions(mService);
}
@Test
public void testSdpFailedWithNoRecordWhileConnecting_deviceDisconnecting() {
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_CONNECTING);
// Send SDP process success with no record found
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, null);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, null);
// Verify we move into the disconnecting state
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_DISCONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_DISCONNECTING);
}
@Test
public void testSdpOrganicFailure_deviceDisconnecting() {
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_CONNECTING);
// Send SDP Failed entirely
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_FAILED, null);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_FAILED, null);
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_DISCONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_DISCONNECTING);
}
/**
@@ -1001,10 +895,8 @@ public class MapClientStateMachineTest {
* 'Success'/'Failure'.
*/
private void testSendMapMessagePendingIntents_base(String action, EventReport.Type type) {
- int expectedFromState = BluetoothProfile.STATE_CONNECTING;
- int expectedToState = BluetoothProfile.STATE_CONNECTED;
sendAndDispatchMessage(MceStateMachine.MSG_MAS_CONNECTED);
- verifyStateTransitionAndIntent(expectedFromState, expectedToState);
+ verifyStateTransitionAndIntent(STATE_CONNECTING, STATE_CONNECTED);
PendingIntent pendingIntentSent;
PendingIntent pendingIntentDelivered;
@@ -1040,7 +932,7 @@ public class MapClientStateMachineTest {
PendingIntent pendingIntentSent,
PendingIntent pendingIntentDelivered,
String messageHandle) {
- mMceStateMachine.sendMapMessage(
+ mStateMachine.sendMapMessage(
TEST_CONTACTS_ONE_PHONENUM, TEST_MESSAGE,
pendingIntentSent, pendingIntentDelivered);
mLooper.dispatchAll();
@@ -1056,7 +948,7 @@ public class MapClientStateMachineTest {
ArgumentCaptor<RequestPushMessage> requestCaptor =
ArgumentCaptor.forClass(RequestPushMessage.class);
- verify(mMockMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
+ verify(mMasClient, atLeastOnce()).makeRequest(requestCaptor.capture());
RequestPushMessage spyRequestPushMessage = spy(requestCaptor.getValue());
when(spyRequestPushMessage.getMsgHandle()).thenReturn(messageHandle);
@@ -1064,34 +956,25 @@ public class MapClientStateMachineTest {
}
private void setupSdpRecordReceipt() {
- assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTING);
+ assertCurrentStateAfterScheduledTask(STATE_CONNECTING);
// Setup receipt of SDP record
SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "MasRecord");
- mMceStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
+ mStateMachine.sendSdpResult(MceStateMachine.SDP_SUCCESS, record);
}
private void assertCurrentStateAfterScheduledTask(int expectedState) {
mLooper.dispatchAll();
- assertThat(mMceStateMachine.getState()).isEqualTo(expectedState);
+ assertThat(mStateMachine.getState()).isEqualTo(expectedState);
}
- private void verifyStateTransitionAndIntent(int expectedFromState, int expectedToState) {
- assertThat(mMceStateMachine.getState()).isEqualTo(expectedToState);
- verify(mMockMapClientService, atLeastOnce())
- .sendBroadcastMultiplePermissions(
- mIntentArgument.capture(),
- any(String[].class),
- any(BroadcastOptions.class));
- Intent capturedIntent = mIntentArgument.getValue();
- int intentFromState =
- capturedIntent.getIntExtra(
- BluetoothProfile.EXTRA_PREVIOUS_STATE, CONNECTION_STATE_UNDEFINED);
- int intentToState =
- capturedIntent.getIntExtra(
- BluetoothProfile.EXTRA_STATE, CONNECTION_STATE_UNDEFINED);
- assertThat(intentFromState).isEqualTo(expectedFromState);
- assertThat(intentToState).isEqualTo(expectedToState);
+ private void verifyStateTransitionAndIntent(int oldState, int newState) {
+ assertThat(mStateMachine.getState()).isEqualTo(newState);
+ verifyIntentSent(
+ new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+ hasAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED),
+ hasExtra(EXTRA_STATE, newState),
+ hasExtra(EXTRA_PREVIOUS_STATE, oldState));
}
private static class MockSmsContentProvider extends MockContentProvider {
@@ -1179,25 +1062,25 @@ public class MapClientStateMachineTest {
// create new Bmessages for testing
void createTestMessages() {
- mOriginator = new VCardEntry();
+ VCardEntry originator = new VCardEntry();
VCardProperty property = new VCardProperty();
property.setName(VCardConstants.PROPERTY_TEL);
property.addValues("555-1212");
- mOriginator.addProperty(property);
+ originator.addProperty(property);
mTestIncomingSmsBmessage = new Bmessage();
mTestIncomingSmsBmessage.setBodyContent("HelloWorld");
mTestIncomingSmsBmessage.setType(Bmessage.Type.SMS_GSM);
mTestIncomingSmsBmessage.setFolder("telecom/msg/inbox");
- mTestIncomingSmsBmessage.addOriginator(mOriginator);
- mTestIncomingSmsBmessage.addRecipient(mOriginator);
+ mTestIncomingSmsBmessage.addOriginator(originator);
+ mTestIncomingSmsBmessage.addRecipient(originator);
mTestIncomingMmsBmessage = new Bmessage();
mTestIncomingMmsBmessage.setBodyContent("HelloWorld");
mTestIncomingMmsBmessage.setType(Bmessage.Type.MMS);
mTestIncomingMmsBmessage.setFolder("telecom/msg/inbox");
- mTestIncomingMmsBmessage.addOriginator(mOriginator);
- mTestIncomingMmsBmessage.addRecipient(mOriginator);
+ mTestIncomingMmsBmessage.addOriginator(originator);
+ mTestIncomingMmsBmessage.addRecipient(originator);
}
private void sendAndDispatchEvent(EventReport ev) {
@@ -1209,7 +1092,22 @@ public class MapClientStateMachineTest {
}
private void sendAndDispatchMessage(int what, Object obj) {
- mMceStateMachine.sendMessage(what, obj);
+ mStateMachine.sendMessage(what, obj);
mLooper.dispatchAll();
}
+
+ @SafeVarargs
+ private void verifyIntentSent(String permission, Matcher<Intent>... matchers) {
+ mInOrder.verify(mService)
+ .sendBroadcast(MockitoHamcrest.argThat(AllOf.allOf(matchers)), eq(permission));
+ }
+
+ @SafeVarargs
+ private void verifyIntentSent(String[] permissions, Matcher<Intent>... matchers) {
+ mInOrder.verify(mService)
+ .sendBroadcastMultiplePermissions(
+ MockitoHamcrest.argThat(AllOf.allOf(matchers)),
+ eq(permissions),
+ any(BroadcastOptions.class));
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
deleted file mode 100644
index 4086981761..0000000000
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.mapclient;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.*;
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
-import android.content.Context;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.bluetooth.TestUtils;
-import com.android.bluetooth.Utils;
-import com.android.bluetooth.btservice.AdapterService;
-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;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class MapClientTest {
- private static final String TAG = MapClientTest.class.getSimpleName();
- private MapClientService mService = null;
- private BluetoothAdapter mAdapter = null;
- private Context mTargetContext;
- private boolean mIsAdapterServiceSet;
- private boolean mIsMapClientServiceStarted;
-
- @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-
- @Mock private AdapterService mAdapterService;
- @Mock private MnsService mMockMnsService;
- @Mock private DatabaseManager mDatabaseManager;
-
- @Before
- public void setUp() throws Exception {
- mTargetContext = InstrumentationRegistry.getTargetContext();
- TestUtils.setAdapterService(mAdapterService);
- mIsAdapterServiceSet = true;
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
- mIsMapClientServiceStarted = true;
- Looper looper = null;
- mService = new MapClientService(mTargetContext, looper, mMockMnsService);
- mService.start();
- mService.setAvailable(true);
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- }
-
- @After
- public void tearDown() throws Exception {
- if (mIsMapClientServiceStarted) {
- mService.stop();
- mService.cleanup();
- mService = MapClientService.getMapClientService();
- assertThat(mService).isNull();
- }
- if (mIsAdapterServiceSet) {
- TestUtils.clearAdapterService(mAdapterService);
- }
- }
-
- /**
- * Mock the priority of a bluetooth device
- *
- * @param device - The bluetooth device you wish to mock the priority of
- * @param priority - The priority value you want the device to have
- */
- private void mockDevicePriority(BluetoothDevice device, int priority) {
- when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.MAP_CLIENT))
- .thenReturn(priority);
- }
-
- @Test
- public void testInitialize() {
- assertThat(MapClientService.getMapClientService()).isNotNull();
- }
-
- /** Test connection of one device. */
- @Test
- public void testConnect() {
- // make sure there is no statemachine already defined for this device
- BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
- assertThat(mService.getInstanceMap()).doesNotContainKey(device);
-
- // connect a bluetooth device
- mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- assertThat(mService.connect(device)).isTrue();
-
- // is the statemachine created
- Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap();
-
- Assert.assertEquals(1, map.size());
- MceStateMachine sm = map.get(device);
- assertThat(sm).isNotNull();
- TestUtils.waitForLooperToFinishScheduledTask(sm.getHandler().getLooper());
-
- Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, sm.getState());
- mService.cleanupDevice(device, sm);
- assertThat(mService.getInstanceMap()).doesNotContainKey(device);
- }
-
- /** Test that a PRIORITY_OFF device is not connected to */
- @Test
- public void testConnectPriorityOffDevice() {
- // make sure there is no statemachine already defined for this device
- BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
- assertThat(mService.getInstanceMap()).doesNotContainKey(device);
-
- // connect a bluetooth device
- mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
- assertThat(mService.connect(device)).isFalse();
-
- // is the statemachine created
- assertThat(mService.getInstanceMap()).isEmpty();
- }
-
- /** Test connecting MAXIMUM_CONNECTED_DEVICES devices. */
- @Test
- public void testConnectMaxDevices() {
- // Create bluetoothdevice & mock statemachine objects to be used in this test
- List<BluetoothDevice> list = new ArrayList<>();
- String address = "11:11:11:11:11:1";
- for (int i = 0; i < MapClientService.MAXIMUM_CONNECTED_DEVICES; ++i) {
- list.add(makeBluetoothDevice(address + i));
- }
-
- // make sure there is no statemachine already defined for the devices defined above
- for (BluetoothDevice d : list) {
- assertThat(mService.getInstanceMap().get(d)).isNull();
- }
-
- // run the test - connect all devices, set their priorities to on
- for (BluetoothDevice d : list) {
- mockDevicePriority(d, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- assertThat(mService.connect(d)).isTrue();
- }
-
- // verify
- Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap();
- Assert.assertEquals(MapClientService.MAXIMUM_CONNECTED_DEVICES, map.size());
- for (BluetoothDevice d : list) {
- assertThat(map).containsKey(d);
- }
-
- // Try to connect one more device. Should fail.
- BluetoothDevice last = makeBluetoothDevice("11:22:33:44:55:66");
- assertThat(mService.connect(last)).isFalse();
- }
-
- /** Test calling connect via Binder */
- @Test
- public void testConnectViaBinder() {
- BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11");
- mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- Utils.setForegroundUserId(UserHandle.getCallingUserId());
- assertThat(mService.connect(device)).isTrue();
- }
-
- private BluetoothDevice makeBluetoothDevice(String address) {
- return mAdapter.getRemoteDevice(address);
- }
-}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java
index 85e5e18607..12bcf80f3c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java
@@ -39,10 +39,10 @@ public class MessagesFilterTest {
assertThat(filter.originator).isEqualTo(originator);
filter.setOriginator("");
- assertThat(filter.originator).isEqualTo(null); // Empty string is stored as null
+ assertThat(filter.originator).isNull(); // Empty string is stored as null
filter.setOriginator(null);
- assertThat(filter.originator).isEqualTo(null);
+ assertThat(filter.originator).isNull();
}
@Test
@@ -74,10 +74,10 @@ public class MessagesFilterTest {
assertThat(filter.recipient).isEqualTo(recipient);
filter.setRecipient("");
- assertThat(filter.recipient).isEqualTo(null); // Empty string is stored as null
+ assertThat(filter.recipient).isNull(); // Empty string is stored as null
filter.setRecipient(null);
- assertThat(filter.recipient).isEqualTo(null);
+ assertThat(filter.recipient).isNull();
}
/** Test Builder creates and sets everything correctly. */
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java
index 5b472d15a4..6f8bad385c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/ObexTimeTest.java
@@ -16,11 +16,12 @@
package com.android.bluetooth.mapclient;
+import static com.google.common.truth.Truth.assertThat;
+
import android.annotation.SuppressLint;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,94 +79,60 @@ public class ObexTimeTest {
@Test
public void createWithValidDateTimeString_TimestampCorrect() {
ObexTime timestamp = new ObexTime(VALID_TIME_STRING);
- Assert.assertEquals(
- "Parsed instant must match expected",
- VALID_INSTANT_LOCAL_TZ,
- timestamp.getInstant());
- Assert.assertEquals(
- "Parsed date must match expected", VALID_DATE_LOCAL_TZ, timestamp.getTime());
+ assertThat(timestamp.getInstant()).isEqualTo(VALID_INSTANT_LOCAL_TZ);
+ assertThat(timestamp.getTime()).isEqualTo(VALID_DATE_LOCAL_TZ);
}
@Test
public void createWithValidDateTimeStringWithPosOffset_TimestampCorrect() {
ObexTime timestamp = new ObexTime(VALID_TIME_STRING_WITH_OFFSET_POS);
- Assert.assertEquals(
- "Parsed instant must match expected",
- VALID_INSTANT_WITH_OFFSET_POS,
- timestamp.getInstant());
- Assert.assertEquals(
- "Parsed date must match expected", VALID_DATE_WITH_OFFSET_POS, timestamp.getTime());
+ assertThat(timestamp.getInstant()).isEqualTo(VALID_INSTANT_WITH_OFFSET_POS);
+ assertThat(timestamp.getTime()).isEqualTo(VALID_DATE_WITH_OFFSET_POS);
}
@Test
public void createWithValidDateTimeStringWithNegOffset_TimestampCorrect() {
ObexTime timestamp = new ObexTime(VALID_TIME_STRING_WITH_OFFSET_NEG);
- Assert.assertEquals(
- "Parsed instant must match expected",
- VALID_INSTANT_WITH_OFFSET_NEG,
- timestamp.getInstant());
- Assert.assertEquals(
- "Parsed date must match expected", VALID_DATE_WITH_OFFSET_NEG, timestamp.getTime());
+ assertThat(timestamp.getInstant()).isEqualTo(VALID_INSTANT_WITH_OFFSET_NEG);
+ assertThat(timestamp.getTime()).isEqualTo(VALID_DATE_WITH_OFFSET_NEG);
}
@Test
public void createWithValidDate_TimestampCorrect() {
ObexTime timestamp = new ObexTime(VALID_DATE_LOCAL_TZ);
- Assert.assertEquals(
- "ObexTime created with a date must return the expected instant",
- VALID_INSTANT_LOCAL_TZ,
- timestamp.getInstant());
- Assert.assertEquals(
- "ObexTime created with a date must return the same date",
- VALID_DATE_LOCAL_TZ,
- timestamp.getTime());
+ assertThat(timestamp.getInstant()).isEqualTo(VALID_INSTANT_LOCAL_TZ);
+ assertThat(timestamp.getTime()).isEqualTo(VALID_DATE_LOCAL_TZ);
}
@SuppressWarnings("JavaUtilDate")
@Test
public void createWithValidInstant_TimestampCorrect() {
ObexTime timestamp = new ObexTime(VALID_INSTANT);
- Assert.assertEquals(
- "ObexTime created with a instant must return the same instant",
- VALID_INSTANT,
- timestamp.getInstant());
- Assert.assertEquals(
- "ObexTime created with a instant must return the expected date",
- VALID_DATE,
- timestamp.getTime());
+ assertThat(timestamp.getInstant()).isEqualTo(VALID_INSTANT);
+ assertThat(timestamp.getTime()).isEqualTo(VALID_DATE);
}
@Test
public void printValidTime_TimestampMatchesInput() {
ObexTime timestamp = new ObexTime(VALID_TIME_STRING);
- Assert.assertEquals(
- "Timestamp as a string must match the input string",
- VALID_TIME_STRING,
- timestamp.toString());
+ assertThat(timestamp.toString()).isEqualTo(VALID_TIME_STRING);
}
@Test
public void createWithInvalidDelimiterString_TimestampIsNull() {
ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_BAD_DELIMITER);
- Assert.assertEquals(
- "Parsed timestamp was invalid and must result in a null object",
- null,
- timestamp.getTime());
+ assertThat(timestamp.getTime()).isNull();
}
@Test
public void createWithInvalidOffsetString_TimestampIsNull() {
ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_OFFSET_EXTRA_DIGITS);
- Assert.assertEquals(
- "Parsed timestamp was invalid and must result in a null object",
- null,
- timestamp.getTime());
+ assertThat(timestamp.getTime()).isNull();
}
@Test
public void printInvalidTime_ReturnsNull() {
ObexTime timestamp = new ObexTime(INVALID_TIME_STRING_BAD_DELIMITER);
- Assert.assertEquals(
- "Invalid timestamps must return null for toString()", null, timestamp.toString());
+ assertThat(timestamp.toString()).isNull();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
index 16cc355cdd..2320b71b1c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/RequestTest.java
@@ -134,7 +134,7 @@ public class RequestTest {
false, /*retry*/
false);
assertThat(newRequest).isNotNull();
- assertThat(newRequest.getMsgHandle()).isEqualTo(null);
+ assertThat(newRequest.getMsgHandle()).isNull();
newRequest.execute(mFakeClientSession);
assertThat(newRequest.isSuccess()).isTrue();
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 dd9e61ae7f..35c7e125a5 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
@@ -34,7 +34,6 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -88,7 +87,7 @@ public class McpServiceTest {
public void testGetService() {
McpService mMcpServiceDuplicate = McpService.getMcpService();
assertThat(mMcpServiceDuplicate).isNotNull();
- Assert.assertSame(mMcpServiceDuplicate, mMcpService);
+ assertThat(mMcpServiceDuplicate).isSameInstanceAs(mMcpService);
}
@Test
@@ -100,13 +99,13 @@ public class McpServiceTest {
mMcpService.setDeviceAuthorized(device0, true);
verify(mMediaControlProfile).onDeviceAuthorizationSet(eq(device0));
- Assert.assertEquals(
- BluetoothDevice.ACCESS_ALLOWED, mMcpService.getDeviceAuthorization(device0));
+ assertThat(mMcpService.getDeviceAuthorization(device0))
+ .isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
mMcpService.setDeviceAuthorized(device1, false);
verify(mMediaControlProfile).onDeviceAuthorizationSet(eq(device1));
- Assert.assertEquals(
- BluetoothDevice.ACCESS_REJECTED, mMcpService.getDeviceAuthorization(device1));
+ assertThat(mMcpService.getDeviceAuthorization(device1))
+ .isEqualTo(BluetoothDevice.ACCESS_REJECTED);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java
index 3da40b9bbd..1e9b210a77 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java
@@ -39,7 +39,6 @@ import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.le_audio.LeAudioService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -135,8 +134,8 @@ public class MediaControlGattServiceTest {
doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags();
assertThat(mMcpService.init(UUID_GMCS)).isTrue();
- Assert.assertEquals(mMcpService.getServiceUuid(), UUID_GMCS);
- Assert.assertEquals(mMcpService.getContentControlId(), TEST_CCID);
+ assertThat(mMcpService.getServiceUuid()).isEqualTo(UUID_GMCS);
+ assertThat(mMcpService.getContentControlId()).isEqualTo(TEST_CCID);
doReturn(true).when(mMockGattServer).removeService(any(BluetoothGattService.class));
mMcpService.destroy();
@@ -210,216 +209,226 @@ public class MediaControlGattServiceTest {
BluetoothGattCharacteristic characteristic =
service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals("", characteristic.getStringValue(0));
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getStringValue(0)).isEmpty();
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals("", characteristic.getStringValue(0));
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getStringValue(0)).isEmpty();
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- 0xFFFFFFFF,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
- .intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
+ .intValue())
+ .isEqualTo(0xFFFFFFFF);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- 0xFFFFFFFF,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
- .intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
+ .intValue())
+ .isEqualTo(0xFFFFFFFF);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- MediaState.INACTIVE.getValue(),
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
+ .intValue())
+ .isEqualTo(MediaState.INACTIVE.getValue());
characteristic = service.getCharacteristic(MediaControlGattService.UUID_CONTENT_CONTROL_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
- Assert.assertEquals(
- TEST_CCID,
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_READ);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
+ .intValue())
+ .isEqualTo(TEST_CCID);
// Check initial state of all optional characteristics
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_READ);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
- Assert.assertEquals("", characteristic.getStringValue(0));
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_READ);
+ assertThat(characteristic.getStringValue(0)).isEmpty();
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_CHANGED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_NOTIFY);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- 0,
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0).intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0)
+ .intValue())
+ .isEqualTo(0);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- 0,
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0).intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0)
+ .intValue())
+ .isEqualTo(0);
characteristic =
service.getCharacteristic(
MediaControlGattService.UUID_CURRENT_TRACK_SEGMENT_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_READ);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_PARENT_GROUP_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- PlayingOrder.SINGLE_ONCE.getValue(),
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
+ .intValue())
+ .isEqualTo(PlayingOrder.SINGLE_ONCE.getValue());
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
- Assert.assertEquals(
- SupportedPlayingOrder.SINGLE_ONCE,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
- .intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(BluetoothGattCharacteristic.PROPERTY_READ);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
+ .intValue())
+ .isEqualTo(SupportedPlayingOrder.SINGLE_ONCE);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_MEDIA_CONTROL_POINT);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_NOTIFY
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_NOTIFY
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
characteristic =
service.getCharacteristic(
MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
- Assert.assertEquals(
- MediaControlGattService.INITIAL_SUPPORTED_OPCODES,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
- .intValue());
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
+ .intValue())
+ .isEqualTo(MediaControlGattService.INITIAL_SUPPORTED_OPCODES);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_SEARCH_RESULT_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_READ
- | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_READ
+ | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
assertThat(characteristic.getValue().length).isEqualTo(0);
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_SEARCH_CONTROL_POINT);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- characteristic.getProperties(),
- BluetoothGattCharacteristic.PROPERTY_NOTIFY
- | BluetoothGattCharacteristic.PROPERTY_WRITE
- | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
+ assertThat(characteristic.getProperties())
+ .isEqualTo(
+ BluetoothGattCharacteristic.PROPERTY_NOTIFY
+ | BluetoothGattCharacteristic.PROPERTY_WRITE
+ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
}
@Test
@@ -488,78 +497,81 @@ public class MediaControlGattServiceTest {
BluetoothGattCharacteristic characteristic =
service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- playback_speed, mMcpService.getPlaybackSpeedChar().floatValue(), 0.001f);
+ assertThat(mMcpService.getPlaybackSpeedChar().floatValue()).isEqualTo(playback_speed);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- playing_order.getValue(),
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
+ .intValue())
+ .isEqualTo(playing_order.getValue());
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
assertThat(characteristic).isNotNull();
// Set value as ms, kept in characteristic as 0.01s
- Assert.assertEquals(
- track_position / 10,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
- .intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
+ .intValue())
+ .isEqualTo(track_position / 10);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(player_name, characteristic.getStringValue(0));
+ assertThat(characteristic.getStringValue(0)).isEqualTo(player_name);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(icon_url, characteristic.getStringValue(0));
+ assertThat(characteristic.getStringValue(0)).isEqualTo(icon_url);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- icon_obj_id.longValue(), mMcpService.byteArray2ObjId(characteristic.getValue()));
+ assertThat(mMcpService.byteArray2ObjId(characteristic.getValue()))
+ .isEqualTo(icon_obj_id.longValue());
characteristic =
service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- playing_order_supported.intValue(),
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
- .intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
+ .intValue())
+ .isEqualTo(playing_order_supported.intValue());
characteristic =
service.getCharacteristic(
MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- opcodes_supported.intValue(),
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
- .intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
+ .intValue())
+ .isEqualTo(opcodes_supported.intValue());
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(track_title, characteristic.getStringValue(0));
+ assertThat(characteristic.getStringValue(0)).isEqualTo(track_title);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION);
assertThat(characteristic).isNotNull();
// Set value as ms, kept in characteristic as 0.01s
- Assert.assertEquals(
- track_duration / 10,
- characteristic
- .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
- .intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
+ .intValue())
+ .isEqualTo(track_duration / 10);
characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(
- playback_state.getValue(),
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
+ assertThat(
+ characteristic
+ .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
+ .intValue())
+ .isEqualTo(playback_state.getValue());
characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED);
assertThat(characteristic).isNotNull();
- Assert.assertEquals(seeking_speed, mMcpService.getSeekingSpeedChar().floatValue(), 0.001f);
+ assertThat(mMcpService.getSeekingSpeedChar().floatValue()).isEqualTo(seeking_speed);
}
private void verifyWriteObjIdsValid(
@@ -931,9 +943,8 @@ public class MediaControlGattServiceTest {
bb.putInt(value);
}
- Assert.assertEquals(
- expectedGattResult,
- mMcpService.handleMediaControlPointRequest(mCurrentDevice, bb.array()));
+ assertThat(mMcpService.handleMediaControlPointRequest(mCurrentDevice, bb.array()))
+ .isEqualTo(expectedGattResult);
if (expectedGattResult == BluetoothGatt.GATT_SUCCESS) {
// Verify if callback comes to profile
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 1da98c68af..6dede08e29 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
@@ -41,7 +41,6 @@ import com.android.bluetooth.audio_util.Metadata;
import com.android.bluetooth.btservice.AdapterService;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -156,57 +155,46 @@ public class MediaControlProfileTest {
// Some duration
mMockMetadata.duration = Long.toString(duration);
- Assert.assertEquals(duration, mMediaControlProfile.getCurrentTrackDuration());
+ assertThat(mMediaControlProfile.getCurrentTrackDuration()).isEqualTo(duration);
// No metadata equals no track duration
mMockMediaData.metadata = null;
- Assert.assertEquals(
- MediaControlGattServiceInterface.TRACK_DURATION_UNAVAILABLE,
- mMediaControlProfile.getCurrentTrackDuration());
+ assertThat(mMediaControlProfile.getCurrentTrackDuration())
+ .isEqualTo(MediaControlGattServiceInterface.TRACK_DURATION_UNAVAILABLE);
}
@Test
public void testPlayerState2McsState() {
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING),
- MediaState.PLAYING);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_NONE),
- MediaState.INACTIVE);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_STOPPED),
- MediaState.PAUSED);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PAUSED),
- MediaState.PAUSED);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING),
- MediaState.PLAYING);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_FAST_FORWARDING),
- MediaState.SEEKING);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_REWINDING),
- MediaState.SEEKING);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_BUFFERING),
- MediaState.PAUSED);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_ERROR),
- MediaState.INACTIVE);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_CONNECTING),
- MediaState.INACTIVE);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_SKIPPING_TO_PREVIOUS),
- MediaState.PAUSED);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(PlaybackState.STATE_SKIPPING_TO_NEXT),
- MediaState.PAUSED);
- Assert.assertEquals(
- mMediaControlProfile.playerState2McsState(
- PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM),
- MediaState.PAUSED);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING))
+ .isEqualTo(MediaState.PLAYING);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_NONE))
+ .isEqualTo(MediaState.INACTIVE);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_STOPPED))
+ .isEqualTo(MediaState.PAUSED);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PAUSED))
+ .isEqualTo(MediaState.PAUSED);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING))
+ .isEqualTo(MediaState.PLAYING);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_FAST_FORWARDING))
+ .isEqualTo(MediaState.SEEKING);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_REWINDING))
+ .isEqualTo(MediaState.SEEKING);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_BUFFERING))
+ .isEqualTo(MediaState.PAUSED);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_ERROR))
+ .isEqualTo(MediaState.INACTIVE);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_CONNECTING))
+ .isEqualTo(MediaState.INACTIVE);
+ assertThat(
+ mMediaControlProfile.playerState2McsState(
+ PlaybackState.STATE_SKIPPING_TO_PREVIOUS))
+ .isEqualTo(MediaState.PAUSED);
+ assertThat(mMediaControlProfile.playerState2McsState(PlaybackState.STATE_SKIPPING_TO_NEXT))
+ .isEqualTo(MediaState.PAUSED);
+ assertThat(
+ mMediaControlProfile.playerState2McsState(
+ PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM))
+ .isEqualTo(MediaState.PAUSED);
}
@Test
@@ -215,18 +203,16 @@ public class MediaControlProfileTest {
long position = 10;
float playback_speed = 1.5f;
- Assert.assertEquals(
- mMcpServiceCallbacks.onGetCurrentTrackPosition(),
- MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
+ assertThat(mMcpServiceCallbacks.onGetCurrentTrackPosition())
+ .isEqualTo(MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
bob.setState(state, position, playback_speed);
mMockMediaData.state = bob.build();
doReturn(mMockMediaData.state).when(mMockMediaPlayerWrapper).getPlaybackState();
- Assert.assertNotEquals(
- mMcpServiceCallbacks.onGetCurrentTrackPosition(),
- MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
+ assertThat(mMcpServiceCallbacks.onGetCurrentTrackPosition())
+ .isNotEqualTo(MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
}
@Test
@@ -272,11 +258,11 @@ public class MediaControlProfileTest {
verify(mMockMediaPlayerWrapper, timeout(100).times(times)).seekTo(positionCaptor.capture());
// position cannot be negative and bigger than track duration
- if (position < 0) Assert.assertEquals(positionCaptor.getValue().longValue(), 0);
+ if (position < 0) assertThat(positionCaptor.getValue().longValue()).isEqualTo(0);
else if (position > duration) {
- Assert.assertEquals(positionCaptor.getValue().longValue(), duration);
+ assertThat(positionCaptor.getValue().longValue()).isEqualTo(duration);
} else {
- Assert.assertEquals(positionCaptor.getValue().longValue(), position);
+ assertThat(positionCaptor.getValue().longValue()).isEqualTo(position);
}
}
@@ -362,7 +348,7 @@ public class MediaControlProfileTest {
verify(mMockMediaPlayerWrapper, timeout(100)).fastForward();
mMockMetadata.duration = Long.toString(duration);
- Assert.assertEquals(duration, mMediaControlProfile.getCurrentTrackDuration());
+ assertThat(mMediaControlProfile.getCurrentTrackDuration()).isEqualTo(duration);
request = new Request(Request.Opcodes.MOVE_RELATIVE, 100);
mMcpServiceCallbacks.onMediaControlRequest(request);
verify(mMockMediaPlayerWrapper, timeout(100)).seekTo(duration);
@@ -398,8 +384,8 @@ public class MediaControlProfileTest {
| PlaybackState.ACTION_FAST_FORWARD
| PlaybackState.ACTION_SKIP_TO_NEXT
| PlaybackState.ACTION_SKIP_TO_PREVIOUS;
- Assert.assertEquals(
- actions | baseFeatures, mMediaControlProfile.getCurrentPlayerSupportedActions());
+ assertThat(mMediaControlProfile.getCurrentPlayerSupportedActions())
+ .isEqualTo(actions | baseFeatures);
}
@Test
@@ -423,15 +409,15 @@ public class MediaControlProfileTest {
| Request.SupportedOpcodes.FAST_FORWARD
| Request.SupportedOpcodes.MOVE_RELATIVE;
- Assert.assertEquals(
- mMediaControlProfile.playerActions2McsSupportedOpcodes(actions), opcodes_supported);
+ assertThat(mMediaControlProfile.playerActions2McsSupportedOpcodes(actions))
+ .isEqualTo(opcodes_supported);
// Verify toggle-style play/pause control support
actions = PlaybackState.ACTION_PLAY_PAUSE;
opcodes_supported = Request.SupportedOpcodes.PAUSE | Request.SupportedOpcodes.PLAY;
- Assert.assertEquals(
- mMediaControlProfile.playerActions2McsSupportedOpcodes(actions), opcodes_supported);
+ assertThat(mMediaControlProfile.playerActions2McsSupportedOpcodes(actions))
+ .isEqualTo(opcodes_supported);
}
@Test
@@ -481,7 +467,7 @@ public class MediaControlProfileTest {
PlayingOrder expected_value, boolean is_shuffle_set, boolean is_repeat_set) {
doReturn(is_shuffle_set).when(mMockMediaPlayerWrapper).isShuffleSet();
doReturn(is_repeat_set).when(mMockMediaPlayerWrapper).isRepeatSet();
- Assert.assertEquals(expected_value, mMediaControlProfile.getCurrentPlayerPlayingOrder());
+ assertThat(mMediaControlProfile.getCurrentPlayerPlayingOrder()).isEqualTo(expected_value);
}
@Test
@@ -503,8 +489,8 @@ public class MediaControlProfileTest {
doReturn(is_shuffle_set).when(mMockMediaPlayerWrapper).isShuffleSupported();
doReturn(is_repeat_set).when(mMockMediaPlayerWrapper).isRepeatSupported();
- Assert.assertEquals(
- expected_value, mMediaControlProfile.getSupportedPlayingOrder().intValue());
+ assertThat(mMediaControlProfile.getSupportedPlayingOrder().intValue())
+ .isEqualTo(expected_value);
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppManagerTest.java
index 77163fa53b..3ce61fd510 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppManagerTest.java
@@ -254,12 +254,12 @@ public class BluetoothOppManagerTest {
public void cleanUpSendingFileInfo_fileInfoCleaned() {
BluetoothOppUtility.sSendFileMap.clear();
Uri uri = Uri.parse("content:///a/new/folder/abc/xyz.txt");
- assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(0);
+ assertThat(BluetoothOppUtility.sSendFileMap).isEmpty();
BluetoothOppManager.getInstance(mContext)
.saveSendingFileInfo("text/plain", uri.toString(), false, true);
- assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(1);
+ assertThat(BluetoothOppUtility.sSendFileMap).hasSize(1);
BluetoothOppManager.getInstance(mContext).cleanUpSendingFileInfo();
- assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(0);
+ assertThat(BluetoothOppUtility.sSendFileMap).isEmpty();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java
index 2183c19347..935c1bf471 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceCleanupTest.java
@@ -61,7 +61,6 @@ public class BluetoothOppServiceCleanupTest {
BluetoothOppService service = null;
try {
service = new BluetoothOppService(adapterService);
- service.start();
service.setAvailable(true);
// Call stop while UpdateThread is running.
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
index 7e54701383..dd14319b17 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
@@ -81,7 +81,6 @@ public class BluetoothOppServiceTest {
AdapterService adapterService = new AdapterService(mTargetContext);
mService = new BluetoothOppService(adapterService);
- mService.start();
mService.setAvailable(true);
mIsBluetoothOppServiceStarted = true;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
index d10246229b..76fd28de89 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java
@@ -319,7 +319,7 @@ public class BluetoothOppUtilityTest {
assertThat(info.mCurrentBytes).isEqualTo(currentBytesValue);
assertThat(info.mTimeStamp).isEqualTo(timestampValue);
assertThat(info.mDestAddr).isEqualTo(destinationValue);
- assertThat(info.mFileUri).isEqualTo(null);
+ assertThat(info.mFileUri).isNull();
assertThat(info.mFileType).isEqualTo(fileTypeValue);
assertThat(info.mDeviceName).isEqualTo(deviceNameValue);
assertThat(info.mHandoverInitiated).isEqualTo(false);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
index d2fddd8078..37927d28ec 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
@@ -32,6 +32,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.net.TetheringInterface;
+import android.net.TetheringManager;
import android.os.UserManager;
import androidx.test.InstrumentationRegistry;
@@ -55,15 +56,6 @@ import org.mockito.junit.MockitoRule;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class PanServiceTest {
- private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
- private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0};
-
- private static final int TIMEOUT_MS = 5_000;
-
- private PanService mService = null;
- private BluetoothAdapter mAdapter = null;
- private BluetoothDevice mRemoteDevice;
-
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private AdapterService mAdapterService;
@@ -71,31 +63,34 @@ public class PanServiceTest {
@Mock private PanNativeInterface mNativeInterface;
@Mock private UserManager mMockUserManager;
+ private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0};
+
+ private static final int TIMEOUT_MS = 5_000;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mRemoteDevice = TestUtils.getTestDevice(mAdapter, 0);
+ private final Context mTargetContext = InstrumentationRegistry.getTargetContext();
+
+ private PanService mService;
+
@Before
- public void setUp() throws Exception {
- Context targetContext = InstrumentationRegistry.getTargetContext();
- TestUtils.setAdapterService(mAdapterService);
+ public void setUp() {
+ doReturn(mTargetContext.getResources()).when(mAdapterService).getResources();
doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
- PanNativeInterface.setInstance(mNativeInterface);
- mService = new PanService(targetContext);
- mService.start();
- mService.setAvailable(true);
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.USER_SERVICE, UserManager.class, mMockUserManager);
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.TETHERING_SERVICE, TetheringManager.class);
- // Try getting the Bluetooth adapter
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- assertThat(mAdapter).isNotNull();
- mService.mUserManager = mMockUserManager;
- mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+ mService = new PanService(mAdapterService, mNativeInterface);
+ mService.setAvailable(true);
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() {
mService.stop();
mService.cleanup();
- PanNativeInterface.setInstance(null);
- mService = PanService.getPanService();
- assertThat(mService).isNull();
- TestUtils.clearAdapterService(mAdapterService);
+ assertThat(PanService.getPanService()).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/PbapStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/PbapStateMachineTest.java
index a1426dedef..5e4abee154 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbap/PbapStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/PbapStateMachineTest.java
@@ -16,6 +16,8 @@
package com.android.bluetooth.pbap;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.*;
import android.bluetooth.BluetoothAdapter;
@@ -31,9 +33,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
-import org.hamcrest.core.IsInstanceOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -97,11 +97,10 @@ public class PbapStateMachineTest {
/** Test that initial state is WaitingForAuth */
@Test
public void testInitialState() {
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTING, mPbapStateMachine.getConnectionState());
- Assert.assertThat(
- mPbapStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(PbapStateMachine.WaitingForAuth.class));
+ assertThat(mPbapStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_CONNECTING);
+ assertThat(mPbapStateMachine.getCurrentState())
+ .isInstanceOf(PbapStateMachine.WaitingForAuth.class);
}
/** Test state transition from WaitingForAuth to Finished when the user rejected */
@@ -109,11 +108,10 @@ public class PbapStateMachineTest {
@Test
public void testStateTransition_WaitingForAuthToFinished() throws Exception {
mPbapStateMachine.sendMessage(PbapStateMachine.REJECTED);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED, mPbapStateMachine.getConnectionState());
- Assert.assertThat(
- mPbapStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(PbapStateMachine.Finished.class));
+ assertThat(mPbapStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mPbapStateMachine.getCurrentState())
+ .isInstanceOf(PbapStateMachine.Finished.class);
}
/** Test state transition from WaitingForAuth to Finished when the user rejected */
@@ -121,11 +119,10 @@ public class PbapStateMachineTest {
@Test
public void testStateTransition_WaitingForAuthToConnected() throws Exception {
mPbapStateMachine.sendMessage(PbapStateMachine.AUTHORIZED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED, mPbapStateMachine.getConnectionState());
- Assert.assertThat(
- mPbapStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(PbapStateMachine.Connected.class));
+ assertThat(mPbapStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mPbapStateMachine.getCurrentState())
+ .isInstanceOf(PbapStateMachine.Connected.class);
}
/** Test state transition from Connected to Finished when the OBEX server is done */
@@ -133,18 +130,16 @@ public class PbapStateMachineTest {
@Test
public void testStateTransition_ConnectedToFinished() throws Exception {
mPbapStateMachine.sendMessage(PbapStateMachine.AUTHORIZED);
- Assert.assertEquals(
- BluetoothProfile.STATE_CONNECTED, mPbapStateMachine.getConnectionState());
- Assert.assertThat(
- mPbapStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(PbapStateMachine.Connected.class));
+ assertThat(mPbapStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mPbapStateMachine.getCurrentState())
+ .isInstanceOf(PbapStateMachine.Connected.class);
// PBAP OBEX transport is done.
mPbapStateMachine.sendMessage(PbapStateMachine.DISCONNECT);
- Assert.assertEquals(
- BluetoothProfile.STATE_DISCONNECTED, mPbapStateMachine.getConnectionState());
- Assert.assertThat(
- mPbapStateMachine.getCurrentState(),
- IsInstanceOf.instanceOf(PbapStateMachine.Finished.class));
+ assertThat(mPbapStateMachine.getConnectionState())
+ .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mPbapStateMachine.getCurrentState())
+ .isInstanceOf(PbapStateMachine.Finished.class);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
index f3e5ddd821..43d12765c2 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/CallLogPullRequestTest.java
@@ -88,7 +88,7 @@ public class CallLogPullRequestTest {
request.onPullComplete();
// No operation has been done.
- assertThat(mCallCounter.size()).isEqualTo(0);
+ assertThat(mCallCounter).isEmpty();
}
@Test
@@ -102,7 +102,7 @@ public class CallLogPullRequestTest {
request.onPullComplete();
// No operation has been done.
- assertThat(mCallCounter.size()).isEqualTo(0);
+ assertThat(mCallCounter).isEmpty();
}
@Test
@@ -116,7 +116,7 @@ public class CallLogPullRequestTest {
request.onPullComplete();
// Call counter should remain same.
- assertThat(mCallCounter.size()).isEqualTo(0);
+ assertThat(mCallCounter).isEmpty();
}
@Test
@@ -140,7 +140,7 @@ public class CallLogPullRequestTest {
request.onPullComplete();
// Call counter should remain same.
- assertThat(mCallCounter.size()).isEqualTo(0);
+ assertThat(mCallCounter).isEmpty();
}
@Test
@@ -165,7 +165,7 @@ public class CallLogPullRequestTest {
request.onPullComplete();
- assertThat(mCallCounter.size()).isEqualTo(1);
+ assertThat(mCallCounter).hasSize(1);
for (String key : mCallCounter.keySet()) {
assertThat(mCallCounter.get(key)).isEqualTo(2);
break;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java
index 1611399d98..26a0ffe2be 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientAccountManagerTest.java
@@ -175,7 +175,7 @@ public class PbapClientAccountManagerTest {
assertThat(fromAccounts).isNull();
assertThat(toAccounts).isNotNull();
- assertThat(toAccounts.size()).isEqualTo(2);
+ assertThat(toAccounts).hasSize(2);
assertThat(toAccounts).contains(accounts[0]);
assertThat(toAccounts).contains(accounts[1]);
}
@@ -250,7 +250,7 @@ public class PbapClientAccountManagerTest {
mTestLooper.dispatchAll();
assertThat(mAccountManager.getAccounts()).isNotNull();
- assertThat(mAccountManager.getAccounts().size()).isEqualTo(2);
+ assertThat(mAccountManager.getAccounts()).hasSize(2);
assertThat(mAccountManager.getAccounts()).contains(accounts[0]);
assertThat(mAccountManager.getAccounts()).contains(accounts[1]);
}
@@ -289,7 +289,7 @@ public class PbapClientAccountManagerTest {
// Add again once its already in there
assertThat(mAccountManager.addAccount(account)).isTrue();
- assertThat(mAccountManager.getAccounts().size()).isEqualTo(1);
+ assertThat(mAccountManager.getAccounts()).hasSize(1);
assertThat(mAccountManager.getAccounts()).contains(account);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientContactsStorageTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientContactsStorageTest.java
index 18cae7d34d..68ab51a0b4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientContactsStorageTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientContactsStorageTest.java
@@ -244,7 +244,7 @@ public class PbapClientContactsStorageTest {
assertThat(mStorage.getStorageAccounts()).contains(account1);
mStorage.removeAccount(account2);
- assertThat(mStorage.getStorageAccounts().size()).isEqualTo(mMockedAccounts.size());
+ assertThat(mStorage.getStorageAccounts()).hasSize(mMockedAccounts.size());
assertThat(mStorage.getStorageAccounts()).contains(account1);
assertThat(mStorage.getStorageAccounts()).doesNotContain(account2);
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexClientTest.java
index 723d219790..a5897cf1c6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexClientTest.java
@@ -270,12 +270,12 @@ public class PbapClientObexClientTest {
assertThat(phonebook.getOffset()).isEqualTo(0);
assertThat(phonebook.getCount()).isEqualTo(1);
assertThat(phonebook.getList()).isNotEmpty();
- assertThat(phonebook.getList().size()).isEqualTo(1);
+ assertThat(phonebook.getList()).hasSize(1);
VCardEntry contact1 = phonebook.getList().get(0);
assertThat(contact1.getDisplayName()).isEqualTo("Foo Bar");
assertThat(contact1.getPhoneList()).isNotNull();
- assertThat(contact1.getPhoneList().size()).isEqualTo(1);
+ assertThat(contact1.getPhoneList()).hasSize(1);
assertThat(contact1.getPhoneList().get(0).getNumber()).isEqualTo("+1-234-567-8901");
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceTest.java
index 63a52d7ac4..6da23ea3f9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceTest.java
@@ -18,17 +18,16 @@ package com.android.bluetooth.sap;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import androidx.test.filters.MediumTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
@@ -47,40 +46,32 @@ import org.mockito.junit.MockitoRule;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class SapServiceTest {
- private SapService mService = null;
- private BluetoothAdapter mAdapter = null;
- private Context mTargetContext;
-
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private AdapterService mAdapterService;
@Mock private DatabaseManager mDatabaseManager;
- private BluetoothDevice mDevice;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 0);
+
+ private SapService mService;
@Before
- public void setUp() throws Exception {
- mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- TestUtils.setAdapterService(mAdapterService);
+ public void setUp() {
+ doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
if (Looper.myLooper() == null) {
Looper.prepare();
}
- mService = new SapService(mTargetContext);
- mService.start();
+ mService = new SapService(mAdapterService);
mService.setAvailable(true);
- // Try getting the Bluetooth adapter
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- assertThat(mAdapter).isNotNull();
- mDevice = TestUtils.getTestDevice(mAdapter, 0);
}
@After
- public void tearDown() throws Exception {
+ public void tearDown() {
mService.stop();
- mService = SapService.getSapService();
- assertThat(mService).isNull();
- TestUtils.clearAdapterService(mAdapterService);
+ assertThat(SapService.getSapService()).isNull();
}
@Test
@@ -89,22 +80,9 @@ public class SapServiceTest {
assertThat(mService.getConnectedDevices()).isEmpty();
}
- /** Test stop SAP Service */
- @Test
- public void testStopSapService() throws Exception {
- // SAP Service is already running: test stop(). Note: must be done on the main thread
- InstrumentationRegistry.getInstrumentation()
- .runOnMainSync(
- () -> {
- mService.stop();
- mService.start();
- });
- }
-
/** Test get connection policy for BluetoothDevice */
@Test
public void testGetConnectionPolicy() {
- when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.SAP))
.thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
assertThat(mService.getConnectionPolicy(mDevice))
@@ -137,6 +115,6 @@ public class SapServiceTest {
Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
intent.putExtra(
BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
- mService.mSapReceiver.onReceive(mTargetContext, intent);
+ mService.mSapReceiver.onReceive(null, intent);
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java b/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
index 3ed17fb529..2e49a1f6e1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
@@ -99,7 +99,7 @@ public class DipTest {
boolean primaryRecord) {
Intent intent = intentArgument.getValue();
- assertThat(intent).isNotEqualTo(null);
+ assertThat(intent).isNotNull();
assertThat(intent.getAction()).isEqualTo(BluetoothDevice.ACTION_SDP_RECORD);
assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
assertThat(Utils.byteArrayToUuid(uuid)[0])
@@ -108,7 +108,7 @@ public class DipTest {
.isEqualTo(intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, -1));
SdpDipRecord record = intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD);
- assertThat(record).isNotEqualTo(null);
+ assertThat(record).isNotNull();
assertThat(specificationId).isEqualTo(record.getSpecificationId());
assertThat(vendorId).isEqualTo(record.getVendorId());
assertThat(vendorIdSource).isEqualTo(record.getVendorIdSource());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
index 798984b273..a959a4a764 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java
@@ -17,7 +17,6 @@
package com.android.bluetooth.tbs;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -40,8 +39,6 @@ import com.android.bluetooth.btservice.AdapterService;
import com.google.common.primitives.Bytes;
-import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -63,9 +60,18 @@ import java.util.UUID;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TbsGattTest {
- private BluetoothAdapter mAdapter;
- private BluetoothDevice mFirstDevice;
- private BluetoothDevice mSecondDevice;
+ @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
+
+ @Mock private AdapterService mAdapterService;
+ @Mock private BluetoothGattServerProxy mGattServer;
+ @Mock private TbsGatt.Callback mCallback;
+ @Mock private TbsService mService;
+ @Captor private ArgumentCaptor<BluetoothGattService> mGattServiceCaptor;
+
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mFirstDevice = TestUtils.getTestDevice(mAdapter, 0);
+ private final BluetoothDevice mSecondDevice = TestUtils.getTestDevice(mAdapter, 1);
private Integer mCurrentCcid;
private String mCurrentUci;
@@ -75,50 +81,19 @@ public class TbsGattTest {
private TbsGatt mTbsGatt;
- @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-
- @Mock private AdapterService mAdapterService;
- @Mock private BluetoothGattServerProxy mMockGattServer;
- @Mock private TbsGatt.Callback mMockTbsGattCallback;
- @Mock private TbsService mMockTbsService;
-
- @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
-
- @Captor private ArgumentCaptor<BluetoothGattService> mGattServiceCaptor;
-
@Before
- public void setUp() throws Exception {
+ public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
- getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
-
- TestUtils.setAdapterService(mAdapterService);
- mAdapter = BluetoothAdapter.getDefaultAdapter();
-
- doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
- doReturn(true).when(mMockGattServer).open(any(BluetoothGattServerCallback.class));
+ doReturn(true).when(mGattServer).addService(any(BluetoothGattService.class));
+ doReturn(true).when(mGattServer).open(any(BluetoothGattServerCallback.class));
doReturn(BluetoothDevice.ACCESS_ALLOWED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
- mTbsGatt = new TbsGatt(mMockTbsService);
- mTbsGatt.setBluetoothGattServerForTesting(mMockGattServer);
-
- mFirstDevice = TestUtils.getTestDevice(mAdapter, 0);
- mSecondDevice = TestUtils.getTestDevice(mAdapter, 1);
-
- when(mMockTbsService.getDeviceAuthorization(any(BluetoothDevice.class)))
- .thenReturn(BluetoothDevice.ACCESS_ALLOWED);
- }
-
- @After
- public void tearDown() throws Exception {
- mFirstDevice = null;
- mSecondDevice = null;
- mTbsGatt = null;
- TestUtils.clearAdapterService(mAdapterService);
+ mTbsGatt = new TbsGatt(mAdapterService, mService, mGattServer);
}
private void prepareDefaultService() {
@@ -137,12 +112,12 @@ public class TbsGattTest {
true,
mCurrentProviderName,
mCurrentTechnology,
- mMockTbsGattCallback))
+ mCallback))
.isTrue();
verify(mAdapterService).registerBluetoothStateCallback(any(), any());
- verify(mMockGattServer).addService(mGattServiceCaptor.capture());
- doReturn(mGattServiceCaptor.getValue()).when(mMockGattServer).getService(any(UUID.class));
+ verify(mGattServer).addService(mGattServiceCaptor.capture());
+ doReturn(mGattServiceCaptor.getValue()).when(mGattServer).getService(any(UUID.class));
}
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
@@ -169,9 +144,9 @@ public class TbsGattTest {
enable
? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
: BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(eq(device), eq(1), eq(BluetoothGatt.GATT_SUCCESS), eq(0), any());
- reset(mMockGattServer);
+ reset(mGattServer);
}
private void verifySetValue(
@@ -189,13 +164,12 @@ public class TbsGattTest {
} else {
assertThat(mTbsGatt.setBearerProviderName((String) value)).isFalse();
}
- Assert.assertEquals((String) value, characteristic.getStringValue(0));
+ assertThat(characteristic.getStringValue(0)).isEqualTo((String) value);
} else if (characteristic.getUuid().equals(TbsGatt.UUID_BEARER_TECHNOLOGY)) {
assertThat(mTbsGatt.setBearerTechnology((Integer) value)).isTrue();
- Assert.assertEquals(
- (Integer) value,
- characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0));
+ assertThat(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0))
+ .isEqualTo((Integer) value);
} else if (characteristic
.getUuid()
@@ -209,7 +183,7 @@ public class TbsGattTest {
assertThat(mTbsGatt.setBearerUriSchemesSupportedList((List<String>) value))
.isFalse();
}
- Assert.assertEquals(valueString, characteristic.getStringValue(0));
+ assertThat(characteristic.getStringValue(0)).isEqualTo(valueString);
} else if (characteristic.getUuid().equals(TbsGatt.UUID_STATUS_FLAGS)) {
Pair<Integer, Boolean> flagStatePair = (Pair<Integer, Boolean>) value;
@@ -252,14 +226,15 @@ public class TbsGattTest {
assertThat(mTbsGatt.setTerminationReason(indexReasonPair.first, indexReasonPair.second))
.isTrue();
assertThat(characteristic.getValue())
- .asList()
- .containsExactly(
- indexReasonPair.first.byteValue(), indexReasonPair.second.byteValue())
- .inOrder();
+ .isEqualTo(
+ new byte[] {
+ indexReasonPair.first.byteValue(),
+ indexReasonPair.second.byteValue()
+ });
} else if (characteristic.getUuid().equals(TbsGatt.UUID_INCOMING_CALL)) {
if (value == null) {
assertThat(mTbsGatt.clearIncomingCall()).isTrue();
- Assert.assertEquals(0, characteristic.getValue().length);
+ assertThat(characteristic.getValue()).isEmpty();
} else {
Pair<Integer, String> indexStrPair = (Pair<Integer, String>) value;
assertThat(mTbsGatt.setIncomingCall(indexStrPair.first, indexStrPair.second))
@@ -274,7 +249,7 @@ public class TbsGattTest {
} else if (characteristic.getUuid().equals(TbsGatt.UUID_CALL_FRIENDLY_NAME)) {
if (value == null) {
assertThat(mTbsGatt.clearFriendlyName()).isTrue();
- Assert.assertEquals(0, characteristic.getValue().length);
+ assertThat(characteristic.getValue()).isEmpty();
} else {
Pair<Integer, String> indexNamePair = (Pair<Integer, String>) value;
assertThat(mTbsGatt.setCallFriendlyName(indexNamePair.first, indexNamePair.second))
@@ -289,26 +264,26 @@ public class TbsGattTest {
if (shouldNotify) {
if (notifyWithValue) {
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(device), eq(characteristic), eq(false), any());
} else {
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(eq(device), eq(characteristic), eq(false));
}
} else {
if (notifyWithValue) {
- verify(mMockGattServer, times(0))
+ verify(mGattServer, never())
.notifyCharacteristicChanged(
eq(device), eq(characteristic), anyBoolean(), any());
} else {
- verify(mMockGattServer, times(0))
+ verify(mGattServer, never())
.notifyCharacteristicChanged(eq(device), eq(characteristic), anyBoolean());
}
}
if (clearGattMock) {
- reset(mMockGattServer);
+ reset(mGattServer);
}
}
@@ -576,9 +551,9 @@ public class TbsGattTest {
.asList()
.containsExactly(requestedOpcode, callIndex, result)
.inOrder();
- verify(mMockGattServer, after(2000))
+ verify(mGattServer, after(2000))
.notifyCharacteristicChanged(eq(mFirstDevice), eq(characteristic), eq(false));
- reset(mMockGattServer);
+ reset(mGattServer);
callIndex = 0x02;
@@ -589,7 +564,7 @@ public class TbsGattTest {
.asList()
.containsExactly(requestedOpcode, callIndex, result)
.inOrder();
- verify(mMockGattServer, after(2000).times(0))
+ verify(mGattServer, after(2000).never())
.notifyCharacteristicChanged(any(), any(), anyBoolean());
}
@@ -696,7 +671,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onCharacteristicWriteRequest(
mFirstDevice, 1, characteristic, false, true, 0, value);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -705,7 +680,7 @@ public class TbsGattTest {
aryEq(new byte[] {0x00, 0x0A}));
// Verify the higher layer callback call
- verify(mMockTbsGattCallback)
+ verify(mCallback)
.onCallControlPointRequest(eq(mFirstDevice), eq(0x00), aryEq(new byte[] {0x0A}));
}
@@ -720,7 +695,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onCharacteristicWriteRequest(
mFirstDevice, 1, characteristic, false, true, 0, value);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -747,15 +722,15 @@ public class TbsGattTest {
mTbsGatt.setInbandRingtoneFlag(mFirstDevice);
mTbsGatt.setInbandRingtoneFlag(mFirstDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mFirstDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
mTbsGatt.setInbandRingtoneFlag(mSecondDevice);
mTbsGatt.setInbandRingtoneFlag(mSecondDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mSecondDevice), eq(characteristic), eq(false), eq(valueBytes));
}
@@ -774,18 +749,18 @@ public class TbsGattTest {
valueBytes[1] = (byte) ((statusFlagValue >> 8) & 0xFF);
mTbsGatt.setInbandRingtoneFlag(mFirstDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mFirstDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
mTbsGatt.setInbandRingtoneFlag(mSecondDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mSecondDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
// clear flag
statusFlagValue = 0;
@@ -794,14 +769,14 @@ public class TbsGattTest {
mTbsGatt.clearInbandRingtoneFlag(mFirstDevice);
mTbsGatt.clearInbandRingtoneFlag(mFirstDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mFirstDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
mTbsGatt.clearInbandRingtoneFlag(mSecondDevice);
mTbsGatt.clearInbandRingtoneFlag(mSecondDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mSecondDevice), eq(characteristic), eq(false), eq(valueBytes));
}
@@ -823,10 +798,10 @@ public class TbsGattTest {
mTbsGatt.setSilentModeFlag();
mTbsGatt.setSilentModeFlag();
mTbsGatt.setSilentModeFlag();
- verify(mMockGattServer, times(2))
+ verify(mGattServer, times(2))
.notifyCharacteristicChanged(any(), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
statusFlagValue =
TbsGatt.STATUS_FLAG_INBAND_RINGTONE_ENABLED
@@ -836,17 +811,17 @@ public class TbsGattTest {
mTbsGatt.setInbandRingtoneFlag(mFirstDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mFirstDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
mTbsGatt.setInbandRingtoneFlag(mSecondDevice);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(
eq(mSecondDevice), eq(characteristic), eq(false), eq(valueBytes));
- reset(mMockGattServer);
+ reset(mGattServer);
statusFlagValue = TbsGatt.STATUS_FLAG_INBAND_RINGTONE_ENABLED;
valueBytes[0] = (byte) (statusFlagValue & 0xFF);
@@ -856,7 +831,7 @@ public class TbsGattTest {
mTbsGatt.clearSilentModeFlag();
mTbsGatt.clearSilentModeFlag();
mTbsGatt.clearSilentModeFlag();
- verify(mMockGattServer, times(2))
+ verify(mGattServer, times(2))
.notifyCharacteristicChanged(any(), eq(characteristic), eq(false), eq(valueBytes));
}
@@ -868,7 +843,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onCharacteristicReadRequest(
mFirstDevice, 1, 0, characteristic);
// Verify the higher layer callback call
- verify(mMockTbsGattCallback).isInbandRingtoneEnabled(eq(mFirstDevice));
+ verify(mCallback).isInbandRingtoneEnabled(eq(mFirstDevice));
}
@Test
@@ -882,31 +857,31 @@ public class TbsGattTest {
// Check with no configuration
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check with notifications enabled
configureNotifications(mFirstDevice, characteristic, true);
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check with notifications disabled
configureNotifications(mFirstDevice, characteristic, false);
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -926,7 +901,7 @@ public class TbsGattTest {
// Check with no configuration
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -935,79 +910,79 @@ public class TbsGattTest {
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mSecondDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mSecondDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check with notifications enabled for first device
configureNotifications(mFirstDevice, characteristic, true);
verifySetValue(characteristic, 4, true, mFirstDevice, true);
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check if second device is still not subscribed for notifications and will not get it
verifySetValue(characteristic, 5, false, mSecondDevice, false);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(eq(mFirstDevice), eq(characteristic), eq(false));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mSecondDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mSecondDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check with notifications enabled for first and second device
configureNotifications(mSecondDevice, characteristic, true);
verifySetValue(characteristic, 6, true, mSecondDevice, false);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(eq(mFirstDevice), eq(characteristic), eq(false));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mSecondDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mSecondDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Disable notification for first device, check if second will get notification
configureNotifications(mFirstDevice, characteristic, false);
verifySetValue(characteristic, 7, false, mFirstDevice, false);
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(eq(mSecondDevice), eq(characteristic), eq(false));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
eq(BluetoothGatt.GATT_SUCCESS),
eq(0),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
- reset(mMockGattServer);
+ reset(mGattServer);
// Check with notifications disabled of both device
configureNotifications(mSecondDevice, characteristic, false);
verifySetValue(characteristic, 4, false, mFirstDevice, false);
- verify(mMockGattServer, times(0))
+ verify(mGattServer, never())
.notifyCharacteristicChanged(eq(mSecondDevice), eq(characteristic), eq(false));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mSecondDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mSecondDevice),
eq(1),
@@ -1024,13 +999,13 @@ public class TbsGattTest {
getCharacteristic(TbsGatt.UUID_BEARER_TECHNOLOGY);
doReturn(BluetoothDevice.ACCESS_REJECTED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
mTbsGatt.mGattServerCallback.onCharacteristicReadRequest(
mFirstDevice, 1, 0, characteristic);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -1048,12 +1023,12 @@ public class TbsGattTest {
configureNotifications(mFirstDevice, characteristic, true);
configureNotifications(mSecondDevice, characteristic, true);
- doReturn(mGattServiceCaptor.getValue()).when(mMockGattServer).getService(any(UUID.class));
+ doReturn(mGattServiceCaptor.getValue()).when(mGattServer).getService(any(UUID.class));
assertThat(mGattServiceCaptor.getValue()).isNotNull();
// Leave it as unauthorized yet
doReturn(BluetoothDevice.ACCESS_REJECTED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
int statusFlagValue = TbsGatt.STATUS_FLAG_SILENT_MODE_ENABLED;
@@ -1069,18 +1044,18 @@ public class TbsGattTest {
valueBytes[0] = (byte) (statusFlagValue & 0xFF);
valueBytes[1] = (byte) ((statusFlagValue >> 8) & 0xFF);
mTbsGatt.setSilentModeFlag();
- verify(mMockGattServer, times(0))
+ verify(mGattServer, never())
.notifyCharacteristicChanged(any(), eq(characteristic), eq(false), eq(valueBytes));
// Expect a single notification for the just authorized device
doReturn(BluetoothDevice.ACCESS_ALLOWED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
assertThat(mGattServiceCaptor.getValue()).isNotNull();
mTbsGatt.onDeviceAuthorizationSet(mFirstDevice);
- verify(mMockGattServer, times(0))
+ verify(mGattServer, never())
.notifyCharacteristicChanged(any(), eq(characteristic2), eq(false));
- verify(mMockGattServer)
+ verify(mGattServer)
.notifyCharacteristicChanged(any(), eq(characteristic), eq(false), eq(valueBytes));
}
@@ -1092,13 +1067,13 @@ public class TbsGattTest {
getCharacteristic(TbsGatt.UUID_BEARER_TECHNOLOGY);
doReturn(BluetoothDevice.ACCESS_UNKNOWN)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
mTbsGatt.mGattServerCallback.onCharacteristicReadRequest(
mFirstDevice, 1, 0, characteristic);
- verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
+ verify(mService, never()).onDeviceUnauthorized(eq(mFirstDevice));
}
@Test
@@ -1109,7 +1084,7 @@ public class TbsGattTest {
getCharacteristic(TbsGatt.UUID_CALL_CONTROL_POINT);
doReturn(BluetoothDevice.ACCESS_REJECTED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
byte[] value =
@@ -1120,7 +1095,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onCharacteristicWriteRequest(
mFirstDevice, 1, characteristic, false, true, 0, value);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -1137,7 +1112,7 @@ public class TbsGattTest {
getCharacteristic(TbsGatt.UUID_CALL_CONTROL_POINT);
doReturn(BluetoothDevice.ACCESS_UNKNOWN)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
byte[] value =
@@ -1148,7 +1123,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onCharacteristicWriteRequest(
mFirstDevice, 1, characteristic, false, true, 0, value);
- verify(mMockTbsService).onDeviceUnauthorized(eq(mFirstDevice));
+ verify(mService).onDeviceUnauthorized(eq(mFirstDevice));
}
@Test
@@ -1161,12 +1136,12 @@ public class TbsGattTest {
assertThat(descriptor).isNotNull();
doReturn(BluetoothDevice.ACCESS_REJECTED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -1185,12 +1160,12 @@ public class TbsGattTest {
assertThat(descriptor).isNotNull();
doReturn(BluetoothDevice.ACCESS_UNKNOWN)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
mTbsGatt.mGattServerCallback.onDescriptorReadRequest(mFirstDevice, 1, 0, descriptor);
- verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
+ verify(mService, never()).onDeviceUnauthorized(eq(mFirstDevice));
}
@Test
@@ -1203,7 +1178,7 @@ public class TbsGattTest {
assertThat(descriptor).isNotNull();
doReturn(BluetoothDevice.ACCESS_REJECTED)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
byte[] value =
@@ -1214,7 +1189,7 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onDescriptorWriteRequest(
mFirstDevice, 1, descriptor, false, true, 0, value);
- verify(mMockGattServer)
+ verify(mGattServer)
.sendResponse(
eq(mFirstDevice),
eq(1),
@@ -1233,7 +1208,7 @@ public class TbsGattTest {
assertThat(descriptor).isNotNull();
doReturn(BluetoothDevice.ACCESS_UNKNOWN)
- .when(mMockTbsService)
+ .when(mService)
.getDeviceAuthorization(any(BluetoothDevice.class));
byte[] value =
@@ -1244,6 +1219,6 @@ public class TbsGattTest {
mTbsGatt.mGattServerCallback.onDescriptorWriteRequest(
mFirstDevice, 1, descriptor, false, true, 0, value);
- verify(mMockTbsService, times(0)).onDeviceUnauthorized(eq(mFirstDevice));
+ verify(mService, never()).onDeviceUnauthorized(eq(mFirstDevice));
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java
index d7ed9eee6c..345dd8a5ee 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java
@@ -33,7 +33,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.le_audio.LeAudioService;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -46,6 +45,7 @@ import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -53,33 +53,27 @@ import java.util.UUID;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TbsGenericTest {
- private BluetoothAdapter mAdapter;
- private BluetoothDevice mCurrentDevice;
-
- private TbsGeneric mTbsGeneric;
-
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @Mock private TbsGatt mTbsGatt;
+ @Mock private IBluetoothLeCallControlCallback mIBluetoothLeCallControlCallback;
+ @Captor private ArgumentCaptor<Integer> mGtbsCcidCaptor;
+ @Captor private ArgumentCaptor<String> mGtbsUciCaptor;
- private @Mock TbsGatt mTbsGatt;
- private @Mock IBluetoothLeCallControlCallback mIBluetoothLeCallControlCallback;
- private @Captor ArgumentCaptor<Integer> mGtbsCcidCaptor;
- private @Captor ArgumentCaptor<String> mGtbsUciCaptor;
- private @Captor ArgumentCaptor<List> mDefaultGtbsUriSchemesCaptor =
- ArgumentCaptor.forClass(List.class);
- private @Captor ArgumentCaptor<String> mDefaultGtbsProviderNameCaptor;
- private @Captor ArgumentCaptor<Integer> mDefaultGtbsTechnologyCaptor;
+ @Captor
+ private ArgumentCaptor<List> mDefaultGtbsUriSchemesCaptor = ArgumentCaptor.forClass(List.class);
- private @Captor ArgumentCaptor<TbsGatt.Callback> mTbsGattCallback;
- private static Context mContext;
-
- @Before
- public void setUp() throws Exception {
+ @Captor private ArgumentCaptor<String> mDefaultGtbsProviderNameCaptor;
+ @Captor private ArgumentCaptor<Integer> mDefaultGtbsTechnologyCaptor;
+ @Captor private ArgumentCaptor<TbsGatt.Callback> mTbsGattCallback;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mContext = getInstrumentation().getTargetContext();
+ private final Context mContext = getInstrumentation().getTargetContext();
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 32);
- getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
+ private TbsGeneric mTbsGeneric;
+ @Before
+ public void setUp() {
// Default TbsGatt mock behavior
doReturn(true)
.when(mTbsGatt)
@@ -106,15 +100,8 @@ public class TbsGenericTest {
doReturn(true).when(mTbsGatt).clearIncomingCall();
doReturn(true).when(mTbsGatt).setCallFriendlyName(anyInt(), anyString());
doReturn(true).when(mTbsGatt).clearFriendlyName();
- doReturn(mContext).when(mTbsGatt).getContext();
- mTbsGeneric = new TbsGeneric();
- mTbsGeneric.init(mTbsGatt);
- }
-
- @After
- public void tearDown() throws Exception {
- mTbsGeneric = null;
+ mTbsGeneric = new TbsGeneric(mContext, mTbsGatt);
}
private Integer prepareTestBearer() {
@@ -150,14 +137,13 @@ public class TbsGenericTest {
@Test
public void testSetClearInbandRingtone() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
prepareTestBearer();
- mTbsGeneric.setInbandRingtoneSupport(mCurrentDevice);
- verify(mTbsGatt).setInbandRingtoneFlag(mCurrentDevice);
+ mTbsGeneric.setInbandRingtoneSupport(mDevice);
+ verify(mTbsGatt).setInbandRingtoneFlag(mDevice);
- mTbsGeneric.clearInbandRingtoneSupport(mCurrentDevice);
- verify(mTbsGatt).clearInbandRingtoneFlag(mCurrentDevice);
+ mTbsGeneric.clearInbandRingtoneSupport(mDevice);
+ verify(mTbsGatt).clearInbandRingtoneFlag(mDevice);
}
@Test
@@ -212,7 +198,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
TbsCall capturedTbsCall = capturedCurrentCalls.get(capturedCallIndex);
assertThat(capturedTbsCall).isNotNull();
assertThat(capturedTbsCall.getState()).isEqualTo(BluetoothLeCall.STATE_INCOMING);
@@ -243,12 +229,12 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
TbsCall capturedTbsCall = capturedCurrentCalls.get(capturedCallIndex);
assertThat(capturedTbsCall).isNotNull();
assertThat(capturedTbsCall.getState()).isEqualTo(BluetoothLeCall.STATE_INCOMING);
- assertThat(capturedTbsCall.getUri()).isEqualTo(null);
- assertThat(capturedTbsCall.getSafeUri()).isEqualTo(null);
+ assertThat(capturedTbsCall.getUri()).isNull();
+ assertThat(capturedTbsCall.getSafeUri()).isNull();
assertThat(capturedTbsCall.getFlags()).isEqualTo(0);
assertThat(capturedTbsCall.isIncoming()).isTrue();
assertThat(capturedTbsCall.getFriendlyName()).isEqualTo("aFriendlyCaller");
@@ -283,9 +269,9 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(0);
+ assertThat(capturedCurrentCalls).isEmpty();
verify(mTbsGatt).setBearerListCurrentCalls(currentCallsCaptor.capture());
- assertThat(capturedCurrentCalls.size()).isEqualTo(0);
+ assertThat(capturedCurrentCalls).isEmpty();
}
@Test
@@ -312,9 +298,9 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
verify(mTbsGatt).setBearerListCurrentCalls(currentCallsCaptor.capture());
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
TbsCall capturedTbsCall = capturedCurrentCalls.get(capturedCallIndex);
assertThat(capturedTbsCall).isNotNull();
assertThat(capturedTbsCall.getState()).isEqualTo(BluetoothLeCall.STATE_ACTIVE);
@@ -355,14 +341,13 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(2);
+ assertThat(capturedCurrentCalls).hasSize(2);
verify(mTbsGatt).setBearerListCurrentCalls(currentCallsCaptor.capture());
- assertThat(capturedCurrentCalls.size()).isEqualTo(2);
+ assertThat(capturedCurrentCalls).hasSize(2);
}
@Test
public void testCallAccept() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -384,7 +369,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey();
reset(mTbsGatt);
@@ -392,8 +377,7 @@ public class TbsGenericTest {
args[0] = (byte) (callIndex & 0xFF);
mTbsGattCallback
.getValue()
- .onCallControlPointRequest(
- mCurrentDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT, args);
+ .onCallControlPointRequest(mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT, args);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<ParcelUuid> callUuidCaptor = ArgumentCaptor.forClass(ParcelUuid.class);
@@ -405,7 +389,7 @@ public class TbsGenericTest {
}
assertThat(callUuidCaptor.getValue().getUuid()).isEqualTo(callUuid);
// Active device should be changed
- verify(leAudioService).setActiveDevice(mCurrentDevice);
+ verify(leAudioService).setActiveDevice(mDevice);
// Respond with requestComplete...
mTbsGeneric.requestResult(
@@ -415,7 +399,7 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT),
eq(callIndex),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
@@ -423,7 +407,6 @@ public class TbsGenericTest {
@Test
public void testCallTerminate() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -442,7 +425,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey();
reset(mTbsGatt);
@@ -451,7 +434,7 @@ public class TbsGenericTest {
mTbsGattCallback
.getValue()
.onCallControlPointRequest(
- mCurrentDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE, args);
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE, args);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<ParcelUuid> callUuidCaptor = ArgumentCaptor.forClass(ParcelUuid.class);
@@ -471,7 +454,7 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE),
eq(callIndex),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
@@ -479,7 +462,6 @@ public class TbsGenericTest {
@Test
public void testCallHold() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -498,7 +480,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey();
reset(mTbsGatt);
@@ -507,7 +489,7 @@ public class TbsGenericTest {
mTbsGattCallback
.getValue()
.onCallControlPointRequest(
- mCurrentDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD, args);
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD, args);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<ParcelUuid> callUuidCaptor = ArgumentCaptor.forClass(ParcelUuid.class);
@@ -527,7 +509,7 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD),
eq(callIndex),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
@@ -535,7 +517,6 @@ public class TbsGenericTest {
@Test
public void testCallRetrieve() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -554,7 +535,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ assertThat(capturedCurrentCalls).hasSize(1);
Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey();
reset(mTbsGatt);
@@ -563,7 +544,7 @@ public class TbsGenericTest {
mTbsGattCallback
.getValue()
.onCallControlPointRequest(
- mCurrentDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE, args);
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE, args);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<ParcelUuid> callUuidCaptor = ArgumentCaptor.forClass(ParcelUuid.class);
@@ -583,7 +564,7 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE),
eq(callIndex),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
@@ -591,7 +572,6 @@ public class TbsGenericTest {
@Test
public void testCallOriginate() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -603,9 +583,7 @@ public class TbsGenericTest {
mTbsGattCallback
.getValue()
.onCallControlPointRequest(
- mCurrentDevice,
- TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE,
- uri.getBytes());
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, uri.getBytes());
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<ParcelUuid> callUuidCaptor = ArgumentCaptor.forClass(ParcelUuid.class);
@@ -617,7 +595,7 @@ public class TbsGenericTest {
}
// Active device should be changed
- verify(leAudioService).setActiveDevice(mCurrentDevice);
+ verify(leAudioService).setActiveDevice(mDevice);
// Respond with requestComplete...
mTbsGeneric.requestResult(
@@ -634,7 +612,7 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE),
anyInt(),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
@@ -642,7 +620,6 @@ public class TbsGenericTest {
@Test
public void testCallJoin() {
- mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
Integer ccid = prepareTestBearer();
reset(mTbsGatt);
@@ -668,7 +645,7 @@ public class TbsGenericTest {
ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
- assertThat(capturedCurrentCalls.size()).isEqualTo(2);
+ assertThat(capturedCurrentCalls).hasSize(2);
reset(mTbsGatt);
byte args[] = new byte[capturedCurrentCalls.size()];
@@ -678,8 +655,7 @@ public class TbsGenericTest {
}
mTbsGattCallback
.getValue()
- .onCallControlPointRequest(
- mCurrentDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN, args);
+ .onCallControlPointRequest(mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN, args);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<List<ParcelUuid>> callUuidCaptor = ArgumentCaptor.forClass(List.class);
@@ -690,7 +666,7 @@ public class TbsGenericTest {
throw e.rethrowFromSystemServer();
}
List<ParcelUuid> callParcelUuids = callUuidCaptor.getValue();
- assertThat(callParcelUuids.size()).isEqualTo(2);
+ assertThat(callParcelUuids).hasSize(2);
for (ParcelUuid callParcelUuid : callParcelUuids) {
assertThat(callUuids.contains(callParcelUuid.getUuid())).isEqualTo(true);
}
@@ -703,9 +679,85 @@ public class TbsGenericTest {
// ..and verify if GTBS control point is updated to notifier the peer about the result
verify(mTbsGatt)
.setCallControlPointResult(
- eq(mCurrentDevice),
+ eq(mDevice),
eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN),
anyInt(),
eq(BluetoothLeCallControl.RESULT_SUCCESS));
}
+
+ @Test
+ public void testCallOperationsBlockedForBroadcastReceiver() {
+ Integer ccid = prepareTestBearer();
+ reset(mTbsGatt);
+
+ LeAudioService leAudioService = mock(LeAudioService.class);
+ mTbsGeneric.setLeAudioServiceForTesting(leAudioService);
+
+ // Prepare the incoming call
+ UUID callUuid = UUID.randomUUID();
+ List<BluetoothLeCall> tbsCalls = new ArrayList<>();
+ tbsCalls.add(
+ new BluetoothLeCall(
+ callUuid,
+ "tel:987654321",
+ "aFriendlyCaller",
+ BluetoothLeCall.STATE_INCOMING,
+ 0));
+ mTbsGeneric.currentCallsList(ccid, tbsCalls);
+
+ ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class);
+ verify(mTbsGatt).setCallState(currentCallsCaptor.capture());
+ Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue();
+ assertThat(capturedCurrentCalls.size()).isEqualTo(1);
+ Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey();
+ reset(mTbsGatt);
+
+ doReturn(new HashSet<>(Arrays.asList(mDevice)))
+ .when(leAudioService)
+ .getLocalBroadcastReceivers();
+
+ doReturn(false).when(leAudioService).isPrimaryDevice(mDevice);
+
+ // Verify call accept
+ byte args[] = new byte[1];
+ args[0] = (byte) (callIndex & 0xFF);
+ mTbsGattCallback
+ .getValue()
+ .onCallControlPointRequest(
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT, args);
+
+ // Active device should not be changed
+ verify(leAudioService, never()).setActiveDevice(mDevice);
+ // Verify if GTBS control point is updated to notify the peer about the result
+ verify(mTbsGatt)
+ .setCallControlPointResult(
+ eq(mDevice),
+ eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT),
+ eq(0),
+ eq(TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE));
+
+ // Verify call terminate
+ tbsCalls.clear();
+ tbsCalls.add(
+ new BluetoothLeCall(
+ callUuid,
+ "tel:987654321",
+ "aFriendlyCaller",
+ BluetoothLeCall.STATE_ACTIVE,
+ 0));
+ mTbsGeneric.currentCallsList(ccid, tbsCalls);
+
+ mTbsGattCallback
+ .getValue()
+ .onCallControlPointRequest(
+ mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE, args);
+
+ // Verify if GTBS control point is updated to notify the peer about the result
+ verify(mTbsGatt)
+ .setCallControlPointResult(
+ eq(mDevice),
+ eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE),
+ eq(0),
+ eq(TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE));
+ }
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
index c0f275b149..1a908912f4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
@@ -51,7 +51,6 @@ import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.hfp.BluetoothHeadsetProxy;
import com.android.bluetooth.tbs.BluetoothLeCallControlProxy;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -172,7 +171,7 @@ public class BluetoothInCallServiceTest {
doReturn(fakePhoneAccount).when(mMockCallInfo).getBestPhoneAccount();
String networkOperator = mBluetoothInCallService.getNetworkOperator();
- Assert.assertEquals(networkOperator, "label0");
+ assertThat(networkOperator).isEqualTo("label0");
}
@Test
@@ -181,7 +180,7 @@ public class BluetoothInCallServiceTest {
doReturn(fakeOperator).when(mMockTelephonyManager).getNetworkOperatorName();
String networkOperator = mBluetoothInCallService.getNetworkOperator();
- Assert.assertEquals(networkOperator, fakeOperator);
+ assertThat(networkOperator).isEqualTo(fakeOperator);
}
@Test
@@ -190,7 +189,7 @@ public class BluetoothInCallServiceTest {
doReturn(fakePhoneAccount).when(mMockCallInfo).getBestPhoneAccount();
String subscriberNumber = mBluetoothInCallService.getSubscriberNumber();
- Assert.assertEquals(subscriberNumber, TEST_ACCOUNT_ADDRESS + TEST_ACCOUNT_INDEX);
+ assertThat(subscriberNumber).isEqualTo(TEST_ACCOUNT_ADDRESS + TEST_ACCOUNT_INDEX);
}
@Test
@@ -199,7 +198,7 @@ public class BluetoothInCallServiceTest {
doReturn(fakeNumber).when(mMockTelephonyManager).getLine1Number();
String subscriberNumber = mBluetoothInCallService.getSubscriberNumber();
- Assert.assertEquals(subscriberNumber, fakeNumber);
+ assertThat(subscriberNumber).isEqualTo(fakeNumber);
}
@Test
@@ -253,12 +252,12 @@ public class BluetoothInCallServiceTest {
BluetoothCallQualityReport report =
(BluetoothCallQualityReport)
bundle.get(BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT);
- Assert.assertEquals(10, report.getSentTimestampMillis());
- Assert.assertEquals(20, report.getRssiDbm());
- Assert.assertEquals(30, report.getSnrDb());
- Assert.assertEquals(40, report.getRetransmittedPacketsCount());
- Assert.assertEquals(50, report.getPacketsNotReceivedCount());
- Assert.assertEquals(60, report.getNegativeAcknowledgementCount());
+ assertThat(report.getSentTimestampMillis()).isEqualTo(10);
+ assertThat(report.getRssiDbm()).isEqualTo(20);
+ assertThat(report.getSnrDb()).isEqualTo(30);
+ assertThat(report.getRetransmittedPacketsCount()).isEqualTo(40);
+ assertThat(report.getPacketsNotReceivedCount()).isEqualTo(50);
+ assertThat(report.getNegativeAcknowledgementCount()).isEqualTo(60);
}
@Test
@@ -907,7 +906,7 @@ public class BluetoothInCallServiceTest {
// parent call arrived, but children have not, then do inference on children
calls.add(conferenceCall);
- Assert.assertEquals(calls.size(), 1);
+ assertThat(calls).hasSize(1);
mBluetoothInCallService.onCallAdded(conferenceCall);
clearInvocations(mMockBluetoothHeadset);
@@ -946,7 +945,7 @@ public class BluetoothInCallServiceTest {
mBluetoothInCallService.onCallRemoved(holdingCall, true);
calls.remove(activeCall);
calls.remove(holdingCall);
- Assert.assertEquals(calls.size(), 1);
+ assertThat(calls).hasSize(1);
clearInvocations(mMockBluetoothHeadset);
mBluetoothInCallService.listCurrentCalls();
@@ -1042,7 +1041,7 @@ public class BluetoothInCallServiceTest {
// parent call arrived, but children have not, then do inference on children
calls.add(conferenceCall);
- Assert.assertEquals(calls.size(), 1);
+ assertThat(calls).hasSize(1);
mBluetoothInCallService.onCallAdded(conferenceCall);
clearInvocations(mMockBluetoothHeadset);
@@ -1080,7 +1079,7 @@ public class BluetoothInCallServiceTest {
mBluetoothInCallService.onCallRemoved(activeCall_1, true);
doReturn(false).when(activeCall_1).isConference();
calls.remove(activeCall_1);
- Assert.assertEquals(calls.size(), 2);
+ assertThat(calls).hasSize(2);
// Call 2 removed from conf
doReturn(cause).when(activeCall_2).getDisconnectCause();
@@ -1690,134 +1689,116 @@ public class BluetoothInCallServiceTest {
doReturn(TelephonyManager.NETWORK_TYPE_GSM)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM);
doReturn(TelephonyManager.NETWORK_TYPE_GPRS)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_2G);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_2G);
doReturn(TelephonyManager.NETWORK_TYPE_EVDO_B)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_3G);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_3G);
doReturn(TelephonyManager.NETWORK_TYPE_TD_SCDMA)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WCDMA);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WCDMA);
doReturn(TelephonyManager.NETWORK_TYPE_LTE)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_LTE);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_LTE);
doReturn(TelephonyManager.NETWORK_TYPE_1xRTT)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_CDMA);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_CDMA);
doReturn(TelephonyManager.NETWORK_TYPE_HSPAP)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_4G);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_4G);
doReturn(TelephonyManager.NETWORK_TYPE_IWLAN)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WIFI);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WIFI);
doReturn(TelephonyManager.NETWORK_TYPE_NR).when(mMockTelephonyManager).getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_5G);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_5G);
doReturn(TelephonyManager.NETWORK_TYPE_LTE_CA)
.when(mMockTelephonyManager)
.getDataNetworkType();
- Assert.assertEquals(
- mBluetoothInCallService.getBearerTechnology(),
- BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM);
+ assertThat(mBluetoothInCallService.getBearerTechnology())
+ .isEqualTo(BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM);
}
@Test
public void getTbsTerminationReason() {
BluetoothCall call = getMockCall(UUID.randomUUID());
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_FAIL);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_FAIL);
DisconnectCause cause = new DisconnectCause(DisconnectCause.BUSY, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_LINE_BUSY);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_LINE_BUSY);
cause = new DisconnectCause(DisconnectCause.REJECTED, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_REMOTE_HANGUP);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_REMOTE_HANGUP);
cause = new DisconnectCause(DisconnectCause.LOCAL, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
mBluetoothInCallService.mIsTerminatedByClient = false;
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_SERVER_HANGUP);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_SERVER_HANGUP);
cause = new DisconnectCause(DisconnectCause.LOCAL, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
mBluetoothInCallService.mIsTerminatedByClient = true;
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_CLIENT_HANGUP);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_CLIENT_HANGUP);
cause = new DisconnectCause(DisconnectCause.ERROR, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_NETWORK_CONGESTION);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_NETWORK_CONGESTION);
cause =
new DisconnectCause(
DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_INVALID_URI);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_INVALID_URI);
cause = new DisconnectCause(DisconnectCause.ERROR, null, null, null, 1);
doReturn(cause).when(call).getDisconnectCause();
- Assert.assertEquals(
- mBluetoothInCallService.getTbsTerminationReason(call),
- BluetoothLeCallControl.TERMINATION_REASON_NETWORK_CONGESTION);
+ assertThat(mBluetoothInCallService.getTbsTerminationReason(call))
+ .isEqualTo(BluetoothLeCallControl.TERMINATION_REASON_NETWORK_CONGESTION);
}
@Test
public void onDestroy() {
- assertThat(mBluetoothInCallService.mOnCreateCalled).isTrue();
+ assertThat(BluetoothInCallService.getInstance()).isNotNull();
mBluetoothInCallService.onDestroy();
- assertThat(mBluetoothInCallService.mOnCreateCalled).isFalse();
+ assertThat(BluetoothInCallService.getInstance()).isNull();
}
@Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlOffsetDescriptorTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlOffsetDescriptorTest.java
index 3ef5c767f6..4f7389b8f8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlOffsetDescriptorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlOffsetDescriptorTest.java
@@ -22,25 +22,12 @@ import static org.mockito.Mockito.*;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class VolumeControlOffsetDescriptorTest {
-
- @Before
- public void setUp() throws Exception {
- // placeholder
- }
-
- @After
- public void tearDown() throws Exception {
- // placeholder
- }
-
@Test
public void testVolumeControlOffsetDescriptorInvalidIdOperations() {
VolumeControlOffsetDescriptor descriptor = new VolumeControlOffsetDescriptor();
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 3e912701f6..94e1db83b1 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
@@ -58,6 +58,7 @@ import android.media.AudioManager;
import android.os.Binder;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -76,7 +77,6 @@ import com.android.bluetooth.le_audio.LeAudioService;
import org.hamcrest.Matcher;
import org.hamcrest.core.AllOf;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -461,7 +461,8 @@ public class VolumeControlServiceTest {
}
@Test
- public void volumeCache() {
+ @DisableFlags(Flags.FLAG_VCP_DEVICE_VOLUME_API_IMPROVEMENTS)
+ public void volumeCacheDeprecated() {
int groupId = 1;
int volume = 6;
@@ -478,6 +479,56 @@ public class VolumeControlServiceTest {
}
@Test
+ @EnableFlags(Flags.FLAG_VCP_DEVICE_VOLUME_API_IMPROVEMENTS)
+ public void volumeCache() {
+ int groupId = 1;
+ int groupVolume = 6;
+ int devOneVolume = 20;
+ int devTwoVolume = 30;
+
+ // Both devices are in the same group
+ when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
+ when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
+ when(mCsipService.getGroupDevicesOrdered(groupId))
+ .thenReturn(Arrays.asList(mDevice, mDeviceTwo));
+
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(VOLUME_CONTROL_UNKNOWN_VOLUME);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(VOLUME_CONTROL_UNKNOWN_VOLUME);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(VOLUME_CONTROL_UNKNOWN_VOLUME);
+
+ // Set group volume
+ mService.setGroupVolume(groupId, groupVolume);
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(groupVolume);
+
+ // Send autonomous volume change.
+ int autonomousVolume = 10;
+ generateVolumeStateChanged(null, groupId, autonomousVolume, 0, false, true);
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(autonomousVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(autonomousVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(autonomousVolume);
+
+ // Set first device volume
+ mService.setDeviceVolume(mDevice, devOneVolume, false);
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(autonomousVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(devOneVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(autonomousVolume);
+
+ // Set second device volume
+ mService.setDeviceVolume(mDeviceTwo, devTwoVolume, false);
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(autonomousVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(devOneVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(devTwoVolume);
+
+ // Set group volume again
+ mService.setGroupVolume(groupId, groupVolume);
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(groupVolume);
+ }
+
+ @Test
public void activeGroupChange() {
int groupId_1 = 1;
int volume_groupId_1 = 6;
@@ -509,7 +560,8 @@ public class VolumeControlServiceTest {
}
@Test
- public void muteCache() {
+ @DisableFlags(Flags.FLAG_VCP_DEVICE_VOLUME_API_IMPROVEMENTS)
+ public void muteCacheDeprecated() {
int groupId = 1;
int volume = 6;
@@ -531,6 +583,61 @@ public class VolumeControlServiceTest {
assertThat(mService.getGroupMute(groupId)).isFalse();
}
+ @Test
+ @EnableFlags(Flags.FLAG_VCP_DEVICE_VOLUME_API_IMPROVEMENTS)
+ public void muteCache() {
+ int groupId = 1;
+ int groupVolume = 6;
+
+ // Both devices are in the same group
+ when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
+ when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
+ when(mCsipService.getGroupDevicesOrdered(groupId))
+ .thenReturn(Arrays.asList(mDevice, mDeviceTwo));
+
+ assertThat(mService.getGroupMute(groupId)).isFalse();
+ assertThat(mService.getMute(mDevice)).isFalse();
+ assertThat(mService.getMute(mDeviceTwo)).isFalse();
+
+ // Send autonomous volume change
+ generateVolumeStateChanged(null, groupId, groupVolume, 0, false, true);
+
+ // Mute
+ mService.muteGroup(groupId);
+ assertThat(mService.getGroupMute(groupId)).isTrue();
+ assertThat(mService.getMute(mDevice)).isTrue();
+ assertThat(mService.getMute(mDeviceTwo)).isTrue();
+
+ // Make sure the volume is kept even when muted
+ assertThat(mService.getGroupVolume(groupId)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(groupVolume);
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(groupVolume);
+
+ // Send autonomous unmute
+ generateVolumeStateChanged(null, groupId, groupVolume, 0, false, true);
+ assertThat(mService.getGroupMute(groupId)).isFalse();
+ assertThat(mService.getMute(mDevice)).isFalse();
+ assertThat(mService.getMute(mDeviceTwo)).isFalse();
+
+ // Mute first device
+ mService.mute(mDevice);
+ assertThat(mService.getGroupMute(groupId)).isFalse();
+ assertThat(mService.getMute(mDevice)).isTrue();
+ assertThat(mService.getMute(mDeviceTwo)).isFalse();
+
+ // Mute second device
+ mService.mute(mDeviceTwo);
+ assertThat(mService.getGroupMute(groupId)).isFalse();
+ assertThat(mService.getMute(mDevice)).isTrue();
+ assertThat(mService.getMute(mDeviceTwo)).isTrue();
+
+ // Unmute group should unmute devices even if group is unmuted
+ mService.unmuteGroup(groupId);
+ assertThat(mService.getGroupMute(groupId)).isFalse();
+ assertThat(mService.getMute(mDevice)).isFalse();
+ assertThat(mService.getMute(mDeviceTwo)).isFalse();
+ }
+
/** Test Volume Control with muted stream. */
@Test
public void volumeChangeWhileMuted() {
@@ -612,6 +719,14 @@ public class VolumeControlServiceTest {
InOrder inOrderAudio = inOrder(mAudioManager);
inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
+ InOrder inOrderNative = inOrder(mNativeInterface);
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ // AF always call setVolume via LeAudioService at first connected remote from group
+ mService.setGroupVolume(groupId, 123);
+ // It should be ignored and not set to native
+ inOrderNative.verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt());
+ }
+
// Make device Active now. This will trigger setting volume to AF
when(mLeAudioService.getActiveGroupId()).thenReturn(groupId);
mService.setGroupActive(groupId, true);
@@ -634,7 +749,11 @@ public class VolumeControlServiceTest {
initialAutonomousFlag);
inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
- verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volumeDevice));
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volumeDevice));
+ } else {
+ inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volumeDevice));
+ }
}
private void testConnectedDeviceWithResetFlag(
@@ -676,6 +795,10 @@ public class VolumeControlServiceTest {
InOrder inOrderAudio = inOrder(mAudioManager);
inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
InOrder inOrderNative = inOrder(mNativeInterface);
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ // AF always call setVolume via LeAudioService at first connected remote from group
+ mService.setGroupVolume(groupId, expectedAfVol);
+ }
inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol));
// Make device Active now. This will trigger setting volume to AF
@@ -698,7 +821,11 @@ public class VolumeControlServiceTest {
initialAutonomousFlag);
inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
- inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol));
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(expectedAfVol));
+ } else {
+ inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol));
+ }
}
/** Test if phone will set volume which is read from the buds */
@@ -742,8 +869,12 @@ public class VolumeControlServiceTest {
assertThat(mService.getDevices()).contains(mDeviceTwo);
generateVolumeStateChanged(mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, volume_2, 0, false, true);
- inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
- inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume));
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
+ } else {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
+ inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume));
+ }
}
/**
@@ -817,9 +948,14 @@ public class VolumeControlServiceTest {
generateVolumeStateChanged(mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, volume_2, 0, false, true);
// Check if new device was muted
- inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume));
- inOrderNative.verify(mNativeInterface).mute(eq(mDeviceTwo));
- inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume));
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume));
+ inOrderNative.verify(mNativeInterface).mute(eq(mDeviceTwo));
+ } else {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume));
+ inOrderNative.verify(mNativeInterface).mute(eq(mDeviceTwo));
+ inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume));
+ }
}
/**
@@ -988,13 +1124,13 @@ public class VolumeControlServiceTest {
mBinder.setDeviceVolume(mDevice, deviceOneVolume, false, mAttributionSource);
inOrderNative.verify(mNativeInterface).setVolume(mDevice, deviceOneVolume);
assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(deviceOneVolume);
- Assert.assertNotEquals(deviceOneVolume, mService.getDeviceVolume(mDeviceTwo));
+ assertThat(mService.getDeviceVolume(mDeviceTwo)).isNotEqualTo(deviceOneVolume);
inOrderNative.verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt());
mBinder.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false, mAttributionSource);
inOrderNative.verify(mNativeInterface).setVolume(mDeviceTwo, deviceTwoVolume);
assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(deviceTwoVolume);
- Assert.assertNotEquals(deviceTwoVolume, mService.getDeviceVolume(mDevice));
+ assertThat(mService.getDeviceVolume(mDevice)).isNotEqualTo(deviceTwoVolume);
inOrderNative.verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt());
}
@@ -1064,8 +1200,12 @@ public class VolumeControlServiceTest {
generateVolumeStateChanged(
mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, groupVolume, 0, false, true);
- inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
- inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume));
+ if (Flags.vcpDeviceVolumeApiImprovements()) {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
+ } else {
+ inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume));
+ inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume));
+ }
// Generate events for both devices
generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 100);
diff --git a/android/leaudio/app/src/androidTest/java/com/android/bluetooth/leaudio/ExampleInstrumentedTest.java b/android/leaudio/app/src/androidTest/java/com/android/bluetooth/leaudio/ExampleInstrumentedTest.java
deleted file mode 100644
index 48e81bb636..0000000000
--- a/android/leaudio/app/src/androidTest/java/com/android/bluetooth/leaudio/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.android.bluetooth.leaudio;
-
-import static org.junit.Assert.assertEquals;
-
-import android.content.Context;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("com.android.bluetooth.leaudio", appContext.getPackageName());
- }
-}
diff --git a/android/leaudio/app/src/test/java/pl/codecoup/ehima/leaudio/ExampleUnitTest.java b/android/leaudio/app/src/test/java/pl/codecoup/ehima/leaudio/ExampleUnitTest.java
deleted file mode 100644
index 53e6ef47b3..0000000000
--- a/android/leaudio/app/src/test/java/pl/codecoup/ehima/leaudio/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.android.bluetooth.leaudio;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
diff --git a/android/pandora/gen_cov.py b/android/pandora/gen_cov.py
index b3d85b32d6..ba62530633 100755
--- a/android/pandora/gen_cov.py
+++ b/android/pandora/gen_cov.py
@@ -310,8 +310,8 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--apex-name',
- default='com.android.btservices',
- help='bluetooth apex name. Default: com.android.btservices')
+ default='com.android.bt',
+ help='bluetooth apex name. Default: com.android.bt')
parser.add_argument(
'--java', action='store_true', help='generate Java coverage')
parser.add_argument(
diff --git a/android/pandora/server/configs/PtsBotTestMts.xml b/android/pandora/server/configs/PtsBotTestMts.xml
index 168eee7dea..9785a39c18 100644
--- a/android/pandora/server/configs/PtsBotTestMts.xml
+++ b/android/pandora/server/configs/PtsBotTestMts.xml
@@ -31,13 +31,16 @@
<!-- TODO(b/267785204): Import python dependencies -->
<option name="dep-module" value="grpcio" />
<option name="dep-module" value="protobuf==3.20.1" />
- <option name="dep-module" value="scipy" />
+
+ <!-- Re-enable when A2DP audio streaming tests are active, disabling to speed up atest runtime
+ (installation takes roughly 30s each time, never cached) -->
+ <!-- <option name="dep-module" value="scipy" /> -->
</target_preparer>
<test class="com.android.tradefed.testtype.pandora.PtsBotTest" >
<!-- Creates a randomized temp dir for pts-bot binaries and avoid
conflicts when running multiple pts-bot on the same machine -->
- <option name="create-bin-temp-dir" value="true"/>
+ <!-- <option name="create-bin-temp-dir" value="true"/> -->
<!-- mmi2grpc is contained inside pts-bot-mts folder -->
<option name="mmi2grpc" value="pts-bot-mts" />
<option name="tests-config-file" value="pts_bot_tests_config.json" />
@@ -53,6 +56,7 @@
<option name="profile" value="BNEP" />
<option name="profile" value="GAP" />
<option name="profile" value="GATT" />
+ <option name="profile" value="HAP" />
<option name="profile" value="HFP/AG" />
<option name="profile" value="HFP/HF" />
<option name="profile" value="HID/HOS" />
@@ -63,17 +67,17 @@
<option name="profile" value="L2CAP/LE" />
<option name="profile" value="MAP" />
<option name="profile" value="OPP" />
- <!-- TODO(b/272303629): Reenable -->
- <!--option name="profile" value="PAN" /-->
+ <option name="profile" value="PAN" />
<option name="profile" value="PBAP/PSE" />
<option name="profile" value="RFCOMM" />
<option name="profile" value="SDP" />
<option name="profile" value="SM" />
+ <option name="profile" value="VCP" />
</test>
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json
index 758d21f07c..7b44a66f18 100644
--- a/android/pandora/server/configs/pts_bot_tests_config.json
+++ b/android/pandora/server/configs/pts_bot_tests_config.json
@@ -3210,8 +3210,7 @@
"SM": {},
"SPP": {},
"SUM ICS": {},
- "VCP": {
- }
+ "VCP": {}
},
"flags": [
{
@@ -3252,6 +3251,69 @@
"VCP/VC/SPE/BI-19-C",
"VCP/VC/SPE/BI-20-C"
]
+ },
+ {
+ "flags": [
+ "avdt_accept_open_timeout_ms"
+ ],
+ "tests": [
+ "A2DP/SNK/SYN/BV-01-C"
+ ]
+ }
+ ],
+ "system_properties": [
+ {
+ "system_properties": {
+ "bluetooth.profile.a2dp.sink.enabled": "true",
+ "bluetooth.profile.a2dp.source.enabled": "false"
+ },
+ "tests": [
+ "A2DP/SNK",
+ "AVCTP/CT",
+ "AVDTP/SNK",
+ "AVRCP/CT/CEC",
+ "AVRCP/CT/CRC",
+ "AVRCP/CT/PTH",
+ "AVRCP/CT/PTT"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.a2dp.sink.enabled": "false",
+ "bluetooth.profile.a2dp.source.enabled": "true"
+ },
+ "tests": [
+ "A2DP/SRC",
+ "AVCTP/TG",
+ "AVDTP/SRC",
+ "AVRCP/TG"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.hfp.hf.enabled": "true",
+ "bluetooth.profile.hfp.ag.enabled": "false"
+ },
+ "tests": [
+ "HFP/HF"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.hfp.hf.enabled": "false",
+ "bluetooth.profile.hfp.ag.enabled": "true"
+ },
+ "tests": [
+ "HFP/AG"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.a2dp.avdt_accept_open_timeout_ms": "15000"
+ },
+ "tests": [
+ "A2DP/SNK/SYN/BV-01-C"
+ ]
}
]
}
diff --git a/android/pandora/server/configs/pts_bot_tests_config_auto.json b/android/pandora/server/configs/pts_bot_tests_config_auto.json
index abc188046c..425b79c8e8 100644
--- a/android/pandora/server/configs/pts_bot_tests_config_auto.json
+++ b/android/pandora/server/configs/pts_bot_tests_config_auto.json
@@ -375,5 +375,67 @@
"SPP": {},
"VCP": {}
},
- "flags": {}
+ "flags": {
+ "flags": [
+ "avdt_accept_open_timeout_ms"
+ ],
+ "tests": [
+ "A2DP/SNK/SYN/BV-01-C"
+ ]
+ },
+ "system_properties": [
+ {
+ "system_properties": {
+ "bluetooth.profile.a2dp.sink.enabled": "true",
+ "bluetooth.profile.a2dp.source.enabled": "false"
+ },
+ "tests": [
+ "A2DP/SNK",
+ "AVCTP/CT",
+ "AVDTP/SNK",
+ "AVRCP/CT/CEC",
+ "AVRCP/CT/CRC",
+ "AVRCP/CT/PTH",
+ "AVRCP/CT/PTT"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.a2dp.sink.enabled": "false",
+ "bluetooth.profile.a2dp.source.enabled": "true"
+ },
+ "tests": [
+ "A2DP/SRC",
+ "AVCTP/TG",
+ "AVDTP/SRC",
+ "AVRCP/TG"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.hfp.hf.enabled": "true",
+ "bluetooth.profile.hfp.ag.enabled": "false"
+ },
+ "tests": [
+ "HFP/HF"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.profile.hfp.hf.enabled": "false",
+ "bluetooth.profile.hfp.ag.enabled": "true"
+ },
+ "tests": [
+ "HFP/AG"
+ ]
+ },
+ {
+ "system_properties": {
+ "bluetooth.a2dp.avdt_accept_open_timeout_ms": "15000"
+ },
+ "tests": [
+ "A2DP/SNK/SYN/BV-01-C"
+ ]
+ }
+ ]
}
diff --git a/android/pandora/server/src/A2dp.kt b/android/pandora/server/src/A2dp.kt
index 6708dd1c46..0b5a0d6640 100644
--- a/android/pandora/server/src/A2dp.kt
+++ b/android/pandora/server/src/A2dp.kt
@@ -41,7 +41,6 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
@@ -110,10 +109,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable {
}
}
- // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too
- // early.
- delay(2000L)
-
val source =
Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8"))
OpenSourceResponse.newBuilder().setSource(source).build()
@@ -147,10 +142,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable {
}
}
- // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too
- // early.
- delay(2000L)
-
val source =
Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8"))
WaitSourceResponse.newBuilder().setSource(source).build()
diff --git a/android/pandora/test/a2dp/signaling_channel.py b/android/pandora/test/a2dp/signaling_channel.py
new file mode 100644
index 0000000000..0fbda414c8
--- /dev/null
+++ b/android/pandora/test/a2dp/signaling_channel.py
@@ -0,0 +1,200 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from __future__ import annotations
+
+from bumble.device import Connection
+try:
+ from packets import avdtp as avdt_packet_module
+ from packets.avdtp import *
+except ImportError:
+ from .packets import avdtp as avdt_packet_module
+ from .packets.avdtp import *
+from pyee import EventEmitter
+from typing import Union
+
+import asyncio
+import bumble.avdtp as avdtp
+import bumble.l2cap as l2cap
+import logging
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+avdt_packet_module.print = lambda *args, **kwargs: logger.debug(" ".join(map(str, args)))
+
+
+class Any:
+ """Helper class that will match all other values.
+ Use an element of this class in expected packets to match any value
+ returned by the AVDTP signaling."""
+
+ def __eq__(self, other) -> bool:
+ return True
+
+ def __format__(self, format_spec: str) -> str:
+ return "_"
+
+ def __len__(self) -> int:
+ return 1
+
+ def show(self, prefix: str = "") -> str:
+ return prefix + "_"
+
+
+class SignalingChannel(EventEmitter):
+ connection: Connection
+ signaling_channel: Optional[l2cap.ClassicChannel] = None
+ transport_channel: Optional[l2cap.ClassicChannel] = None
+ avdtp_server: Optional[l2cap.ClassicChannelServer] = None
+ role: Optional[str] = None
+
+ def __init__(self, connection: Connection):
+ super().__init__()
+ self.connection = connection
+ self.signaling_queue = asyncio.Queue()
+ self.transport_queue = asyncio.Queue()
+
+ @classmethod
+ async def initiate(cls, connection: Connection) -> SignalingChannel:
+ channel = cls(connection)
+ await channel._initiate_signaling_channel()
+ return channel
+
+ @classmethod
+ def accept(cls, connection: Connection) -> SignalingChannel:
+ channel = cls(connection)
+ channel._accept_signaling_channel()
+ return channel
+
+ async def disconnect(self):
+ if not self.signaling_channel:
+ raise ValueError("No connected signaling channel")
+ await self.signaling_channel.disconnect()
+ self.signaling_channel = None
+
+ async def initiate_transport_channel(self):
+ if self.transport_channel:
+ raise ValueError("RTP L2CAP channel already exists")
+ self.transport_channel = await self.connection.create_l2cap_channel(
+ l2cap.ClassicChannelSpec(psm=avdtp.AVDTP_PSM))
+
+ async def disconnect_transport_channel(self):
+ if not self.transport_channel:
+ raise ValueError("No connected RTP channel")
+ await self.transport_channel.disconnect()
+ self.transport_channel = None
+
+ async def expect_signal(self, expected_sig: Union[SignalingPacket, type], timeout: float = 3) -> SignalingPacket:
+ packet = await asyncio.wait_for(self.signaling_queue.get(), timeout=timeout)
+ sig = SignalingPacket.parse_all(packet)
+
+ if isinstance(expected_sig, type) and not isinstance(sig, expected_sig):
+ logger.error("Received unexpected signal")
+ logger.error(f"Expected signal: {expected_sig.__class__.__name__}")
+ logger.error("Received signal:")
+ sig.show()
+ raise ValueError(f"Received unexpected signal")
+
+ if isinstance(expected_sig, SignalingPacket) and sig != expected_sig:
+ logger.error("Received unexpected signal")
+ logger.error("Expected signal:")
+ expected_sig.show()
+ logger.error("Received signal:")
+ sig.show()
+ raise ValueError(f"Received unexpected signal")
+
+ logger.debug(f"<<< {self.connection.self_address} {self.role} received signal: <<<")
+ sig.show()
+ return sig
+
+ async def expect_media(self, timeout: float = 3.0) -> bytes:
+ packet = await asyncio.wait_for(self.transport_queue.get(), timeout=timeout)
+ logger.debug(f"<<< {self.connection.self_address} {self.role} received media <<<")
+ logger.debug(f"RTP Packet: {packet.hex()}")
+ return packet
+
+ def send_signal(self, packet: SignalingPacket):
+ logger.debug(f">>> {self.connection.self_address} {self.role} sending signal: >>>")
+ packet.show()
+ self.signaling_channel.send_pdu(packet.serialize())
+
+ def send_media(self, packet: bytes):
+ logger.debug(f">>> {self.connection.self_address} {self.role} sending media >>>")
+ self.transport_channel.send_pdu(packet)
+
+ async def _initiate_signaling_channel(self):
+ if self.signaling_channel:
+ raise ValueError("Signaling L2CAP channel already exists")
+ self.role = "initiator"
+ self.signaling_channel = await self.connection.create_l2cap_channel(spec=l2cap.ClassicChannelSpec(
+ psm=avdtp.AVDTP_PSM))
+ # Register to receive PDUs from the channel
+ self.signaling_channel.sink = self._on_pdu
+
+ def _accept_signaling_channel(self):
+ if self.avdtp_server:
+ raise ValueError("L2CAP server already exists")
+ self.role = "acceptor"
+ avdtp_server = self.connection.device.l2cap_channel_manager.servers.get(avdtp.AVDTP_PSM)
+ if not avdtp_server:
+ self.avdtp_server = self.connection.device.create_l2cap_server(spec=l2cap.ClassicChannelSpec(
+ psm=avdtp.AVDTP_PSM))
+ else:
+ self.avdtp_server = avdtp_server
+ self.avdtp_server.on('connection', self._on_l2cap_connection)
+
+ def _on_l2cap_connection(self, channel: l2cap.ClassicChannel):
+ logger.info(f"Incoming L2CAP channel: {channel}")
+
+ if not self.signaling_channel:
+
+ def _on_channel_open():
+ logger.info(f"Signaling opened on channel {self.signaling_channel}")
+ # Register to receive PDUs from the channel
+ self.signaling_channel.sink = self._on_pdu
+ self.emit('connection')
+
+ def _on_channel_close():
+ logger.info("Signaling channel closed")
+ self.signaling_channel = None
+
+ self.signaling_channel = channel
+ self.signaling_channel.on('open', _on_channel_open)
+ self.signaling_channel.on('close', _on_channel_close)
+ elif not self.transport_channel:
+
+ def _on_channel_open():
+ logger.info(f"RTP opened on channel {self.transport_channel}")
+ # Register to receive PDUs from the channel
+ self.transport_channel.sink = self._on_avdtp_packet
+
+ def _on_channel_close():
+ logger.info('RTP channel closed')
+ self.transport_channel = None
+
+ self.transport_channel = channel
+ self.transport_channel.on('open', _on_channel_open)
+ self.transport_channel.on('close', _on_channel_close)
+
+ def _on_pdu(self, pdu: bytes):
+ self.signaling_queue.put_nowait(pdu)
+
+ def _on_avdtp_packet(self, packet):
+ self.transport_queue.put_nowait(packet)
diff --git a/android/pandora/test/a2dp/signaling_channel_test.py b/android/pandora/test/a2dp/signaling_channel_test.py
new file mode 100644
index 0000000000..daf5c8f40b
--- /dev/null
+++ b/android/pandora/test/a2dp/signaling_channel_test.py
@@ -0,0 +1,378 @@
+# Copyright 2024 Google LLC
+#
+# 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
+#
+# https://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.
+"""
+Bumble tests for SignalingChannel implementation.
+
+Create venv and upgrade pip:
+
+ python -m venv .venv
+ source .venv/bin/activate
+ python -m pip install --upgrade pip
+
+Install the required dependencies using pip:
+
+ pip install pyee pytest bumble
+
+Run the tests:
+ python /path/signaling_channel_test.py
+"""
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import logging
+import os
+import pytest
+import bumble.avdtp as avdtp
+from bumble.a2dp import (A2DP_SBC_CODEC_TYPE, SbcMediaCodecInformation)
+from bumble.controller import Controller
+from bumble.core import BT_BR_EDR_TRANSPORT
+from bumble.device import Device
+from bumble.host import Host
+from bumble.link import LocalLink
+from bumble.transport import AsyncPipeSink
+from packets.avdtp import *
+from signaling_channel import SignalingChannel, Any
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+class TwoDevices:
+
+ def __init__(self):
+ self.connections = [None, None]
+
+ addresses = ['F0:F1:F2:F3:F4:F5', 'F5:F4:F3:F2:F1:F0']
+ self.link = LocalLink()
+ self.controllers = [
+ Controller('C1', link=self.link, public_address=addresses[0]),
+ Controller('C2', link=self.link, public_address=addresses[1]),
+ ]
+ self.devices = [
+ Device(
+ address=addresses[0],
+ host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])),
+ ),
+ Device(
+ address=addresses[1],
+ host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])),
+ ),
+ ]
+
+ self.paired = [None, None]
+
+ def on_connection(self, which, connection):
+ self.connections[which] = connection
+
+ def on_paired(self, which, keys):
+ self.paired[which] = keys
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_self_connection():
+ # Create two devices, each with a controller, attached to the same link
+ two_devices = TwoDevices()
+
+ # Attach listeners
+ two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection))
+ two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection))
+
+ # Enable Classic connections
+ two_devices.devices[0].classic_enabled = True
+ two_devices.devices[1].classic_enabled = True
+
+ # Start
+ await two_devices.devices[0].power_on()
+ await two_devices.devices[1].power_on()
+
+ # Connect the two devices
+ await asyncio.gather(
+ two_devices.devices[0].connect(two_devices.devices[1].public_address, transport=BT_BR_EDR_TRANSPORT),
+ two_devices.devices[1].accept(two_devices.devices[0].public_address),
+ )
+
+ # Check the post conditions
+ assert two_devices.connections[0] is not None
+ assert two_devices.connections[1] is not None
+
+
+# -----------------------------------------------------------------------------
+def sink_codec_capabilities():
+ return avdtp.MediaCodecCapabilities(
+ media_type=avdtp.AVDTP_AUDIO_MEDIA_TYPE,
+ media_codec_type=A2DP_SBC_CODEC_TYPE,
+ media_codec_information=SbcMediaCodecInformation.from_bytes(bytes([255, 255, 2, 53])),
+ )
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_signaling_channel_as_source():
+ any = Any()
+
+ two_devices = TwoDevices()
+ # Enable Classic connections
+ two_devices.devices[0].classic_enabled = True
+ two_devices.devices[1].classic_enabled = True
+ await two_devices.devices[0].power_on()
+ await two_devices.devices[1].power_on()
+
+ def on_rtp_packet(packet):
+ rtp_packets.append(packet)
+ if len(rtp_packets) == rtp_packets_expected:
+ rtp_packets_fully_received.set_result(None)
+
+ device_1_avdt_sink = None
+ avdtp_future = asyncio.get_running_loop().create_future()
+
+ def on_avdtp_connection(server):
+ logger.info("AVDTP Opened")
+ nonlocal device_1_avdt_sink
+ device_1_avdt_sink = server.add_sink(sink_codec_capabilities())
+ device_1_avdt_sink.on('rtp_packet', on_rtp_packet)
+ nonlocal avdtp_future
+ avdtp_future.set_result(None)
+
+ # Create a listener to wait for AVDTP connections
+ listener = avdtp.Listener.for_device(two_devices.devices[1])
+ listener.on('connection', on_avdtp_connection)
+
+ async def make_connection():
+ connections = await asyncio.gather(
+ two_devices.devices[0].connect(two_devices.devices[1].public_address, BT_BR_EDR_TRANSPORT),
+ two_devices.devices[1].accept(two_devices.devices[0].public_address),
+ )
+ return connections[0]
+
+ connection = await make_connection()
+
+ channel_int = await SignalingChannel.initiate(connection)
+
+ channel_int.send_signal(DiscoverCommand())
+
+ result = await channel_int.expect_signal(
+ DiscoverResponse(transaction_label=any, seid_information=[SeidInformation(acp_seid=1, tsep=Tsep.SINK)]))
+
+ acp_seid = result.seid_information[0].acp_seid
+
+ channel_int.send_signal(GetAllCapabilitiesCommand(acp_seid=acp_seid))
+
+ result = await channel_int.expect_signal(
+ GetAllCapabilitiesResponse(transaction_label=any,
+ service_capabilities=[
+ MediaTransportCapability(),
+ MediaCodecCapability(service_category=ServiceCategory.MEDIA_CODEC,
+ media_codec_specific_information_elements=[255, 255, 2, 53])
+ ]))
+
+ channel_int.send_signal(
+ SetConfigurationCommand(acp_seid=acp_seid, service_capabilities=[result.service_capabilities[0]]))
+
+ await channel_int.expect_signal(SetConfigurationResponse(transaction_label=any))
+
+ channel_int.send_signal(OpenCommand(acp_seid=acp_seid))
+
+ await channel_int.expect_signal(OpenResponse(transaction_label=any))
+
+ await asyncio.wait_for(avdtp_future, timeout=10.0)
+
+ assert device_1_avdt_sink.in_use == 1
+ assert device_1_avdt_sink.stream is not None
+ assert device_1_avdt_sink.stream.state == avdtp.AVDTP_OPEN_STATE
+
+ async def generate_packets(packet_count):
+ sequence_number = 0
+ timestamp = 0
+ for i in range(packet_count):
+ payload = bytes([sequence_number % 256])
+ packet = avdtp.MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, payload)
+ packet.timestamp_seconds = timestamp / 44100
+ timestamp += 10
+ sequence_number += 1
+ yield packet
+
+ # # Send packets using a pump object
+ rtp_packets_fully_received = asyncio.get_running_loop().create_future()
+ rtp_packets_expected = 3
+ rtp_packets = []
+ pump = avdtp.MediaPacketPump(generate_packets(3))
+
+ await channel_int.initiate_transport_channel()
+
+ channel_int.send_signal(StartCommand(acp_seid=acp_seid))
+
+ await channel_int.expect_signal(StartResponse(transaction_label=any))
+
+ assert device_1_avdt_sink.in_use == 1
+ assert device_1_avdt_sink.stream is not None
+ assert device_1_avdt_sink.stream.state == avdtp.AVDTP_STREAMING_STATE
+
+ await pump.start(channel_int.transport_channel)
+
+ await rtp_packets_fully_received
+
+ await pump.stop()
+
+ channel_int.send_signal(CloseCommand(acp_seid=acp_seid))
+
+ await channel_int.expect_signal(CloseResponse(transaction_label=any))
+
+ await channel_int.disconnect_transport_channel()
+
+ assert device_1_avdt_sink.in_use == 0
+ assert device_1_avdt_sink.stream.state == avdtp.AVDTP_IDLE_STATE
+
+
+# -----------------------------------------------------------------------------
+@pytest.mark.asyncio
+async def test_signaling_channel_as_sink():
+ any = Any()
+
+ two_devices = TwoDevices()
+ # Enable Classic connections
+ two_devices.devices[0].classic_enabled = True
+ two_devices.devices[1].classic_enabled = True
+ await two_devices.devices[0].power_on()
+ await two_devices.devices[1].power_on()
+
+ dev_0_dev_1_conn, dev_1_dev_0_conn = await asyncio.gather(
+ two_devices.devices[0].connect(two_devices.devices[1].public_address, BT_BR_EDR_TRANSPORT),
+ two_devices.devices[1].accept(two_devices.devices[0].public_address),
+ )
+
+ channel_acp = SignalingChannel.accept(dev_1_dev_0_conn)
+
+ avdtp_future = asyncio.get_running_loop().create_future()
+
+ def on_avdtp_connection():
+ logger.info(f" AVDTP Opened")
+ nonlocal avdtp_future
+ avdtp_future.set_result(None)
+
+ channel_acp.on('connection', on_avdtp_connection)
+
+ channel_int = await SignalingChannel.initiate(dev_0_dev_1_conn)
+
+ channel_int.send_signal(DiscoverCommand())
+
+ cmd = await channel_acp.expect_signal(DiscoverCommand(transaction_label=any))
+
+ seid_information = [SeidInformation(tsep=Tsep.SINK, media_type=avdtp.AVDTP_AUDIO_MEDIA_TYPE)]
+
+ channel_acp.send_signal(DiscoverResponse(transaction_label=cmd.transaction_label,
+ seid_information=seid_information))
+
+ result = await channel_int.expect_signal(
+ DiscoverResponse(
+ seid_information=[SeidInformation(acp_seid=0x0, tsep=Tsep.SINK, media_type=avdtp.AVDTP_AUDIO_MEDIA_TYPE)]))
+
+ int_to_acp_seid = result.seid_information[0].acp_seid
+
+ channel_int.send_signal(GetAllCapabilitiesCommand(acp_seid=int_to_acp_seid))
+
+ cmd = await channel_acp.expect_signal(GetAllCapabilitiesCommand(acp_seid=int_to_acp_seid, transaction_label=any))
+
+ acceptor_service_capabilities = [
+ MediaTransportCapability(),
+ MediaCodecCapability(service_category=ServiceCategory.MEDIA_CODEC,
+ media_codec_specific_information_elements=[255, 255, 2, 53])
+ ]
+
+ channel_acp.send_signal(
+ GetAllCapabilitiesResponse(transaction_label=cmd.transaction_label,
+ service_capabilities=acceptor_service_capabilities))
+
+ result = await channel_int.expect_signal(
+ GetAllCapabilitiesResponse(transaction_label=any,
+ service_capabilities=[
+ MediaTransportCapability(),
+ MediaCodecCapability(service_category=ServiceCategory.MEDIA_CODEC,
+ media_codec_specific_information_elements=[255, 255, 2, 53])
+ ]))
+
+ channel_int.send_signal(
+ SetConfigurationCommand(acp_seid=int_to_acp_seid, service_capabilities=[result.service_capabilities[0]]))
+
+ cmd = await channel_acp.expect_signal(
+ SetConfigurationCommand(transaction_label=any,
+ acp_seid=int_to_acp_seid,
+ service_capabilities=[result.service_capabilities[0]]))
+
+ channel_acp.send_signal(SetConfigurationResponse(transaction_label=cmd.transaction_label))
+
+ await channel_int.expect_signal(SetConfigurationResponse(transaction_label=any))
+
+ channel_int.send_signal(OpenCommand(acp_seid=int_to_acp_seid))
+
+ cmd = await channel_acp.expect_signal(OpenCommand(transaction_label=any, acp_seid=int_to_acp_seid))
+
+ channel_acp.send_signal(OpenResponse(transaction_label=cmd.transaction_label))
+
+ await channel_int.expect_signal(OpenResponse(transaction_label=any))
+
+ await asyncio.wait_for(avdtp_future, timeout=10.0)
+
+ rtp_packets_expected = 3
+ received_rtp_packets = []
+ source_packets = [
+ avdtp.MediaPacket(2, 0, 0, 0, i, i * 10, 0, [], 96, bytes([i])) for i in range(rtp_packets_expected)
+ ]
+
+ await channel_int.initiate_transport_channel()
+
+ channel_int.send_signal(StartCommand(acp_seid=int_to_acp_seid))
+
+ cmd = await channel_acp.expect_signal(StartCommand(transaction_label=any, acp_seid=int_to_acp_seid))
+
+ channel_acp.send_signal(StartResponse(transaction_label=cmd.transaction_label))
+
+ await channel_int.expect_signal(StartResponse(transaction_label=any))
+
+ channel_int.send_media(bytes(source_packets[0]))
+ channel_int.send_media(bytes(source_packets[1]))
+ channel_int.send_media(bytes(source_packets[2]))
+
+ for _ in range(rtp_packets_expected):
+ received_rtp_packets.append(await channel_acp.expect_media())
+ assert channel_acp.transport_queue.empty()
+
+ channel_int.send_signal(CloseCommand(acp_seid=int_to_acp_seid))
+
+ cmd = await channel_acp.expect_signal(CloseCommand(transaction_label=any, acp_seid=int_to_acp_seid))
+
+ channel_acp.send_signal(CloseResponse(transaction_label=cmd.transaction_label))
+
+ await channel_int.expect_signal(CloseResponse(transaction_label=any))
+
+ await channel_int.disconnect_transport_channel()
+ await channel_int.disconnect()
+
+
+# -----------------------------------------------------------------------------
+async def run_test_self():
+ await test_self_connection()
+ await test_signaling_channel_as_source()
+ await test_signaling_channel_as_sink()
+
+
+# -----------------------------------------------------------------------------
+if __name__ == '__main__':
+ logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+ asyncio.run(run_test_self())
diff --git a/android/pandora/test/a2dp_test.py b/android/pandora/test/a2dp_test.py
index 92df133d62..a1efc015c3 100644
--- a/android/pandora/test/a2dp_test.py
+++ b/android/pandora/test/a2dp_test.py
@@ -20,9 +20,9 @@ import itertools
import logging
import numpy as np
-from a2dp.packets import avdtp
-from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices, pandora
-from avatar.pandora_server import AndroidPandoraServer
+from a2dp.packets.avdtp import *
+from a2dp.signaling_channel import Any, SignalingChannel
+from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices, pandora_snippet, enableFlag
from bumble.a2dp import (
A2DP_MPEG_2_4_AAC_CODEC_TYPE,
A2DP_SBC_CODEC_TYPE,
@@ -41,8 +41,6 @@ from bumble.avctp import AVCTP_PSM
from bumble.avdtp import (
AVDTP_AUDIO_MEDIA_TYPE,
AVDTP_BAD_STATE_ERROR,
- AVDTP_CLOSING_STATE,
- AVDTP_IDLE_STATE,
AVDTP_OPEN_STATE,
AVDTP_PSM,
AVDTP_STREAMING_STATE,
@@ -50,6 +48,7 @@ from bumble.avdtp import (
Listener,
MediaCodecCapabilities,
Protocol,
+ Stream,
Suspend_Reject,
)
from bumble.l2cap import (
@@ -58,6 +57,7 @@ from bumble.l2cap import (
ClassicChannel,
ClassicChannelSpec,
L2CAP_Configure_Request,
+ L2CAP_Connection_Request,
L2CAP_Connection_Response,
)
from bumble.pairing import PairingDelegate
@@ -76,6 +76,7 @@ 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'
+A2DP_SM_IGNORE_CONNECT_EVENTS_IN_CONNECTING_STATE = 'com.android.bluetooth.flags.a2dp_sm_ignore_connect_events_in_connecting_state'
async def initiate_pairing(device, address) -> Connection:
@@ -290,6 +291,94 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
assert_equal(self.ref1.a2dp_sink.stream.state, AVDTP_OPEN_STATE)
@avatar.asynchronous
+ async def test_signaling_channel_and_streaming(self) -> None:
+ """Basic A2DP connection and streaming with SignalingChannel used by acceptor device test.
+
+ 1. Pair and Connect RD1
+ 2. Setup the acceptor expectations on signalling channel
+ 2. Start streaming
+ 4. Stop streaming
+ """
+ any = Any()
+
+ # Connect and pair RD1.
+ dut_ref1, ref1_dut = await asyncio.gather(
+ initiate_pairing(self.dut, self.ref1.address),
+ accept_pairing(self.ref1, self.dut.address),
+ )
+
+ connection = pandora_snippet.get_raw_connection(device=self.ref1, connection=ref1_dut)
+ channel = SignalingChannel.accept(connection)
+
+ async def acceptor_avdt_open(channel: SignalingChannel):
+
+ avdtp_future = asyncio.get_running_loop().create_future()
+
+ def on_avdtp_connection():
+ logger.info(f"AVDTP Opened")
+ nonlocal avdtp_future
+ avdtp_future.set_result(None)
+
+ channel.on('connection', on_avdtp_connection)
+
+ cmd = await channel.expect_signal(DiscoverCommand(transaction_label=any))
+
+ seid_information = [SeidInformation(acp_seid=0x01, tsep=Tsep.SINK, media_type=AVDTP_AUDIO_MEDIA_TYPE)]
+
+ channel.send_signal(
+ DiscoverResponse(transaction_label=cmd.transaction_label, seid_information=seid_information))
+
+ cmd = await channel.expect_signal(GetAllCapabilitiesCommand(acp_seid=any, transaction_label=any))
+
+ acceptor_service_capabilities = [
+ MediaTransportCapability(),
+ MediaCodecCapability(service_category=ServiceCategory.MEDIA_CODEC,
+ media_codec_specific_information_elements=[255, 255, 2, 53])
+ ]
+
+ channel.send_signal(
+ GetAllCapabilitiesResponse(transaction_label=cmd.transaction_label,
+ service_capabilities=acceptor_service_capabilities))
+
+ cmd = await channel.expect_signal(
+ SetConfigurationCommand(transaction_label=any,
+ acp_seid=any,
+ int_seid=any,
+ service_capabilities=[MediaTransportCapability(), any]))
+
+ channel.send_signal(SetConfigurationResponse(transaction_label=cmd.transaction_label))
+
+ cmd = await channel.expect_signal(OpenCommand(transaction_label=any, acp_seid=any))
+
+ channel.send_signal(OpenResponse(transaction_label=cmd.transaction_label))
+
+ await asyncio.wait_for(avdtp_future, timeout=10.0)
+
+ # Connect AVDTP to RD1.
+ _, dut_ref1_source = await asyncio.gather(acceptor_avdt_open(channel), open_source(self.dut, dut_ref1))
+
+ async def acceptor_avdt_start(channel: SignalingChannel):
+ cmd = await channel.expect_signal(StartCommand(transaction_label=any, acp_seid=any))
+
+ channel.send_signal(StartResponse(transaction_label=cmd.transaction_label))
+
+ # Start streaming to RD1.
+ await asyncio.gather(self.dut.a2dp.Start(source=dut_ref1_source), acceptor_avdt_start(channel))
+
+ audio = AudioSignal(self.dut.a2dp, dut_ref1_source, 0.8, 44100)
+
+ # Verify that at least one audio frame is received on the transport channel.
+ await asyncio.wait_for(channel.expect_media(), 5.0)
+
+ async def acceptor_avdt_suspend(channel: SignalingChannel):
+ cmd = await channel.expect_signal(SuspendCommand(transaction_label=any, acp_seid=any))
+
+ channel.send_signal(SuspendResponse(transaction_label=cmd.transaction_label))
+
+ # Stop streaming to RD1.
+ await asyncio.gather(self.dut.a2dp.Suspend(source=dut_ref1_source), acceptor_avdt_suspend(channel))
+
+ @avatar.asynchronous
async def test_avdtp_autoconnect_when_only_avctp_connected(self) -> None:
"""Test AVDTP automatically connects if peer device connects only AVCTP.
@@ -316,7 +405,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
self.ref1.a2dp.on('connection', on_avdtp_connection)
# Retrieve Bumble connection object from Pandora connection token
- connection = pandora.get_raw_connection(device=self.ref1, connection=ref1_dut)
+ connection = pandora_snippet.get_raw_connection(device=self.ref1, connection=ref1_dut)
# Open AVCTP L2CAP channel
avctp = await connection.create_l2cap_channel(spec=ClassicChannelSpec(AVCTP_PSM))
@@ -326,7 +415,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
await asyncio.wait_for(avdtp_future, timeout=10.0)
@avatar.asynchronous
- async def test_avdt_signaling_channel_connection_collision(self) -> None:
+ async def test_avdt_signaling_channel_connection_collision_case1(self) -> None:
"""Test AVDTP signaling channel connection collision.
Test steps after DUT and RD1 connected and paired:
@@ -416,7 +505,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
)
# Retrieve Bumble connection object from Pandora connection token
- connection = pandora.get_raw_connection(device=self.ref1, connection=ref1_dut)
+ connection = pandora_snippet.get_raw_connection(device=self.ref1, connection=ref1_dut)
# Find a free CID for a new channel
connection_channels = self.ref1.device.l2cap_channel_manager.channels.setdefault(connection.handle, {})
source_cid = self.ref1.device.l2cap_channel_manager.find_free_br_edr_cid(connection_channels)
@@ -561,6 +650,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
assert configurationResponse.configuration.id.HasField('mpeg_aac')
@avatar.asynchronous
+ @enableFlag(AVDTP_HANDLE_SUSPEND_CFM_BAD_STATE)
async def test_avdt_handle_suspend_cfm_bad_state_error(self) -> None:
"""Test AVDTP handling of suspend confirmation BAD_STATE error.
@@ -611,13 +701,6 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
channel.on('open', on_channel_open)
channel.on('close', on_channel_close)
- # Enable BAD_STATE handling
- for server in self.devices._servers:
- if isinstance(server, AndroidPandoraServer):
- server.device.adb.shell(
- ['device_config override bluetooth', AVDTP_HANDLE_SUSPEND_CFM_BAD_STATE, 'true']) # type: ignore
- break
-
self.ref1.device.l2cap_channel_manager.servers.pop(AVDTP_PSM)
self.ref1.a2dp = TestA2dpListener.for_device(self.ref1.device)
self.ref1.a2dp_sink = None
@@ -663,6 +746,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
await asyncio.wait_for(avdtp_future, timeout=10.0)
@avatar.asynchronous
+ @enableFlag(AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE)
async def test_avdt_open_after_timeout(self) -> None:
"""Test AVDTP automatically opens stream after timeout if peer device only configures codec.
@@ -679,14 +763,6 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
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),
@@ -697,7 +773,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
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)
+ connection = pandora_snippet.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))
@@ -705,7 +781,7 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
sink = client.add_sink(sbc_codec_capabilities())
endpoints = await client.discover_remote_endpoints()
logger.info(f"endpoints: {endpoints}")
- assert len(endpoints) >= 1
+ assert endpoints
remote_source = list(endpoints)[0]
assert remote_source.in_use == 0
assert remote_source.media_type == AVDTP_AUDIO_MEDIA_TYPE
@@ -732,6 +808,166 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc]
# Wait for AVDTP Open from DUT
await asyncio.wait_for(avdtp_future, timeout=10.0)
+ @avatar.asynchronous
+ @enableFlag(A2DP_SM_IGNORE_CONNECT_EVENTS_IN_CONNECTING_STATE)
+ async def test_avdt_signaling_channel_connection_collision_case2(self) -> None:
+ """Test AVDTP signaling channel connection collision with Android as initiator.
+
+ Test steps after DUT and RD1 connected and paired:
+ 1. RD1 waits for connection request from DUT
+ 2. DUT connects RD1 over AVDTP - first AVDTP signaling channel
+ 3. RD1 sends connection request to DUT to simulate collision
+ 4. RD1 rejects connection from DUT
+ 5. DUT closed initiated connection and allowed for the incoming to proceed. RD1 opens AVDT connection
+ 6. DUT A2DP source configured and connected
+ """
+
+ wait_for_l2cap_open = asyncio.get_running_loop().create_future()
+
+ class TestClassicChannel(ClassicChannel):
+
+ def test_connect(self, connection: Connection, cid: int, request: L2CAP_Connection_Request) -> None:
+ assert self.state == self.State.CLOSED
+
+ # Check that we can start a new connection
+ assert not self.connection_result
+
+ self._change_state(self.State.WAIT_CONNECT_RSP)
+ logger.info("<< 3. RD1 sends connection request to DUT to simulate collision >>")
+ self.send_control_frame(
+ L2CAP_Connection_Request(
+ identifier=self.manager.next_identifier(self.connection),
+ psm=self.psm,
+ source_cid=self.source_cid,
+ ))
+ if (self.psm == AVDTP_PSM):
+ logger.info("<< 4. RD1 rejects connection from DUT >>")
+ self.manager.send_control_frame(
+ connection, cid,
+ L2CAP_Connection_Response(
+ identifier=request.identifier,
+ destination_cid=0,
+ source_cid=request.source_cid,
+ result=L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
+ status=0x0000,
+ ))
+
+ class TestChannelManager(ChannelManager):
+
+ def __init__(
+ self,
+ device: BumblePandoraDevice,
+ ) -> None:
+ super().__init__(
+ device.l2cap_channel_manager.extended_features,
+ device.l2cap_channel_manager.connectionless_mtu,
+ )
+ self.register_fixed_channel(bumble.smp.SMP_CID, device.on_smp_pdu)
+ device.sdp_server.register(self)
+ self.register_fixed_channel(bumble.att.ATT_CID, device.on_gatt_pdu)
+ self.host = device.host
+
+ def on_l2cap_connection_request(self, connection: Connection, cid: int,
+ request: L2CAP_Connection_Request) -> None:
+ if (request.psm == AVDTP_PSM):
+ logger.info("<< 2. DUT connects RD1 over AVDTP - first AVDTP signaling channel >>")
+ spec = ClassicChannelSpec(AVDTP_PSM)
+ assert spec.psm is not None
+
+ # Find a free CID for a new channel
+ connection_channels = self.channels.setdefault(connection.handle, {})
+ source_cid = self.find_free_br_edr_cid(connection_channels)
+ assert source_cid is not None
+
+ # Create the channel
+ logger.debug(f'creating client channel with cid={source_cid} for psm {spec.psm}')
+ channel = TestClassicChannel(
+ self,
+ connection,
+ L2CAP_SIGNALING_CID,
+ AVDTP_PSM,
+ source_cid,
+ spec.mtu,
+ )
+ connection_channels[source_cid] = channel
+
+ def on_channel_open():
+ # Initiate AVDTP with connected L2CAP signaling channel
+ nonlocal wait_for_l2cap_open
+ wait_for_l2cap_open.set_result(channel)
+
+ channel.on('open', on_channel_open)
+ channel.test_connect(connection, cid, request)
+ return
+
+ super().on_l2cap_connection_request(connection, cid, request)
+
+ handle = 0x00010001
+ self.ref1.device.sdp_service_records = {handle: make_audio_sink_service_sdp_records(handle)}
+
+ # Override L2CAP Channel Manager to control signaling
+ self.ref1.device.l2cap_channel_manager = TestChannelManager(self.ref1.device)
+
+ # Create listener on RD1 for initial incoming AVDT connection from DUT
+ self.ref1.a2dp = Listener.for_device(self.ref1.device)
+
+ logger.info("<< 1. RD1 waits for connection request from DUT >>")
+
+ # Connect and pair DUT -> RD1.
+ dut_ref1, ref1_dut = await asyncio.gather(
+ initiate_pairing(self.dut, self.ref1.address),
+ accept_pairing(self.ref1, self.dut.address),
+ )
+
+ # Wait until RD1 will initiate and open L2CAP channel for AVDTP
+ channel = await asyncio.wait_for(wait_for_l2cap_open, timeout=10.0)
+
+ logger.info(
+ "<< 5. DUT closed initiated connection and allowed for the incoming to proceed. RD1 opens AVDT connection >>"
+ )
+
+ protocol = Protocol(channel)
+ sink = protocol.add_sink(sbc_codec_capabilities())
+ endpoints = await protocol.discover_remote_endpoints()
+ logger.debug(f"endpoints: {endpoints}")
+ assert endpoints
+ 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.debug(f"remote_source: {remote_source}")
+
+ sink.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,
+ ),
+ )
+ ]
+
+ # Start waiting for DUT A2DP source configured and connected
+ wait_source = self.dut.a2dp.WaitSource(connection=dut_ref1)
+
+ # Open stream
+ stream = Stream(protocol, sink, remote_source)
+ protocol.streams[sink.seid] = stream
+ await stream.configure()
+ await stream.open()
+
+ # Check that DUT source is configured and connected
+ result = await wait_source
+ assert result.source
+
+ logger.info("<< 6. DUT A2DP source configured and connected >>")
+
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
diff --git a/android/pandora/test/main.py b/android/pandora/test/main.py
index d8eadf1e60..d5c5da0bad 100644
--- a/android/pandora/test/main.py
+++ b/android/pandora/test/main.py
@@ -4,18 +4,18 @@ site.main()
import argparse
import logging
+import itertools
import os
import sys
from argparse import Namespace
from mobly import suite_runner
-from typing import List, Tuple
+from typing import List, Tuple, Union, Literal
_BUMBLE_BTSNOOP_FMT = 'bumble_btsnoop_{pid}_{instance}.log'
-import a2dp_test
-
# Import test cases modules.
+import a2dp_test
import aics_test
import asha_test
import avatar.cases.host_test
@@ -25,12 +25,53 @@ import avatar.cases.security_test
import gatt_test
import hap_test
import hfpclient_test
-from pairing import _test_class_list as _pairing_test_class_list
import sdp_test
+from pairing import _test_class_list as _pairing_test_class_list
+from pandora.host_pb2 import PrimaryPhy, PRIMARY_1M, PRIMARY_CODED
+
+
+class LeHostTestFiltered(avatar.cases.le_host_test.LeHostTest):
+ """
+ LeHostTestFiltered inherits from LeHostTest to skip currently broken and unfeasible to fix tests.
+ Overridden tests will be visible as PASS when run.
+ """
+ skipped_tests = [
+ # Reason for skipping tests: b/272120114
+ "test_extended_scan('non_connectable_scannable','directed',150,0)",
+ "test_extended_scan('non_connectable_scannable','undirected',150,0)",
+ "test_extended_scan('non_connectable_scannable','directed',150,2)",
+ "test_extended_scan('non_connectable_scannable','undirected',150,2)",
+ ]
+
+ @avatar.parameterized(
+ *itertools.product(
+ # The advertisement cannot be both connectable and scannable.
+ ('connectable', 'non_connectable', 'non_connectable_scannable'),
+ ('directed', 'undirected'),
+ # Bumble does not send multiple HCI commands, so it must also fit in
+ # 1 HCI command (max length 251 minus overhead).
+ (0, 150),
+ (PRIMARY_1M, PRIMARY_CODED),
+ ),) # type: ignore[misc]
+ def test_extended_scan(
+ self,
+ connectable_scannable: Union[Literal['connectable'], Literal['non_connectable'],
+ Literal['non_connectable_scannable']],
+ directed: Union[Literal['directed'], Literal['undirected']],
+ data_len: int,
+ primary_phy: PrimaryPhy,
+ ) -> None:
+ current_test = f"test_extended_scan('{connectable_scannable}','{directed}',{data_len},{primary_phy})"
+ logging.info(f"current test: {current_test}")
+ if current_test not in self.skipped_tests:
+ assert current_test in avatar.cases.le_host_test.LeHostTest.__dict__
+ avatar.cases.le_host_test.LeHostTest.__dict__[current_test](self)
+
+
_TEST_CLASSES_LIST = [
avatar.cases.host_test.HostTest,
- avatar.cases.le_host_test.LeHostTest,
+ LeHostTestFiltered,
avatar.cases.security_test.SecurityTest,
avatar.cases.le_security_test.LeSecurityTest,
a2dp_test.A2dpTest,
diff --git a/apex/Android.bp b/apex/Android.bp
index 4eccda2a11..d8f4603a8c 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -23,39 +23,14 @@ linker_config {
}
// Legacy Bluetooth apex prior to Baklava
+// This is kept for soong purposes but this apex doesn't do anything anymore
// TODO b/383863941 delete
apex {
+ enabled: false,
name: "com.android.btservices",
defaults: ["t-launched-apex-module"],
manifest: "apex_manifest.json",
- bootclasspath_fragments: ["com.android.btservices-bootclasspath-fragment"],
- systemserverclasspath_fragments: ["com.android.btservices-systemserverclasspath-fragment"],
- compat_configs: [
- "bluetooth-compat-config",
- "bluetoothapk-platform-compat-config",
- "framework-bluetooth-compat-config",
- ],
- apps: ["Bluetooth"],
- multilib: {
- first: {
- // Extractor process runs only with the primary ABI.
- jni_libs: [
- "libbluetooth_jni",
- ],
- },
- },
-
- prebuilts: [
- "audio_set_configurations_bfbs",
- "audio_set_configurations_json",
- "audio_set_scenarios_bfbs",
- "audio_set_scenarios_json",
- "bt_did.conf",
- "bt_stack.conf",
- "btservices-linker-config",
- "interop_database.conf",
- ],
key: "com.android.btservices.key",
certificate: ":com.android.btservices.certificate",
updatable: true,
@@ -74,71 +49,6 @@ android_app_certificate {
certificate: "com.android.btservices",
}
-sdk {
- name: "btservices-module-sdk",
- apexes: [
- // Adds exportable dependencies of the APEX to the sdk,
- // e.g. *classpath_fragments.
- "com.android.btservices",
- ],
-}
-
-// Encapsulate the contributions made by the com.android.bluetooth to the bootclasspath.
-bootclasspath_fragment {
- name: "com.android.btservices-bootclasspath-fragment",
- contents: ["framework-bluetooth"],
- apex_available: ["com.android.btservices"],
-
- // The bootclasspath_fragments that provide APIs on which this depends.
- fragments: [
- {
- apex: "com.android.art",
- module: "art-bootclasspath-fragment",
- },
- ],
-
- // Additional stubs libraries that this fragment's contents use which are
- // not provided by another bootclasspath_fragment.
- additional_stubs: [
- "android-non-updatable",
- ],
-
- // Additional hidden API flag files to override the defaults. This must only be
- // modified by the Soong or platform compat team.
- hidden_api: {
- max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
- max_target_r_low_priority: ["hiddenapi/hiddenapi-max-target-r-low-priority.txt"],
- unsupported: ["hiddenapi/hiddenapi-unsupported.txt"],
-
- // The following packages contain classes from other modules on the
- // bootclasspath. That means that the hidden API flags for this module
- // has to explicitly list every single class this module provides in
- // that package to differentiate them from the classes provided by other
- // modules. That can include private classes that are not part of the
- // API.
- split_packages: [
- "android.bluetooth",
- ],
-
- // The following packages and all their subpackages currently only
- // contain classes from this bootclasspath_fragment. Listing a package
- // here won't prevent other bootclasspath modules from adding classes in
- // any of those packages but it will prevent them from adding those
- // classes into an API surface, e.g. public, system, etc.. Doing so will
- // result in a build failure due to inconsistent flags.
- package_prefixes: [
- "android.bluetooth.le",
- "com.android.bluetooth",
- ],
- },
-}
-
-systemserverclasspath_fragment {
- name: "com.android.btservices-systemserverclasspath-fragment",
- standalone_contents: ["service-bluetooth"],
- apex_available: ["com.android.btservices"],
-}
-
// Mainline bluetooth apex module.
apex {
name: "com.android.bt",
@@ -242,6 +152,6 @@ bootclasspath_fragment {
systemserverclasspath_fragment {
name: "com.android.bt-systemserverclasspath-fragment",
- standalone_contents: ["service-bluetooth-new"],
+ standalone_contents: ["service-bluetooth"],
apex_available: ["com.android.bt"],
}
diff --git a/common/Android.bp b/common/Android.bp
index 900cc365d7..f472c9e3d0 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -22,7 +22,7 @@ java_library {
"bluetooth/constants/aics/GainMode.aidl",
"bluetooth/constants/aics/Mute.aidl",
],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
sdk_version: "module_current",
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
diff --git a/flags/Android.bp b/flags/Android.bp
index f43322d4ed..80a57891a5 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -6,7 +6,7 @@ package {
aconfig_declarations {
name: "bluetooth_aconfig_flags",
package: "com.android.bluetooth.flags",
- container: "com.android.btservices",
+ container: "com.android.bt",
visibility: ["//packages/modules/Bluetooth/framework"],
// LINT.IfChange
srcs: [
@@ -19,7 +19,6 @@ aconfig_declarations {
"btif_dm.aconfig",
"btm_ble.aconfig",
"connectivity.aconfig",
- "device_iot_config.aconfig",
"dis.aconfig",
"framework.aconfig",
"gap.aconfig",
@@ -32,6 +31,7 @@ aconfig_declarations {
"hid.aconfig",
"l2cap.aconfig",
"le_advertising.aconfig",
+ "le_scanning.aconfig",
"leaudio.aconfig",
"mapclient.aconfig",
"mcp.aconfig",
@@ -59,7 +59,7 @@ java_aconfig_library {
name: "bluetooth_flags_java_lib",
aconfig_declarations: "bluetooth_aconfig_flags",
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
libs: ["framework-configinfrastructure.stubs.module_lib"],
sdk_version: "module_current",
min_sdk_version: "Tiramisu",
@@ -83,7 +83,7 @@ cc_aconfig_library {
aconfig_declarations: "bluetooth_aconfig_flags",
host_supported: true,
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
@@ -101,7 +101,7 @@ rust_aconfig_library {
host_supported: true,
crate_name: "bluetooth_aconfig_flags_rust",
aconfig_declarations: "bluetooth_aconfig_flags",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
visibility: ["//packages/modules/Bluetooth/system:__subpackages__"],
}
diff --git a/flags/BUILD.gn b/flags/BUILD.gn
index 6c7185a24a..da5aa8621b 100644
--- a/flags/BUILD.gn
+++ b/flags/BUILD.gn
@@ -12,7 +12,6 @@ aconfig("bluetooth_flags_c_lib") {
"btif_dm.aconfig",
"btm_ble.aconfig",
"connectivity.aconfig",
- "device_iot_config.aconfig",
"dis.aconfig",
"framework.aconfig",
"gap.aconfig",
@@ -25,6 +24,7 @@ aconfig("bluetooth_flags_c_lib") {
"hid.aconfig",
"l2cap.aconfig",
"le_advertising.aconfig",
+ "le_scanning.aconfig",
"leaudio.aconfig",
"mapclient.aconfig",
"mcp.aconfig",
diff --git a/flags/a2dp.aconfig b/flags/a2dp.aconfig
index e6275bf010..06d46a7f76 100644
--- a/flags/a2dp.aconfig
+++ b/flags/a2dp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "bta_av_use_peer_codec"
diff --git a/flags/active_device_manager.aconfig b/flags/active_device_manager.aconfig
index fefcdd1d87..b2438be452 100644
--- a/flags/active_device_manager.aconfig
+++ b/flags/active_device_manager.aconfig
@@ -1,41 +1,31 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
- name: "adm_fallback_when_wired_audio_disconnected"
- namespace: "bluetooth"
- description: "Fallback to other connected device when wired audio device disconnects"
- bug: "348124361"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "adm_always_fallback_to_available_device"
+ name: "adm_verify_active_fallback_device"
namespace: "bluetooth"
- description: "Fix audio path and always fallback to available device"
- bug: "351820274"
+ description: "Verify if device selected for fallback is different then last one"
+ bug: "369799111"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
- name: "adm_verify_active_fallback_device"
+ name: "adm_fix_disconnect_of_set_member"
namespace: "bluetooth"
- description: "Verify if device selected for fallback is different then last one"
- bug: "369799111"
+ description: "Fix disconnecting of the set member device. Make sure the other set member is not considered as fallback device."
+ bug: "374320313"
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
- name: "adm_fix_disconnect_of_set_member"
+ name: "adm_remove_handling_wired"
namespace: "bluetooth"
- description: "Fix disconnecting of the set member device. Make sure the other set member is not considered as fallback device."
- bug: "374320313"
+ description: "ActiveDeviceManager doesn't need to handle adding and removing wired devices."
+ bug: "390372480"
metadata {
purpose: PURPOSE_BUGFIX
}
diff --git a/flags/adapter.aconfig b/flags/adapter.aconfig
index f355113fe0..f6bf65e1f6 100644
--- a/flags/adapter.aconfig
+++ b/flags/adapter.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "adapter_properties_looper"
diff --git a/flags/avrcp.aconfig b/flags/avrcp.aconfig
index 5e806efb11..7a30583c07 100644
--- a/flags/avrcp.aconfig
+++ b/flags/avrcp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "abs_volume_sdp_conflict"
diff --git a/flags/avrcp_controller.aconfig b/flags/avrcp_controller.aconfig
index 4374eef8fa..be732ee1ad 100644
--- a/flags/avrcp_controller.aconfig
+++ b/flags/avrcp_controller.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "uncache_player_when_browsed_player_changes"
diff --git a/flags/bta_dm.aconfig b/flags/bta_dm.aconfig
index f35d562ebf..287a0f0187 100644
--- a/flags/bta_dm.aconfig
+++ b/flags/bta_dm.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "bta_dm_defer_device_discovery_state_change_until_rnr_complete"
@@ -9,13 +9,6 @@ flag {
}
flag {
- name: "bta_dm_discover_both"
- namespace: "bluetooth"
- description: "perform both LE and Classic service discovery simulteanously on capable devices"
- bug: "339217881"
-}
-
-flag {
name: "cancel_open_discovery_client"
namespace: "bluetooth"
description: "Cancel connection from discovery client correctly"
@@ -34,3 +27,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "extend_and_randomize_role_switch_delay"
+ namespace: "bluetooth"
+ description: "Fix the possible conflicts between role switch and authentication"
+ bug: "388459732"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/btif_dm.aconfig b/flags/btif_dm.aconfig
index 2f16904b8f..b8802a9db1 100644
--- a/flags/btif_dm.aconfig
+++ b/flags/btif_dm.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "bond_transport_after_bond_cancel_fix"
diff --git a/flags/btm_ble.aconfig b/flags/btm_ble.aconfig
index 0922fd1aa0..2413c74be1 100644
--- a/flags/btm_ble.aconfig
+++ b/flags/btm_ble.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "floss_separate_host_privacy_and_llprivacy"
diff --git a/flags/connectivity.aconfig b/flags/connectivity.aconfig
index 80efed1aa2..c8dca9cb5f 100644
--- a/flags/connectivity.aconfig
+++ b/flags/connectivity.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "api_get_connection_state_using_identity_address"
@@ -34,3 +34,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "allow_gatt_connect_from_the_apps_without_making_leaudio_device_active"
+ namespace: "bluetooth"
+ description: "Allows for GATT connection without making LeAudio device active after connection"
+ bug: "389274300"
+}
+
diff --git a/flags/device_iot_config.aconfig b/flags/device_iot_config.aconfig
deleted file mode 100644
index 77ede517d4..0000000000
--- a/flags/device_iot_config.aconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
-
-flag {
- name: "device_iot_config_logging"
- namespace: "bluetooth"
- description: "Enable device IOT information storage."
- bug: "290844229"
-}
diff --git a/flags/dis.aconfig b/flags/dis.aconfig
index f96d6c3b5f..ace19bca21 100644
--- a/flags/dis.aconfig
+++ b/flags/dis.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "queue_dis_requests"
diff --git a/flags/framework.aconfig b/flags/framework.aconfig
index ae23329345..6464f91927 100644
--- a/flags/framework.aconfig
+++ b/flags/framework.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "support_metadata_device_types_apis"
@@ -86,3 +86,13 @@ flag {
description: "Make BluetoothDevice.ACTION_KEY_MISSING into public API"
bug: "379729762"
}
+
+flag {
+ name: "set_component_available_fix"
+ namespace: "bluetooth"
+ description: "Ensure the state in PackageManager has DISABLED to ENABLED to trigger PACKAGE_CHANGED"
+ bug: "391084450"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/gap.aconfig b/flags/gap.aconfig
index 0ef4a7aa20..5da5144aa1 100644
--- a/flags/gap.aconfig
+++ b/flags/gap.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "encrypted_advertising_data"
@@ -44,26 +44,6 @@ flag {
}
flag {
- name: "ble_context_map_remove_fix"
- namespace: "bluetooth"
- description: "Fix connection removal logic in ContextMap class"
- bug: "329154715"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "scan_record_manufacturer_data_merge"
- namespace: "bluetooth"
- description: "If a scan record has multiple datas under same manufacturer id, merge the values"
- bug: "331723396"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "msft_addr_tracking_quirk"
namespace: "bluetooth"
description: "Scanning with MSFT paddress tracking for Realtek BT controllers"
@@ -131,16 +111,6 @@ flag {
}
flag {
- name: "le_inquiry_duration"
- namespace: "bluetooth"
- description: "Use the same duration for LE inquiry scan that classic discovery uses"
- bug: "357894405"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "non_wake_alarm_for_rpa_rotation"
namespace: "bluetooth"
description: "Use non-wake alarm for LE RPA rotation. go/non-wake-alarm-for-rpa-rotation"
@@ -281,3 +251,43 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "change_default_trackable_adv_number"
+ namespace: "bluetooth"
+ description: "Change the default value for number of trackable advertisements for onFound"
+ bug: "389568695"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "fix_unusable_adv_slot_due_to_map_access"
+ namespace: "bluetooth"
+ description: "Fixes the advertising slot becoming unusable due to incorrect map access via [] operator."
+ bug: "388615378"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "fix_bluetooth_gatt_getting_duplicate_services"
+ namespace: "bluetooth"
+ description: "Fixes BluetoothGatt getting duplicate GATT services"
+ bug: "391773937"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "batch_scan_optimization"
+ namespace: "bluetooth"
+ description: "Optimized batch scan for less wakeups"
+ bug: "392132489"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/gatt.aconfig b/flags/gatt.aconfig
index 55e5b27077..e054fb3f7a 100644
--- a/flags/gatt.aconfig
+++ b/flags/gatt.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "default_gatt_transport"
@@ -18,3 +18,14 @@ flag {
bug: "384794418"
is_exported: true
}
+
+flag {
+ name: "advertise_thread"
+ namespace: "bluetooth"
+ description: "Run all advertise functions on a single thread"
+ bug: "391508617"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/flags/hal.aconfig b/flags/hal.aconfig
index 286d1fceb4..3e16fa5355 100644
--- a/flags/hal.aconfig
+++ b/flags/hal.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "audio_port_binder_inherit_rt"
@@ -19,4 +19,14 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "snoop_logger_recreate_logs_directory"
+ namespace: "bluetooth"
+ description: "Recreate logs directory if removed"
+ bug: "383876267"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/flags/hap.aconfig b/flags/hap.aconfig
index 3ce320f88d..634000d0bd 100644
--- a/flags/hap.aconfig
+++ b/flags/hap.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "aics_api"
diff --git a/flags/hci.aconfig b/flags/hci.aconfig
index 79da26788b..cdd5ca3ab6 100644
--- a/flags/hci.aconfig
+++ b/flags/hci.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "encryption_change_v2"
diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig
index 96985f2750..026c3b22b3 100644
--- a/flags/hfp.aconfig
+++ b/flags/hfp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "auto_connect_on_multiple_hfp_when_no_a2dp_device"
@@ -60,16 +60,6 @@ flag {
}
flag {
- name: "headset_client_am_hf_volume_symmetric"
- namespace: "bluetooth"
- description: "Fix AM/HF volume conversion to be symmetric"
- bug: "340482648"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "maintain_call_index_after_conference"
namespace: "bluetooth"
description: "Avoid change of clcc call index after call disconnects from conference"
@@ -110,16 +100,6 @@ flag {
}
flag {
- name: "hfp_allow_volume_change_without_sco"
- namespace: "bluetooth"
- description: "Allow Audio Fwk to change SCO volume when HFP profile is connected and SCO not connected"
- bug: "362313390"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "choose_wrong_hfp_codec_in_specific_config"
namespace: "bluetooth"
description: "Flag to fix codec selection in nego when the peer device only support NB and SWB."
diff --git a/flags/hfpclient.aconfig b/flags/hfpclient.aconfig
index ded90b3578..c134baac7e 100644
--- a/flags/hfpclient.aconfig
+++ b/flags/hfpclient.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "hfp_client_disconnecting_state"
diff --git a/flags/hid.aconfig b/flags/hid.aconfig
index 5085df326b..17c66db035 100644
--- a/flags/hid.aconfig
+++ b/flags/hid.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "allow_switching_hid_and_hogp"
diff --git a/flags/l2cap.aconfig b/flags/l2cap.aconfig
index fb53c10bef..7e1ca0b016 100644
--- a/flags/l2cap.aconfig
+++ b/flags/l2cap.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "l2cap_tx_complete_cb_info"
diff --git a/flags/le_advertising.aconfig b/flags/le_advertising.aconfig
index 524d0461e5..56f849ab7a 100644
--- a/flags/le_advertising.aconfig
+++ b/flags/le_advertising.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "nrpa_non_connectable_adv"
diff --git a/flags/le_scanning.aconfig b/flags/le_scanning.aconfig
new file mode 100644
index 0000000000..0b4985e45e
--- /dev/null
+++ b/flags/le_scanning.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.bluetooth.flags"
+container: "com.android.bt"
+
+flag {
+ name: "scan_results_in_main_thread"
+ namespace: "bluetooth"
+ description: "Use main thread for handling scan results"
+ bug: "392693506"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig
index 9c5132afd5..03cc9a4c02 100644
--- a/flags/leaudio.aconfig
+++ b/flags/leaudio.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "leaudio_broadcast_monitor_source_sync_status"
@@ -441,3 +441,23 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "leaudio_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: "384460395"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "leaudio_disable_broadcast_for_hap_device"
+ namespace: "bluetooth"
+ description: "Disable broadcast feature for HAP device"
+ bug: "391702876"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/mapclient.aconfig b/flags/mapclient.aconfig
index 97d0411683..fe8f90d81d 100644
--- a/flags/mapclient.aconfig
+++ b/flags/mapclient.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "handle_delivery_sending_failure_events"
diff --git a/flags/mcp.aconfig b/flags/mcp.aconfig
index 430bacd761..80567acae7 100644
--- a/flags/mcp.aconfig
+++ b/flags/mcp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "mcp_allow_play_without_active_player"
diff --git a/flags/metric.aconfig b/flags/metric.aconfig
index f0c62e37fc..e8b8628ba2 100644
--- a/flags/metric.aconfig
+++ b/flags/metric.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "bluetooth_power_telemetry"
diff --git a/flags/opp.aconfig b/flags/opp.aconfig
index bd0f380c45..0e95bd42f3 100644
--- a/flags/opp.aconfig
+++ b/flags/opp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "opp_ignore_content_observer_after_service_stop"
@@ -20,3 +20,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "update_opp_launcher_user_changed"
+ namespace: "bluetooth"
+ description: "Enable/disable OPP launcher when user changed"
+ bug: "389596902"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/pairing.aconfig b/flags/pairing.aconfig
index a668b8dfb1..ae3d19a6ac 100644
--- a/flags/pairing.aconfig
+++ b/flags/pairing.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "clear_auth_collision_state_on_pairing_complete"
diff --git a/flags/pbapclient.aconfig b/flags/pbapclient.aconfig
index dbbbb7773b..2464557d58 100644
--- a/flags/pbapclient.aconfig
+++ b/flags/pbapclient.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "pbap_client_storage_refactor"
diff --git a/flags/ranging.aconfig b/flags/ranging.aconfig
index 6b75003163..7851d5ff82 100644
--- a/flags/ranging.aconfig
+++ b/flags/ranging.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "channel_sounding"
diff --git a/flags/rfcomm.aconfig b/flags/rfcomm.aconfig
index df53931078..a5d1ec94ce 100644
--- a/flags/rfcomm.aconfig
+++ b/flags/rfcomm.aconfig
@@ -1,12 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
-
-flag {
- name: "rfcomm_always_use_mitm"
- namespace: "bluetooth"
- description: "Use MITM initially to avoid abrupt peer disconnection b/312840315"
- bug: "316824288"
-}
+container: "com.android.bt"
flag {
name: "rfcomm_prevent_unnecessary_collisions"
diff --git a/flags/rnr.aconfig b/flags/rnr.aconfig
index 6b19a5f77b..425db568d6 100644
--- a/flags/rnr.aconfig
+++ b/flags/rnr.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "rnr_store_device_type"
diff --git a/flags/sco.aconfig b/flags/sco.aconfig
index 26379f4ea1..51c6e14e24 100644
--- a/flags/sco.aconfig
+++ b/flags/sco.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "fix_sco_command_status_handling"
diff --git a/flags/sdp.aconfig b/flags/sdp.aconfig
index a20adb914a..418fa04663 100644
--- a/flags/sdp.aconfig
+++ b/flags/sdp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "stack_sdp_detect_nil_property_type"
diff --git a/flags/security.aconfig b/flags/security.aconfig
index 2bb69eac95..ddfd78611c 100644
--- a/flags/security.aconfig
+++ b/flags/security.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "key_missing_classic_device"
@@ -36,10 +36,10 @@ flag {
}
flag {
- name: "le_enc_on_reconnection"
+ name: "le_enc_on_reconnect"
namespace: "bluetooth"
description: "Encrypt LE link on reconnection with bonded devices"
- bug: "356201480"
+ bug: "388864535"
metadata {
purpose: PURPOSE_BUGFIX
}
diff --git a/flags/service_discovery.aconfig b/flags/service_discovery.aconfig
index f5569086b9..00c0741ff3 100644
--- a/flags/service_discovery.aconfig
+++ b/flags/service_discovery.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "prevent_duplicate_uuid_intent"
diff --git a/flags/sockets.aconfig b/flags/sockets.aconfig
index 688d27323c..ded22e3da0 100644
--- a/flags/sockets.aconfig
+++ b/flags/sockets.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "unix_file_socket_creation_failure"
diff --git a/flags/system_service.aconfig b/flags/system_service.aconfig
index fba0792eca..65caf1e60c 100644
--- a/flags/system_service.aconfig
+++ b/flags/system_service.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "enforce_resolve_system_service_behavior"
diff --git a/flags/vcp.aconfig b/flags/vcp.aconfig
index 2c7e4983ea..12e55de6ba 100644
--- a/flags/vcp.aconfig
+++ b/flags/vcp.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "vcp_mute_unmute"
diff --git a/flags/vsc.aconfig b/flags/vsc.aconfig
index bd67729689..af4f375849 100644
--- a/flags/vsc.aconfig
+++ b/flags/vsc.aconfig
@@ -1,5 +1,5 @@
package: "com.android.bluetooth.flags"
-container: "com.android.btservices"
+container: "com.android.bt"
flag {
name: "hci_vendor_specific_extension"
diff --git a/framework/Android.bp b/framework/Android.bp
index 6e5dafc3e1..cd09db00e6 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -82,9 +82,7 @@ java_sdk_library {
":__subpackages__",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
permitted_packages: [
"android.bluetooth",
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 62b4aa6062..60657262e3 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -53,7 +53,7 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void disableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void enableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BufferConstraints getBufferConstraints();
- method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getDynamicBufferSupport();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice);
@@ -383,16 +383,16 @@ package android.bluetooth {
public final class BluetoothHapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
method public void close();
method protected void finalize();
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getActivePresetIndex(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getActivePresetIndex(@NonNull android.bluetooth.BluetoothDevice);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothHapPresetInfo getActivePresetInfo(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothHapPresetInfo> getAllPresetInfo(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getHapGroup(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getHapGroup(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getHearingAidType(@NonNull android.bluetooth.BluetoothDevice);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothHapPresetInfo getPresetInfo(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothHapPresetInfo getPresetInfo(@NonNull android.bluetooth.BluetoothDevice, int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothHapClient.Callback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void selectPreset(@NonNull android.bluetooth.BluetoothDevice, int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void selectPresetForGroup(int, int);
@@ -403,13 +403,13 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean supportsIndependentPresets(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean supportsSynchronizedPresets(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean supportsWritablePresets(@NonNull android.bluetooth.BluetoothDevice);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToNextPreset(@NonNull android.bluetooth.BluetoothDevice);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToNextPresetForGroup(int);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToPreviousPreset(@NonNull android.bluetooth.BluetoothDevice);
- method @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToPreviousPresetForGroup(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToNextPreset(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToNextPresetForGroup(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToPreviousPreset(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void switchToPreviousPresetForGroup(int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void unregisterCallback(@NonNull android.bluetooth.BluetoothHapClient.Callback);
field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_HAP_CONNECTION_STATE_CHANGED = "android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED";
- field @FlaggedApi("com.android.bluetooth.flags.settings_can_control_hap_preset") public static final int PRESET_INDEX_UNAVAILABLE = 0; // 0x0
+ field public static final int PRESET_INDEX_UNAVAILABLE = 0; // 0x0
field public static final int TYPE_BANDED = 2; // 0x2
field public static final int TYPE_BINAURAL = 0; // 0x0
field public static final int TYPE_MONAURAL = 1; // 0x1
@@ -1416,7 +1416,6 @@ package android.bluetooth.le {
public final class BluetoothLeScanner {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
}
public final class ChannelSoundingParams implements android.os.Parcelable {
diff --git a/framework/api/system-removed.txt b/framework/api/system-removed.txt
index d802177e24..ac62cca0a3 100644
--- a/framework/api/system-removed.txt
+++ b/framework/api/system-removed.txt
@@ -1 +1,9 @@
// Signature format: 2.0
+package android.bluetooth.le {
+
+ public final class BluetoothLeScanner {
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ }
+
+}
+
diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java
index 5bcd0789ab..54f2c702aa 100644
--- a/framework/java/android/bluetooth/BluetoothA2dp.java
+++ b/framework/java/android/bluetooth/BluetoothA2dp.java
@@ -724,20 +724,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Gets the current codec status (configuration and capability).
*
+ * <p>This method requires the calling app to have the {@link
+ * android.Manifest.permission#BLUETOOTH_CONNECT} permission. Additionally, an app must either
+ * have the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} or be associated with the
+ * Companion Device manager (see {@link android.companion.CompanionDeviceManager#associate(
+ * AssociationRequest, android.companion.CompanionDeviceManager.Callback, Handler)})
+ *
* @param device the remote Bluetooth device.
* @return the current codec status
* @hide
*/
@SystemApi
- @Nullable
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(
- allOf = {
- BLUETOOTH_CONNECT,
- BLUETOOTH_PRIVILEGED,
- })
- public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
+ allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+ conditional = true)
+ public @Nullable BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
final IBluetoothA2dp service = getService();
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index fad88d4b5e..08f880e04a 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -4203,6 +4203,46 @@ public final class BluetoothAdapter {
return null;
}
+ /**
+ * Return a binder to BluetoothAdvertise
+ *
+ * @hide
+ */
+ @RequiresNoPermission
+ public @Nullable IBluetoothAdvertise getBluetoothAdvertise() {
+ mServiceLock.readLock().lock();
+ try {
+ if (mService != null) {
+ return IBluetoothAdvertise.Stub.asInterface(mService.getBluetoothAdvertise());
+ }
+ } catch (RemoteException e) {
+ logRemoteException(TAG, e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return null;
+ }
+
+ /**
+ * Return a binder to DistanceMeasurement
+ *
+ * @hide
+ */
+ @RequiresNoPermission
+ public @Nullable IDistanceMeasurement getDistanceMeasurement() {
+ mServiceLock.readLock().lock();
+ try {
+ if (mService != null) {
+ return IDistanceMeasurement.Stub.asInterface(mService.getDistanceMeasurement());
+ }
+ } catch (RemoteException e) {
+ logRemoteException(TAG, e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return null;
+ }
+
/** Return a binder to a Profile service */
private @Nullable IBinder getProfile(int profile) {
mServiceLock.readLock().lock();
@@ -5830,8 +5870,8 @@ public final class BluetoothAdapter {
* Register an {@link BluetoothHciVendorCallback} to listen for HCI vendor responses and events
*
* @param eventCodeSet Set of vendor-specific event codes to listen for updates. Each
- * vendor-specific event code must be in the range 0x00 to 0x4f or 0x60 to 0xff.
- * The inclusive range 0x50-0x5f is reserved by the system.
+ * vendor-specific event code must be in the range 0x00 to 0x4f or 0x60 to 0xff. The
+ * inclusive range 0x52-0x5f is reserved by the system.
* @param executor an {@link Executor} to execute given callback
* @param callback user implementation of the {@link BluetoothHciVendorCallback}
* @throws IllegalArgumentException if the callback is already registered, or event codes not in
@@ -5851,7 +5891,7 @@ public final class BluetoothAdapter {
requireNonNull(executor);
requireNonNull(callback);
if (eventCodeSet.stream()
- .anyMatch((n) -> (n < 0) || (n >= 0x50 && n < 0x60) || (n > 0xff))) {
+ .anyMatch((n) -> (n < 0) || (n >= 0x52 && n < 0x60) || (n > 0xff))) {
throw new IllegalArgumentException("Event code not in valid range");
}
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index 58dd3ba1da..439c9349cb 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -1726,7 +1726,9 @@ public final class BluetoothDevice implements Parcelable, Attributable {
}
/**
- * Returns the identity address and identity address type of this BluetoothDevice.
+ * Returns the identity address and identity address type of this BluetoothDevice. An identity
+ * address is a public or static random Bluetooth LE device address that serves as a
+ * unique identifier.
*
* @return a {@link BluetoothAddress} containing identity address and identity address type. If
* Bluetooth is not enabled or identity address type is not available, it will return a
diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java
index e67d823c7e..a69c8ba792 100644
--- a/framework/java/android/bluetooth/BluetoothGatt.java
+++ b/framework/java/android/bluetooth/BluetoothGatt.java
@@ -37,6 +37,8 @@ import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
+import com.android.bluetooth.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -243,6 +245,9 @@ public final class BluetoothGatt implements BluetoothProfile {
+ " unregistering");
}
unregisterApp();
+ if (Flags.unregisterGattClientDisconnected()) {
+ mCallback = null;
+ }
return;
}
if (VDBG) {
@@ -274,7 +279,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
// autoConnect is inverse of "isDirect"
mService.clientConnect(
- mClientIf,
+ clientIf,
mDevice.getAddress(),
mDevice.getAddressType(),
!mAutoConnect,
@@ -361,6 +366,8 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(BLUETOOTH_CONNECT)
public void onClientConnectionState(
int status, int clientIf, boolean connected, String address) {
if (DBG) {
@@ -381,6 +388,10 @@ public final class BluetoothGatt implements BluetoothProfile {
? BluetoothProfile.STATE_CONNECTED
: BluetoothProfile.STATE_DISCONNECTED;
+ if (Flags.unregisterGattClientDisconnected() && !connected && !mAutoConnect) {
+ unregisterApp();
+ }
+
runOrQueueCallback(
new Runnable() {
@Override
@@ -433,6 +444,10 @@ public final class BluetoothGatt implements BluetoothProfile {
s.setDevice(mDevice);
}
+ if (Flags.fixBluetoothGattGettingDuplicateServices()) {
+ mServices.clear();
+ }
+
mServices.addAll(services);
// Fix references to included services, as they doesn't point to right objects.
@@ -493,16 +508,18 @@ public final class BluetoothGatt implements BluetoothProfile {
mDeviceBusy = false;
}
+ int clientIf = mClientIf;
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)
+ && (clientIf > 0)) {
try {
final int authReq =
(mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM
: AUTHENTICATION_MITM;
mService.readCharacteristic(
- mClientIf, address, handle, authReq, mAttributionSource);
+ clientIf, address, handle, authReq, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -573,10 +590,13 @@ public final class BluetoothGatt implements BluetoothProfile {
? AUTHENTICATION_NO_MITM
: AUTHENTICATION_MITM;
int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
- for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+ int clientIf = mClientIf;
+ for (int i = 0;
+ (i < WRITE_CHARACTERISTIC_MAX_RETRIES) && (clientIf > 0);
+ i++) {
requestStatus =
mService.writeCharacteristic(
- mClientIf,
+ clientIf,
address,
handle,
characteristic.getWriteType(),
@@ -679,16 +699,18 @@ public final class BluetoothGatt implements BluetoothProfile {
BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
if (descriptor == null) return;
+ int clientIf = mClientIf;
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)
+ && (clientIf > 0)) {
try {
final int authReq =
(mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM
: AUTHENTICATION_MITM;
mService.readDescriptor(
- mClientIf, address, handle, authReq, mAttributionSource);
+ clientIf, address, handle, authReq, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -741,16 +763,18 @@ public final class BluetoothGatt implements BluetoothProfile {
BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
if (descriptor == null) return;
+ int clientIf = mClientIf;
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
- && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
+ && (mAuthRetryState != AUTH_RETRY_STATE_MITM)
+ && (clientIf > 0)) {
try {
final int authReq =
(mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM
: AUTHENTICATION_MITM;
mService.writeDescriptor(
- mClientIf, address, handle, authReq, value, mAttributionSource);
+ clientIf, address, handle, authReq, value, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -1033,6 +1057,10 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) Log.d(TAG, "close()");
unregisterApp();
+ if (Flags.unregisterGattClientDisconnected()) {
+ mCallback = null;
+ }
+
mConnState = CONN_STATE_CLOSED;
mAuthRetryState = AUTH_RETRY_STATE_IDLE;
}
@@ -1166,7 +1194,9 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
try {
- mCallback = null;
+ if (!Flags.unregisterGattClientDisconnected()) {
+ mCallback = null;
+ }
mService.unregisterClient(mClientIf, mAttributionSource);
mClientIf = 0;
} catch (RemoteException e) {
@@ -1229,10 +1259,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public void disconnect() {
if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return;
try {
- mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.clientDisconnect(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1250,6 +1281,40 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean connect() {
+ int clientIf = mClientIf;
+ if (mService == null) return false;
+ if (clientIf == 0) {
+ if (!Flags.unregisterGattClientDisconnected()) {
+ return false;
+ }
+ synchronized (mStateLock) {
+ if (mConnState != CONN_STATE_IDLE) {
+ return false;
+ }
+ mConnState = CONN_STATE_CONNECTING;
+ }
+
+ UUID uuid = UUID.randomUUID();
+ if (DBG) Log.d(TAG, "reconnect from connect(), UUID=" + uuid);
+
+ try {
+ mService.registerClient(
+ new ParcelUuid(uuid),
+ mBluetoothGattCallback,
+ /* eatt_support= */ false,
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ synchronized (mStateLock) {
+ mConnState = CONN_STATE_IDLE;
+ }
+ Log.e(TAG, "Failed to register callback");
+ return false;
+ }
+
+ return true;
+ }
+
try {
if (DBG) {
Log.d(TAG, "connect(void) - device: " + mDevice + ", auto=" + mAutoConnect);
@@ -1257,7 +1322,7 @@ public final class BluetoothGatt implements BluetoothProfile {
// autoConnect is inverse of "isDirect"
mService.clientConnect(
- mClientIf,
+ clientIf,
mDevice.getAddress(),
mDevice.getAddressType(),
!mAutoConnect,
@@ -1293,9 +1358,12 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(BLUETOOTH_CONNECT)
public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return;
+
try {
mService.clientSetPreferredPhy(
- mClientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource);
+ clientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1308,8 +1376,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(BLUETOOTH_CONNECT)
public void readPhy() {
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return;
+
try {
- mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.clientReadPhy(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1340,12 +1411,16 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean discoverServices() {
if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
- mServices.clear();
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
+
+ if (!Flags.fixBluetoothGattGettingDuplicateServices()) {
+ mServices.clear();
+ }
try {
- mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.discoverServices(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1367,13 +1442,16 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean discoverServiceByUuid(UUID uuid) {
if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
- mServices.clear();
+ if (!Flags.fixBluetoothGattGettingDuplicateServices()) {
+ mServices.clear();
+ }
try {
mService.discoverServiceByUuid(
- mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
+ clientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1447,7 +1525,8 @@ public final class BluetoothGatt implements BluetoothProfile {
}
if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
BluetoothGattService service = characteristic.getService();
if (service == null) return false;
@@ -1462,7 +1541,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readCharacteristic(
- mClientIf,
+ clientIf,
device.getAddress(),
characteristic.getInstanceId(),
AUTHENTICATION_NONE,
@@ -1494,7 +1573,8 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
@@ -1503,7 +1583,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readUsingCharacteristicUuid(
- mClientIf,
+ clientIf,
mDevice.getAddress(),
new ParcelUuid(uuid),
startHandle,
@@ -1601,7 +1681,8 @@ public final class BluetoothGatt implements BluetoothProfile {
== 0) {
return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
}
- if (mService == null || mClientIf == 0) {
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) {
return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
}
@@ -1627,7 +1708,7 @@ public final class BluetoothGatt implements BluetoothProfile {
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
requestStatus =
mService.writeCharacteristic(
- mClientIf,
+ clientIf,
device.getAddress(),
characteristic.getInstanceId(),
writeType,
@@ -1674,7 +1755,8 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
if (characteristic == null) return false;
@@ -1692,7 +1774,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.readDescriptor(
- mClientIf,
+ clientIf,
device.getAddress(),
descriptor.getInstanceId(),
AUTHENTICATION_NONE,
@@ -1755,7 +1837,8 @@ public final class BluetoothGatt implements BluetoothProfile {
throw new IllegalArgumentException("value must not be null");
}
if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
- if (mService == null || mClientIf == 0) {
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) {
return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
}
@@ -1781,7 +1864,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
return mService.writeDescriptor(
- mClientIf,
+ clientIf,
device.getAddress(),
descriptor.getInstanceId(),
AUTHENTICATION_NONE,
@@ -1818,10 +1901,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean beginReliableWrite() {
if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
- mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.beginReliableWrite(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1846,7 +1930,8 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean executeReliableWrite() {
if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
synchronized (mDeviceBusyLock) {
if (mDeviceBusy) return false;
@@ -1854,7 +1939,7 @@ public final class BluetoothGatt implements BluetoothProfile {
}
try {
- mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
+ mService.endReliableWrite(clientIf, mDevice.getAddress(), true, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
@@ -1877,10 +1962,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public void abortReliableWrite() {
if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return;
try {
- mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource);
+ mService.endReliableWrite(clientIf, mDevice.getAddress(), false, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1921,7 +2007,8 @@ public final class BluetoothGatt implements BluetoothProfile {
+ " enable: "
+ enable);
}
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
BluetoothGattService service = characteristic.getService();
if (service == null) return false;
@@ -1931,7 +2018,7 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
mService.registerForNotification(
- mClientIf,
+ clientIf,
device.getAddress(),
characteristic.getInstanceId(),
enable,
@@ -1954,10 +2041,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean refresh() {
if (DBG) Log.d(TAG, "refresh() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
- mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.refreshDevice(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1979,10 +2067,11 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(BLUETOOTH_CONNECT)
public boolean readRemoteRssi() {
if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
- mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource);
+ mService.readRemoteRssi(clientIf, mDevice.getAddress(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -2014,10 +2103,11 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) {
Log.d(TAG, "configureMTU() - device: " + mDevice + " mtu: " + mtu);
}
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
- mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource);
+ mService.configureMTU(clientIf, mDevice.getAddress(), mtu, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -2047,11 +2137,12 @@ public final class BluetoothGatt implements BluetoothProfile {
}
if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
mService.connectionParameterUpdate(
- mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
+ clientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -2098,11 +2189,12 @@ public final class BluetoothGatt implements BluetoothProfile {
+ ", max_ce="
+ maxConnectionEventLen);
}
- if (mService == null || mClientIf == 0) return false;
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) return false;
try {
mService.leConnectionUpdate(
- mClientIf,
+ clientIf,
mDevice.getAddress(),
minConnectionInterval,
maxConnectionInterval,
@@ -2148,12 +2240,13 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) {
Log.d(TAG, "requestsubrateMode(" + subrateMode + ")");
}
- if (mService == null || mClientIf == 0) {
+ int clientIf = mClientIf;
+ if (mService == null || clientIf == 0) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
try {
- return mService.subrateModeRequest(mClientIf, mDevice, subrateMode, mAttributionSource);
+ return mService.subrateModeRequest(clientIf, mDevice, subrateMode, mAttributionSource);
} catch (RemoteException e) {
logRemoteException(TAG, e);
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
diff --git a/framework/java/android/bluetooth/BluetoothHapClient.java b/framework/java/android/bluetooth/BluetoothHapClient.java
index 910faa7faa..c11249bc9e 100644
--- a/framework/java/android/bluetooth/BluetoothHapClient.java
+++ b/framework/java/android/bluetooth/BluetoothHapClient.java
@@ -24,7 +24,6 @@ import static android.bluetooth.BluetoothUtils.callServiceIfEnabled;
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -41,8 +40,6 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
-import com.android.bluetooth.flags.Flags;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
@@ -336,7 +333,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
public static final int PRESET_INDEX_UNAVAILABLE = IBluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
/**
@@ -691,7 +687,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public int getHapGroup(@NonNull BluetoothDevice device) {
@@ -715,7 +710,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public int getActivePresetIndex(@NonNull BluetoothDevice device) {
@@ -815,7 +809,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void switchToNextPreset(@NonNull BluetoothDevice device) {
@@ -840,7 +833,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void switchToNextPresetForGroup(int groupId) {
@@ -860,7 +852,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void switchToPreviousPreset(@NonNull BluetoothDevice device) {
@@ -887,7 +878,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public void switchToPreviousPresetForGroup(int groupId) {
@@ -906,7 +896,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_SETTINGS_CAN_CONTROL_HAP_PRESET)
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
@Nullable
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index d33542e6d6..cde23baf4d 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -978,6 +978,8 @@ public final class BluetoothSocket implements Closeable {
if (mL2capBuffer.remaining() == 0) {
if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
if (fillL2capRxBuffer() == -1) {
+ Log.d(TAG, "socket EOF, returning -1");
+ mSocketState = SocketState.CLOSED;
return -1;
}
}
@@ -994,7 +996,8 @@ public final class BluetoothSocket implements Closeable {
ret = mSocketIS.read(b, offset, length);
}
if (ret < 0) {
- return -1;
+ mSocketState = SocketState.CLOSED;
+ throw new IOException("bt socket closed, read return: " + ret);
}
if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
return ret;
diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java
index 8632fcf671..5b3299b120 100644
--- a/framework/java/android/bluetooth/le/AdvertisingSet.java
+++ b/framework/java/android/bluetooth/le/AdvertisingSet.java
@@ -25,7 +25,7 @@ import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothAdvertise;
import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.content.AttributionSource;
@@ -43,18 +43,18 @@ import android.util.Log;
public final class AdvertisingSet {
private static final String TAG = "AdvertisingSet";
- private final IBluetoothGatt mGatt;
+ private final IBluetoothAdvertise mAdvertise;
private int mAdvertiserId;
private AttributionSource mAttributionSource;
AdvertisingSet(
- IBluetoothGatt gatt,
+ IBluetoothAdvertise advertise,
int advertiserId,
BluetoothAdapter bluetoothAdapter,
AttributionSource attributionSource) {
mAdvertiserId = advertiserId;
mAttributionSource = attributionSource;
- mGatt = requireNonNull(gatt, "Bluetooth gatt cannot be null");
+ mAdvertise = requireNonNull(advertise, "Bluetooth advertise cannot be null");
}
/* package */ void setAdvertiserId(int advertiserId) {
@@ -77,7 +77,7 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void enableAdvertising(boolean enable, int duration, int maxExtendedAdvertisingEvents) {
try {
- mGatt.enableAdvertisingSet(
+ mAdvertise.enableAdvertisingSet(
mAdvertiserId,
enable,
duration,
@@ -105,7 +105,7 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void setAdvertisingData(AdvertiseData advertiseData) {
try {
- mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource);
+ mAdvertise.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -125,7 +125,7 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void setScanResponseData(AdvertiseData scanResponse) {
try {
- mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource);
+ mAdvertise.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -149,7 +149,7 @@ public final class AdvertisingSet {
conditional = true)
public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
try {
- mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
+ mAdvertise.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -165,7 +165,8 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
try {
- mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource);
+ mAdvertise.setPeriodicAdvertisingParameters(
+ mAdvertiserId, parameters, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -186,7 +187,7 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
try {
- mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource);
+ mAdvertise.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -203,7 +204,7 @@ public final class AdvertisingSet {
@RequiresPermission(BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingEnabled(boolean enable) {
try {
- mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource);
+ mAdvertise.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -223,7 +224,7 @@ public final class AdvertisingSet {
})
public void getOwnAddress() {
try {
- mGatt.getOwnAddress(mAdvertiserId, mAttributionSource);
+ mAdvertise.getOwnAddress(mAdvertiserId, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 057053e817..e43838f8be 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -29,7 +29,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothUuid;
-import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothAdvertise;
import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.content.AttributionSource;
@@ -610,10 +610,10 @@ public final class BluetoothLeAdvertiser {
throw new IllegalArgumentException("duration out of range: " + duration);
}
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
+ IBluetoothAdvertise advertise = mBluetoothAdapter.getBluetoothAdvertise();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ if (advertise == null) {
+ Log.e(TAG, "Bluetooth Advertise is null");
postStartSetFailure(
handler, callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
return;
@@ -626,7 +626,7 @@ public final class BluetoothLeAdvertiser {
}
try {
- gatt.startAdvertisingSet(
+ advertise.startAdvertisingSet(
parameters,
advertiseData,
scanResponse,
@@ -665,13 +665,13 @@ public final class BluetoothLeAdvertiser {
return;
}
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IBluetoothAdvertise advertise = mBluetoothAdapter.getBluetoothAdvertise();
+ if (advertise == null) {
+ Log.e(TAG, "Bluetooth Advertise is null");
return;
}
try {
- gatt.stopAdvertisingSet(wrapped, mAttributionSource);
+ advertise.stopAdvertisingSet(wrapped, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop advertising - ", e);
}
@@ -789,7 +789,7 @@ public final class BluetoothLeAdvertiser {
return new IAdvertisingSetCallback.Stub() {
@Override
public void onAdvertisingSetStarted(
- IBinder gattBinder, int advertiserId, int txPower, int status) {
+ IBinder advertiseBinder, int advertiserId, int txPower, int status) {
handler.post(
() -> {
if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
@@ -800,7 +800,7 @@ public final class BluetoothLeAdvertiser {
AdvertisingSet advertisingSet =
new AdvertisingSet(
- IBluetoothGatt.Stub.asInterface(gattBinder),
+ IBluetoothAdvertise.Stub.asInterface(advertiseBinder),
advertiserId,
mBluetoothAdapter,
mAttributionSource);
diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
index 1d6087b19e..c116540496 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -348,7 +348,7 @@ public final class BluetoothLeScanner {
/**
* Start truncated scan.
*
- * @deprecated this is not used anywhere
+ * @removed this is not used anywhere
* @hide
*/
@Deprecated
diff --git a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
index 1760bc0bf1..b68d3556e4 100644
--- a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
+++ b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java
@@ -27,7 +27,7 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IDistanceMeasurement;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.le.ChannelSoundingParams.CsSecurityLevel;
import android.content.AttributionSource;
@@ -94,12 +94,12 @@ public final class DistanceMeasurementManager {
public @NonNull List<DistanceMeasurementMethod> getSupportedMethods() {
final List<DistanceMeasurementMethod> supportedMethods = new ArrayList<>();
try {
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IDistanceMeasurement distanceMeasurement = mBluetoothAdapter.getDistanceMeasurement();
+ if (distanceMeasurement == null) {
+ Log.e(TAG, "Distance Measurement is null");
return supportedMethods;
}
- return gatt.getSupportedDistanceMeasurementMethods(mAttributionSource);
+ return distanceMeasurement.getSupportedDistanceMeasurementMethods(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get supported methods - ", e);
}
@@ -137,14 +137,19 @@ public final class DistanceMeasurementManager {
Objects.requireNonNull(executor, "executor is null");
Objects.requireNonNull(callback, "callback is null");
try {
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IDistanceMeasurement distanceMeasurement = mBluetoothAdapter.getDistanceMeasurement();
+ if (distanceMeasurement == null) {
+ Log.e(TAG, "Distance Measurement is null");
return null;
}
DistanceMeasurementSession session =
new DistanceMeasurementSession(
- gatt, mUuid, params, executor, mAttributionSource, callback);
+ distanceMeasurement,
+ mUuid,
+ params,
+ executor,
+ mAttributionSource,
+ callback);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(() -> session.stopSession());
@@ -154,7 +159,8 @@ public final class DistanceMeasurementManager {
}
mSessionMap.put(params.getDevice(), session);
- gatt.startDistanceMeasurement(mUuid, params, mCallbackWrapper, mAttributionSource);
+ distanceMeasurement.startDistanceMeasurement(
+ mUuid, params, mCallbackWrapper, mAttributionSource);
return cancellationSignal;
} catch (RemoteException e) {
Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
@@ -185,12 +191,12 @@ public final class DistanceMeasurementManager {
Objects.requireNonNull(remoteDevice, "remote device is null");
final int defaultValue = ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
try {
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IDistanceMeasurement distanceMeasurement = mBluetoothAdapter.getDistanceMeasurement();
+ if (distanceMeasurement == null) {
+ Log.e(TAG, "Distance Measurement is null");
return defaultValue;
}
- return gatt.getChannelSoundingMaxSupportedSecurityLevel(
+ return distanceMeasurement.getChannelSoundingMaxSupportedSecurityLevel(
remoteDevice, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get supported security Level - ", e);
@@ -217,12 +223,13 @@ public final class DistanceMeasurementManager {
public @CsSecurityLevel int getLocalChannelSoundingMaxSupportedSecurityLevel() {
final int defaultValue = ChannelSoundingParams.CS_SECURITY_LEVEL_UNKNOWN;
try {
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IDistanceMeasurement distanceMeasurement = mBluetoothAdapter.getDistanceMeasurement();
+ if (distanceMeasurement == null) {
+ Log.e(TAG, "Distance Measurement is null");
return defaultValue;
}
- return gatt.getLocalChannelSoundingMaxSupportedSecurityLevel(mAttributionSource);
+ return distanceMeasurement.getLocalChannelSoundingMaxSupportedSecurityLevel(
+ mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get supported security Level - ", e);
}
@@ -247,12 +254,14 @@ public final class DistanceMeasurementManager {
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @NonNull Set<@CsSecurityLevel Integer> getChannelSoundingSupportedSecurityLevels() {
try {
- IBluetoothGatt gatt = mBluetoothAdapter.getBluetoothGatt();
- if (gatt == null) {
- Log.e(TAG, "Bluetooth GATT is null");
+ IDistanceMeasurement distanceMeasurement = mBluetoothAdapter.getDistanceMeasurement();
+ if (distanceMeasurement == null) {
+ Log.e(TAG, "Distance Measurement is null");
return Collections.emptySet();
}
- return Arrays.stream(gatt.getChannelSoundingSupportedSecurityLevels(mAttributionSource))
+ return Arrays.stream(
+ distanceMeasurement.getChannelSoundingSupportedSecurityLevels(
+ mAttributionSource))
.boxed()
.collect(Collectors.toUnmodifiableSet());
} catch (RemoteException e) {
diff --git a/framework/java/android/bluetooth/le/DistanceMeasurementSession.java b/framework/java/android/bluetooth/le/DistanceMeasurementSession.java
index a36887ba9b..d0c9547967 100644
--- a/framework/java/android/bluetooth/le/DistanceMeasurementSession.java
+++ b/framework/java/android/bluetooth/le/DistanceMeasurementSession.java
@@ -28,7 +28,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothStatusCodes;
-import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IDistanceMeasurement;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.os.ParcelUuid;
@@ -60,7 +60,7 @@ import java.util.concurrent.Executor;
public final class DistanceMeasurementSession {
private static final String TAG = "DistanceMeasurementSession";
- private final IBluetoothGatt mGatt;
+ private final IDistanceMeasurement mDistanceMeasurement;
private final ParcelUuid mUuid;
private final DistanceMeasurementParams mDistanceMeasurementParams;
private final Executor mExecutor;
@@ -79,13 +79,13 @@ public final class DistanceMeasurementSession {
/** @hide */
public DistanceMeasurementSession(
- IBluetoothGatt gatt,
+ IDistanceMeasurement distanceMeasurement,
ParcelUuid uuid,
DistanceMeasurementParams params,
Executor executor,
AttributionSource attributionSource,
Callback callback) {
- mGatt = requireNonNull(gatt);
+ mDistanceMeasurement = requireNonNull(distanceMeasurement);
mDistanceMeasurementParams = requireNonNull(params);
mExecutor = requireNonNull(executor);
mCallback = requireNonNull(callback);
@@ -104,7 +104,7 @@ public final class DistanceMeasurementSession {
@RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
public @StopSessionReturnValues int stopSession() {
try {
- return mGatt.stopDistanceMeasurement(
+ return mDistanceMeasurement.stopDistanceMeasurement(
mUuid,
mDistanceMeasurementParams.getDevice(),
mDistanceMeasurementParams.getMethodId(),
diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java
index 57bd7445c7..d5b3b46e4e 100644
--- a/framework/java/android/bluetooth/le/ScanRecord.java
+++ b/framework/java/android/bluetooth/le/ScanRecord.java
@@ -28,8 +28,6 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
-import com.android.bluetooth.flags.Flags;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@@ -644,18 +642,14 @@ public final class ScanRecord {
+ (scanRecord[currentPos] & 0xFF);
byte[] manufacturerDataBytes =
extractBytes(scanRecord, currentPos + 2, dataLength - 2);
- if (Flags.scanRecordManufacturerDataMerge()) {
- if (manufacturerData.contains(manufacturerId)) {
- byte[] firstValue = manufacturerData.get(manufacturerId);
- ByteBuffer buffer =
- ByteBuffer.allocate(
- firstValue.length + manufacturerDataBytes.length);
- buffer.put(firstValue);
- buffer.put(manufacturerDataBytes);
- manufacturerData.put(manufacturerId, buffer.array());
- } else {
- manufacturerData.put(manufacturerId, manufacturerDataBytes);
- }
+ if (manufacturerData.contains(manufacturerId)) {
+ byte[] firstValue = manufacturerData.get(manufacturerId);
+ ByteBuffer buffer =
+ ByteBuffer.allocate(
+ firstValue.length + manufacturerDataBytes.length);
+ buffer.put(firstValue);
+ buffer.put(manufacturerDataBytes);
+ manufacturerData.put(manufacturerId, buffer.array());
} else {
manufacturerData.put(manufacturerId, manufacturerDataBytes);
}
diff --git a/framework/tests/bumble/AndroidPhyTest.xml b/framework/tests/bumble/AndroidPhyTest.xml
index 4e5eb02e5f..b762ec7480 100644
--- a/framework/tests/bumble/AndroidPhyTest.xml
+++ b/framework/tests/bumble/AndroidPhyTest.xml
@@ -47,8 +47,8 @@
<!-- Only run if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
<!-- Collect Bluetooth snoop logs for each test run -->
diff --git a/framework/tests/bumble/AndroidTest.xml b/framework/tests/bumble/AndroidTest.xml
index 4855fec134..04aedce061 100644
--- a/framework/tests/bumble/AndroidTest.xml
+++ b/framework/tests/bumble/AndroidTest.xml
@@ -48,8 +48,8 @@
<!-- Only run if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
<!-- Collect Bluetooth snoop logs for each test run -->
diff --git a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
index e658e8c645..ea41c55ee0 100644
--- a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
+++ b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
@@ -226,6 +226,51 @@ public class DckL2capTest() : Closeable {
Log.d(TAG, "testReceive: done")
}
+ @Test
+ @VirtualOnly
+ fun testReadReturnOnRemoteSocketDisconnect() {
+ Log.d(TAG, "testReadReturnonSocketDisconnect: Connect L2CAP")
+ var bluetoothSocket: BluetoothSocket?
+ val l2capServer = bluetoothAdapter.listenUsingInsecureL2capChannel()
+ val socketFlow = flow { emit(l2capServer.accept()) }
+ val connectResponse = createAndConnectL2capChannelWithBumble(l2capServer.psm)
+ runBlocking {
+ bluetoothSocket = socketFlow.first()
+ assertThat(connectResponse.hasChannel()).isTrue()
+ }
+
+ val inputStream = bluetoothSocket!!.inputStream
+
+ // block on read() on server thread
+ val readThread = Thread {
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: Receive data on Android")
+ val ret = inputStream.read()
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: read returns : " + ret)
+ Log.d(
+ TAG,
+ "testReadReturnOnRemoteSocketDisconnect: isConnected() : " +
+ bluetoothSocket!!.isConnected(),
+ )
+ assertThat(ret).isEqualTo(-1)
+ assertThat(bluetoothSocket!!.isConnected()).isFalse()
+ }
+ readThread.start()
+ // check that socket is still connected
+ assertThat(bluetoothSocket!!.isConnected()).isTrue()
+
+ // read() would be blocking till underlying l2cap is disconnected
+ Thread.sleep(1000 * 10)
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: disconnect after 10 secs")
+ val disconnectRequest =
+ DisconnectRequest.newBuilder().setChannel(connectResponse.channel).build()
+ val disconnectResponse = mBumble.l2capBlocking().disconnect(disconnectRequest)
+ assertThat(disconnectResponse.hasSuccess()).isTrue()
+ inputStream.close()
+ bluetoothSocket?.close()
+ l2capServer.close()
+ Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: done")
+ }
+
private fun createAndConnectL2capChannelWithBumble(psm: Int): ConnectResponse {
Log.d(TAG, "createAndConnectL2capChannelWithBumble")
val remoteDevice =
diff --git a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java
index c9fa50bbd9..64ada411d6 100644
--- a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java
@@ -720,6 +720,44 @@ public class GattClientTest {
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_UNREGISTER_GATT_CLIENT_DISCONNECTED)
+ public void connectAndDisconnectManyClientsWithoutClose() throws Exception {
+ advertiseWithBumble();
+
+ List<BluetoothGatt> gatts = new ArrayList<>();
+ try {
+ for (int i = 0; i < 100; i++) {
+ BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class);
+ InOrder inOrder = inOrder(gattCallback);
+
+ BluetoothGatt gatt = mRemoteLeDevice.connectGatt(mContext, false, gattCallback);
+ gatts.add(gatt);
+
+ inOrder.verify(gattCallback, timeout(1000))
+ .onConnectionStateChange(any(), anyInt(), eq(STATE_CONNECTED));
+
+ gatt.disconnect();
+ inOrder.verify(gattCallback, timeout(1000))
+ .onConnectionStateChange(
+ any(), anyInt(), eq(BluetoothProfile.STATE_DISCONNECTED));
+
+ gatt.connect();
+ inOrder.verify(gattCallback, timeout(1000))
+ .onConnectionStateChange(any(), anyInt(), eq(STATE_CONNECTED));
+
+ gatt.disconnect();
+ inOrder.verify(gattCallback, timeout(1000))
+ .onConnectionStateChange(
+ any(), anyInt(), eq(BluetoothProfile.STATE_DISCONNECTED));
+ }
+ } finally {
+ for (BluetoothGatt gatt : gatts) {
+ gatt.close();
+ }
+ }
+ }
+
private void createLeBondAndWaitBonding(BluetoothDevice device) {
advertiseWithBumble();
mHost.createBondAndVerify(device);
diff --git a/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
index 506586bbe3..9ee8c279f1 100644
--- a/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
@@ -68,7 +68,7 @@ import java.util.stream.Stream;
@RunWith(TestParameterInjector.class)
public class LeScanningTest {
private static final String TAG = "LeScanningTest";
- private static final int TIMEOUT_SCANNING_MS = 2000;
+ private static final int TIMEOUT_SCANNING_MS = 3000;
private static final String TEST_UUID_STRING = "00001805-0000-1000-8000-00805f9b34fb";
private static final String TEST_ADDRESS_RANDOM_STATIC = "F0:43:A8:23:10:11";
private static final String ACTION_DYNAMIC_RECEIVER_SCAN_RESULT =
@@ -376,6 +376,7 @@ public class LeScanningTest {
// Test against UUIDs that are close to TEST_UUID_STRING, one that has a few bits unset and one
// that has an extra bit set.
@Test
+ @VirtualOnly
public void startBleScan_withServiceData_uuidDoesntMatch(
@TestParameter({"00001800", "00001815"}) String uuid) {
advertiseWithBumbleWithServiceData();
diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java
index 44c6564803..d289b7c43e 100644
--- a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java
@@ -22,12 +22,8 @@ import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
@@ -43,10 +39,8 @@ import android.bluetooth.StreamObserverSpliterator;
import android.bluetooth.Utils;
import android.bluetooth.test_utils.BlockingBluetoothAdapter;
import android.bluetooth.test_utils.EnableBluetoothRule;
-import android.content.BroadcastReceiver;
+import android.bluetooth.pairing.utils.IntentReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -63,19 +57,15 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import io.grpc.stub.StreamObserver;
-import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
-import org.hamcrest.core.AllOf;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.hamcrest.MockitoHamcrest;
import pandora.GattProto;
import pandora.HostProto.AdvertiseRequest;
@@ -90,9 +80,6 @@ import pandora.SecurityProto.SecureRequest;
import pandora.SecurityProto.SecureResponse;
import java.time.Duration;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -127,12 +114,9 @@ public class PairingTest {
public final EnableBluetoothRule mEnableBluetoothRule =
new EnableBluetoothRule(false /* enableTestMode */, true /* toggleBluetooth */);
- private final Map<String, Integer> mActionRegistrationCounts = new HashMap<>();
private final StreamObserverSpliterator<PairingEvent> mPairingEventStreamObserver =
new StreamObserverSpliterator<>();
- @Mock private BroadcastReceiver mReceiver;
@Mock private BluetoothProfile.ServiceListener mProfileServiceListener;
- private InOrder mInOrder = null;
private BluetoothDevice mBumbleDevice;
private BluetoothDevice mRemoteLeDevice;
private BluetoothHidHost mHidService;
@@ -142,30 +126,6 @@ public class PairingTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doAnswer(
- inv -> {
- Log.d(
- TAG,
- "onReceive(): intent=" + Arrays.toString(inv.getArguments()));
- Intent intent = inv.getArgument(1);
- String action = intent.getAction();
- if (BluetoothDevice.ACTION_UUID.equals(action)) {
- ParcelUuid[] uuids =
- intent.getParcelableArrayExtra(
- BluetoothDevice.EXTRA_UUID, ParcelUuid.class);
- Log.d(TAG, "onReceive(): UUID=" + Arrays.toString(uuids));
- } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
- int bondState =
- intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
- Log.d(TAG, "onReceive(): bondState=" + bondState);
- }
- return null;
- })
- .when(mReceiver)
- .onReceive(any(), any());
-
- mInOrder = inOrder(mReceiver);
-
// Get profile proxies
mHidService = (BluetoothHidHost) getProfileProxy(BluetoothProfile.HID_HOST);
mHfpService = (BluetoothHeadset) getProfileProxy(BluetoothProfile.HEADSET);
@@ -175,28 +135,54 @@ public class PairingTest {
sAdapter.getRemoteLeDevice(
Utils.BUMBLE_RANDOM_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM);
+ /*
+ * Note: Since there was no IntentReceiver registered, passing the instance as
+ * NULL in testStep_RemoveBond(). But, if there is an instance already present, that
+ * must be passed instead of NULL.
+ */
for (BluetoothDevice device : sAdapter.getBondedDevices()) {
- removeBond(device);
+ testStep_RemoveBond(null, device);
}
}
@After
public void tearDown() throws Exception {
Set<BluetoothDevice> bondedDevices = sAdapter.getBondedDevices();
+
+ /*
+ * Note: Since there was no IntentReceiver registered, passing the instance as
+ * NULL in testStep_RemoveBond(). But, if there is an instance already present, that
+ * must be passed instead of NULL.
+ */
if (bondedDevices.contains(mBumbleDevice)) {
- removeBond(mBumbleDevice);
+ testStep_RemoveBond(null, mBumbleDevice);
}
if (bondedDevices.contains(mRemoteLeDevice)) {
- removeBond(mRemoteLeDevice);
+ testStep_RemoveBond(null, mRemoteLeDevice);
}
mBumbleDevice = null;
mRemoteLeDevice = null;
- if (getTotalActionRegistrationCounts() > 0) {
- sTargetContext.unregisterReceiver(mReceiver);
- mActionRegistrationCounts.clear();
- }
}
+ /** All the test function goes here */
+
+ /**
+ * Process of writing a test function
+ *
+ * 1. Create an IntentReceiver object first with following way:
+ * IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ * BluetoothDevice.ACTION_1,
+ * BluetoothDevice.ACTION_2)
+ * .setIntentListener(--) // optional
+ * .setIntentTimeout(--) // optional
+ * .build();
+ * 2. Use the intentReceiver instance for all Intent related verification, and pass
+ * the same instance to all the helper/testStep functions which has similar Intent
+ * requirements.
+ * 3. Once all the verification is done, call `intentReceiver.close()` before returning
+ * from the function.
+ */
+
/**
* Test a simple BR/EDR just works pairing flow in the follow steps:
*
@@ -211,8 +197,10 @@ public class PairingTest {
*/
@Test
public void testBrEdrPairing_phoneInitiatedBrEdrInquiryOnlyJustWorks() {
- registerIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothDevice.ACTION_PAIRING_REQUEST);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED,
+ BluetoothDevice.ACTION_PAIRING_REQUEST)
+ .build();
StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
mBumble.security()
@@ -220,12 +208,12 @@ public class PairingTest {
.onPairing(mPairingEventStreamObserver);
assertThat(mBumbleDevice.createBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(
@@ -238,15 +226,12 @@ public class PairingTest {
pairingEventAnswerObserver.onNext(
PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build());
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
- verifyNoMoreInteractions(mReceiver);
-
- unregisterIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intentReceiver.close();
}
/**
@@ -266,8 +251,10 @@ public class PairingTest {
@Test
@RequiresFlagsEnabled({Flags.FLAG_IGNORE_UNRELATED_CANCEL_BOND})
public void testBrEdrPairing_cancelBond_forUnrelatedDevice() {
- registerIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothDevice.ACTION_PAIRING_REQUEST);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED,
+ BluetoothDevice.ACTION_PAIRING_REQUEST)
+ .build();
StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
mBumble.security()
@@ -275,12 +262,12 @@ public class PairingTest {
.onPairing(mPairingEventStreamObserver);
assertThat(mBumbleDevice.createBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(
@@ -296,15 +283,12 @@ public class PairingTest {
pairingEventAnswerObserver.onNext(
PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build());
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
- verifyNoMoreInteractions(mReceiver);
-
- unregisterIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intentReceiver.close();
}
/**
@@ -322,10 +306,11 @@ public class PairingTest {
*/
@Test
public void testBrEdrPairing_phoneInitiatedBrEdrInquiryOnlyJustWorksWhileSdpConnected() {
- registerIntentActions(
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
BluetoothDevice.ACTION_ACL_CONNECTED,
BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_PAIRING_REQUEST);
+ BluetoothDevice.ACTION_PAIRING_REQUEST)
+ .build();
StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
mBumble.security()
@@ -335,17 +320,17 @@ public class PairingTest {
// Start SDP. This will create an ACL connection before the bonding starts.
assertThat(mBumbleDevice.fetchUuidsWithSdp(BluetoothDevice.TRANSPORT_BREDR)).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
assertThat(mBumbleDevice.createBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(
@@ -358,17 +343,12 @@ public class PairingTest {
pairingEventAnswerObserver.onNext(
PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build());
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
- verifyNoMoreInteractions(mReceiver);
-
- unregisterIntentActions(
- BluetoothDevice.ACTION_ACL_CONNECTED,
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intentReceiver.close();
}
/**
@@ -398,11 +378,13 @@ public class PairingTest {
@Test
@RequiresFlagsEnabled({Flags.FLAG_PREVENT_DUPLICATE_UUID_INTENT})
public void testCancelBondLe_WithGattServiceDiscovery() {
- registerIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED)
+ .build();
// Outgoing GATT service discovery and incoming LE pairing in parallel
StreamObserverSpliterator<SecureResponse> responseObserver =
- helper_OutgoingGattServiceDiscoveryWithIncomingLePairing();
+ helper_OutgoingGattServiceDiscoveryWithIncomingLePairing(intentReceiver);
// Cancel pairing from Android
assertThat(mBumbleDevice.cancelBondProcess()).isTrue();
@@ -412,14 +394,12 @@ public class PairingTest {
// Pairing should be cancelled in a moment instead of timing out in 30
// seconds
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
- verifyNoMoreInteractions(mReceiver);
-
- unregisterIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ intentReceiver.close();
}
/**
@@ -449,11 +429,13 @@ public class PairingTest {
@Test
@RequiresFlagsEnabled({Flags.FLAG_PREVENT_DUPLICATE_UUID_INTENT})
public void testBondLe_WithGattServiceDiscovery() {
- registerIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED)
+ .build();
// Outgoing GATT service discovery and incoming LE pairing in parallel
StreamObserverSpliterator<SecureResponse> responseObserver =
- helper_OutgoingGattServiceDiscoveryWithIncomingLePairing();
+ helper_OutgoingGattServiceDiscoveryWithIncomingLePairing(intentReceiver);
// Approve pairing from Android
assertThat(mBumbleDevice.setPairingConfirmation(true)).isTrue();
@@ -462,14 +444,12 @@ public class PairingTest {
assertThat(secureResponse.hasSuccess()).isTrue();
// Ensure that pairing succeeds
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
- verifyNoMoreInteractions(mReceiver);
-
- unregisterIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ intentReceiver.close();
}
/**
@@ -495,9 +475,11 @@ public class PairingTest {
*/
@Test
public void testBondLe_Reconnect() {
- registerIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_ACL_CONNECTED)
+ .build();
- testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC);
+ testStep_BondLe(intentReceiver, mBumbleDevice, OwnAddressType.PUBLIC);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
testStep_restartBt();
@@ -521,12 +503,12 @@ public class PairingTest {
.build());
assertThat(mBumbleDevice.connect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED);
+
+ intentReceiver.close();
}
/**
@@ -559,98 +541,6 @@ public class PairingTest {
}
}
- private void doTestIdentityAddressWithType(
- BluetoothDevice device, OwnAddressType ownAddressType) {
- BluetoothAddress identityAddress = device.getIdentityAddressWithType();
- assertThat(identityAddress.getAddress()).isNull();
- assertThat(identityAddress.getAddressType())
- .isEqualTo(BluetoothDevice.ADDRESS_TYPE_UNKNOWN);
-
- testStep_BondLe(device, ownAddressType);
- assertThat(sAdapter.getBondedDevices()).contains(device);
-
- identityAddress = device.getIdentityAddressWithType();
- assertThat(identityAddress.getAddress()).isEqualTo(device.getAddress());
- assertThat(identityAddress.getAddressType())
- .isEqualTo(
- ownAddressType == OwnAddressType.RANDOM
- ? BluetoothDevice.ADDRESS_TYPE_RANDOM
- : BluetoothDevice.ADDRESS_TYPE_PUBLIC);
- }
-
- private void testStep_BondLe(BluetoothDevice device, OwnAddressType ownAddressType) {
- registerIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_ACL_CONNECTED,
- BluetoothDevice.ACTION_PAIRING_REQUEST);
-
- mBumble.gattBlocking()
- .registerService(
- GattProto.RegisterServiceRequest.newBuilder()
- .setService(
- GattProto.GattServiceParams.newBuilder()
- .setUuid(BATTERY_UUID.toString())
- .build())
- .build());
- mBumble.gattBlocking()
- .registerService(
- GattProto.RegisterServiceRequest.newBuilder()
- .setService(
- GattProto.GattServiceParams.newBuilder()
- .setUuid(HOGP_UUID.toString())
- .build())
- .build());
-
- mBumble.hostBlocking()
- .advertise(
- AdvertiseRequest.newBuilder()
- .setLegacy(true)
- .setConnectable(true)
- .setOwnAddressType(ownAddressType)
- .build());
-
- StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
- mBumble.security()
- .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
- .onPairing(mPairingEventStreamObserver);
-
- assertThat(device.createBond(BluetoothDevice.TRANSPORT_LE)).isTrue();
-
- verifyIntentReceivedUnordered(
- hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
- hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceived(
- hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
- hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
- hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE));
- verifyIntentReceivedUnordered(
- hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
- hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
- hasExtra(
- BluetoothDevice.EXTRA_PAIRING_VARIANT,
- BluetoothDevice.PAIRING_VARIANT_CONSENT));
-
- // Approve pairing from Android
- assertThat(device.setPairingConfirmation(true)).isTrue();
-
- PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next();
- assertThat(pairingEvent.hasJustWorks()).isTrue();
- pairingEventAnswerObserver.onNext(
- PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build());
-
- // Ensure that pairing succeeds
- verifyIntentReceived(
- hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
- hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
-
- unregisterIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_ACL_CONNECTED,
- BluetoothDevice.ACTION_PAIRING_REQUEST);
- }
-
/**
* Test if bonded BR/EDR device can reconnect after BT restart
*
@@ -674,9 +564,11 @@ public class PairingTest {
*/
@Test
public void testBondBredr_Reconnect() {
- registerIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_ACL_CONNECTED)
+ .build();
- testStep_BondBredr();
+ testStep_BondBredr(intentReceiver);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
testStep_restartBt();
@@ -689,12 +581,12 @@ public class PairingTest {
.build();
mBumble.hostBlocking().setConnectabilityMode(request);
assertThat(mBumbleDevice.connect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED);
+
+ intentReceiver.close();
}
/**
@@ -720,27 +612,27 @@ public class PairingTest {
@Test
@RequiresFlagsEnabled({Flags.FLAG_WAIT_FOR_DISCONNECT_BEFORE_UNBOND})
public void testRemoveBondLe_WhenConnected() {
- registerIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_ACL_DISCONNECTED,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED)
+ .build();
- testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC);
+ testStep_BondLe(intentReceiver, mBumbleDevice, OwnAddressType.PUBLIC);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
assertThat(mBumbleDevice.removeBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ intentReceiver.close();
}
/**
@@ -766,27 +658,27 @@ public class PairingTest {
@Test
@RequiresFlagsEnabled({Flags.FLAG_WAIT_FOR_DISCONNECT_BEFORE_UNBOND})
public void testRemoveBondBredr_WhenConnected() {
- registerIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ BluetoothDevice.ACTION_ACL_DISCONNECTED,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED)
+ .build();
- testStep_BondBredr();
+ testStep_BondBredr(intentReceiver);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
assertThat(mBumbleDevice.removeBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ intentReceiver.close();
}
/**
@@ -813,54 +705,51 @@ public class PairingTest {
*/
@Test
public void testRemoveBondLe_WhenDisconnected() {
- registerIntentActions(
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
BluetoothDevice.ACTION_ACL_DISCONNECTED,
BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
+ BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED)
+ .build();
- testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC);
+ testStep_BondLe(intentReceiver, mBumbleDevice, OwnAddressType.PUBLIC);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
// Wait for profiles to get connected
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_CONNECTING));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_CONNECTED));
// Disconnect Bumble
assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_DISCONNECTING));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_DISCONNECTED));
// Wait for ACL to get disconnected
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
// Remove bond
assertThat(mBumbleDevice.removeBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED,
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
+ intentReceiver.close();
}
/**
@@ -887,10 +776,11 @@ public class PairingTest {
*/
@Test
public void testRemoveBondBredr_WhenDisconnected() {
- registerIntentActions(
+ IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
BluetoothDevice.ACTION_ACL_DISCONNECTED,
BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)
+ .build();
// Disable all profiles other than A2DP as profile connections take too long
assertThat(
@@ -902,15 +792,15 @@ public class PairingTest {
mBumbleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
.isTrue();
- testStep_BondBredr();
+ testStep_BondBredr(intentReceiver);
assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);
// Wait for profiles to get connected
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_CONNECTING),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_CONNECTED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
@@ -921,58 +811,78 @@ public class PairingTest {
future.completeOnTimeout(null, TEST_DELAY_MS, TimeUnit.MILLISECONDS).join();
// Disconnect all profiles
assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTING),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
// Wait for the ACL to get disconnected
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
// Remove bond
assertThat(mBumbleDevice.removeBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);
- verifyNoMoreInteractions(mReceiver);
- unregisterIntentActions(
- BluetoothDevice.ACTION_ACL_DISCONNECTED,
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ intentReceiver.close();
}
- private void testStep_BondBredr() {
- registerIntentActions(
+ /** Helper/testStep functions goes here */
+
+ /**
+ * Process of writing a helper/test_step function.
+ *
+ * 1. All the helper functions should have IntentReceiver instance passed as an
+ * argument to them (if any intents needs to be registered).
+ * 2. The caller (if a test function) can initiate a fresh instance of IntentReceiver
+ * and use it for all subsequent helper/testStep functions.
+ * 3. The helper function should first register all required intent actions through the
+ * helper -> IntentReceiver.updateNewIntentActionsInParentReceiver()
+ * which either modifies the intentReceiver instance, or creates
+ * one (if the caller has passed a `null`).
+ * 4. At the end, all functions should call `intentReceiver.close()` which either
+ * unregisters the recent actions, or frees the original instance as per the call.
+ */
+
+ private void testStep_BondBredr(IntentReceiver parentIntentReceiver) {
+ IntentReceiver intentReceiver =
+ IntentReceiver.updateNewIntentActionsInParentReceiver(
+ parentIntentReceiver,
+ sTargetContext,
BluetoothDevice.ACTION_BOND_STATE_CHANGED,
BluetoothDevice.ACTION_ACL_CONNECTED,
BluetoothDevice.ACTION_PAIRING_REQUEST);
StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
mBumble.security()
- .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
+ .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(),
+ TimeUnit.MILLISECONDS)
.onPairing(mPairingEventStreamObserver);
- assertThat(mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_BREDR)).isTrue();
+ assertThat(mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_BREDR)).
+ isTrue();
- verifyIntentReceivedUnordered(
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceived(
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_BONDING));
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
- hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR));
- verifyIntentReceivedUnordered(
+ hasExtra(BluetoothDevice.EXTRA_TRANSPORT,
+ BluetoothDevice.TRANSPORT_BREDR));
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(
@@ -985,18 +895,18 @@ public class PairingTest {
PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next();
assertThat(pairingEvent.hasJustWorks()).isTrue();
pairingEventAnswerObserver.onNext(
- PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build());
+ PairingEventAnswer.newBuilder().setEvent(pairingEvent)
+ .setConfirm(true).build());
// Ensure that pairing succeeds
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED));
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_BONDED));
- unregisterIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_ACL_CONNECTED,
- BluetoothDevice.ACTION_PAIRING_REQUEST);
+ /* Unregisters all intent actions registered in this function */
+ intentReceiver.close();
}
private void testStep_restartBt() {
@@ -1006,9 +916,13 @@ public class PairingTest {
/* Starts outgoing GATT service discovery and incoming LE pairing in parallel */
private StreamObserverSpliterator<SecureResponse>
- helper_OutgoingGattServiceDiscoveryWithIncomingLePairing() {
- // Setup intent filters
- registerIntentActions(
+ helper_OutgoingGattServiceDiscoveryWithIncomingLePairing(
+ IntentReceiver parentIntentReceiver) {
+ // Register new actions specific to this helper function
+ IntentReceiver intentReceiver =
+ IntentReceiver.updateNewIntentActionsInParentReceiver(
+ parentIntentReceiver,
+ sTargetContext,
BluetoothDevice.ACTION_BOND_STATE_CHANGED,
BluetoothDevice.ACTION_PAIRING_REQUEST,
BluetoothDevice.ACTION_UUID,
@@ -1027,7 +941,8 @@ public class PairingTest {
}
// Start GATT service discovery, this will establish LE ACL
- assertThat(mBumbleDevice.fetchUuidsWithSdp(BluetoothDevice.TRANSPORT_LE)).isTrue();
+ assertThat(mBumbleDevice.fetchUuidsWithSdp(BluetoothDevice.TRANSPORT_LE))
+ .isTrue();
// Make Bumble connectable
AdvertiseResponse advertiseResponse =
@@ -1041,12 +956,13 @@ public class PairingTest {
.next();
// Todo: Unexpected empty ACTION_UUID intent is generated
- verifyIntentReceivedUnordered(hasAction(BluetoothDevice.ACTION_UUID));
+ intentReceiver.verifyReceived(hasAction(BluetoothDevice.ACTION_UUID));
// Wait for connection on Android
- verifyIntentReceivedUnordered(
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
- hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE));
+ hasExtra(BluetoothDevice.EXTRA_TRANSPORT,
+ BluetoothDevice.TRANSPORT_LE));
// Start pairing from Bumble
StreamObserverSpliterator<SecureResponse> responseObserver =
@@ -1061,11 +977,12 @@ public class PairingTest {
// Wait for incoming pairing notification on Android
// TODO: Order of these events is not deterministic
- verifyIntentReceivedUnordered(
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING));
- verifyIntentReceivedUnordered(
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_BONDING));
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
hasExtra(
@@ -1076,7 +993,7 @@ public class PairingTest {
assertThat(mBumbleDevice.setPairingConfirmation(true)).isTrue();
// Wait for pairing approval notification on Android
- verifyIntentReceivedUnordered(
+ intentReceiver.verifyReceived(
2,
hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
@@ -1086,134 +1003,142 @@ public class PairingTest {
// Wait for GATT service discovery to complete on Android
// so that ACTION_UUID is received here.
- verifyIntentReceivedUnordered(
+ intentReceiver.verifyReceived(
hasAction(BluetoothDevice.ACTION_UUID),
- hasExtra(BluetoothDevice.EXTRA_UUID, Matchers.hasItemInArray(BATTERY_UUID)));
-
- unregisterIntentActions(
- BluetoothDevice.ACTION_BOND_STATE_CHANGED,
- BluetoothDevice.ACTION_PAIRING_REQUEST,
- BluetoothDevice.ACTION_UUID,
- BluetoothDevice.ACTION_ACL_CONNECTED);
+ hasExtra(BluetoothDevice.EXTRA_UUID,
+ Matchers.hasItemInArray(BATTERY_UUID)));
+ intentReceiver.close();
return responseObserver;
}
- private void removeBond(BluetoothDevice device) {
- registerIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ private void testStep_RemoveBond(IntentReceiver parentIntentReceiver,
+ BluetoothDevice device) {
+ IntentReceiver intentReceiver =
+ IntentReceiver.updateNewIntentActionsInParentReceiver(
+ parentIntentReceiver,
+ sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED);
assertThat(device.removeBond()).isTrue();
- verifyIntentReceived(
+ intentReceiver.verifyReceivedOrdered(
hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
- hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_NONE));
- unregisterIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ intentReceiver.close();
}
- @SafeVarargs
- private void verifyIntentReceived(Matcher<Intent>... matchers) {
- mInOrder.verify(mReceiver, timeout(BOND_INTENT_TIMEOUT.toMillis()))
- .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers)));
+ private BluetoothProfile getProfileProxy(int profile) {
+ sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, profile);
+ ArgumentCaptor<BluetoothProfile> proxyCaptor =
+ ArgumentCaptor.forClass(BluetoothProfile.class);
+ verify(mProfileServiceListener, timeout(BOND_INTENT_TIMEOUT.toMillis()))
+ .onServiceConnected(eq(profile), proxyCaptor.capture());
+ return proxyCaptor.getValue();
}
- @SafeVarargs
- private void verifyIntentReceivedUnordered(int num, Matcher<Intent>... matchers) {
- verify(mReceiver, timeout(BOND_INTENT_TIMEOUT.toMillis()).times(num))
- .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers)));
- }
+ private void testStep_BondLe(IntentReceiver parentIntentReceiver,
+ BluetoothDevice device, OwnAddressType ownAddressType) {
+ IntentReceiver intentReceiver =
+ IntentReceiver.updateNewIntentActionsInParentReceiver(
+ parentIntentReceiver,
+ sTargetContext,
+ BluetoothDevice.ACTION_BOND_STATE_CHANGED,
+ BluetoothDevice.ACTION_ACL_CONNECTED,
+ BluetoothDevice.ACTION_PAIRING_REQUEST);
- @SafeVarargs
- private void verifyIntentReceivedUnordered(Matcher<Intent>... matchers) {
- verifyIntentReceivedUnordered(1, matchers);
- }
+ mBumble.gattBlocking()
+ .registerService(
+ GattProto.RegisterServiceRequest.newBuilder()
+ .setService(
+ GattProto.GattServiceParams.newBuilder()
+ .setUuid(BATTERY_UUID.toString())
+ .build())
+ .build());
+ mBumble.gattBlocking()
+ .registerService(
+ GattProto.RegisterServiceRequest.newBuilder()
+ .setService(
+ GattProto.GattServiceParams.newBuilder()
+ .setUuid(HOGP_UUID.toString())
+ .build())
+ .build());
- /**
- * Helper function to add reference count to registered intent actions
- *
- * @param actions new intent actions to add. If the array is empty, it is a no-op.
- */
- private void registerIntentActions(String... actions) {
- if (actions.length == 0) {
- return;
- }
- if (getTotalActionRegistrationCounts() > 0) {
- Log.d(TAG, "registerIntentActions(): unregister ALL intents");
- sTargetContext.unregisterReceiver(mReceiver);
- }
- for (String action : actions) {
- mActionRegistrationCounts.merge(action, 1, Integer::sum);
- }
- IntentFilter filter = new IntentFilter();
- mActionRegistrationCounts.entrySet().stream()
- .filter(entry -> entry.getValue() > 0)
- .forEach(
- entry -> {
- Log.d(
- TAG,
- "registerIntentActions(): Registering action = "
- + entry.getKey());
- filter.addAction(entry.getKey());
- });
- sTargetContext.registerReceiver(mReceiver, filter);
- }
+ mBumble.hostBlocking()
+ .advertise(
+ AdvertiseRequest.newBuilder()
+ .setLegacy(true)
+ .setConnectable(true)
+ .setOwnAddressType(ownAddressType)
+ .build());
- /**
- * Helper function to reduce reference count to registered intent actions If total reference
- * count is zero after removal, no broadcast receiver will be registered.
- *
- * @param actions intent actions to be removed. If some action is not registered, it is no-op
- * for that action. If the actions array is empty, it is also a no-op.
- */
- private void unregisterIntentActions(String... actions) {
- if (actions.length == 0) {
- return;
- }
- if (getTotalActionRegistrationCounts() <= 0) {
- return;
- }
- Log.d(TAG, "unregisterIntentActions(): unregister ALL intents");
- sTargetContext.unregisterReceiver(mReceiver);
- for (String action : actions) {
- if (!mActionRegistrationCounts.containsKey(action)) {
- continue;
- }
- mActionRegistrationCounts.put(action, mActionRegistrationCounts.get(action) - 1);
- if (mActionRegistrationCounts.get(action) <= 0) {
- mActionRegistrationCounts.remove(action);
- }
- }
- if (getTotalActionRegistrationCounts() > 0) {
- IntentFilter filter = new IntentFilter();
- mActionRegistrationCounts.entrySet().stream()
- .filter(entry -> entry.getValue() > 0)
- .forEach(
- entry -> {
- Log.d(
- TAG,
- "unregisterIntentActions(): Registering action = "
- + entry.getKey());
- filter.addAction(entry.getKey());
- });
- sTargetContext.registerReceiver(mReceiver, filter);
- }
- }
+ StreamObserver<PairingEventAnswer> pairingEventAnswerObserver =
+ mBumble.security()
+ .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(),
+ TimeUnit.MILLISECONDS)
+ .onPairing(mPairingEventStreamObserver);
- /**
- * Get sum of reference count from all registered actions
- *
- * @return sum of reference count from all registered actions
- */
- private int getTotalActionRegistrationCounts() {
- return mActionRegistrationCounts.values().stream().reduce(0, Integer::sum);
+ assertThat(device.createBond(BluetoothDevice.TRANSPORT_LE)).isTrue();
+
+ intentReceiver.verifyReceived(
+ hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
+ hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_BONDING));
+ intentReceiver.verifyReceivedOrdered(
+ hasAction(BluetoothDevice.ACTION_ACL_CONNECTED),
+ hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+ hasExtra(BluetoothDevice.EXTRA_TRANSPORT,
+ BluetoothDevice.TRANSPORT_LE));
+ intentReceiver.verifyReceived(
+ hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST),
+ hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+ hasExtra(
+ BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_CONSENT));
+
+ // Approve pairing from Android
+ assertThat(device.setPairingConfirmation(true)).isTrue();
+
+ PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next();
+ assertThat(pairingEvent.hasJustWorks()).isTrue();
+ pairingEventAnswerObserver.onNext(
+ PairingEventAnswer.newBuilder().setEvent(pairingEvent)
+ .setConfirm(true).build());
+
+ // Ensure that pairing succeeds
+ intentReceiver.verifyReceivedOrdered(
+ hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
+ hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+ hasExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_BONDED));
+
+ intentReceiver.close();
}
- private BluetoothProfile getProfileProxy(int profile) {
- sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, profile);
- ArgumentCaptor<BluetoothProfile> proxyCaptor =
- ArgumentCaptor.forClass(BluetoothProfile.class);
- verify(mProfileServiceListener, timeout(BOND_INTENT_TIMEOUT.toMillis()))
- .onServiceConnected(eq(profile), proxyCaptor.capture());
- return proxyCaptor.getValue();
+ private void doTestIdentityAddressWithType(BluetoothDevice device,
+ OwnAddressType ownAddressType) {
+ BluetoothAddress identityAddress = device.getIdentityAddressWithType();
+ assertThat(identityAddress.getAddress()).isNull();
+ assertThat(identityAddress.getAddressType())
+ .isEqualTo(BluetoothDevice.ADDRESS_TYPE_UNKNOWN);
+
+ /*
+ * Note: Since there was no IntentReceiver registered, passing the
+ * instance as NULL. But, if there is an instance already present, that
+ * must be passed instead of NULL.
+ */
+ testStep_BondLe(null, device, ownAddressType);
+ assertThat(sAdapter.getBondedDevices()).contains(device);
+
+ identityAddress = device.getIdentityAddressWithType();
+ assertThat(identityAddress.getAddress()).isEqualTo(device.getAddress());
+ assertThat(identityAddress.getAddressType())
+ .isEqualTo(
+ ownAddressType == OwnAddressType.RANDOM
+ ? BluetoothDevice.ADDRESS_TYPE_RANDOM
+ : BluetoothDevice.ADDRESS_TYPE_PUBLIC);
}
}
diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/utils/IntentReceiver.java b/framework/tests/bumble/src/android/bluetooth/pairing/utils/IntentReceiver.java
new file mode 100644
index 0000000000..ef8ab310dd
--- /dev/null
+++ b/framework/tests/bumble/src/android/bluetooth/pairing/utils/IntentReceiver.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.pairing.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import com.google.common.collect.Iterators;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.AllOf;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.hamcrest.MockitoHamcrest;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+
+/**
+ * IntentReceiver helps in managing the Intents received through the Broadcast
+ * receiver, with specific intent actions registered.
+ * It uses Builder pattern for instance creation, and also allows setting up
+ * a custom listener's onReceive().
+ *
+ * Use the following way to create an instance of the IntentReceiver.
+ * IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext,
+ * BluetoothDevice.ACTION_1,
+ * BluetoothDevice.ACTION_2)
+ * .setIntentListener(--) // optional
+ * .setIntentTimeout(--) // optional
+ * .build();
+ *
+ * Ordered and unordered verification mechanisms are also provided through public methods.
+ */
+
+public class IntentReceiver {
+ private static final String TAG = IntentReceiver.class.getSimpleName();
+
+ /** Interface for listening & processing the received intents */
+ public interface IntentListener {
+ /**
+ * Callback for receiving intents
+ *
+ * @param intent Received intent
+ */
+ void onReceive(Intent intent);
+ }
+
+ @Mock private BroadcastReceiver mReceiver;
+
+ /** Intent timeout value, can be configured through constructor, or setter method */
+ private final Duration mIntentTimeout;
+
+ /** To verify the received intents in-order */
+ private final InOrder mInOrder;
+ private final Context mContext;
+ private final String[] mIntentStrings;
+ private final Deque<IntentFilter> mDqIntentFilter;
+ /*
+ * Note: Since we are using Builder pattern, also add new variables added
+ * to the Builder class
+ */
+
+ /** Listener for the received intents */
+ private final IntentListener mIntentListener;
+
+ /**
+ * Creates an Intent receiver from the builder instance
+ * Note: This is a private constructor, so always prepare IntentReceiver's
+ * instance through Builder().
+ *
+ * @param builder Pre-built builder instance
+ */
+ private IntentReceiver(Builder builder) {
+ this.mIntentTimeout = builder.mIntentTimeout;
+ this.mContext = builder.mContext;
+ this.mIntentStrings = builder.mIntentStrings;
+ this.mIntentListener = builder.mIntentListener;
+
+ /* Perform other calls required for instantiation */
+ MockitoAnnotations.initMocks(this);
+ mInOrder = inOrder(mReceiver);
+ mDqIntentFilter = new ArrayDeque<>();
+ mDqIntentFilter.addFirst(prepareIntentFilter(mIntentStrings));
+
+ setupListener();
+ registerReceiver();
+ }
+
+ /** Private constructor to avoid creation of IntentReceiver instance directly */
+ private IntentReceiver() {
+ mIntentTimeout = null;
+ mInOrder = null;
+ mContext = null;
+ mIntentStrings = null;
+ mDqIntentFilter = null;
+ mIntentListener = null;
+ }
+
+ /**
+ * Builder class which helps in avoiding overloading constructors (as the class grows)
+ * Usage:
+ * new IntentReceiver.Builder(ARGS)
+ * .setterMethods() **Optional calls, as these are default params
+ * .build();
+ */
+ public static class Builder {
+ /**
+ * Add all the instance variables from IntentReceiver,
+ * which needs to be initiated from the constructor,
+ * with either default, or user provided value.
+ */
+ private final Context mContext;
+ private final String[] mIntentStrings;
+
+ /** Non-final variables as there are setters available */
+ private Duration mIntentTimeout;
+ private IntentListener mIntentListener;
+
+ /**
+ * Private default constructor to avoid creation of Builder default
+ * instance directly as we need some instance variables to be initiated
+ * with user defined values.
+ */
+ private Builder() {
+ mContext = null;
+ mIntentStrings = null;
+ }
+
+ /**
+ * Creates a Builder instance with following required params
+ *
+ * @param context Context
+ * @param intentStrings Array of intents to filter and register
+ */
+ public Builder(@NonNull Context context, String... intentStrings) {
+ mContext = context;
+ mIntentStrings = requireNonNull(intentStrings,
+ "IntentReceiver.Builder(): Intent string cannot be null");
+
+ if (mIntentStrings.length == 0) {
+ throw new RuntimeException("IntentReceiver.Builder(): No intents to register");
+ }
+
+ /* Default values for remaining vars */
+ mIntentTimeout = Duration.ofSeconds(10);
+ mIntentListener = null;
+ }
+
+ public Builder setIntentListener(IntentListener intentListener) {
+ mIntentListener = intentListener;
+ return this;
+ }
+
+ public Builder setIntentTimeout(Duration intentTimeout) {
+ mIntentTimeout = intentTimeout;
+ return this;
+ }
+
+ /**
+ * Builds and returns the IntentReceiver object with all the passed,
+ * and default params supplied to Builder().
+ */
+ public IntentReceiver build() {
+ return new IntentReceiver(this);
+ }
+ }
+
+ /**
+ * Verifies if the intent is received in order
+ *
+ * @param matchers Matchers
+ */
+ public void verifyReceivedOrdered(Matcher<Intent>... matchers) {
+ mInOrder.verify(mReceiver, timeout(mIntentTimeout.toMillis()))
+ .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers)));
+ }
+
+ /**
+ * Verifies if requested number of intents are received (unordered)
+ *
+ * @param num Number of intents
+ * @param matchers Matchers
+ */
+ public void verifyReceived(int num, Matcher<Intent>... matchers) {
+ verify(mReceiver, timeout(mIntentTimeout.toMillis()).times(num))
+ .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers)));
+ }
+
+ /**
+ * Verifies if the intent is received (unordered)
+ *
+ * @param matchers Matchers
+ */
+ public void verifyReceived(Matcher<Intent>... matchers) {
+ verifyReceived(1, matchers);
+ }
+
+ /**
+ * This function will make sure that the instance is properly cleared
+ * based on the registered actions.
+ * Note: This function MUST be called before returning from the caller function,
+ * as this either unregisters the latest registered actions, or free resources.
+ */
+ public void close() {
+ Log.d(TAG, "close(): " + mDqIntentFilter.size());
+
+ /* More than 1 IntentFilters are present */
+ if(mDqIntentFilter.size() > 1) {
+ /*
+ * It represents there are IntentFilters present to be rolled back.
+ * So, unregister and roll back to previous IntentFilter.
+ */
+ unregisterRecentAllIntentActions();
+ }
+ else {
+ /*
+ * It represents that this close() is called in the scope of creation of
+ * the object, and hence there is only 1 IntentFilter which is present.
+ * So, we can safely close this instance.
+ */
+ verifyNoMoreInteractions();
+ unregisterReceiver();
+ }
+ }
+
+ /**
+ * Registers the new actions passed as argument.
+ * 1. Unregister the receiver, and in turn old IntentFilter.
+ * 2. Creates a new IntentFilter from the String[], and treat that as latest.
+ * 3. Registers the new IntentFilter with the receiver to the current context.
+ */
+ public void registerIntentActions(String... intentStrings) {
+ IntentFilter intentFilter = prepareIntentFilter(intentStrings);
+
+ unregisterReceiver();
+ /* Pushes the new intentFilter to top to make it the latest registered */
+ mDqIntentFilter.addFirst(intentFilter);
+ registerReceiver();
+ }
+
+ /**
+ * Helper function to register intent actions, and get the IntentReceiver
+ * instance.
+ *
+ * @param parentIntentReceiver IntentReceiver instance from the parent test caller
+ * This should be `null` if there is no parent IntentReceiver instance.
+ * @param targetContext Context instance
+ * @param intentStrings Intent actions string array
+ *
+ * This should be used to register new intent actions in a testStep
+ * function always.
+ */
+ public static IntentReceiver updateNewIntentActionsInParentReceiver(
+ IntentReceiver parentIntentReceiver, Context targetContext, String... intentStrings) {
+ /*
+ * If parentIntentReceiver is NULL, it indicates that the caller
+ * is a fresh test/testStep and a new IntentReceiver will be returned.
+ * else, update the intent actions and return the same instance.
+ */
+ // Create a new instance for the current test/testStep function.
+ if(parentIntentReceiver == null)
+ return new IntentReceiver.Builder(targetContext, intentStrings)
+ .build();
+
+ /* Update the intent actions in the parent IntentReceiver instance */
+ parentIntentReceiver.registerIntentActions(intentStrings);
+ return parentIntentReceiver;
+ }
+
+ /** Helper functions are added below, usually private */
+
+ /** Registers the listener for the received intents, and perform a custom logic as required */
+ private void setupListener() {
+ doAnswer(
+ inv -> {
+ Log.d(
+ TAG,
+ "onReceive(): intent=" +
+ Arrays.toString(inv.getArguments()));
+
+ if (mIntentListener == null) return null;
+
+ Intent intent = inv.getArgument(1);
+
+ /* Custom `onReceive` will be provided by the caller */
+ mIntentListener.onReceive(intent);
+ return null;
+ })
+ .when(mReceiver)
+ .onReceive(any(), any());
+ }
+
+ private IntentFilter prepareIntentFilter(String... intentStrings) {
+ IntentFilter intentFilter = new IntentFilter();
+ for (String intentString : intentStrings) {
+ intentFilter.addAction(intentString);
+ }
+
+ return intentFilter;
+ }
+
+ /**
+ * Registers the latest intent filter which is at the deque.peekFirst()
+ * Note: The mDqIntentFilter must not be empty here.
+ */
+ private void registerReceiver() {
+ Log.d(TAG, "registerReceiver(): Registering for intents: " +
+ getActionsFromIntentFilter(mDqIntentFilter.peekFirst()));
+
+ /* ArrayDeque should not be empty at all while registering a receiver */
+ assertThat(mDqIntentFilter.isEmpty()).isFalse();
+ mContext.registerReceiver(mReceiver,
+ (IntentFilter)mDqIntentFilter.peekFirst());
+ }
+
+ /**
+ * Unregisters the receiver from the list of active receivers.
+ * Also, we can now re-use the same receiver, or register a new
+ * receiver with the same or different intent filter, the old
+ * registration is no longer valid.
+ * Source: Intents and intent filters (Android Developers)
+ */
+ private void unregisterReceiver() {
+ Log.d(TAG, "unregisterReceiver()");
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ /** Verifies that no more intents are received */
+ private void verifyNoMoreInteractions() {
+ Log.d(TAG, "verifyNoMoreInteractions()");
+ Mockito.verifyNoMoreInteractions(mReceiver);
+ }
+
+ /**
+ * Registers the new actions passed as argument.
+ * 1. Unregister the receiver, and in turn new IntentFilter.
+ * 2. Pops the new IntentFilter to roll-back to the old one.
+ * 3. Registers the old IntentFilter with the receiver to the current context.
+ */
+ private void unregisterRecentAllIntentActions() {
+ assertThat(mDqIntentFilter.isEmpty()).isFalse();
+
+ unregisterReceiver();
+ /* Restores the previous intent filter, and discard the latest */
+ mDqIntentFilter.removeFirst();
+ registerReceiver();
+ }
+
+ /**
+ * Helper function to get the actions from the IntentFilter
+ *
+ * @param intentFilter IntentFilter instance
+ *
+ * This is a helper function to get the actions from the IntentFilter,
+ * and return as a String.
+ */
+ private String getActionsFromIntentFilter(
+ IntentFilter intentFilter) {
+ Iterator<String> iterator = intentFilter.actionsIterator();
+ StringBuilder allIntentActions = new StringBuilder();
+ while (iterator.hasNext()) {
+ allIntentActions.append(iterator.next() + ", ");
+ }
+
+ return allIntentActions.toString();
+ }
+} \ No newline at end of file
diff --git a/framework/tests/unit/AndroidTest.xml b/framework/tests/unit/AndroidTest.xml
index f3e71f3b75..2f675b852e 100644
--- a/framework/tests/unit/AndroidTest.xml
+++ b/framework/tests/unit/AndroidTest.xml
@@ -39,7 +39,7 @@
<!-- Only run FrameworkBluetoothTests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/framework/tests/unit/src/android/bluetooth/BluetoothActivityEnergyInfoTest.java b/framework/tests/unit/src/android/bluetooth/BluetoothActivityEnergyInfoTest.java
index 75f604fe36..eb8e074953 100644
--- a/framework/tests/unit/src/android/bluetooth/BluetoothActivityEnergyInfoTest.java
+++ b/framework/tests/unit/src/android/bluetooth/BluetoothActivityEnergyInfoTest.java
@@ -74,7 +74,7 @@ public class BluetoothActivityEnergyInfoTest {
traffics.add(traffic);
info.setUidTraffic(traffics);
- assertThat(info.getUidTraffic().size()).isEqualTo(1);
+ assertThat(info.getUidTraffic()).hasSize(1);
assertThat(info.getUidTraffic().get(0)).isEqualTo(traffic);
}
diff --git a/framework/tests/unit/src/android/bluetooth/le/ScanRecordTest.java b/framework/tests/unit/src/android/bluetooth/le/ScanRecordTest.java
index a690e510f5..4292583191 100644
--- a/framework/tests/unit/src/android/bluetooth/le/ScanRecordTest.java
+++ b/framework/tests/unit/src/android/bluetooth/le/ScanRecordTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.ParcelUuid;
import android.platform.test.flag.junit.SetFlagsRule;
-import com.android.bluetooth.flags.Flags;
import com.android.modules.utils.BytesMatcher;
import org.junit.Rule;
@@ -195,8 +194,6 @@ public class ScanRecordTest {
@Test
public void testParserMultipleManufacturerSpecificData() {
- mSetFlagsRule.enableFlags(Flags.FLAG_SCAN_RECORD_MANUFACTURER_DATA_MERGE);
-
byte[] scanRecord =
new byte[] {
0x02,
diff --git a/framework/tests/util/src/android/bluetooth/cts/TestUtils.java b/framework/tests/util/src/android/bluetooth/cts/TestUtils.java
index 4d3a39fe60..dd1e777164 100644
--- a/framework/tests/util/src/android/bluetooth/cts/TestUtils.java
+++ b/framework/tests/util/src/android/bluetooth/cts/TestUtils.java
@@ -30,8 +30,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.google.errorprone.annotations.InlineMe;
-
public class TestUtils extends android.bluetooth.test_utils.TestUtils {
/**
* Get the current enabled status of a given profile.
@@ -161,23 +159,6 @@ public class TestUtils extends android.bluetooth.test_utils.TestUtils {
}
/**
- * Utility method to assert two byte arrays are equal.
- *
- * @param expected expected value
- * @param actual actual value
- * @deprecated Please use {@link com.google.common.truth.Truth},
- * "assertThat(actual).isEqualTo(expected)". Keeping it here since some tests are still
- * using it.
- */
- @Deprecated
- @InlineMe(
- replacement = "assertThat(actual).isEqualTo(expected)",
- staticImports = "com.google.common.truth.Truth.assertThat")
- public static void assertArrayEquals(byte[] expected, byte[] actual) {
- assertThat(actual).isEqualTo(expected);
- }
-
- /**
* DANGER: Put the current thread to sleep. Please only use this when it is ok to block the
* current thread.
*
diff --git a/offload/hal/Android.bp b/offload/hal/Android.bp
new file mode 100644
index 0000000000..91324d0d94
--- /dev/null
+++ b/offload/hal/Android.bp
@@ -0,0 +1,48 @@
+// Copyright 2025, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+ name: "libbluetooth_offload_hal",
+ vendor_available: true,
+ crate_name: "bluetooth_offload_hal",
+ crate_root: "lib.rs",
+ edition: "2021",
+ rustlibs: [
+ "android.hardware.bluetooth-V1-rust",
+ "libbinder_rs",
+ "libbluetooth_offload_hci",
+ "liblog_rust",
+ "liblogger",
+ ],
+ visibility: [
+ "//hardware/interfaces/bluetooth:__subpackages__",
+ "//packages/modules/Bluetooth/offload:__subpackages__",
+ ],
+}
+
+cc_library_headers {
+ name: "libbluetooth_offload_hal_headers",
+ vendor_available: true,
+ host_supported: true,
+ export_include_dirs: [
+ "include",
+ ],
+ visibility: [
+ "//hardware/interfaces/bluetooth:__subpackages__",
+ ],
+}
diff --git a/offload/hal/ffi.rs b/offload/hal/ffi.rs
new file mode 100644
index 0000000000..762e3c9d6b
--- /dev/null
+++ b/offload/hal/ffi.rs
@@ -0,0 +1,279 @@
+// Copyright 2024, 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.
+
+use core::{ffi::c_void, slice};
+use std::sync::{Mutex, RwLock};
+
+/// Callbacks from C to Rust
+/// `handle` is allocated as an `Option<T: Callbacks>`; It must be valid from the
+/// `CInterface.initialize()` call to the `CInterface.close()` call. This value
+/// is returned as the first parameter to all other functions.
+/// To prevent scheduling issues from the HAL Implementer, we enforce the validity
+/// until the end of `Ffi<T>` instance; aka until the end of process life.
+#[repr(C)]
+#[allow(dead_code)]
+pub struct CCallbacks {
+ handle: *const c_void,
+ initialization_complete: unsafe extern "C" fn(*mut c_void, CStatus),
+ event_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
+ acl_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
+ sco_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
+ iso_received: unsafe extern "C" fn(*mut c_void, *const u8, usize),
+}
+
+/// C Interface called from Rust
+/// `handle` is a pointer initialized by the C code and passed to all other functions.
+/// `callbacks` is only valid during the `initialize()` call.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct CInterface {
+ handle: *mut c_void,
+ initialize: unsafe extern "C" fn(handle: *mut c_void, callbacks: *const CCallbacks),
+ close: unsafe extern "C" fn(handle: *mut c_void),
+ send_command: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
+ send_acl: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
+ send_sco: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
+ send_iso: unsafe extern "C" fn(handle: *mut c_void, data: *const u8, len: usize),
+}
+
+//SAFETY: CInterface is safe to send between threads because we require the C code
+// which initialises it to only use pointers to functions which are safe
+// to call from any thread.
+unsafe impl Send for CInterface {}
+
+#[repr(C)]
+#[allow(dead_code)]
+#[derive(Debug, PartialEq)]
+pub(crate) enum CStatus {
+ Success,
+ AlreadyInitialized,
+ UnableToOpenInterface,
+ HardwareInitializationError,
+ Unknown,
+}
+
+pub(crate) trait Callbacks: DataCallbacks {
+ fn initialization_complete(&self, status: CStatus);
+}
+
+pub(crate) trait DataCallbacks: Send + Sync {
+ fn event_received(&self, data: &[u8]);
+ fn acl_received(&self, data: &[u8]);
+ fn sco_received(&self, data: &[u8]);
+ fn iso_received(&self, data: &[u8]);
+}
+
+pub(crate) struct Ffi<T: Callbacks> {
+ intf: Mutex<CInterface>,
+ wrapper: RwLock<Option<T>>,
+}
+
+impl<T: Callbacks> Ffi<T> {
+ pub(crate) fn new(intf: CInterface) -> Self {
+ Self { intf: Mutex::new(intf), wrapper: RwLock::new(None) }
+ }
+
+ pub(crate) fn initialize(&self, client: T) {
+ let intf = self.intf.lock().unwrap();
+ self.set_client(client);
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer.
+ unsafe {
+ (intf.initialize)(intf.handle, &CCallbacks::new(&self.wrapper));
+ }
+ }
+
+ pub(crate) fn send_command(&self, data: &[u8]) {
+ let intf = self.intf.lock().unwrap();
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer and an initialized `handle`.
+ unsafe {
+ (intf.send_command)(intf.handle, data.as_ptr(), data.len());
+ }
+ }
+
+ pub(crate) fn send_acl(&self, data: &[u8]) {
+ let intf = self.intf.lock().unwrap();
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer and an initialized `handle`.
+ unsafe {
+ (intf.send_acl)(intf.handle, data.as_ptr(), data.len());
+ }
+ }
+
+ pub(crate) fn send_iso(&self, data: &[u8]) {
+ let intf = self.intf.lock().unwrap();
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer and an initialized `handle`.
+ unsafe {
+ (intf.send_iso)(intf.handle, data.as_ptr(), data.len());
+ }
+ }
+
+ pub(crate) fn send_sco(&self, data: &[u8]) {
+ let intf = self.intf.lock().unwrap();
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer and an initialized `handle`.
+ unsafe {
+ (intf.send_sco)(intf.handle, data.as_ptr(), data.len());
+ }
+ }
+
+ pub(crate) fn close(&self) {
+ let intf = self.intf.lock().unwrap();
+
+ // SAFETY: The C Code has initialized the `CInterface` with a valid
+ // function pointer and an initialized `handle`.
+ unsafe {
+ (intf.close)(intf.handle);
+ }
+ self.remove_client();
+ }
+
+ fn set_client(&self, client: T) {
+ *self.wrapper.write().unwrap() = Some(client);
+ }
+
+ fn remove_client(&self) {
+ *self.wrapper.write().unwrap() = None;
+ }
+}
+
+impl CCallbacks {
+ fn new<T: Callbacks>(wrapper: &RwLock<Option<T>>) -> Self {
+ Self {
+ handle: (wrapper as *const RwLock<Option<T>>).cast(),
+ initialization_complete: Self::initialization_complete::<T>,
+ event_received: Self::event_received::<T>,
+ acl_received: Self::acl_received::<T>,
+ sco_received: Self::sco_received::<T>,
+ iso_received: Self::iso_received::<T>,
+ }
+ }
+
+ /// #Safety
+ ///
+ /// `handle` must be a valid pointer previously passed to the corresponding `initialize()`,
+ /// and not yet destroyed (this is in fact an `RwLock<Option<T>>`).
+ unsafe fn unwrap_client<T: Callbacks, F: FnOnce(&T)>(handle: *mut c_void, f: F) {
+ let wrapper: *const RwLock<Option<T>> = handle.cast();
+
+ // SAFETY: The `handle` points the `RwLock<Option<T>>` wrapper object; it was allocated
+ // at the creation of the `Ffi` object and remain alive until its destruction.
+ if let Some(client) = unsafe { &*(*wrapper).read().unwrap() } {
+ f(client);
+ } else {
+ log::error!("FFI Callback called in bad state");
+ }
+ }
+
+ /// #Safety
+ ///
+ /// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`
+ unsafe extern "C" fn initialization_complete<T: Callbacks>(
+ handle: *mut c_void,
+ status: CStatus,
+ ) {
+ // SAFETY: The vendor HAL returns `handle` pointing `wrapper` object which has
+ // the same lifetime as the base `Ffi` instance.
+ unsafe {
+ Self::unwrap_client(handle, |client: &T| client.initialization_complete(status));
+ }
+ }
+
+ /// #Safety
+ ///
+ /// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
+ /// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
+ /// is not mutated for the duration of this call.
+ unsafe extern "C" fn event_received<T: Callbacks>(
+ handle: *mut c_void,
+ data: *const u8,
+ len: usize,
+ ) {
+ // SAFETY: The C code returns `handle` pointing `wrapper` object which has
+ // the same lifetime as the base `Ffi` instance. `data` points to a buffer
+ // of `len` bytes valid until the function returns.
+ unsafe {
+ Self::unwrap_client(handle, |client: &T| {
+ client.event_received(slice::from_raw_parts(data, len))
+ });
+ }
+ }
+
+ /// #Safety
+ ///
+ /// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
+ /// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
+ /// is not mutated for the duration of this call.
+ unsafe extern "C" fn acl_received<T: Callbacks>(
+ handle: *mut c_void,
+ data: *const u8,
+ len: usize,
+ ) {
+ // SAFETY: The C code returns `handle` pointing `wrapper` object which has
+ // the same lifetime as the base `Ffi` instance. `data` points to a buffer
+ // of `len` bytes valid until the function returns.
+ unsafe {
+ Self::unwrap_client(handle, |client: &T| {
+ client.acl_received(slice::from_raw_parts(data, len))
+ });
+ }
+ }
+
+ /// #Safety
+ ///
+ /// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
+ /// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
+ /// is not mutated for the duration of this call.
+ unsafe extern "C" fn sco_received<T: Callbacks>(
+ handle: *mut c_void,
+ data: *const u8,
+ len: usize,
+ ) {
+ // SAFETY: The C code returns `handle` pointing `wrapper` object which has
+ // the same lifetime as the base `Ffi` instance. `data` points to a buffer
+ // of `len` bytes valid until the function returns.
+ unsafe {
+ Self::unwrap_client(handle, |client: &T| {
+ client.sco_received(slice::from_raw_parts(data, len))
+ });
+ }
+ }
+
+ /// #Safety
+ ///
+ /// The C Interface requires that `handle` is a copy of the value given in `CCallbacks.handle`.
+ /// `data` must be a valid pointer to at least `len` bytes of memory, which remains valid and
+ /// is not mutated for the duration of this call.
+ unsafe extern "C" fn iso_received<T: Callbacks>(
+ handle: *mut c_void,
+ data: *const u8,
+ len: usize,
+ ) {
+ // SAFETY: The C code returns `handle` pointing `wrapper` object which has
+ // the same lifetime as the base `Ffi` instance. `data` points to a buffer
+ // of `len` bytes valid until the function returns.
+ unsafe {
+ Self::unwrap_client(handle, |client: &T| {
+ client.iso_received(slice::from_raw_parts(data, len))
+ });
+ }
+ }
+}
diff --git a/offload/hal/include/hal/ffi.h b/offload/hal/include/hal/ffi.h
new file mode 100644
index 0000000000..e066c68a09
--- /dev/null
+++ b/offload/hal/include/hal/ffi.h
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2024, 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.
+ */
+
+extern "C" {
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Callabcks from C to Rust
+ * The given `handle` must be passed as the first parameter of all functions.
+ * The functions can be called from `hal_interface.initialize()` call to
+ * `hal_interface.close()` call.
+ */
+
+enum HalStatus {
+ STATUS_SUCCESS,
+ STATUS_ALREADY_INITIALIZED,
+ STATUS_UNABLE_TO_OPEN_INTERFACE,
+ STATUS_HARDWARE_INITIALIZATION_ERROR,
+ STATUS_UNKNOWN,
+};
+
+struct hal_callbacks {
+ void *handle;
+ void (*initialization_complete)(const void *handle, enum HalStatus);
+ void (*event_received)(const void *handle, const uint8_t *data, size_t len);
+ void (*acl_received)(const void *handle, const uint8_t *data, size_t len);
+ void (*sco_received)(const void *handle, const uint8_t *data, size_t len);
+ void (*iso_received)(const void *handle, const uint8_t *data, size_t len);
+};
+
+/**
+ * Interface from Rust to C
+ * The `handle` value is passed as the first parameter of all functions.
+ * Theses functions can be called from different threads, but NOT concurrently.
+ * Locking over `handle` is not necessary.
+ */
+
+struct hal_interface {
+ void *handle;
+ void (*initialize)(void *handle, const struct hal_callbacks *);
+ void (*close)(void *handle);
+ void (*send_command)(void *handle, const uint8_t *data, size_t len);
+ void (*send_acl)(void *handle, const uint8_t *data, size_t len);
+ void (*send_sco)(void *handle, const uint8_t *data, size_t len);
+ void (*send_iso)(void *handle, const uint8_t *data, size_t len);
+};
+}
diff --git a/offload/hal/lib.rs b/offload/hal/lib.rs
new file mode 100644
index 0000000000..76730428fa
--- /dev/null
+++ b/offload/hal/lib.rs
@@ -0,0 +1,22 @@
+// Copyright 2024, 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.
+
+//! HCI HAL Binder implementation with proxy integration
+//! The Binder HAL interface is replicated as C Interface in `ffi` module
+
+mod ffi;
+mod service;
+
+pub use ffi::{CCallbacks, CInterface};
+pub use service::HciHalProxy;
diff --git a/offload/hal/service.rs b/offload/hal/service.rs
new file mode 100644
index 0000000000..779be6da8c
--- /dev/null
+++ b/offload/hal/service.rs
@@ -0,0 +1,245 @@
+// Copyright 2024, 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.
+
+use crate::ffi::{CInterface, CStatus, Callbacks, DataCallbacks, Ffi};
+use android_hardware_bluetooth::aidl::android::hardware::bluetooth::{
+ IBluetoothHci::IBluetoothHci, IBluetoothHciCallbacks::IBluetoothHciCallbacks, Status::Status,
+};
+use binder::{DeathRecipient, ExceptionCode, Interface, Result as BinderResult, Strong};
+use bluetooth_offload_hci::{Module, ModuleBuilder};
+use std::sync::{Arc, RwLock};
+
+/// Service Implementation of AIDL interface `hardware/interface/bluetoot/aidl`,
+/// including a proxy interface usable by third party modules.
+pub struct HciHalProxy {
+ modules: Vec<Box<dyn ModuleBuilder>>,
+ ffi: Arc<Ffi<FfiCallbacks>>,
+ state: Arc<RwLock<State>>,
+}
+
+struct FfiCallbacks {
+ callbacks: Strong<dyn IBluetoothHciCallbacks>,
+ proxy: Arc<dyn Module>,
+ state: Arc<RwLock<State>>,
+}
+
+struct SinkModule<T: Callbacks> {
+ ffi: Arc<Ffi<T>>,
+ callbacks: Strong<dyn IBluetoothHciCallbacks>,
+}
+
+enum State {
+ Closed,
+ Opening { ffi: Arc<Ffi<FfiCallbacks>>, proxy: Arc<dyn Module> },
+ Opened { proxy: Arc<dyn Module>, _death_recipient: DeathRecipient },
+}
+
+impl Interface for HciHalProxy {}
+
+impl HciHalProxy {
+ /// Create the HAL Proxy interface binded to the Bluetooth HCI HAL interface.
+ pub fn new(modules: Vec<Box<dyn ModuleBuilder>>, cintf: CInterface) -> Self {
+ Self {
+ modules,
+ ffi: Arc::new(Ffi::new(cintf)),
+ state: Arc::new(RwLock::new(State::Closed)),
+ }
+ }
+}
+
+impl IBluetoothHci for HciHalProxy {
+ fn initialize(&self, callbacks: &Strong<dyn IBluetoothHciCallbacks>) -> BinderResult<()> {
+ let (ffi, callbacks) = {
+ let mut state = self.state.write().unwrap();
+
+ if !matches!(*state, State::Closed) {
+ let _ = callbacks.initializationComplete(Status::ALREADY_INITIALIZED);
+ return Ok(());
+ }
+
+ let mut proxy: Arc<dyn Module> =
+ Arc::new(SinkModule::new(self.ffi.clone(), callbacks.clone()));
+ for m in self.modules.iter().rev() {
+ proxy = m.build(proxy);
+ }
+ let callbacks = FfiCallbacks::new(callbacks.clone(), proxy.clone(), self.state.clone());
+
+ *state = State::Opening { ffi: self.ffi.clone(), proxy: proxy.clone() };
+ (self.ffi.clone(), callbacks)
+ };
+
+ ffi.initialize(callbacks);
+ Ok(())
+ }
+
+ fn close(&self) -> BinderResult<()> {
+ *self.state.write().unwrap() = State::Closed;
+ self.ffi.close();
+ Ok(())
+ }
+
+ fn sendHciCommand(&self, data: &[u8]) -> BinderResult<()> {
+ let State::Opened { ref proxy, .. } = *self.state.read().unwrap() else {
+ return Err(ExceptionCode::ILLEGAL_STATE.into());
+ };
+
+ proxy.out_cmd(data);
+ Ok(())
+ }
+
+ fn sendAclData(&self, data: &[u8]) -> BinderResult<()> {
+ let State::Opened { ref proxy, .. } = *self.state.read().unwrap() else {
+ return Err(ExceptionCode::ILLEGAL_STATE.into());
+ };
+
+ proxy.out_acl(data);
+ Ok(())
+ }
+
+ fn sendScoData(&self, data: &[u8]) -> BinderResult<()> {
+ let State::Opened { ref proxy, .. } = *self.state.read().unwrap() else {
+ return Err(ExceptionCode::ILLEGAL_STATE.into());
+ };
+
+ proxy.out_sco(data);
+ Ok(())
+ }
+
+ fn sendIsoData(&self, data: &[u8]) -> BinderResult<()> {
+ let State::Opened { ref proxy, .. } = *self.state.read().unwrap() else {
+ return Err(ExceptionCode::ILLEGAL_STATE.into());
+ };
+
+ proxy.out_iso(data);
+ Ok(())
+ }
+}
+
+impl<T: Callbacks> SinkModule<T> {
+ pub(crate) fn new(ffi: Arc<Ffi<T>>, callbacks: Strong<dyn IBluetoothHciCallbacks>) -> Self {
+ Self { ffi, callbacks }
+ }
+}
+
+impl<T: Callbacks> Module for SinkModule<T> {
+ fn next(&self) -> &dyn Module {
+ unreachable!()
+ }
+
+ fn out_cmd(&self, data: &[u8]) {
+ self.ffi.send_command(data);
+ }
+ fn out_acl(&self, data: &[u8]) {
+ self.ffi.send_acl(data);
+ }
+ fn out_iso(&self, data: &[u8]) {
+ self.ffi.send_iso(data);
+ }
+ fn out_sco(&self, data: &[u8]) {
+ self.ffi.send_sco(data);
+ }
+
+ fn in_evt(&self, data: &[u8]) {
+ if let Err(e) = self.callbacks.hciEventReceived(data) {
+ log::error!("Cannot send event to client: {:?}", e);
+ }
+ }
+ fn in_acl(&self, data: &[u8]) {
+ if let Err(e) = self.callbacks.aclDataReceived(data) {
+ log::error!("Cannot send ACL to client: {:?}", e);
+ }
+ }
+ fn in_sco(&self, data: &[u8]) {
+ if let Err(e) = self.callbacks.scoDataReceived(data) {
+ log::error!("Cannot send SCO to client: {:?}", e);
+ }
+ }
+ fn in_iso(&self, data: &[u8]) {
+ if let Err(e) = self.callbacks.isoDataReceived(data) {
+ log::error!("Cannot send ISO to client: {:?}", e);
+ }
+ }
+}
+
+impl FfiCallbacks {
+ fn new(
+ callbacks: Strong<dyn IBluetoothHciCallbacks>,
+ proxy: Arc<dyn Module>,
+ state: Arc<RwLock<State>>,
+ ) -> Self {
+ Self { callbacks, proxy, state }
+ }
+}
+
+impl Callbacks for FfiCallbacks {
+ fn initialization_complete(&self, status: CStatus) {
+ let mut state = self.state.write().unwrap();
+ match status {
+ CStatus::Success => {
+ let State::Opening { ref ffi, ref proxy } = *state else {
+ panic!("Initialization completed called in bad state");
+ };
+
+ *state = State::Opened {
+ proxy: proxy.clone(),
+ _death_recipient: {
+ let (ffi, state) = (ffi.clone(), self.state.clone());
+ DeathRecipient::new(move || {
+ log::info!("Bluetooth stack has died");
+ *state.write().unwrap() = State::Closed;
+ ffi.close();
+ })
+ },
+ };
+ }
+
+ CStatus::AlreadyInitialized => panic!("Initialization completed called in bad state"),
+ _ => *state = State::Closed,
+ };
+
+ if let Err(e) = self.callbacks.initializationComplete(status.into()) {
+ log::error!("Cannot call-back client: {:?}", e);
+ }
+ }
+}
+
+impl DataCallbacks for FfiCallbacks {
+ fn event_received(&self, data: &[u8]) {
+ self.proxy.in_evt(data);
+ }
+
+ fn acl_received(&self, data: &[u8]) {
+ self.proxy.in_acl(data);
+ }
+
+ fn sco_received(&self, data: &[u8]) {
+ self.proxy.in_sco(data);
+ }
+
+ fn iso_received(&self, data: &[u8]) {
+ self.proxy.in_iso(data);
+ }
+}
+
+impl From<CStatus> for Status {
+ fn from(value: CStatus) -> Self {
+ match value {
+ CStatus::Success => Status::SUCCESS,
+ CStatus::AlreadyInitialized => Status::ALREADY_INITIALIZED,
+ CStatus::UnableToOpenInterface => Status::UNABLE_TO_OPEN_INTERFACE,
+ CStatus::HardwareInitializationError => Status::HARDWARE_INITIALIZATION_ERROR,
+ CStatus::Unknown => Status::UNKNOWN,
+ }
+ }
+}
diff --git a/offload/hci/Android.bp b/offload/hci/Android.bp
new file mode 100644
index 0000000000..323e9245b6
--- /dev/null
+++ b/offload/hci/Android.bp
@@ -0,0 +1,53 @@
+// Copyright 2025, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_proc_macro {
+ name: "libbluetooth_offload_hci_derive",
+ crate_name: "bluetooth_offload_hci_derive",
+ crate_root: "derive/lib.rs",
+ edition: "2021",
+ rustlibs: [
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+}
+
+rust_defaults {
+ name: "bluetooth_offload_hci_defaults",
+ crate_root: "lib.rs",
+ crate_name: "bluetooth_offload_hci",
+ edition: "2021",
+ proc_macros: [
+ "libbluetooth_offload_hci_derive",
+ ],
+ visibility: [
+ "//packages/modules/Bluetooth/offload:__subpackages__",
+ ],
+}
+
+rust_library {
+ name: "libbluetooth_offload_hci",
+ defaults: ["bluetooth_offload_hci_defaults"],
+ vendor_available: true,
+}
+
+rust_test_host {
+ name: "libbluetooth_offload_hci_test",
+ defaults: ["bluetooth_offload_hci_defaults"],
+}
diff --git a/offload/hci/command.rs b/offload/hci/command.rs
new file mode 100644
index 0000000000..a4bbe6f701
--- /dev/null
+++ b/offload/hci/command.rs
@@ -0,0 +1,544 @@
+// Copyright 2024, 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.
+
+use crate::derive::{Read, Write};
+use crate::reader::{Read, Reader};
+use crate::writer::{pack, Write, Writer};
+
+/// HCI Command, as defined in Part E - 5.4.1
+#[derive(Debug)]
+pub enum Command {
+ /// 7.3.2 Reset Command
+ Reset(Reset),
+ /// 7.8.97 LE Set CIG Parameters
+ LeSetCigParameters(LeSetCigParameters),
+ /// 7.8.99 LE Create CIS
+ LeCreateCis(LeCreateCis),
+ /// 7.8.100 LE Remove CIG
+ LeRemoveCig(LeRemoveCig),
+ /// 7.8.103 LE Create BIG
+ LeCreateBig(LeCreateBig),
+ /// 7.8.109 LE Setup ISO Data Path
+ LeSetupIsoDataPath(LeSetupIsoDataPath),
+ /// 7.8.110 LE Remove ISO Data Path
+ LeRemoveIsoDataPath(LeRemoveIsoDataPath),
+ /// Unknown command
+ Unknown(OpCode),
+}
+
+/// HCI Command Return Parameters
+#[derive(Debug, Read, Write)]
+pub enum ReturnParameters {
+ /// 7.3.2 Reset Command
+ Reset(ResetComplete),
+ /// 7.8.2 LE Read Buffer Size [V1]
+ LeReadBufferSizeV1(LeReadBufferSizeV1Complete),
+ /// 7.8.2 LE Read Buffer Size [V2]
+ LeReadBufferSizeV2(LeReadBufferSizeV2Complete),
+ /// 7.8.97 LE Set CIG Parameters
+ LeSetCigParameters(LeSetCigParametersComplete),
+ /// 7.8.100 LE Remove CIG
+ LeRemoveCig(LeRemoveCigComplete),
+ /// 7.8.109 LE Setup ISO Data Path
+ LeSetupIsoDataPath(LeIsoDataPathComplete),
+ /// 7.8.110 LE Remove ISO Data Path
+ LeRemoveIsoDataPath(LeIsoDataPathComplete),
+ /// Unknown command
+ Unknown(OpCode),
+}
+
+impl Command {
+ /// Read an HCI Command packet
+ pub fn from_bytes(data: &[u8]) -> Result<Self, Option<OpCode>> {
+ fn parse_packet(data: &[u8]) -> Option<(OpCode, Reader)> {
+ let mut r = Reader::new(data);
+ let opcode = r.read()?;
+ let len = r.read_u8()? as usize;
+ Some((opcode, Reader::new(r.get(len)?)))
+ }
+
+ let Some((opcode, mut r)) = parse_packet(data) else {
+ return Err(None);
+ };
+ Self::dispatch_read(opcode, &mut r).ok_or(Some(opcode))
+ }
+
+ fn dispatch_read(opcode: OpCode, r: &mut Reader) -> Option<Command> {
+ Some(match opcode {
+ Reset::OPCODE => Self::Reset(r.read()?),
+ LeSetCigParameters::OPCODE => Self::LeSetCigParameters(r.read()?),
+ LeCreateCis::OPCODE => Self::LeCreateCis(r.read()?),
+ LeRemoveCig::OPCODE => Self::LeRemoveCig(r.read()?),
+ LeCreateBig::OPCODE => Self::LeCreateBig(r.read()?),
+ LeSetupIsoDataPath::OPCODE => Self::LeSetupIsoDataPath(r.read()?),
+ LeRemoveIsoDataPath::OPCODE => Self::LeRemoveIsoDataPath(r.read()?),
+ opcode => Self::Unknown(opcode),
+ })
+ }
+
+ fn to_bytes<T: CommandOpCode + Write>(command: &T) -> Vec<u8> {
+ let mut w = Writer::new(Vec::with_capacity(3 + 255));
+ w.write(&T::OPCODE);
+ w.write_u8(0);
+ w.write(command);
+
+ let mut vec = w.into_vec();
+ vec[2] = (vec.len() - 3).try_into().unwrap();
+ vec
+ }
+}
+
+/// OpCode of HCI Command, as defined in Part E - 5.4.1
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct OpCode(u16);
+
+impl OpCode {
+ /// OpCode from OpCode Group Field (OGF) and OpCode Command Field (OCF).
+ pub const fn from(ogf: u16, ocf: u16) -> Self {
+ Self(pack!((ocf, 10), (ogf, 6)))
+ }
+}
+
+impl From<u16> for OpCode {
+ fn from(v: u16) -> Self {
+ OpCode(v)
+ }
+}
+
+impl Read for OpCode {
+ fn read(r: &mut Reader) -> Option<Self> {
+ Some(r.read_u16()?.into())
+ }
+}
+
+impl Write for OpCode {
+ fn write(&self, w: &mut Writer) {
+ w.write_u16(self.0)
+ }
+}
+
+/// Define command OpCode
+pub trait CommandOpCode {
+ /// OpCode of the command
+ const OPCODE: OpCode;
+}
+
+/// Build command from definition
+pub trait CommandToBytes: CommandOpCode + Write {
+ /// Output the HCI Command packet
+ fn to_bytes(&self) -> Vec<u8>
+ where
+ Self: Sized + CommandOpCode + Write;
+}
+
+pub use defs::*;
+
+#[allow(missing_docs)]
+#[rustfmt::skip]
+mod defs {
+
+use super::*;
+use crate::derive::CommandToBytes;
+use crate::status::*;
+
+#[cfg(test)]
+use crate::{Event, EventToBytes};
+
+
+// 7.3.2 Reset Command
+
+impl CommandOpCode for Reset {
+ const OPCODE: OpCode = OpCode::from(0x03, 0x003);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct Reset {}
+
+#[derive(Debug, Read, Write)]
+pub struct ResetComplete {
+ pub status: Status,
+}
+
+#[test]
+fn test_reset() {
+ let dump = [0x03, 0x0c, 0x00];
+ let Ok(Command::Reset(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+#[test]
+fn test_reset_complete() {
+ let dump = [0x0e, 0x04, 0x01, 0x03, 0x0c, 0x00];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::Reset(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.2 LE Read Buffer Size
+
+impl CommandOpCode for LeReadBufferSizeV1 {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x002);
+}
+
+#[derive(Debug)]
+pub struct LeReadBufferSizeV1;
+
+#[derive(Debug, Read, Write)]
+pub struct LeReadBufferSizeV1Complete {
+ pub status: Status,
+ pub le_acl_data_packet_length: u16,
+ pub total_num_le_acl_data_packets: u8,
+}
+
+#[test]
+fn test_le_read_buffer_size_v1_complete() {
+ let dump = [0x0e, 0x07, 0x01, 0x02, 0x20, 0x00, 0xfb, 0x00, 0x0f];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::LeReadBufferSizeV1(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(p.le_acl_data_packet_length, 251);
+ assert_eq!(p.total_num_le_acl_data_packets, 15);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+impl CommandOpCode for LeReadBufferSizeV2 {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x060);
+}
+
+#[derive(Debug)]
+pub struct LeReadBufferSizeV2;
+
+#[derive(Debug, Read, Write)]
+pub struct LeReadBufferSizeV2Complete {
+ pub status: Status,
+ pub le_acl_data_packet_length: u16,
+ pub total_num_le_acl_data_packets: u8,
+ pub iso_data_packet_length: u16,
+ pub total_num_iso_data_packets: u8,
+}
+
+#[test]
+fn test_le_read_buffer_size_v2_complete() {
+ let dump = [0x0e, 0x0a, 0x01, 0x60, 0x20, 0x00, 0xfb, 0x00, 0x0f, 0xfd, 0x03, 0x18];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::LeReadBufferSizeV2(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(p.le_acl_data_packet_length, 251);
+ assert_eq!(p.total_num_le_acl_data_packets, 15);
+ assert_eq!(p.iso_data_packet_length, 1021);
+ assert_eq!(p.total_num_iso_data_packets, 24);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.97 LE Set CIG Parameters
+
+impl CommandOpCode for LeSetCigParameters {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x062);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeSetCigParameters {
+ pub cig_id: u8,
+ #[N(3)] pub sdu_interval_c_to_p: u32,
+ #[N(3)] pub sdu_interval_p_to_c: u32,
+ pub worst_case_sca: u8,
+ pub packing: u8,
+ pub framing: u8,
+ pub max_transport_latency_c_to_p: u16,
+ pub max_transport_latency_p_to_c: u16,
+ pub cis: Vec<LeCisInCigParameters>,
+}
+
+#[derive(Debug, Read, Write)]
+pub struct LeCisInCigParameters {
+ pub cis_id: u8,
+ pub max_sdu_c_to_p: u16,
+ pub max_sdu_p_to_c: u16,
+ pub phy_c_to_p: u8,
+ pub phy_p_to_c: u8,
+ pub rtn_c_to_p: u8,
+ pub rtn_p_to_c: u8,
+}
+
+#[test]
+fn test_le_set_cig_parameters() {
+ let dump = [
+ 0x62, 0x20, 0x21, 0x01, 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x64, 0x00, 0x05,
+ 0x00, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0d, 0x00, 0x01, 0x78, 0x00, 0x00, 0x00,
+ 0x02, 0x03, 0x0d, 0x00
+ ];
+ let Ok(Command::LeSetCigParameters(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.cig_id, 0x01);
+ assert_eq!(c.sdu_interval_c_to_p, 10_000);
+ assert_eq!(c.sdu_interval_p_to_c, 0);
+ assert_eq!(c.worst_case_sca, 1);
+ assert_eq!(c.packing, 0);
+ assert_eq!(c.framing, 0);
+ assert_eq!(c.max_transport_latency_c_to_p, 100);
+ assert_eq!(c.max_transport_latency_p_to_c, 5);
+ assert_eq!(c.cis.len(), 2);
+ assert_eq!(c.cis[0].cis_id, 0);
+ assert_eq!(c.cis[0].max_sdu_c_to_p, 120);
+ assert_eq!(c.cis[0].max_sdu_p_to_c, 0);
+ assert_eq!(c.cis[0].phy_c_to_p, 0x02);
+ assert_eq!(c.cis[0].phy_p_to_c, 0x03);
+ assert_eq!(c.cis[0].rtn_c_to_p, 13);
+ assert_eq!(c.cis[0].rtn_p_to_c, 0);
+ assert_eq!(c.cis[1].cis_id, 1);
+ assert_eq!(c.cis[1].max_sdu_c_to_p, 120);
+ assert_eq!(c.cis[1].max_sdu_p_to_c, 0);
+ assert_eq!(c.cis[1].phy_c_to_p, 0x02);
+ assert_eq!(c.cis[1].phy_p_to_c, 0x03);
+ assert_eq!(c.cis[1].rtn_c_to_p, 13);
+ assert_eq!(c.cis[1].rtn_p_to_c, 0);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+#[derive(Debug, Read, Write)]
+pub struct LeSetCigParametersComplete {
+ pub status: Status,
+ pub cig_id: u8,
+ pub connection_handles: Vec<u16>,
+}
+
+#[test]
+fn test_le_set_cig_parameters_complete() {
+ let dump = [0x0e, 0x0a, 0x01, 0x62, 0x20, 0x00, 0x01, 0x02, 0x60, 0x00, 0x61, 0x00];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::LeSetCigParameters(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(p.cig_id, 1);
+ assert_eq!(p.connection_handles.len(), 2);
+ assert_eq!(p.connection_handles[0], 0x60);
+ assert_eq!(p.connection_handles[1], 0x61);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.99 LE Create CIS
+
+impl CommandOpCode for LeCreateCis {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x064);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeCreateCis {
+ pub connection_handles: Vec<CisAclConnectionHandle>,
+}
+
+#[derive(Debug, Read, Write)]
+pub struct CisAclConnectionHandle {
+ pub cis: u16,
+ pub acl: u16,
+}
+
+#[test]
+fn test_le_create_cis () {
+ let dump = [0x64, 0x20, 0x09, 0x02, 0x60, 0x00, 0x40, 0x00, 0x61, 0x00, 0x41, 0x00];
+ let Ok(Command::LeCreateCis(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.connection_handles.len(), 2);
+ assert_eq!(c.connection_handles[0].cis, 0x60);
+ assert_eq!(c.connection_handles[0].acl, 0x40);
+ assert_eq!(c.connection_handles[1].cis, 0x61);
+ assert_eq!(c.connection_handles[1].acl, 0x41);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.100 LE Remove CIG
+
+impl CommandOpCode for LeRemoveCig {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x065);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeRemoveCig {
+ pub cig_id: u8,
+}
+
+#[derive(Debug, Read, Write)]
+pub struct LeRemoveCigComplete {
+ pub status: Status,
+ pub cig_id: u8,
+}
+
+#[test]
+fn test_le_remove_cig() {
+ let dump = [0x65, 0x20, 0x01, 0x01];
+ let Ok(Command::LeRemoveCig(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.cig_id, 0x01);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+#[test]
+fn test_le_remove_cig_complete() {
+ let dump = [0x0e, 0x05, 0x01, 0x65, 0x20, 0x00, 0x01];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::LeRemoveCig(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(p.cig_id, 0x01);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.103 LE Create BIG
+
+impl CommandOpCode for LeCreateBig {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x068);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeCreateBig {
+ pub big_handle: u8,
+ pub advertising_handle: u8,
+ pub num_bis: u8,
+ #[N(3)] pub sdu_interval: u32,
+ pub max_sdu: u16,
+ pub max_transport_latency: u16,
+ pub rtn: u8,
+ pub phy: u8,
+ pub packing: u8,
+ pub framing: u8,
+ pub encryption: u8,
+ pub broadcast_code: [u8; 16],
+}
+
+#[test]
+fn test_le_create_big() {
+ let dump = [
+ 0x68, 0x20, 0x1f, 0x00, 0x00, 0x02, 0x10, 0x27, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x04, 0x02, 0x00,
+ 0x00, 0x01, 0x31, 0x32, 0x33, 0x34, 0x31, 0x32, 0x33, 0x34, 0x31, 0x32, 0x33, 0x34, 0x31, 0x32,
+ 0x33, 0x34
+ ];
+ let Ok(Command::LeCreateBig(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.big_handle, 0x00);
+ assert_eq!(c.advertising_handle, 0x00);
+ assert_eq!(c.num_bis, 2);
+ assert_eq!(c.sdu_interval, 10_000);
+ assert_eq!(c.max_sdu, 120);
+ assert_eq!(c.max_transport_latency, 60);
+ assert_eq!(c.rtn, 4);
+ assert_eq!(c.phy, 0x02);
+ assert_eq!(c.packing, 0x00);
+ assert_eq!(c.framing, 0x00);
+ assert_eq!(c.encryption, 1);
+ assert_eq!(c.broadcast_code, [
+ 0x31, 0x32, 0x33, 0x34, 0x31, 0x32, 0x33, 0x34,
+ 0x31, 0x32, 0x33, 0x34, 0x31, 0x32, 0x33, 0x34
+ ]);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.109 LE Setup ISO Data Path
+
+impl CommandOpCode for LeSetupIsoDataPath {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x06e);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeSetupIsoDataPath {
+ pub connection_handle: u16,
+ pub data_path_direction: LeDataPathDirection,
+ pub data_path_id: u8,
+ pub codec_id: LeCodecId,
+ #[N(3)] pub controller_delay: u32,
+ pub codec_configuration: Vec<u8>,
+}
+
+#[derive(Debug, PartialEq, Read, Write)]
+pub enum LeDataPathDirection {
+ Input = 0x00,
+ Output = 0x01,
+}
+
+#[derive(Debug, Read, Write)]
+pub struct LeCodecId {
+ pub coding_format: CodingFormat,
+ pub company_id: u16,
+ pub vendor_id: u16,
+}
+
+#[derive(Debug, PartialEq, Read, Write)]
+pub enum CodingFormat {
+ ULawLog = 0x00,
+ ALawLog = 0x01,
+ Cvsd = 0x02,
+ Transparent = 0x03,
+ LinearPcm = 0x04,
+ MSbc = 0x05,
+ Lc3 = 0x06,
+ G729A = 0x07,
+ VendorSpecific = 0xff,
+}
+
+#[derive(Debug, Read, Write)]
+pub struct LeIsoDataPathComplete {
+ pub status: Status,
+ pub connection_handle: u16,
+}
+
+#[test]
+fn test_le_setup_iso_data_path() {
+ let dump = [
+ 0x6e, 0x20, 0x0d, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ ];
+ let Ok(Command::LeSetupIsoDataPath(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.connection_handle, 0x60);
+ assert_eq!(c.data_path_direction, LeDataPathDirection::Input);
+ assert_eq!(c.data_path_id, 0x00);
+ assert_eq!(c.codec_id.coding_format, CodingFormat::Transparent);
+ assert_eq!(c.codec_id.company_id, 0);
+ assert_eq!(c.codec_id.vendor_id, 0);
+ assert_eq!(c.controller_delay, 0);
+ assert_eq!(c.codec_configuration.len(), 0);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+#[test]
+fn test_le_setup_iso_data_path_complete() {
+ let dump = [0x0e, 0x06, 0x01, 0x6e, 0x20, 0x00, 0x60, 0x00];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ let ReturnParameters::LeSetupIsoDataPath(ref p) = e.return_parameters else { panic!() };
+ assert_eq!(p.status, Status::Success);
+ assert_eq!(p.connection_handle, 0x60);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.8.110 LE Remove ISO Data Path
+
+impl CommandOpCode for LeRemoveIsoDataPath {
+ const OPCODE: OpCode = OpCode::from(0x08, 0x06f);
+}
+
+#[derive(Debug, Read, Write, CommandToBytes)]
+pub struct LeRemoveIsoDataPath {
+ pub connection_handle: u16,
+ pub data_path_direction: u8,
+}
+
+#[test]
+fn test_le_remove_iso_data_path() {
+ let dump = [0x6f, 0x20, 0x03, 0x60, 0x00, 0x01];
+ let Ok(Command::LeRemoveIsoDataPath(c)) = Command::from_bytes(&dump) else { panic!() };
+ assert_eq!(c.connection_handle, 0x60);
+ assert_eq!(c.data_path_direction, 0x01);
+ assert_eq!(c.to_bytes(), &dump[..]);
+}
+
+}
diff --git a/offload/hci/data.rs b/offload/hci/data.rs
new file mode 100644
index 0000000000..bb4a452a59
--- /dev/null
+++ b/offload/hci/data.rs
@@ -0,0 +1,204 @@
+// Copyright 2024, 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.
+
+use crate::reader::{unpack, Reader};
+use crate::writer::{pack, Write, Writer};
+
+/// 5.4.5 ISO Data Packets
+
+/// Exchange of Isochronous Data between the Host and Controller
+#[derive(Debug)]
+pub struct IsoData<'a> {
+ /// Identify the connection
+ pub connection_handle: u16,
+ /// Fragmentation of the packet
+ pub sdu_fragment: IsoSduFragment,
+ /// Payload
+ pub payload: &'a [u8],
+}
+
+/// Fragmentation indication of the SDU
+#[derive(Debug)]
+pub enum IsoSduFragment {
+ /// First SDU Fragment
+ First {
+ /// SDU Header
+ hdr: IsoSduHeader,
+ /// Last SDU fragment indication
+ is_last: bool,
+ },
+ /// Continuous fragment
+ Continue {
+ /// Last SDU fragment indication
+ is_last: bool,
+ },
+}
+
+/// SDU Header information, when ISO Data in a first SDU fragment
+#[derive(Debug, Default)]
+pub struct IsoSduHeader {
+ /// Optional timestamp in microseconds
+ pub timestamp: Option<u32>,
+ /// Sequence number of the SDU
+ pub sequence_number: u16,
+ /// Total length of the SDU (sum of all fragments)
+ pub sdu_length: u16,
+ /// Only valid from Controller, indicate valid SDU data when 0
+ pub status: u16,
+}
+
+impl<'a> IsoData<'a> {
+ /// Read an HCI ISO Data packet
+ pub fn from_bytes(data: &'a [u8]) -> Option<Self> {
+ Self::parse(&mut Reader::new(data))
+ }
+
+ /// Output the HCI ISO Data packet
+ pub fn to_bytes(&self) -> Vec<u8> {
+ let mut w = Writer::new(Vec::with_capacity(12 + self.payload.len()));
+ w.write(self);
+ w.into_vec()
+ }
+
+ /// New ISO Data packet, including a complete SDU
+ pub fn new(connection_handle: u16, sequence_number: u16, data: &'a [u8]) -> Self {
+ Self {
+ connection_handle,
+ sdu_fragment: IsoSduFragment::First {
+ hdr: IsoSduHeader {
+ sequence_number,
+ sdu_length: data.len().try_into().unwrap(),
+ ..Default::default()
+ },
+ is_last: true,
+ },
+ payload: data,
+ }
+ }
+
+ fn parse(r: &mut Reader<'a>) -> Option<Self> {
+ let (connection_handle, pb_flag, ts_present) = unpack!(r.read_u16()?, (12, 2, 1));
+ let data_len = unpack!(r.read_u16()?, 14) as usize;
+
+ let sdu_fragment = match pb_flag {
+ 0b00 => IsoSduFragment::First {
+ hdr: IsoSduHeader::parse(r, ts_present != 0)?,
+ is_last: false,
+ },
+ 0b10 => IsoSduFragment::First {
+ hdr: IsoSduHeader::parse(r, ts_present != 0)?,
+ is_last: true,
+ },
+ 0b01 => IsoSduFragment::Continue { is_last: false },
+ 0b11 => IsoSduFragment::Continue { is_last: true },
+ _ => unreachable!(),
+ };
+ let sdu_header_len = Self::sdu_header_len(&sdu_fragment);
+ if data_len < sdu_header_len {
+ return None;
+ }
+
+ Some(Self { connection_handle, sdu_fragment, payload: r.get(data_len - sdu_header_len)? })
+ }
+
+ fn sdu_header_len(sdu_fragment: &IsoSduFragment) -> usize {
+ match sdu_fragment {
+ IsoSduFragment::First { ref hdr, .. } => 4 * (1 + hdr.timestamp.is_some() as usize),
+ IsoSduFragment::Continue { .. } => 0,
+ }
+ }
+}
+
+impl Write for IsoData<'_> {
+ fn write(&self, w: &mut Writer) {
+ let (pb_flag, hdr) = match self.sdu_fragment {
+ IsoSduFragment::First { ref hdr, is_last: false } => (0b00, Some(hdr)),
+ IsoSduFragment::First { ref hdr, is_last: true } => (0b10, Some(hdr)),
+ IsoSduFragment::Continue { is_last: false } => (0b01, None),
+ IsoSduFragment::Continue { is_last: true } => (0b11, None),
+ };
+
+ let ts_present = hdr.is_some() && hdr.unwrap().timestamp.is_some();
+ w.write_u16(pack!((self.connection_handle, 12), (pb_flag, 2), ((ts_present as u16), 1)));
+
+ let packet_len = Self::sdu_header_len(&self.sdu_fragment) + self.payload.len();
+ w.write_u16(pack!(u16::try_from(packet_len).unwrap(), 14));
+
+ if let Some(hdr) = hdr {
+ w.write(hdr);
+ }
+ w.put(self.payload);
+ }
+}
+
+impl IsoSduHeader {
+ fn parse(r: &mut Reader, ts_present: bool) -> Option<Self> {
+ let timestamp = match ts_present {
+ true => Some(r.read_u32::<4>()?),
+ false => None,
+ };
+ let sequence_number = r.read_u16()?;
+ let (sdu_length, _, status) = unpack!(r.read_u16()?, (12, 2, 2));
+ Some(Self { timestamp, sequence_number, sdu_length, status })
+ }
+}
+
+impl Write for IsoSduHeader {
+ fn write(&self, w: &mut Writer) {
+ if let Some(timestamp) = self.timestamp {
+ w.write_u32::<4>(timestamp);
+ };
+ w.write_u16(self.sequence_number);
+ w.write_u16(pack!((self.sdu_length, 12), (0, 2), (self.status, 2)));
+ }
+}
+
+#[test]
+fn test_iso_data() {
+ let dump = [
+ 0x60, 0x60, 0x80, 0x00, 0x4d, 0xc8, 0xd0, 0x2f, 0x19, 0x03, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x93, 0xe5, 0x28, 0x34, 0x00, 0x00, 0x04,
+ ];
+ let Some(pkt) = IsoData::from_bytes(&dump) else { panic!() };
+ assert_eq!(pkt.connection_handle, 0x060);
+
+ let IsoSduFragment::First { ref hdr, is_last } = pkt.sdu_fragment else { panic!() };
+ assert_eq!(hdr.timestamp, Some(802_211_917));
+ assert_eq!(hdr.sequence_number, 793);
+ assert_eq!(hdr.sdu_length, 120);
+ assert!(is_last);
+
+ assert_eq!(
+ pkt.payload,
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe0, 0x93, 0xe5, 0x28, 0x34, 0x00, 0x00, 0x04
+ ]
+ );
+ assert_eq!(pkt.to_bytes(), &dump[..]);
+}
diff --git a/offload/hci/derive/enum_data.rs b/offload/hci/derive/enum_data.rs
new file mode 100644
index 0000000000..c758fd756c
--- /dev/null
+++ b/offload/hci/derive/enum_data.rs
@@ -0,0 +1,95 @@
+// Copyright 2024, 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.
+
+//! Derive of `hci::reader::Read` and `hci::writer::Write` traits on an `enum`
+//!
+//! ```
+//! #[derive(Read, Write)]
+//! enum Example {
+//! FirstVariant = 0,
+//! SecondVariant = 1,
+//! }
+//! ```
+//!
+//! Produces:
+//!
+//! ```
+//! impl Read for Example {
+//! fn read(r: &mut Reader) -> Option<Self> {
+//! match r.read_u8()? {
+//! 0 => Some(Self::FirstVariant),
+//! 1 => Some(Self::SecondVariant),
+//! _ => None,
+//! }
+//! }
+//! }
+//!
+//! impl Write for Example {
+//! fn write(&self, w: &mut Writer) {
+//! w.write_u8(match self {
+//! Self::FirstVariant => 0,
+//! Self::SecondVariant => 1,
+//! })
+//! }
+//! }
+//! ```
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{spanned::Spanned, Error};
+
+pub(crate) fn derive_read(name: &syn::Ident, data: &syn::DataEnum) -> Result<TokenStream, Error> {
+ let mut variants: Vec<TokenStream> = Vec::new();
+ for variant in &data.variants {
+ let ident = &variant.ident;
+ let Some((_, ref discriminant)) = variant.discriminant else {
+ return Err(Error::new(variant.span(), "Missing discriminant"));
+ };
+ variants.push(quote! { #discriminant => Some(Self::#ident) });
+ }
+ variants.push(quote! { _ => None });
+
+ Ok(quote! {
+ impl Read for #name {
+ fn read(r: &mut Reader) -> Option<Self> {
+ match r.read_u8()? {
+ #( #variants ),*
+ }
+ }
+ }
+ })
+}
+
+pub(crate) fn derive_write(name: &syn::Ident, data: &syn::DataEnum) -> Result<TokenStream, Error> {
+ let mut variants: Vec<TokenStream> = Vec::new();
+ for variant in &data.variants {
+ let ident = &variant.ident;
+ let Some((_, ref discriminant)) = variant.discriminant else {
+ return Err(Error::new(variant.span(), "Missing discriminant"));
+ };
+ variants.push(quote! { Self::#ident => #discriminant });
+ }
+
+ Ok(quote! {
+ impl Write for #name {
+ fn write(&self, w: &mut Writer) {
+ w.write_u8(
+ match self {
+ #( #variants ),*
+ }
+ )
+ }
+ }
+ })
+}
diff --git a/offload/hci/derive/lib.rs b/offload/hci/derive/lib.rs
new file mode 100644
index 0000000000..97b5d15789
--- /dev/null
+++ b/offload/hci/derive/lib.rs
@@ -0,0 +1,89 @@
+// Copyright 2024, 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.
+
+//! Derive of traits :
+//! - `hci::reader::Read`, `hci::writer::Write`
+//! - `hci::command::CommandToBytes`
+//! - `hci::event::EventToBytes`
+
+extern crate proc_macro;
+mod enum_data;
+mod return_parameters;
+mod struct_data;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{DeriveInput, Error};
+
+/// Derive of `hci::reader::Read` trait
+#[proc_macro_derive(Read, attributes(N))]
+pub fn derive_read(input: TokenStream) -> TokenStream {
+ let input = syn::parse_macro_input!(input as DeriveInput);
+ let (ident, data) = (&input.ident, &input.data);
+ let expanded = match (ident.to_string().as_str(), data) {
+ ("ReturnParameters", syn::Data::Enum(ref data)) => {
+ return_parameters::derive_read(ident, data)
+ }
+ (_, syn::Data::Enum(ref data)) => enum_data::derive_read(ident, data),
+ (_, syn::Data::Struct(ref data)) => struct_data::derive_read(ident, data),
+ (_, _) => panic!("Unsupported kind of input"),
+ }
+ .unwrap_or_else(Error::into_compile_error);
+ TokenStream::from(expanded)
+}
+
+/// Derive of `hci::reader::Write` trait
+#[proc_macro_derive(Write)]
+pub fn derive_write(input: TokenStream) -> TokenStream {
+ let input = syn::parse_macro_input!(input as DeriveInput);
+ let (ident, data) = (&input.ident, &input.data);
+ let expanded = match (ident.to_string().as_str(), &data) {
+ ("ReturnParameters", syn::Data::Enum(ref data)) => {
+ return_parameters::derive_write(ident, data)
+ }
+ (_, syn::Data::Enum(ref data)) => enum_data::derive_write(ident, data),
+ (_, syn::Data::Struct(ref data)) => struct_data::derive_write(ident, data),
+ (_, _) => panic!("Unsupported kind of input"),
+ }
+ .unwrap_or_else(Error::into_compile_error);
+ TokenStream::from(expanded)
+}
+
+/// Derive of `hci::command::CommandToBytes`
+#[proc_macro_derive(CommandToBytes)]
+pub fn derive_command_to_bytes(input: TokenStream) -> TokenStream {
+ let input = syn::parse_macro_input!(input as DeriveInput);
+ let name = &input.ident;
+ TokenStream::from(quote! {
+ impl CommandToBytes for #name {
+ fn to_bytes(&self) -> Vec<u8> {
+ Command::to_bytes(self)
+ }
+ }
+ })
+}
+
+/// Derive of `hci::command::EventToBytes`
+#[proc_macro_derive(EventToBytes)]
+pub fn derive_event_to_bytes(input: TokenStream) -> TokenStream {
+ let input = syn::parse_macro_input!(input as DeriveInput);
+ let name = &input.ident;
+ TokenStream::from(quote! {
+ impl EventToBytes for #name {
+ fn to_bytes(&self) -> Vec<u8> {
+ Event::to_bytes(self)
+ }
+ }
+ })
+}
diff --git a/offload/hci/derive/return_parameters.rs b/offload/hci/derive/return_parameters.rs
new file mode 100644
index 0000000000..20baf439c5
--- /dev/null
+++ b/offload/hci/derive/return_parameters.rs
@@ -0,0 +1,116 @@
+// Copyright 2024, 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.
+
+//! Derive of `hci::reader::Read` and `hci::writer::Write` traits on
+//! `enum returnReturnParameters`.
+//!
+//! ```
+//! #[derive(Read, Write)]
+//! enum ReturnParameters {
+//! CommandOne(CommandOneComplete),
+//! CommandTwo(CommandTwoComplete),
+//! LastIsDefault(OpCode),
+//! }
+//! ```
+//!
+//! Produces:
+//!
+//! ```
+//! impl Read for ReturnParameters {
+//! fn read(r: &mut Reader) -> Option<Self> {
+//! Some(match r.read_u16()?.into() {
+//! CommandOne::OPCODE => Self::CommandOne(r.read()?),
+//! CommandTwo::OPCODE => Self::CommandTwo(r.read()?),
+//! opcode => Self::LastIsDefault(opcode),
+//! })
+//! }
+//! }
+//!
+//! impl Write for ReturnParameters {
+//! fn write(&self, w: &mut Writer) {
+//! match self {
+//! Self::CommandOne(p) => {
+//! w.write(&CommandOne::OPCODE);
+//! w.write(p);
+//! }
+//! Self::CommandTwo(p) => {
+//! w.write(&CommandOne::OPCODE);
+//! w.write(p);
+//! }
+//! Self::LastIsDefault(..) => panic!(),
+//! };
+//! }
+//! }
+//! ```
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::Error;
+
+pub(crate) fn derive_read(name: &syn::Ident, data: &syn::DataEnum) -> Result<TokenStream, Error> {
+ let mut variants = Vec::new();
+ for (i, variant) in data.variants.iter().enumerate() {
+ let ident = &variant.ident;
+
+ if i < data.variants.len() - 1 {
+ variants.push(quote! {
+ #ident::OPCODE => Self::#ident(r.read()?),
+ });
+ } else {
+ variants.push(quote! {
+ opcode => Self::#ident(opcode),
+ });
+ }
+ }
+
+ Ok(quote! {
+ impl Read for #name {
+ fn read(r: &mut Reader) -> Option<Self> {
+ Some(match r.read_u16()?.into() {
+ #( #variants )*
+ })
+ }
+ }
+ })
+}
+
+pub(crate) fn derive_write(name: &syn::Ident, data: &syn::DataEnum) -> Result<TokenStream, Error> {
+ let mut variants = Vec::new();
+ for (i, variant) in data.variants.iter().enumerate() {
+ let ident = &variant.ident;
+
+ if i < data.variants.len() - 1 {
+ variants.push(quote! {
+ Self::#ident(p) => {
+ w.write(&#ident::OPCODE);
+ w.write(p);
+ }
+ });
+ } else {
+ variants.push(quote! {
+ Self::#ident(..) => panic!(),
+ });
+ }
+ }
+
+ Ok(quote! {
+ impl Write for #name {
+ fn write(&self, w: &mut Writer) {
+ match self {
+ #( #variants )*
+ };
+ }
+ }
+ })
+}
diff --git a/offload/hci/derive/struct_data.rs b/offload/hci/derive/struct_data.rs
new file mode 100644
index 0000000000..757a71baec
--- /dev/null
+++ b/offload/hci/derive/struct_data.rs
@@ -0,0 +1,178 @@
+// Copyright 2024, 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.
+
+//! Derive of `hci::reader::Read` and `hci::writer::Write` traits on a `struct`
+//!
+//! ```
+//! #[derive(Read, Write)]
+//! struct Example {
+//! one_byte: u8,
+//! two_bytes: u16,
+//! #[N(3)] three_bytes: u32,
+//! #[N(4)] four_bytes: u32,
+//! bytes: [u8; 123],
+//! other: OtherType,
+//! }
+//! ```
+//!
+//! Produces:
+//!
+//! ```
+//! impl Read for Example {
+//! fn read(r: &mut Reader) -> Option<Self> {
+//! Some(Self {
+//! one_byte: r.read_u8()?,
+//! two_bytes: r.read_u16()?,
+//! three_bytes: r.read_u32::<3>()?,
+//! four_bytes: r.read_u32::<4>()?,
+//! bytes: r.read_bytes()?,
+//! other_type: r.read()?,
+//! })
+//! }
+//! }
+//!
+//! impl Write for Example {
+//! fn write(&self, w: &mut Writer) {
+//! w.write_u8(self.one_byte);
+//! w.write_u16(self.two_bytes);
+//! w.write_u32::<3>(self.three_bytes);
+//! w.write_u32::<4>(self.four_bytes);
+//! w.write_bytes(&self.bytes);
+//! w.write(&self.other_type);
+//! }
+//! }
+//! ```
+
+use proc_macro2::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::{spanned::Spanned, Error};
+
+struct Attributes {
+ n: Option<usize>,
+}
+
+impl Attributes {
+ fn parse(syn_attrs: &[syn::Attribute]) -> Result<Attributes, Error> {
+ let mut n = None;
+ for attr in syn_attrs.iter() {
+ match attr {
+ attr if attr.path().is_ident("N") => {
+ let lit: syn::LitInt = attr.parse_args()?;
+ n = Some(lit.base10_parse()?);
+ }
+ attr => return Err(Error::new(attr.span(), "Unrecognized attribute")),
+ }
+ }
+ Ok(Attributes { n })
+ }
+}
+
+pub(crate) fn derive_read(name: &syn::Ident, data: &syn::DataStruct) -> Result<TokenStream, Error> {
+ let mut fields = Vec::new();
+ for field in &data.fields {
+ let ident = &field.ident.as_ref().unwrap();
+ let attrs = Attributes::parse(&field.attrs)?;
+ let fn_token = match &field.ty {
+ syn::Type::Path(v) if v.path.is_ident("u8") => {
+ if attrs.n.unwrap_or(1) != 1 {
+ return Err(Error::new(v.span(), "Expected N(1) for type `u8`"));
+ }
+ quote_spanned! { v.span() => read_u8()? }
+ }
+ syn::Type::Path(v) if v.path.is_ident("u16") => {
+ if attrs.n.unwrap_or(2) != 2 {
+ return Err(Error::new(v.span(), "Expected N(2) for type `u16`"));
+ }
+ quote_spanned! { v.span() => read_u16()? }
+ }
+ syn::Type::Path(v) if v.path.is_ident("u32") => {
+ let Some(n) = attrs.n else {
+ return Err(Error::new(v.span(), "`N()` attribute required"));
+ };
+ if n > 4 {
+ return Err(Error::new(v.span(), "Expected N(n <= 4)"));
+ }
+ quote_spanned! { v.span() => read_u32::<#n>()? }
+ }
+ syn::Type::Array(v) => match &*v.elem {
+ syn::Type::Path(v) if v.path.is_ident("u8") => {
+ quote_spanned! { v.span() => read_bytes()? }
+ }
+ _ => return Err(Error::new(v.elem.span(), "Only Byte array supported")),
+ },
+ ty => quote_spanned! { ty.span() => read()? },
+ };
+ fields.push(quote! { #ident: r.#fn_token });
+ }
+
+ Ok(quote! {
+ impl Read for #name {
+ fn read(r: &mut Reader) -> Option<Self> {
+ Some(Self {
+ #( #fields ),*
+ })
+ }
+ }
+ })
+}
+
+pub(crate) fn derive_write(
+ name: &syn::Ident,
+ data: &syn::DataStruct,
+) -> Result<TokenStream, Error> {
+ let mut fields = Vec::new();
+ for field in &data.fields {
+ let ident = &field.ident.as_ref().unwrap();
+ let attrs = Attributes::parse(&field.attrs)?;
+ let fn_token = match &field.ty {
+ syn::Type::Path(v) if v.path.is_ident("u8") => {
+ if attrs.n.unwrap_or(1) != 1 {
+ return Err(Error::new(v.span(), "Expected N(1) for type `u8`"));
+ }
+ quote_spanned! { v.span() => write_u8(self.#ident) }
+ }
+ syn::Type::Path(v) if v.path.is_ident("u16") => {
+ if attrs.n.unwrap_or(2) != 2 {
+ return Err(Error::new(v.span(), "Expected N(2) for type `u16`"));
+ }
+ quote_spanned! { v.span() => write_u16(self.#ident) }
+ }
+ syn::Type::Path(v) if v.path.is_ident("u32") => {
+ let Some(n) = attrs.n else {
+ return Err(Error::new(v.span(), "`N()` attribute required"));
+ };
+ if n > 4 {
+ return Err(Error::new(v.span(), "Expected N(n <= 4)"));
+ }
+ quote_spanned! { v.span() => write_u32::<#n>(self.#ident) }
+ }
+ syn::Type::Array(v) => match &*v.elem {
+ syn::Type::Path(v) if v.path.is_ident("u8") => {
+ quote_spanned! { v.span() => write_bytes(&self.#ident) }
+ }
+ _ => return Err(Error::new(v.elem.span(), "Only Byte array supported")),
+ },
+ ty => quote_spanned! { ty.span() => write(&self.#ident) },
+ };
+ fields.push(quote! { w.#fn_token; });
+ }
+
+ Ok(quote! {
+ impl Write for #name {
+ fn write(&self, w: &mut Writer) {
+ #( #fields )*
+ }
+ }
+ })
+}
diff --git a/offload/hci/event.rs b/offload/hci/event.rs
new file mode 100644
index 0000000000..222d0e3546
--- /dev/null
+++ b/offload/hci/event.rs
@@ -0,0 +1,343 @@
+// Copyright 2024, 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.
+
+use crate::reader::{Read, Reader};
+use crate::writer::{Write, Writer};
+
+/// HCI Event Packet, as defined in Part E - 5.4.4
+#[derive(Debug)]
+pub enum Event {
+ /// 7.7.5 Disconnection Complete
+ DisconnectionComplete(DisconnectionComplete),
+ /// 7.7.14 Command Complete
+ CommandComplete(CommandComplete),
+ /// 7.7.15 Command Status
+ CommandStatus(CommandStatus),
+ /// 7.7.19 Number Of Completed Packets
+ NumberOfCompletedPackets(NumberOfCompletedPackets),
+ /// 7.7.65.25 LE CIS Established
+ LeCisEstablished(LeCisEstablished),
+ /// 7.7.65.27 LE Create BIG Complete
+ LeCreateBigComplete(LeCreateBigComplete),
+ /// 7.7.65.28 LE Terminate BIG Complete
+ LeTerminateBigComplete(LeTerminateBigComplete),
+ /// Unknown Event
+ Unknown(Code),
+}
+
+impl Event {
+ /// Read an HCI Event packet
+ pub fn from_bytes(data: &[u8]) -> Result<Self, Option<Code>> {
+ fn parse_packet(data: &[u8]) -> Option<(Code, Reader)> {
+ let mut r = Reader::new(data);
+ let code = r.read_u8()?;
+ let len = r.read_u8()? as usize;
+
+ let mut r = Reader::new(r.get(len)?);
+ let code = match code {
+ Code::LE_META => Code(Code::LE_META, Some(r.read_u8()?)),
+ _ => Code(code, None),
+ };
+
+ Some((code, r))
+ }
+
+ let Some((code, mut r)) = parse_packet(data) else {
+ return Err(None);
+ };
+ Self::dispatch_read(code, &mut r).ok_or(Some(code))
+ }
+
+ fn dispatch_read(code: Code, r: &mut Reader) -> Option<Event> {
+ Some(match code {
+ CommandComplete::CODE => Self::CommandComplete(r.read()?),
+ CommandStatus::CODE => Self::CommandStatus(r.read()?),
+ DisconnectionComplete::CODE => Self::DisconnectionComplete(r.read()?),
+ NumberOfCompletedPackets::CODE => Self::NumberOfCompletedPackets(r.read()?),
+ LeCisEstablished::CODE => Self::LeCisEstablished(r.read()?),
+ LeCreateBigComplete::CODE => Self::LeCreateBigComplete(r.read()?),
+ LeTerminateBigComplete::CODE => Self::LeTerminateBigComplete(r.read()?),
+ code => Self::Unknown(code),
+ })
+ }
+
+ fn to_bytes<T: EventCode + Write>(event: &T) -> Vec<u8> {
+ let mut w = Writer::new(Vec::with_capacity(2 + 255));
+ w.write_u8(T::CODE.0);
+ w.write_u8(0);
+ if let Some(sub_code) = T::CODE.1 {
+ w.write_u8(sub_code)
+ }
+ w.write(event);
+
+ let mut vec = w.into_vec();
+ vec[1] = (vec.len() - 2).try_into().unwrap();
+ vec
+ }
+}
+
+/// Code of HCI Event, as defined in Part E - 5.4.4
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Code(u8, Option<u8>);
+
+impl Code {
+ const LE_META: u8 = 0x3e;
+}
+
+/// Define event Code
+pub trait EventCode {
+ /// Code of the event
+ const CODE: Code;
+}
+
+/// Build event from definition
+pub trait EventToBytes: EventCode + Write {
+ /// Output the HCI Event packet
+ fn to_bytes(&self) -> Vec<u8>
+ where
+ Self: Sized + EventCode + Write;
+}
+
+pub use defs::*;
+
+#[allow(missing_docs)]
+#[rustfmt::skip]
+mod defs {
+
+use super::*;
+use crate::derive::{Read, Write, EventToBytes};
+use crate::command::{OpCode, ReturnParameters};
+use crate::status::Status;
+
+
+// 7.7.5 Disconnection Complete
+
+impl EventCode for DisconnectionComplete {
+ const CODE: Code = Code(0x05, None);
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct DisconnectionComplete {
+ pub status: Status,
+ pub connection_handle: u16,
+ pub reason: u8,
+}
+
+#[test]
+fn test_disconnection_complete() {
+ let dump = [0x05, 0x04, 0x00, 0x60, 0x00, 0x16];
+ let Ok(Event::DisconnectionComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.status, Status::Success);
+ assert_eq!(e.connection_handle, 0x60);
+ assert_eq!(e.reason, 0x16);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.14 Command Complete
+
+impl EventCode for CommandComplete {
+ const CODE: Code = Code(0x0e, None);
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct CommandComplete {
+ pub num_hci_command_packets: u8,
+ pub return_parameters: ReturnParameters,
+}
+
+#[test]
+fn test_command_complete() {
+ let dump = [0x0e, 0x04, 0x01, 0x03, 0x0c, 0x00];
+ let Ok(Event::CommandComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.num_hci_command_packets, 1);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.15 Command Status
+
+impl EventCode for CommandStatus {
+ const CODE: Code = Code(0x0f, None);
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct CommandStatus {
+ pub status: Status,
+ pub num_hci_command_packets: u8,
+ pub opcode: OpCode,
+}
+
+#[test]
+fn test_command_status() {
+ let dump = [0x0f, 0x04, 0x00, 0x01, 0x01, 0x04];
+ let Ok(Event::CommandStatus(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.status, Status::Success);
+ assert_eq!(e.num_hci_command_packets, 1);
+ assert_eq!(e.opcode, OpCode::from(0x01, 0x001));
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.19 Number Of Completed Packets
+
+impl EventCode for NumberOfCompletedPackets {
+ const CODE: Code = Code(0x13, None);
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct NumberOfCompletedPackets {
+ pub handles: Vec<NumberOfCompletedPacketsHandle>,
+}
+
+#[derive(Debug, Copy, Clone, Read, Write)]
+pub struct NumberOfCompletedPacketsHandle {
+ pub connection_handle: u16,
+ pub num_completed_packets: u16,
+}
+
+#[test]
+fn test_number_of_completed_packets() {
+ let dump = [0x13, 0x09, 0x02, 0x40, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00];
+ let Ok(Event::NumberOfCompletedPackets(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.handles.len(), 2);
+ assert_eq!(e.handles[0].connection_handle, 0x40);
+ assert_eq!(e.handles[0].num_completed_packets, 1);
+ assert_eq!(e.handles[1].connection_handle, 0x41);
+ assert_eq!(e.handles[1].num_completed_packets, 1);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.65.25 LE CIS Established
+
+impl EventCode for LeCisEstablished {
+ const CODE: Code = Code(Code::LE_META, Some(0x19));
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct LeCisEstablished {
+ pub status: Status,
+ pub connection_handle: u16,
+ #[N(3)] pub cig_sync_delay: u32,
+ #[N(3)] pub cis_sync_delay: u32,
+ #[N(3)] pub transport_latency_c_to_p: u32,
+ #[N(3)] pub transport_latency_p_to_c: u32,
+ pub phy_c_to_p: u8,
+ pub phy_p_to_c: u8,
+ pub nse: u8,
+ pub bn_c_to_p: u8,
+ pub bn_p_to_c: u8,
+ pub ft_c_to_p: u8,
+ pub ft_p_to_c: u8,
+ pub max_pdu_c_to_p: u16,
+ pub max_pdu_p_to_c: u16,
+ pub iso_interval: u16,
+}
+
+#[test]
+fn test_le_cis_established() {
+ let dump = [
+ 0x3e, 0x1d, 0x19, 0x00, 0x60, 0x00, 0x40, 0x2c, 0x00, 0x40, 0x2c, 0x00, 0xd0, 0x8b, 0x01, 0x60,
+ 0x7a, 0x00, 0x02, 0x02, 0x06, 0x02, 0x00, 0x05, 0x01, 0x78, 0x00, 0x00, 0x00, 0x10, 0x00 ];
+ let Ok(Event::LeCisEstablished(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.status, Status::Success);
+ assert_eq!(e.connection_handle, 0x60);
+ assert_eq!(e.cig_sync_delay, 11_328);
+ assert_eq!(e.cis_sync_delay, 11_328);
+ assert_eq!(e.transport_latency_c_to_p, 101_328);
+ assert_eq!(e.transport_latency_p_to_c, 31_328);
+ assert_eq!(e.phy_c_to_p, 0x02);
+ assert_eq!(e.phy_p_to_c, 0x02);
+ assert_eq!(e.nse, 6);
+ assert_eq!(e.bn_c_to_p, 2);
+ assert_eq!(e.bn_p_to_c, 0);
+ assert_eq!(e.ft_c_to_p, 5);
+ assert_eq!(e.ft_p_to_c, 1);
+ assert_eq!(e.max_pdu_c_to_p, 120);
+ assert_eq!(e.max_pdu_p_to_c, 0);
+ assert_eq!(e.iso_interval, 16);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.65.27 LE Create BIG Complete
+
+impl EventCode for LeCreateBigComplete {
+ const CODE: Code = Code(Code::LE_META, Some(0x1b));
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct LeCreateBigComplete {
+ pub status: Status,
+ pub big_handle: u8,
+ #[N(3)] pub big_sync_delay: u32,
+ #[N(3)] pub big_transport_latency: u32,
+ pub phy: u8,
+ pub nse: u8,
+ pub bn: u8,
+ pub pto: u8,
+ pub irc: u8,
+ pub max_pdu: u16,
+ pub iso_interval: u16,
+ pub bis_handles: Vec<u16>,
+}
+
+#[test]
+fn test_le_create_big_complete() {
+ let dump = [
+ 0x3e, 0x17, 0x1b, 0x00, 0x00, 0x46, 0x50, 0x00, 0x66, 0x9e, 0x00, 0x02, 0x0f, 0x03, 0x00, 0x05,
+ 0x78, 0x00, 0x18, 0x00, 0x02, 0x00, 0x04, 0x01, 0x04
+ ];
+ let Ok(Event::LeCreateBigComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.status, Status::Success);
+ assert_eq!(e.big_handle, 0x00);
+ assert_eq!(e.big_sync_delay, 20_550);
+ assert_eq!(e.big_transport_latency, 40_550);
+ assert_eq!(e.phy, 0x02);
+ assert_eq!(e.nse, 15);
+ assert_eq!(e.bn, 3);
+ assert_eq!(e.pto, 0);
+ assert_eq!(e.irc, 5);
+ assert_eq!(e.max_pdu, 120);
+ assert_eq!(e.iso_interval, 24);
+ assert_eq!(e.bis_handles.len(), 2);
+ assert_eq!(e.bis_handles[0], 0x400);
+ assert_eq!(e.bis_handles[1], 0x401);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+
+// 7.7.65.28 LE Terminate BIG Complete
+
+impl EventCode for LeTerminateBigComplete {
+ const CODE: Code = Code(Code::LE_META, Some(0x1c));
+}
+
+#[derive(Debug, Read, Write, EventToBytes)]
+pub struct LeTerminateBigComplete {
+ pub big_handle: u8,
+ pub reason: u8,
+}
+
+#[test]
+fn test_le_terminate_big_complete() {
+ let dump = [0x3e, 0x03, 0x1c, 0x00, 0x16];
+ let Ok(Event::LeTerminateBigComplete(e)) = Event::from_bytes(&dump) else { panic!() };
+ assert_eq!(e.big_handle, 0x00);
+ assert_eq!(e.reason, 0x16);
+ assert_eq!(e.to_bytes(), &dump[..]);
+}
+
+}
diff --git a/offload/hci/lib.rs b/offload/hci/lib.rs
new file mode 100644
index 0000000000..a84e538fec
--- /dev/null
+++ b/offload/hci/lib.rs
@@ -0,0 +1,78 @@
+// Copyright 2024, 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.
+
+//! HCI Proxy module implementation, along with
+//! reading / writing helpers of Bluetooth HCI Commands, Events and Data encapsulations.
+
+use std::sync::Arc;
+
+/// Interface for building a module
+pub trait ModuleBuilder: Send + Sync {
+ /// Build the module from the next module in the chain
+ fn build(&self, next_module: Arc<dyn Module>) -> Arc<dyn Module>;
+}
+
+/// Interface of a an HCI proxy module
+pub trait Module: Send + Sync {
+ /// Returns the next chained proxy module
+ fn next(&self) -> &dyn Module;
+
+ /// HCI Command from Host to Controller
+ fn out_cmd(&self, data: &[u8]) {
+ self.next().out_cmd(data);
+ }
+ /// ACL Data from Host to Controller
+ fn out_acl(&self, data: &[u8]) {
+ self.next().out_acl(data);
+ }
+ /// SCO Data from Host to Controller
+ fn out_sco(&self, data: &[u8]) {
+ self.next().out_sco(data);
+ }
+ /// ISO Data from Host to Controller
+ fn out_iso(&self, data: &[u8]) {
+ self.next().out_iso(data);
+ }
+
+ /// HCI Command from Controller to Host
+ fn in_evt(&self, data: &[u8]) {
+ self.next().in_evt(data);
+ }
+ /// ACL Data from Controller to Host
+ fn in_acl(&self, data: &[u8]) {
+ self.next().in_acl(data);
+ }
+ /// SCO Data from Controller to Host
+ fn in_sco(&self, data: &[u8]) {
+ self.next().in_sco(data);
+ }
+ /// ISO Data from Controller to Host
+ fn in_iso(&self, data: &[u8]) {
+ self.next().in_iso(data);
+ }
+}
+
+use bluetooth_offload_hci_derive as derive;
+
+mod command;
+mod data;
+mod event;
+mod reader;
+mod status;
+mod writer;
+
+pub use command::*;
+pub use data::*;
+pub use event::*;
+pub use status::*;
diff --git a/offload/hci/reader.rs b/offload/hci/reader.rs
new file mode 100644
index 0000000000..98aea61735
--- /dev/null
+++ b/offload/hci/reader.rs
@@ -0,0 +1,99 @@
+// Copyright 2024, 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.
+
+pub(crate) trait Read {
+ fn read(r: &mut Reader) -> Option<Self>
+ where
+ Self: Sized;
+}
+
+pub(crate) struct Reader<'a> {
+ data: &'a [u8],
+ pos: usize,
+}
+
+impl<'a> Reader<'a> {
+ pub(crate) fn new(data: &'a [u8]) -> Self {
+ Self { data, pos: 0 }
+ }
+
+ pub(crate) fn get(&mut self, n: usize) -> Option<&'a [u8]> {
+ if self.pos + n > self.data.len() {
+ return None;
+ }
+ let old_pos = self.pos;
+ self.pos += n;
+ Some(&self.data[old_pos..self.pos])
+ }
+
+ pub(crate) fn read<T: Read>(&mut self) -> Option<T> {
+ T::read(self)
+ }
+
+ pub(crate) fn read_u8(&mut self) -> Option<u8> {
+ Some(self.read_u32::<1>()? as u8)
+ }
+
+ pub(crate) fn read_u16(&mut self) -> Option<u16> {
+ Some(self.read_u32::<2>()? as u16)
+ }
+
+ pub(crate) fn read_u32<const N: usize>(&mut self) -> Option<u32> {
+ let data_it = self.get(N)?.iter().enumerate();
+ Some(data_it.fold(0u32, |v, (i, byte)| v | (*byte as u32) << (i * 8)))
+ }
+
+ pub(crate) fn read_bytes<const N: usize>(&mut self) -> Option<[u8; N]> {
+ Some(<[u8; N]>::try_from(self.get(N)?).unwrap())
+ }
+}
+
+impl Read for Vec<u8> {
+ fn read(r: &mut Reader) -> Option<Self> {
+ let len = r.read_u8()? as usize;
+ Some(Vec::from(r.get(len)?))
+ }
+}
+
+impl Read for Vec<u16> {
+ fn read(r: &mut Reader) -> Option<Self> {
+ let len = r.read_u8()? as usize;
+ let vec: Vec<_> = (0..len).map_while(|_| r.read_u16()).collect();
+ Some(vec).take_if(|v| v.len() == len)
+ }
+}
+
+impl<T: Read> Read for Vec<T> {
+ fn read(r: &mut Reader) -> Option<Self> {
+ let len = r.read_u8()? as usize;
+ let vec: Vec<_> = (0..len).map_while(|_| r.read()).collect();
+ Some(vec).take_if(|v| v.len() == len)
+ }
+}
+
+macro_rules! unpack {
+ ($v:expr, ($( $n:expr ),*)) => {
+ {
+ let mut _x = $v;
+ ($({
+ let y = _x & ((1 << $n) - 1);
+ _x >>= $n;
+ y
+ }),*)
+ }
+ };
+ ($v:expr, $n:expr) => { unpack!($v, ($n)) };
+}
+
+pub(crate) use unpack;
diff --git a/offload/hci/status.rs b/offload/hci/status.rs
new file mode 100644
index 0000000000..ee92d06bad
--- /dev/null
+++ b/offload/hci/status.rs
@@ -0,0 +1,95 @@
+// Copyright 2024, 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.
+
+use crate::derive::{Read, Write};
+use crate::reader::{Read, Reader};
+use crate::writer::{Write, Writer};
+
+/// Status / Error codes, as defined in Part F
+#[derive(Debug, PartialEq, Read, Write)]
+#[allow(missing_docs)]
+pub enum Status {
+ Success = 0x00,
+ UnknownHciCommand = 0x01,
+ UnknownConnectionIdentifier = 0x02,
+ HardwareFailure = 0x03,
+ PageTimeout = 0x04,
+ AuthenticationFailure = 0x05,
+ PinorKeyMissing = 0x06,
+ MemoryCapacityExceeded = 0x07,
+ ConnectionTimeout = 0x08,
+ ConnectionLimitExceeded = 0x09,
+ SynchronousConnectionLimitExceeded = 0x0A,
+ ConnectionAlreadyExists = 0x0B,
+ CommandDisallowed = 0x0C,
+ ConnectionRejectedLimitedResources = 0x0D,
+ ConnectionRejectedSecurityReasons = 0x0E,
+ ConnectionRejectedUnacceptableBdAddr = 0x0F,
+ ConnectionAcceptTimeoutExceeded = 0x10,
+ UnsupportedFeatureOrParameterValue = 0x11,
+ InvalidHciCommandParameters = 0x12,
+ RemoteUserTerminatedConnection = 0x13,
+ RemoteDeviceTerminatedConnectionLowResources = 0x14,
+ RemoteDeviceTerminatedConnectionPowerOff = 0x15,
+ ConnectionTerminatedByLocalHost = 0x16,
+ RepeatedAttempts = 0x17,
+ PairingNotAllowed = 0x18,
+ UnknownLmpPdu = 0x19,
+ UnsupportedRemoteFeature = 0x1A,
+ ScoOffsetRejected = 0x1B,
+ ScoIntervalRejected = 0x1C,
+ ScoAirModeRejected = 0x1D,
+ InvalidLmpParameters = 0x1E,
+ UnspecifiedError = 0x1F,
+ UnsupportedLmpParameterValue = 0x20,
+ RoleChangeNotAllowed = 0x21,
+ LmpResponseTimeout = 0x22,
+ LmpErrorTransactionCollision = 0x23,
+ LmpPduNotAllowed = 0x24,
+ EncryptionModeNotAcceptable = 0x25,
+ LinkKeyCannotBeChanged = 0x26,
+ RequestedQosNotSupported = 0x27,
+ InstantPassed = 0x28,
+ PairingWithUnitKeyNotSupported = 0x29,
+ DifferentTransactionCollision = 0x2A,
+ ReservedForUse2B = 0x2B,
+ QosUnacceptableParameter = 0x2C,
+ QosRejected = 0x2D,
+ ChannelClassificationNotSupported = 0x2E,
+ InsufficientSecurity = 0x2F,
+ ParameterOutOfMandatoryRange = 0x30,
+ ReservedForUse31 = 0x31,
+ RoleSwitchPending = 0x32,
+ ReservedForUse33 = 0x33,
+ ReservedSlotViolation = 0x34,
+ RoleSwitchFailed = 0x35,
+ ExtendedInquiryResponseTooLarge = 0x36,
+ SecureSimplePairingNotSupportedByHost = 0x37,
+ HostBusy = 0x38,
+ ConnectionRejectedNoSuitableChannelFound = 0x39,
+ ControllerBusy = 0x3A,
+ UnacceptableConnectionParameters = 0x3B,
+ AdvertisingTimeout = 0x3C,
+ ConnectionTerminatedMicFailure = 0x3D,
+ ConnectionFailedEstablished = 0x3E,
+ PreviouslyUsed3F = 0x3F,
+ CoarseClockAdjustmentRejected = 0x40,
+ Type0SubmapNotDefined = 0x41,
+ UnknownAdvertisingIdentifier = 0x42,
+ LimitReached = 0x43,
+ OperationCancelledByHost = 0x44,
+ PacketTooLong = 0x45,
+ TooLate = 0x46,
+ TooEarly = 0x47,
+}
diff --git a/offload/hci/writer.rs b/offload/hci/writer.rs
new file mode 100644
index 0000000000..ca0e8e274a
--- /dev/null
+++ b/offload/hci/writer.rs
@@ -0,0 +1,103 @@
+// Copyright 2024, 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.
+
+pub trait Write {
+ fn write(&self, w: &mut Writer)
+ where
+ Self: Sized;
+}
+
+pub struct Writer {
+ vec: Vec<u8>,
+}
+
+impl Writer {
+ pub(crate) fn new(vec: Vec<u8>) -> Self {
+ Self { vec }
+ }
+
+ pub(crate) fn into_vec(self) -> Vec<u8> {
+ self.vec
+ }
+
+ pub(crate) fn put(&mut self, slice: &[u8]) {
+ self.vec.extend_from_slice(slice);
+ }
+
+ pub(crate) fn write<T: Write>(&mut self, v: &T) {
+ v.write(self)
+ }
+
+ pub(crate) fn write_u8(&mut self, v: u8) {
+ self.write_u32::<1>(v.into());
+ }
+
+ pub(crate) fn write_u16(&mut self, v: u16) {
+ self.write_u32::<2>(v.into());
+ }
+
+ pub(crate) fn write_u32<const N: usize>(&mut self, mut v: u32) {
+ for _ in 0..N {
+ self.vec.push((v & 0xff) as u8);
+ v >>= 8;
+ }
+ }
+
+ pub(crate) fn write_bytes<const N: usize>(&mut self, bytes: &[u8; N]) {
+ self.put(bytes);
+ }
+}
+
+impl Write for Vec<u8> {
+ fn write(&self, w: &mut Writer) {
+ w.write_u8(self.len().try_into().unwrap());
+ w.put(self);
+ }
+}
+
+impl Write for Vec<u16> {
+ fn write(&self, w: &mut Writer) {
+ w.write_u8(self.len().try_into().unwrap());
+ for item in self {
+ w.write_u16(*item);
+ }
+ }
+}
+
+impl<T: Write> Write for Vec<T> {
+ fn write(&self, w: &mut Writer) {
+ w.write_u8(self.len().try_into().unwrap());
+ for item in self {
+ w.write(item);
+ }
+ }
+}
+
+macro_rules! pack {
+ ( $( ($x:expr, $n:expr) ),* ) => {
+ {
+ let mut y = 0;
+ let mut _shl = 0;
+ $(
+ assert!($x & !((1 << $n) - 1) == 0);
+ y |= ($x << _shl);
+ _shl += $n;
+ )*
+ y
+ }
+ };
+ ( $x:expr, $n:expr ) => { pack!(($x, $n)) };
+}
+
+pub(crate) use pack;
diff --git a/offload/leaudio/aidl/Android.bp b/offload/leaudio/aidl/Android.bp
new file mode 100644
index 0000000000..1e904d2a64
--- /dev/null
+++ b/offload/leaudio/aidl/Android.bp
@@ -0,0 +1,33 @@
+// Copyright 2025, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.hardware.bluetooth.offload.leaudio",
+ vendor_available: true,
+ unstable: true,
+ srcs: ["android/hardware/bluetooth/offload/leaudio/*.aidl"],
+ backend: {
+ rust: {
+ enabled: true,
+ },
+ },
+ visibility: [
+ "//packages/modules/Bluetooth/offload/leaudio:__subpackages__",
+ "//system/tools/aidl/build",
+ ],
+}
diff --git a/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxy.aidl b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxy.aidl
new file mode 100644
index 0000000000..a9a1289181
--- /dev/null
+++ b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxy.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.offload.leaudio;
+
+import android.hardware.bluetooth.offload.leaudio.IHciProxyCallbacks;
+
+/**
+ * Interface of the HCI-Proxy module
+ */
+interface IHciProxy {
+
+ /**
+ * Register callbacks for events in the other direction
+ */
+ void registerCallbacks(in IHciProxyCallbacks callbacks);
+
+ /**
+ * Send an ISO packet on a `handle`
+ */
+ oneway void sendPacket(in int handle, in int sequence_number, in byte[] data);
+}
diff --git a/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxyCallbacks.aidl b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxyCallbacks.aidl
new file mode 100644
index 0000000000..619b22b9ea
--- /dev/null
+++ b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/IHciProxyCallbacks.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.offload.leaudio;
+
+import android.hardware.bluetooth.offload.leaudio.StreamConfiguration;
+
+/**
+ * Interface from HCI-Proxy module to the audio module
+ */
+interface IHciProxyCallbacks {
+
+ /**
+ * Start indication of a stream
+ */
+ void startStream(in int handle, in StreamConfiguration configuration);
+
+ /**
+ * Stop indication of a stream
+ */
+ void stopStream(in int handle);
+}
diff --git a/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/StreamConfiguration.aidl b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/StreamConfiguration.aidl
new file mode 100644
index 0000000000..944facb2ef
--- /dev/null
+++ b/offload/leaudio/aidl/android/hardware/bluetooth/offload/leaudio/StreamConfiguration.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.offload.leaudio;
+
+/**
+ * Configuration relative to a stream
+ */
+parcelable StreamConfiguration {
+
+ /**
+ * Constant ISO time transmission interval, in micro-seconds
+ */
+ int isoIntervalUs;
+
+ /**
+ * Constant SDU transmission interval, in micro-seconds
+ */
+ int sduIntervalUs;
+
+ /**
+ * Maximum size of a SDU
+ */
+ int maxSduSize;
+
+ /**
+ * Number of PDU's transmitted by ISO-Intervals
+ */
+ int burstNumber;
+
+ /**
+ * How many consecutive Isochronous Intervals can be used to transmit a PDU
+ */
+ int flushTimeout;
+}
diff --git a/offload/leaudio/hci/Android.bp b/offload/leaudio/hci/Android.bp
new file mode 100644
index 0000000000..e4f1b51881
--- /dev/null
+++ b/offload/leaudio/hci/Android.bp
@@ -0,0 +1,46 @@
+// Copyright 2025, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+ name: "bluetooth_offload_leaudio_hci_defaults",
+ crate_root: "lib.rs",
+ crate_name: "bluetooth_offload_leaudio_hci",
+ edition: "2021",
+ rustlibs: [
+ "android.hardware.bluetooth.offload.leaudio-rust",
+ "libbinder_rs",
+ "libbluetooth_offload_hci",
+ "liblog_rust",
+ "liblogger",
+ ],
+}
+
+rust_library {
+ name: "libbluetooth_offload_leaudio_hci",
+ defaults: ["bluetooth_offload_leaudio_hci_defaults"],
+ vendor_available: true,
+ visibility: [
+ "//hardware/interfaces/bluetooth:__subpackages__",
+ "//packages/modules/Bluetooth/offload:__subpackages__",
+ ],
+}
+
+rust_test {
+ name: "libbluetooth_offload_leaudio_hci_test",
+ defaults: ["bluetooth_offload_leaudio_hci_defaults"],
+}
diff --git a/offload/leaudio/hci/arbiter.rs b/offload/leaudio/hci/arbiter.rs
new file mode 100644
index 0000000000..072295f568
--- /dev/null
+++ b/offload/leaudio/hci/arbiter.rs
@@ -0,0 +1,154 @@
+// Copyright 2024, 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.
+
+use bluetooth_offload_hci::{IsoData, Module};
+use std::collections::{HashMap, VecDeque};
+use std::sync::{Arc, Condvar, Mutex, MutexGuard};
+use std::thread::{self, JoinHandle};
+
+pub struct Arbiter {
+ state_cvar: Arc<(Mutex<State>, Condvar)>,
+ thread: Option<JoinHandle<()>>,
+ max_buf_len: usize,
+}
+
+#[derive(Default)]
+struct State {
+ /// Halt indication of the sender thread
+ halt: bool,
+
+ /// Software transmission queues for each `Origin`.
+ /// A queue is pair of connection handle, and packet raw ISO data.
+ queues: [VecDeque<(u16, Vec<u8>)>; 2],
+
+ /// Count of packets sent to the controller and not yet acknowledged,
+ /// by connection handle stored on `u16`.
+ in_transit: HashMap<u16, usize>,
+}
+
+enum Origin {
+ Audio,
+ Incoming,
+}
+
+impl Arbiter {
+ pub fn new(sink: Arc<dyn Module>, max_buf_len: usize, max_buf_count: usize) -> Self {
+ let state_cvar = Arc::new((Mutex::<State>::new(Default::default()), Condvar::new()));
+ let thread = {
+ let state_cvar = state_cvar.clone();
+ thread::spawn(move || Self::thread_loop(state_cvar.clone(), sink, max_buf_count))
+ };
+
+ Self { state_cvar, thread: Some(thread), max_buf_len }
+ }
+
+ pub fn add_connection(&self, handle: u16) {
+ let (state, _) = &*self.state_cvar;
+ if state.lock().unwrap().in_transit.insert(handle, 0).is_some() {
+ panic!("Connection with handle 0x{:03x} already exists", handle);
+ }
+ }
+
+ pub fn remove_connection(&self, handle: u16) {
+ let (state, cvar) = &*self.state_cvar;
+ let mut state = state.lock().unwrap();
+ for q in state.queues.iter_mut() {
+ while let Some(idx) = q.iter().position(|&(h, _)| h == handle) {
+ q.remove(idx);
+ }
+ }
+ if state.in_transit.remove(&handle).is_some() {
+ cvar.notify_one();
+ }
+ }
+
+ pub fn push_incoming(&self, iso_data: &IsoData) {
+ self.push(Origin::Incoming, iso_data);
+ }
+
+ pub fn push_audio(&self, iso_data: &IsoData) {
+ self.push(Origin::Audio, iso_data);
+ }
+
+ pub fn set_completed(&self, handle: u16, num: usize) {
+ let (state, cvar) = &*self.state_cvar;
+ if let Some(buf_usage) = state.lock().unwrap().in_transit.get_mut(&handle) {
+ *buf_usage -= num;
+ cvar.notify_one();
+ }
+ }
+
+ fn push(&self, origin: Origin, iso_data: &IsoData) {
+ let handle = iso_data.connection_handle;
+ let data = iso_data.to_bytes();
+ assert!(data.len() <= self.max_buf_len + 4);
+
+ let (state, cvar) = &*self.state_cvar;
+ let mut state = state.lock().unwrap();
+ if state.in_transit.contains_key(&handle) {
+ state.queues[origin as usize].push_back((handle, data));
+ cvar.notify_one();
+ }
+ }
+
+ fn thread_loop(
+ state_cvar: Arc<(Mutex<State>, Condvar)>,
+ sink: Arc<dyn Module>,
+ max_buf_count: usize,
+ ) {
+ let (state, cvar) = &*state_cvar;
+ 'main: loop {
+ let packet = {
+ let mut state = state.lock().unwrap();
+ let mut packet = None;
+ while !state.halt && {
+ packet = Self::pull(&mut state, max_buf_count);
+ packet.is_none()
+ } {
+ state = cvar.wait(state).unwrap();
+ }
+ if state.halt {
+ break 'main;
+ }
+ packet.unwrap()
+ };
+ sink.out_iso(&packet);
+ }
+ }
+
+ fn pull(state: &mut MutexGuard<'_, State>, max_buf_count: usize) -> Option<Vec<u8>> {
+ for idx in 0..state.queues.len() {
+ if state.queues[idx].is_empty() || max_buf_count <= state.in_transit.values().sum() {
+ continue;
+ }
+ let (handle, vec) = state.queues[idx].pop_front().unwrap();
+ *state.in_transit.get_mut(&handle).unwrap() += 1;
+ return Some(vec);
+ }
+ None
+ }
+}
+
+impl Drop for Arbiter {
+ fn drop(&mut self) {
+ let (state, cvar) = &*self.state_cvar;
+ {
+ let mut state = state.lock().unwrap();
+ state.halt = true;
+ cvar.notify_one();
+ }
+ let thread = self.thread.take().unwrap();
+ thread.join().expect("End of thread loop");
+ }
+}
diff --git a/offload/leaudio/hci/lib.rs b/offload/leaudio/hci/lib.rs
new file mode 100644
index 0000000000..e06cd79356
--- /dev/null
+++ b/offload/leaudio/hci/lib.rs
@@ -0,0 +1,42 @@
+// Copyright 2024, 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.
+
+//! LE Audio HCI-Proxy module
+//!
+//! The module looks at HCI Commands / Events to control the mux of ISO packet
+//! flows coming from the stack and the exposed "LE Audio HCI Proxy" AIDL service:
+//!
+//! HCI | ^ | HCI AIDL
+//! Command | | | ISO Packets Interface
+//! ___|_|________|__ ___________ ____________
+//! | : : proxy | | | arbiter | | service |
+//! | : : | | | | | |
+//! | : : `-|-------|---- ----|--------| |
+//! | : : | | \ / | | |
+//! | : : | Ctrl | : | | |
+//! | : : ---------|------>| : | Ctrl | |
+//! | : : ---------|------ | : | ------>| |
+//! |___:_:________ __| |_____:_____| |____________|
+//! | | |
+//! | | HCI | HCI
+//! v | Event V ISO Packets
+
+mod arbiter;
+mod proxy;
+mod service;
+
+#[cfg(test)]
+mod tests;
+
+pub use proxy::LeAudioModuleBuilder;
diff --git a/offload/leaudio/hci/proxy.rs b/offload/leaudio/hci/proxy.rs
new file mode 100644
index 0000000000..413a7f84b5
--- /dev/null
+++ b/offload/leaudio/hci/proxy.rs
@@ -0,0 +1,381 @@
+// Copyright 2024, 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.
+
+use bluetooth_offload_hci as hci;
+
+use crate::arbiter::Arbiter;
+use crate::service::{Service, StreamConfiguration};
+use hci::{Command, Event, EventToBytes, IsoData, ReturnParameters, Status};
+use hci::{Module, ModuleBuilder};
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
+
+const DATA_PATH_ID_SOFTWARE: u8 = 0x19; // TODO
+
+/// LE Audio HCI-Proxy module builder
+pub struct LeAudioModuleBuilder {}
+
+pub(crate) struct LeAudioModule {
+ next_module: Arc<dyn Module>,
+ state: Mutex<State>,
+ service: Service,
+}
+
+#[derive(Default)]
+struct State {
+ big: HashMap<u8, BigParameters>,
+ cig: HashMap<u8, CigParameters>,
+ stream: HashMap<u16, Stream>,
+ arbiter: Option<Arc<Arbiter>>,
+}
+
+struct BigParameters {
+ bis_handles: Vec<u16>,
+ sdu_interval: u32,
+}
+
+struct CigParameters {
+ cis_handles: Vec<u16>,
+ sdu_interval_c_to_p: u32,
+ sdu_interval_p_to_c: u32,
+}
+
+#[derive(Debug, Clone)]
+struct Stream {
+ state: StreamState,
+ iso_type: IsoType,
+ iso_interval_us: u32,
+}
+
+#[derive(Debug, PartialEq, Clone)]
+enum StreamState {
+ Disabled,
+ Enabling,
+ Enabled,
+}
+
+#[derive(Debug, Clone)]
+enum IsoType {
+ Cis { c_to_p: IsoInDirection, _p_to_c: IsoInDirection },
+ Bis { c_to_p: IsoInDirection },
+}
+
+#[derive(Debug, Clone)]
+struct IsoInDirection {
+ sdu_interval_us: u32,
+ max_sdu_size: u16,
+ burst_number: u8,
+ flush_timeout: u8,
+}
+
+impl Stream {
+ fn new_cis(cig: &CigParameters, e: &hci::LeCisEstablished) -> Self {
+ let iso_interval_us = (e.iso_interval as u32) * 1250;
+
+ assert_eq!(iso_interval_us % cig.sdu_interval_c_to_p, 0, "Framing mode not supported");
+ assert_eq!(iso_interval_us % cig.sdu_interval_p_to_c, 0, "Framing mode not supported");
+
+ assert_eq!(
+ iso_interval_us / cig.sdu_interval_c_to_p,
+ e.bn_c_to_p.into(),
+ "SDU fragmentation not supported"
+ );
+ assert_eq!(
+ iso_interval_us / cig.sdu_interval_p_to_c,
+ e.bn_p_to_c.into(),
+ "SDU fragmentation not supported"
+ );
+
+ Self {
+ state: StreamState::Disabled,
+ iso_interval_us,
+ iso_type: IsoType::Cis {
+ c_to_p: IsoInDirection {
+ sdu_interval_us: cig.sdu_interval_c_to_p,
+ max_sdu_size: e.max_pdu_c_to_p,
+ burst_number: e.bn_c_to_p,
+ flush_timeout: e.ft_c_to_p,
+ },
+ _p_to_c: IsoInDirection {
+ sdu_interval_us: cig.sdu_interval_p_to_c,
+ max_sdu_size: e.max_pdu_p_to_c,
+ burst_number: e.bn_p_to_c,
+ flush_timeout: e.ft_p_to_c,
+ },
+ },
+ }
+ }
+
+ fn new_bis(big: &BigParameters, e: &hci::LeCreateBigComplete) -> Self {
+ let iso_interval_us = (e.iso_interval as u32) * 1250;
+
+ assert_eq!(iso_interval_us % big.sdu_interval, 0, "Framing mode not supported");
+ assert_eq!(
+ iso_interval_us / big.sdu_interval,
+ e.bn.into(),
+ "SDU fragmentation not supported"
+ );
+
+ Self {
+ state: StreamState::Disabled,
+ iso_interval_us,
+ iso_type: IsoType::Bis {
+ c_to_p: IsoInDirection {
+ sdu_interval_us: big.sdu_interval,
+ max_sdu_size: e.max_pdu,
+ burst_number: e.bn,
+ flush_timeout: e.irc,
+ },
+ },
+ }
+ }
+}
+
+impl ModuleBuilder for LeAudioModuleBuilder {
+ /// Build the HCI-Proxy module from the next module in the chain
+ fn build(&self, next_module: Arc<dyn Module>) -> Arc<dyn Module> {
+ Arc::new(LeAudioModule::new(next_module))
+ }
+}
+
+impl LeAudioModule {
+ pub(crate) fn new(next_module: Arc<dyn Module>) -> Self {
+ Self { next_module, state: Mutex::new(Default::default()), service: Service::new() }
+ }
+
+ #[cfg(test)]
+ pub(crate) fn arbiter(&self) -> Option<Arc<Arbiter>> {
+ let state = self.state.lock().unwrap();
+ state.arbiter.clone()
+ }
+}
+
+impl Module for LeAudioModule {
+ fn next(&self) -> &dyn Module {
+ &*self.next_module
+ }
+
+ fn out_cmd(&self, data: &[u8]) {
+ match Command::from_bytes(data) {
+ Ok(Command::LeSetCigParameters(ref c)) => {
+ let mut state = self.state.lock().unwrap();
+ state.cig.insert(
+ c.cig_id,
+ CigParameters {
+ cis_handles: vec![],
+ sdu_interval_c_to_p: c.sdu_interval_c_to_p,
+ sdu_interval_p_to_c: c.sdu_interval_p_to_c,
+ },
+ );
+ }
+
+ Ok(Command::LeCreateBig(ref c)) => {
+ let mut state = self.state.lock().unwrap();
+ state.big.insert(
+ c.big_handle,
+ BigParameters { bis_handles: vec![], sdu_interval: c.sdu_interval },
+ );
+ }
+
+ Ok(Command::LeSetupIsoDataPath(ref c)) if c.data_path_id == DATA_PATH_ID_SOFTWARE => {
+ assert_eq!(c.data_path_direction, hci::LeDataPathDirection::Input);
+ let mut state = self.state.lock().unwrap();
+ let stream = state.stream.get_mut(&c.connection_handle).unwrap();
+ stream.state = StreamState::Enabling;
+ }
+
+ _ => (),
+ }
+
+ self.next().out_cmd(data);
+ }
+
+ fn in_evt(&self, data: &[u8]) {
+ match Event::from_bytes(data) {
+ Ok(Event::CommandComplete(ref e)) => match e.return_parameters {
+ ReturnParameters::Reset(ref ret) if ret.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ *state = Default::default();
+ }
+
+ ReturnParameters::LeReadBufferSizeV2(ref ret) if ret.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ state.arbiter = Some(Arc::new(Arbiter::new(
+ self.next_module.clone(),
+ ret.iso_data_packet_length.into(),
+ ret.total_num_iso_data_packets.into(),
+ )));
+ self.service.reset(Arc::downgrade(state.arbiter.as_ref().unwrap()));
+ }
+
+ ReturnParameters::LeSetCigParameters(ref ret) if ret.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ let cig = state.cig.get_mut(&ret.cig_id).unwrap();
+ cig.cis_handles = ret.connection_handles.clone();
+ }
+
+ ReturnParameters::LeRemoveCig(ref ret) if ret.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ state.cig.remove(&ret.cig_id);
+ }
+
+ ReturnParameters::LeSetupIsoDataPath(ref ret) => 'event: {
+ let mut state = self.state.lock().unwrap();
+ let stream = state.stream.get_mut(&ret.connection_handle).unwrap();
+ stream.state =
+ if stream.state == StreamState::Enabling && ret.status == Status::Success {
+ StreamState::Enabled
+ } else {
+ StreamState::Disabled
+ };
+
+ if stream.state != StreamState::Enabled {
+ break 'event;
+ }
+
+ let c_to_p = match stream.iso_type {
+ IsoType::Cis { ref c_to_p, .. } => c_to_p,
+ IsoType::Bis { ref c_to_p } => c_to_p,
+ };
+
+ self.service.start_stream(
+ ret.connection_handle,
+ StreamConfiguration {
+ isoIntervalUs: stream.iso_interval_us as i32,
+ sduIntervalUs: c_to_p.sdu_interval_us as i32,
+ maxSduSize: c_to_p.max_sdu_size as i32,
+ burstNumber: c_to_p.burst_number as i32,
+ flushTimeout: c_to_p.flush_timeout as i32,
+ },
+ );
+ }
+
+ ReturnParameters::LeRemoveIsoDataPath(ref ret) if ret.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ let stream = state.stream.get_mut(&ret.connection_handle).unwrap();
+ if stream.state == StreamState::Enabled {
+ self.service.stop_stream(ret.connection_handle);
+ }
+ stream.state = StreamState::Disabled;
+ }
+
+ _ => (),
+ },
+
+ Ok(Event::LeCisEstablished(ref e)) if e.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ let mut cig_values = state.cig.values();
+ let Some(cig) =
+ cig_values.find(|&g| g.cis_handles.iter().any(|&h| h == e.connection_handle))
+ else {
+ panic!("CIG not set-up for CIS 0x{:03x}", e.connection_handle);
+ };
+
+ let cis = Stream::new_cis(cig, e);
+ if state.stream.insert(e.connection_handle, cis).is_some() {
+ log::error!("CIS already established");
+ } else {
+ let arbiter = state.arbiter.as_ref().unwrap();
+ arbiter.add_connection(e.connection_handle);
+ }
+ }
+
+ Ok(Event::DisconnectionComplete(ref e)) if e.status == Status::Success => {
+ let mut state = self.state.lock().unwrap();
+ if state.stream.remove(&e.connection_handle).is_some() {
+ let arbiter = state.arbiter.as_ref().unwrap();
+ arbiter.remove_connection(e.connection_handle);
+ }
+ }
+
+ Ok(Event::LeCreateBigComplete(ref e)) if e.status == Status::Success => {
+ let mut state_guard = self.state.lock().unwrap();
+ let state = &mut *state_guard;
+
+ let big = state.big.get_mut(&e.big_handle).unwrap();
+ big.bis_handles = e.bis_handles.clone();
+
+ let bis = Stream::new_bis(big, e);
+ for h in &big.bis_handles {
+ if state.stream.insert(*h, bis.clone()).is_some() {
+ log::error!("BIS already established");
+ } else {
+ let arbiter = state.arbiter.as_ref().unwrap();
+ arbiter.add_connection(*h);
+ }
+ }
+ }
+
+ Ok(Event::LeTerminateBigComplete(ref e)) => {
+ let mut state = self.state.lock().unwrap();
+ let big = state.big.remove(&e.big_handle).unwrap();
+ for h in big.bis_handles {
+ state.stream.remove(&h);
+
+ let arbiter = state.arbiter.as_ref().unwrap();
+ arbiter.remove_connection(h);
+ }
+ }
+
+ Ok(Event::NumberOfCompletedPackets(ref e)) => 'event: {
+ let state = self.state.lock().unwrap();
+ let Some(arbiter) = state.arbiter.as_ref() else {
+ break 'event;
+ };
+
+ let (stack_event, _) = {
+ let mut stack_event = hci::NumberOfCompletedPackets {
+ handles: Vec::with_capacity(e.handles.len()),
+ };
+ let mut audio_event = hci::NumberOfCompletedPackets {
+ handles: Vec::with_capacity(e.handles.len()),
+ };
+ for item in &e.handles {
+ let handle = item.connection_handle;
+ arbiter.set_completed(handle, item.num_completed_packets.into());
+
+ if match state.stream.get(&handle) {
+ Some(stream) => stream.state == StreamState::Enabled,
+ None => false,
+ } {
+ audio_event.handles.push(*item);
+ } else {
+ stack_event.handles.push(*item);
+ }
+ }
+ (stack_event, audio_event)
+ };
+
+ if !stack_event.handles.is_empty() {
+ self.next().in_evt(&stack_event.to_bytes());
+ }
+ return;
+ }
+
+ Ok(..) => (),
+
+ Err(code) => {
+ log::error!("Malformed event with code: {:?}", code);
+ }
+ }
+
+ self.next().in_evt(data);
+ }
+
+ fn out_iso(&self, data: &[u8]) {
+ let state = self.state.lock().unwrap();
+ let arbiter = state.arbiter.as_ref().unwrap();
+ arbiter.push_incoming(&IsoData::from_bytes(data).unwrap());
+ }
+}
diff --git a/offload/leaudio/hci/service.rs b/offload/leaudio/hci/service.rs
new file mode 100644
index 0000000000..85fac7168c
--- /dev/null
+++ b/offload/leaudio/hci/service.rs
@@ -0,0 +1,126 @@
+// Copyright 2024, 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.
+
+use android_hardware_bluetooth_offload_leaudio::{aidl, binder};
+
+use crate::arbiter::Arbiter;
+use aidl::android::hardware::bluetooth::offload::leaudio::{
+ IHciProxy::{BnHciProxy, BpHciProxy, IHciProxy},
+ IHciProxyCallbacks::IHciProxyCallbacks,
+};
+use binder::{
+ BinderFeatures, DeathRecipient, ExceptionCode, Interface, Result as BinderResult, Strong,
+};
+use bluetooth_offload_hci::IsoData;
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex, Weak};
+
+pub(crate) use aidl::android::hardware::bluetooth::offload::leaudio::StreamConfiguration::StreamConfiguration;
+
+pub(crate) struct Service {
+ state: Arc<Mutex<State>>,
+}
+
+#[derive(Default)]
+struct State {
+ arbiter: Weak<Arbiter>,
+ callbacks: Option<Strong<dyn IHciProxyCallbacks>>,
+ streams: HashMap<u16, StreamConfiguration>,
+}
+
+impl Service {
+ pub(crate) fn new() -> Self {
+ let state = Arc::new(Mutex::new(State::default()));
+ HciProxy::register(state.clone());
+ Self { state }
+ }
+
+ pub(crate) fn reset(&self, arbiter: Weak<Arbiter>) {
+ let mut state = self.state.lock().unwrap();
+ *state = State { arbiter, ..Default::default() }
+ }
+
+ pub(crate) fn start_stream(&self, handle: u16, config: StreamConfiguration) {
+ let mut state = self.state.lock().unwrap();
+ if let Some(callbacks) = &state.callbacks {
+ let _ = callbacks.startStream(handle.into(), &config);
+ } else {
+ log::warn!("Stream started without registered client");
+ };
+ state.streams.insert(handle, config);
+ }
+
+ pub(crate) fn stop_stream(&self, handle: u16) {
+ let mut state = self.state.lock().unwrap();
+ state.streams.remove(&handle);
+ if let Some(callbacks) = &state.callbacks {
+ let _ = callbacks.stopStream(handle.into());
+ };
+ }
+}
+
+struct HciProxy {
+ state: Arc<Mutex<State>>,
+ _death_recipient: DeathRecipient,
+}
+
+impl Interface for HciProxy {}
+
+impl HciProxy {
+ fn register(state: Arc<Mutex<State>>) {
+ let death_recipient = {
+ let state = state.clone();
+ DeathRecipient::new(move || {
+ log::info!("Client has died");
+ state.lock().unwrap().callbacks = None;
+ })
+ };
+
+ binder::add_service(
+ &format!("{}/default", BpHciProxy::get_descriptor()),
+ BnHciProxy::new_binder(
+ Self { state, _death_recipient: death_recipient },
+ BinderFeatures::default(),
+ )
+ .as_binder(),
+ )
+ .expect("Failed to register service");
+ }
+}
+
+impl IHciProxy for HciProxy {
+ fn registerCallbacks(&self, callbacks: &Strong<dyn IHciProxyCallbacks>) -> BinderResult<()> {
+ let mut state = self.state.lock().unwrap();
+ state.callbacks = Some(callbacks.clone());
+ for (handle, config) in &state.streams {
+ let _ = callbacks.startStream((*handle).into(), config);
+ }
+
+ Ok(())
+ }
+
+ fn sendPacket(&self, handle: i32, seqnum: i32, data: &[u8]) -> BinderResult<()> {
+ let handle: u16 = handle.try_into().map_err(|_| ExceptionCode::ILLEGAL_ARGUMENT)?;
+ let seqnum: u16 = seqnum.try_into().map_err(|_| ExceptionCode::ILLEGAL_ARGUMENT)?;
+
+ let state = self.state.lock().unwrap();
+ if let Some(arbiter) = state.arbiter.upgrade() {
+ arbiter.push_audio(&IsoData::new(handle, seqnum, data));
+ } else {
+ log::warn!("Trashing packet received in bad state");
+ }
+
+ Ok(())
+ }
+}
diff --git a/offload/leaudio/hci/tests.rs b/offload/leaudio/hci/tests.rs
new file mode 100644
index 0000000000..dd55a93c2c
--- /dev/null
+++ b/offload/leaudio/hci/tests.rs
@@ -0,0 +1,914 @@
+// Copyright 2024, 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.
+
+use bluetooth_offload_hci as hci;
+
+use crate::proxy::LeAudioModule;
+use hci::{CommandToBytes, EventToBytes, IsoData, Module, ReturnParameters, Status};
+use std::sync::{mpsc, Arc, Mutex};
+use std::time::Duration;
+
+struct ModuleSinkState {
+ out_cmd: Vec<Vec<u8>>,
+ in_evt: Vec<Vec<u8>>,
+ out_iso: mpsc::Receiver<Vec<u8>>,
+}
+
+struct ModuleSink {
+ state: Mutex<ModuleSinkState>,
+ out_iso: mpsc::Sender<Vec<u8>>,
+}
+
+impl ModuleSink {
+ fn new() -> Self {
+ let (out_iso_tx, out_iso_rx) = mpsc::channel();
+ ModuleSink {
+ state: Mutex::new(ModuleSinkState {
+ out_cmd: Default::default(),
+ in_evt: Default::default(),
+ out_iso: out_iso_rx,
+ }),
+ out_iso: out_iso_tx,
+ }
+ }
+}
+
+impl Module for ModuleSink {
+ fn out_cmd(&self, data: &[u8]) {
+ self.state.lock().unwrap().out_cmd.push(data.to_vec());
+ }
+ fn in_evt(&self, data: &[u8]) {
+ self.state.lock().unwrap().in_evt.push(data.to_vec());
+ }
+ fn out_iso(&self, data: &[u8]) {
+ self.out_iso.send(data.to_vec()).expect("Sending ISO packet");
+ }
+
+ fn next(&self) -> &dyn Module {
+ panic!();
+ }
+}
+
+#[test]
+fn cig() {
+ let sink: Arc<ModuleSink> = Arc::new(ModuleSink::new());
+ let m = LeAudioModule::new(sink.clone());
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::Reset(hci::ResetComplete {
+ status: Status::Success,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeReadBufferSizeV2(
+ hci::LeReadBufferSizeV2Complete {
+ status: Status::Success,
+ le_acl_data_packet_length: 0,
+ total_num_le_acl_data_packets: 0,
+ iso_data_packet_length: 16,
+ total_num_iso_data_packets: 2,
+ },
+ ),
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetCigParameters {
+ cig_id: 0x01,
+ sdu_interval_c_to_p: 10_000,
+ sdu_interval_p_to_c: 10_000,
+ worst_case_sca: 0,
+ packing: 0,
+ framing: 0,
+ max_transport_latency_c_to_p: 0,
+ max_transport_latency_p_to_c: 0,
+ cis: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetCigParameters(
+ hci::LeSetCigParametersComplete {
+ status: Status::Success,
+ cig_id: 0x01,
+ connection_handles: vec![0x123, 0x456],
+ },
+ ),
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::LeCisEstablished {
+ status: Status::Success,
+ connection_handle: 0x456,
+ cig_sync_delay: 0,
+ cis_sync_delay: 0,
+ transport_latency_c_to_p: 0,
+ transport_latency_p_to_c: 0,
+ phy_c_to_p: 0x02,
+ phy_p_to_c: 0x02,
+ nse: 0,
+ bn_c_to_p: 2,
+ bn_p_to_c: 2,
+ ft_c_to_p: 1,
+ ft_p_to_c: 1,
+ max_pdu_c_to_p: 10,
+ max_pdu_p_to_c: 0,
+ iso_interval: 20_000 / 1250,
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x456,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x456,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x456, 0, &[0x00, 0x11]).to_bytes());
+ m.out_iso(&IsoData::new(0x456, 1, &[]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x456, 2, &[0x22]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::LeCisEstablished {
+ status: Status::Success,
+ connection_handle: 0x123,
+ cig_sync_delay: 0,
+ cis_sync_delay: 0,
+ transport_latency_c_to_p: 0,
+ transport_latency_p_to_c: 0,
+ phy_c_to_p: 0x02,
+ phy_p_to_c: 0x02,
+ nse: 0,
+ bn_c_to_p: 2,
+ bn_p_to_c: 2,
+ ft_c_to_p: 1,
+ ft_p_to_c: 1,
+ max_pdu_c_to_p: 10,
+ max_pdu_p_to_c: 0,
+ iso_interval: 20_000 / 1250,
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x123, 0, &[]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::DisconnectionComplete {
+ status: Status::Success,
+ connection_handle: 0x456,
+ reason: 0,
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x456, 3, &[0x33]).to_bytes());
+ m.out_iso(&IsoData::new(0x123, 1, &[0x11, 0x22]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ {
+ let state = sink.state.lock().unwrap();
+ assert_eq!(state.out_cmd.len(), 2);
+ assert_eq!(state.in_evt.len(), 9);
+ }
+}
+
+#[test]
+fn big() {
+ let sink: Arc<ModuleSink> = Arc::new(ModuleSink::new());
+ let m = LeAudioModule::new(sink.clone());
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::Reset(hci::ResetComplete {
+ status: Status::Success,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeReadBufferSizeV2(
+ hci::LeReadBufferSizeV2Complete {
+ status: Status::Success,
+ le_acl_data_packet_length: 0,
+ total_num_le_acl_data_packets: 0,
+ iso_data_packet_length: 16,
+ total_num_iso_data_packets: 2,
+ },
+ ),
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeCreateBig {
+ big_handle: 0x10,
+ advertising_handle: 0,
+ num_bis: 2,
+ sdu_interval: 10_000,
+ max_sdu: 120,
+ max_transport_latency: 0,
+ rtn: 5,
+ phy: 0x02,
+ packing: 0,
+ framing: 0,
+ encryption: 0,
+ broadcast_code: [0u8; 16],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::LeCreateBigComplete {
+ status: Status::Success,
+ big_handle: 0x10,
+ big_sync_delay: 0,
+ big_transport_latency: 0,
+ phy: 0x02,
+ nse: 0,
+ bn: 2,
+ pto: 0,
+ irc: 0,
+ max_pdu: 10,
+ iso_interval: 20_000 / 1250,
+ bis_handles: vec![0x123, 0x456],
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x123,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x123,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x456,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x456,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x456, 0, &[0x00, 0x11]).to_bytes());
+ m.out_iso(&IsoData::new(0x456, 1, &[]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x123, 0, &[0x22, 0x33]).to_bytes());
+ m.out_iso(&IsoData::new(0x456, 2, &[]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![
+ hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ },
+ hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x123,
+ num_completed_packets: 1,
+ },
+ ],
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x123, 1, &[0x44]).to_bytes());
+ m.out_iso(&IsoData::new(0x123, 2, &[0x55, 0x66]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x123,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ m.out_iso(&IsoData::new(0x123, 3, &[]).to_bytes());
+ m.out_iso(&IsoData::new(0x123, 4, &[]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ {
+ let state = sink.state.lock().unwrap();
+ assert_eq!(state.out_cmd.len(), 3);
+ assert_eq!(state.in_evt.len(), 8);
+ }
+}
+
+#[test]
+fn merge() {
+ let sink: Arc<ModuleSink> = Arc::new(ModuleSink::new());
+ let m = LeAudioModule::new(sink.clone());
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::Reset(hci::ResetComplete {
+ status: Status::Success,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeReadBufferSizeV2(
+ hci::LeReadBufferSizeV2Complete {
+ status: Status::Success,
+ le_acl_data_packet_length: 0,
+ total_num_le_acl_data_packets: 0,
+ iso_data_packet_length: 16,
+ total_num_iso_data_packets: 2,
+ },
+ ),
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetCigParameters {
+ cig_id: 0x01,
+ sdu_interval_c_to_p: 10_000,
+ sdu_interval_p_to_c: 10_000,
+ worst_case_sca: 0,
+ packing: 0,
+ framing: 0,
+ max_transport_latency_c_to_p: 0,
+ max_transport_latency_p_to_c: 0,
+ cis: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetCigParameters(
+ hci::LeSetCigParametersComplete {
+ status: Status::Success,
+ cig_id: 0x01,
+ connection_handles: vec![0x123, 0x456],
+ },
+ ),
+ }
+ .to_bytes(),
+ );
+
+ // Establish CIS 0x123, using Software Offload path
+ // Establish CIS 0x456, using HCI (stack) path
+
+ m.in_evt(
+ &hci::LeCisEstablished {
+ status: Status::Success,
+ connection_handle: 0x123,
+ cig_sync_delay: 0,
+ cis_sync_delay: 0,
+ transport_latency_c_to_p: 0,
+ transport_latency_p_to_c: 0,
+ phy_c_to_p: 0x02,
+ phy_p_to_c: 0x02,
+ nse: 0,
+ bn_c_to_p: 2,
+ bn_p_to_c: 2,
+ ft_c_to_p: 1,
+ ft_p_to_c: 1,
+ max_pdu_c_to_p: 10,
+ max_pdu_p_to_c: 0,
+ iso_interval: 20_000 / 1250,
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::LeCisEstablished {
+ status: Status::Success,
+ connection_handle: 0x456,
+ cig_sync_delay: 0,
+ cis_sync_delay: 0,
+ transport_latency_c_to_p: 0,
+ transport_latency_p_to_c: 0,
+ phy_c_to_p: 0x02,
+ phy_p_to_c: 0x02,
+ nse: 0,
+ bn_c_to_p: 2,
+ bn_p_to_c: 2,
+ ft_c_to_p: 1,
+ ft_p_to_c: 1,
+ max_pdu_c_to_p: 10,
+ max_pdu_p_to_c: 0,
+ iso_interval: 20_000 / 1250,
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x123,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0x19,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x123,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x456,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x456,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(state.out_cmd.len(), 3);
+ assert_eq!(state.in_evt.len(), 7);
+ state.out_cmd.clear();
+ state.in_evt.clear();
+ }
+
+ // Send 2 Packets on 0x123
+ // -> The packets are sent to the controller, and fulfill the FIFO
+
+ m.arbiter().unwrap().push_audio(&IsoData::new(0x123, 1, &[0x44]));
+ m.arbiter().unwrap().push_audio(&IsoData::new(0x123, 2, &[0x55, 0x66]));
+ {
+ let state = sink.state.lock().unwrap();
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ state.out_iso.recv_timeout(Duration::from_millis(100)).expect("Receiving ISO Packet");
+ }
+
+ // Send 2 packets on 0x456
+ // -> The packets are buffered, the controller FIFO is full
+
+ m.out_iso(&IsoData::new(0x456, 1, &[0x11]).to_bytes());
+ m.out_iso(&IsoData::new(0x456, 2, &[0x22, 0x33]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Err(mpsc::RecvTimeoutError::Timeout),
+ );
+ }
+
+ // Acknowledge packet 1 on 0x123:
+ // -> The acknowledgment is filtered
+ // -> Packet 1 on 0x456 is tranmitted
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x123,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(state.in_evt.pop(), None);
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Ok(IsoData::new(0x456, 1, &[0x11]).to_bytes())
+ );
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Err(mpsc::RecvTimeoutError::Timeout),
+ );
+ }
+
+ // Link 0x123 disconnect (implicitly ack packet 2 on 0x123)
+ // -> Packet 2 on 0x456 is tranmitted
+
+ m.in_evt(
+ &hci::DisconnectionComplete {
+ status: Status::Success,
+ connection_handle: 0x123,
+ reason: 0,
+ }
+ .to_bytes(),
+ );
+
+ {
+ let state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Ok(IsoData::new(0x456, 2, &[0x22, 0x33]).to_bytes())
+ );
+ }
+
+ // Send packets and ack packets 1 and 2 on 0x456.
+ // -> Connection 0x123 is disconnected, ignored
+ // -> Packets 3, and 4 on 0x456 are sent
+ // -> Packet 5 on 0x456 is buffered
+
+ m.out_iso(&IsoData::new(0x456, 3, &[0x33]).to_bytes());
+ m.arbiter().unwrap().push_audio(&IsoData::new(0x123, 3, &[0x33]));
+ m.arbiter().unwrap().push_audio(&IsoData::new(0x123, 4, &[0x44, 0x55]));
+ m.out_iso(&IsoData::new(0x456, 4, &[0x44, 0x55]).to_bytes());
+ m.out_iso(&IsoData::new(0x456, 5, &[0x55, 0x66]).to_bytes());
+ {
+ let state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Err(mpsc::RecvTimeoutError::Timeout),
+ );
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.in_evt.pop(),
+ Some(
+ hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ )
+ );
+ assert_eq!(state.in_evt.len(), 1);
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Ok(IsoData::new(0x456, 3, &[0x33]).to_bytes())
+ );
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Ok(IsoData::new(0x456, 4, &[0x44, 0x55]).to_bytes())
+ );
+ state.in_evt.clear();
+ }
+
+ // Re-establish CIS 0x123, using Software Offload path
+
+ m.in_evt(
+ &hci::LeCisEstablished {
+ status: Status::Success,
+ connection_handle: 0x123,
+ cig_sync_delay: 0,
+ cis_sync_delay: 0,
+ transport_latency_c_to_p: 0,
+ transport_latency_p_to_c: 0,
+ phy_c_to_p: 0x02,
+ phy_p_to_c: 0x02,
+ nse: 0,
+ bn_c_to_p: 2,
+ bn_p_to_c: 2,
+ ft_c_to_p: 1,
+ ft_p_to_c: 1,
+ max_pdu_c_to_p: 10,
+ max_pdu_p_to_c: 0,
+ iso_interval: 20_000 / 1250,
+ }
+ .to_bytes(),
+ );
+
+ m.out_cmd(
+ &hci::LeSetupIsoDataPath {
+ connection_handle: 0x123,
+ data_path_direction: hci::LeDataPathDirection::Input,
+ data_path_id: 0x19,
+ codec_id: hci::LeCodecId {
+ coding_format: hci::CodingFormat::Transparent,
+ company_id: 0,
+ vendor_id: 0,
+ },
+ controller_delay: 0,
+ codec_configuration: vec![],
+ }
+ .to_bytes(),
+ );
+
+ m.in_evt(
+ &hci::CommandComplete {
+ num_hci_command_packets: 0,
+ return_parameters: ReturnParameters::LeSetupIsoDataPath(hci::LeIsoDataPathComplete {
+ status: Status::Success,
+ connection_handle: 0x123,
+ }),
+ }
+ .to_bytes(),
+ );
+
+ // Acknowledge packets 3 and 4 on 0x456
+ // -> Packet 5 on 0x456 is sent
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.in_evt.pop(),
+ Some(
+ hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 2,
+ }],
+ }
+ .to_bytes(),
+ )
+ );
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Ok(IsoData::new(0x456, 5, &[0x55, 0x66]).to_bytes())
+ );
+ assert_eq!(
+ state.out_iso.recv_timeout(Duration::from_millis(100)),
+ Err(mpsc::RecvTimeoutError::Timeout),
+ );
+ state.out_cmd.clear();
+ state.in_evt.clear();
+ }
+
+ // Acknowledge packet 5 on 0x456
+ // -> Controller FIFO is now empty
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.in_evt.pop(),
+ Some(
+ hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ )
+ );
+ }
+
+ // Send 1 packet on each CIS, and acknowledge them
+ // -> The CIS 0x123 is removed from "NumberOfCompletedPackets" event
+
+ m.out_iso(&IsoData::new(0x123, 0, &[]).to_bytes());
+ m.arbiter().unwrap().push_audio(&IsoData::new(0x456, 6, &[0x66, 0x77]));
+
+ {
+ let state = sink.state.lock().unwrap();
+ for _ in 0..2 {
+ let pkt = state.out_iso.recv_timeout(Duration::from_millis(100));
+ assert!(
+ pkt == Ok(IsoData::new(0x123, 0, &[]).to_bytes())
+ || pkt == Ok(IsoData::new(0x456, 6, &[0x66, 0x77]).to_bytes())
+ );
+ }
+ }
+
+ m.in_evt(
+ &hci::NumberOfCompletedPackets {
+ handles: vec![
+ hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ },
+ hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x123,
+ num_completed_packets: 1,
+ },
+ ],
+ }
+ .to_bytes(),
+ );
+
+ {
+ let mut state = sink.state.lock().unwrap();
+ assert_eq!(
+ state.in_evt.pop(),
+ Some(
+ hci::NumberOfCompletedPackets {
+ handles: vec![hci::NumberOfCompletedPacketsHandle {
+ connection_handle: 0x456,
+ num_completed_packets: 1,
+ }],
+ }
+ .to_bytes(),
+ )
+ );
+ }
+}
diff --git a/service/Android.bp b/service/Android.bp
index 5fbf29b91c..63fd7b8faa 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -88,12 +88,11 @@ java_library {
sdk_version: "system_server_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
visibility: [":__subpackages__"],
}
// Apply jarjaring before using library in the apex
-// TODO b/383863941 delete and merge with service-bluetooth-new
java_library {
name: "service-bluetooth",
static_libs: ["service-bluetooth-pre-jarjar"],
@@ -113,30 +112,6 @@ java_library {
sdk_version: "system_server_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
- visibility: ["//packages/modules/Bluetooth/apex"],
-}
-
-// Apply jarjaring before using library in the apex
-java_library {
- name: "service-bluetooth-new",
- static_libs: ["service-bluetooth-pre-jarjar"],
- installable: true,
-
- jarjar_rules: ":bluetooth-jarjar-rules",
-
- optimize: {
- enabled: true,
- shrink: true,
- proguard_flags_files: ["proguard.flags"],
- },
-
- libs: [
- "framework-bluetooth.impl",
- ],
-
- sdk_version: "system_server_current",
- min_sdk_version: "Tiramisu",
apex_available: ["com.android.bt"],
visibility: ["//packages/modules/Bluetooth/apex"],
}
@@ -153,7 +128,7 @@ java_library {
sdk_version: "system_server_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
}
java_library {
@@ -172,7 +147,7 @@ java_library {
sdk_version: "system_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
}
android_robolectric_test {
@@ -226,7 +201,6 @@ android_robolectric_test {
],
sdk_version: "test_current",
- upstream: true,
test_suites: ["general-tests"],
strict_mode: false,
}
diff --git a/service/aidl/Android.bp b/service/aidl/Android.bp
index d7adba534e..14980c7520 100644
--- a/service/aidl/Android.bp
+++ b/service/aidl/Android.bp
@@ -17,6 +17,6 @@ java_library {
libs: ["framework-annotations-lib"],
sdk_version: "module_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
}
diff --git a/service/change-ids/Android.bp b/service/change-ids/Android.bp
index 3544d6b1e2..b717d09e36 100644
--- a/service/change-ids/Android.bp
+++ b/service/change-ids/Android.bp
@@ -22,7 +22,7 @@ java_library {
libs: ["app-compat-annotations"],
sdk_version: "system_server_current",
min_sdk_version: "Tiramisu",
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
visibility: ["//packages/modules/Bluetooth/service:__subpackages__"],
}
diff --git a/service/src/com/android/server/bluetooth/BtPermissionUtils.java b/service/src/com/android/server/bluetooth/BtPermissionUtils.java
index b9e736b0f8..bdfbc9f304 100644
--- a/service/src/com/android/server/bluetooth/BtPermissionUtils.java
+++ b/service/src/com/android/server/bluetooth/BtPermissionUtils.java
@@ -96,7 +96,8 @@ class BtPermissionUtils {
return true;
}
- final String msg = "Need " + permission + " permission for " + source + ": " + message;
+ final String msg =
+ "Need " + permission + " permission for " + currentSource + ": " + message;
if (result == PERMISSION_HARD_DENIED) {
throw new SecurityException(msg);
}
diff --git a/service/tests/AndroidTest.xml b/service/tests/AndroidTest.xml
index 4e1be42419..c759326927 100644
--- a/service/tests/AndroidTest.xml
+++ b/service/tests/AndroidTest.xml
@@ -28,8 +28,7 @@
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="ServiceBluetoothTests" />
- <option name="config-descriptor:metadata" key="mainline-param"
- value="com.google.android.btservices.apex" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.bt.apex" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.server.bluetooth.test" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
@@ -39,7 +38,7 @@
<!-- Only run FrameworkBluetoothTests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
index 2cd702f233..b2042b5e79 100644
--- a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
+++ b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java
@@ -320,7 +320,7 @@ public class BluetoothManagerServiceTest {
verify(mContext)
.bindServiceAsUser(
any(Intent.class), captor.capture(), anyInt(), any(UserHandle.class));
- assertThat(captor.getAllValues().size()).isEqualTo(1);
+ assertThat(captor.getAllValues()).hasSize(1);
BluetoothManagerService.BluetoothServiceConnection serviceConnection =
captor.getAllValues().get(0);
@@ -334,7 +334,7 @@ public class BluetoothManagerServiceTest {
ArgumentCaptor<IBluetoothCallback> captor =
ArgumentCaptor.forClass(IBluetoothCallback.class);
verify(adapterBinder).registerCallback(captor.capture(), any());
- assertThat(captor.getAllValues().size()).isEqualTo(1);
+ assertThat(captor.getAllValues()).hasSize(1);
return captor.getValue();
}
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index e9d820b2f1..241e8711f7 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -20,7 +20,7 @@ sysprop_library {
cpp: {
min_sdk_version: "Tiramisu",
},
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
}
cc_library_static {
@@ -30,6 +30,6 @@ cc_library_static {
export_include_dirs: ["exported_include"],
export_static_lib_headers: ["libcom.android.sysprop.bluetooth"],
visibility: ["//packages/modules/Bluetooth/system:__subpackages__"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/sysprop/a2dp.sysprop b/sysprop/a2dp.sysprop
index 80b359c390..ae02fbbb99 100644
--- a/sysprop/a2dp.sysprop
+++ b/sysprop/a2dp.sysprop
@@ -9,3 +9,10 @@ prop {
prop_name: "bluetooth.a2dp.src_sink_coexist.enabled"
}
+prop {
+ api_name: "avdt_accept_open_timeout_ms"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "bluetooth.a2dp.avdt_accept_open_timeout_ms"
+} \ No newline at end of file
diff --git a/system/Android.bp b/system/Android.bp
index 1ecb8b81d7..9d47e06596 100644
--- a/system/Android.bp
+++ b/system/Android.bp
@@ -28,7 +28,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "30",
}
diff --git a/system/OWNERS b/system/OWNERS
index d60842e9c9..b2acb91716 100644
--- a/system/OWNERS
+++ b/system/OWNERS
@@ -10,3 +10,9 @@ poahlo@google.com
rongxuan@google.com
rwt@google.com
wescande@google.com
+
+# Reviewers for Channel Sounding related files
+per-file /bta/ras/*=file:/OWNERS_channel_sounding
+per-file /gd/hal/ranging_hal*=file:/OWNERS_channel_sounding
+per-file /gd/hci/distance_measurement_*=file:/OWNERS_channel_sounding
+per-file /main/shim/distance_measurement_manager*=file:/OWNERS_channel_sounding
diff --git a/system/audio/Android.bp b/system/audio/Android.bp
index 481182dc5f..98677c206a 100644
--- a/system/audio/Android.bp
+++ b/system/audio/Android.bp
@@ -41,9 +41,7 @@ cc_library_static {
],
host_supported: true,
min_sdk_version: "33",
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
}
cc_library_host_shared {
diff --git a/system/audio_hal_interface/Android.bp b/system/audio_hal_interface/Android.bp
index 363627401f..cc33db9a51 100644
--- a/system/audio_hal_interface/Android.bp
+++ b/system/audio_hal_interface/Android.bp
@@ -79,9 +79,7 @@ cc_library_static {
cflags: [
"-Wthread-safety",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
}
diff --git a/system/audio_hal_interface/aidl/le_audio_utils.cc b/system/audio_hal_interface/aidl/le_audio_utils.cc
index 1a6a4f4e4c..6cd96a423a 100644
--- a/system/audio_hal_interface/aidl/le_audio_utils.cc
+++ b/system/audio_hal_interface/aidl/le_audio_utils.cc
@@ -18,14 +18,23 @@
#include <com_android_bluetooth_flags.h>
+#include <iomanip>
#include <optional>
+#include <sstream>
+#include "aidl/android/hardware/bluetooth/audio/ConfigurationFlags.h"
+#include "bta/le_audio/gmap_server.h"
+#include "bta/le_audio/le_audio_types.h"
#include "hardware/bt_le_audio.h"
namespace bluetooth {
namespace audio {
namespace aidl {
+using ::aidl::android::hardware::bluetooth::audio::CodecInfo;
+using ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags;
+using ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProviderFactory;
+
::aidl::android::hardware::bluetooth::audio::CodecId GetAidlCodecIdFromStackFormat(
const ::bluetooth::le_audio::types::LeAudioCodecId& codec_id) {
::aidl::android::hardware::bluetooth::audio::CodecId codec;
@@ -507,6 +516,24 @@ static ::bluetooth::le_audio::set_configurations::AseConfiguration GetStackAseCo
return config;
}
+static std::string StackTargetLatencyToString(uint8_t target_latency) {
+ switch (target_latency) {
+ case ::bluetooth::le_audio::types::kTargetLatencyUndefined:
+ return "TargetLatencyUndefined";
+ case ::bluetooth::le_audio::types::kTargetLatencyLower:
+ return "LowLatency";
+ case ::bluetooth::le_audio::types::kTargetLatencyBalancedLatencyReliability:
+ return "BalancedReliability";
+ case ::bluetooth::le_audio::types::kTargetLatencyHigherReliability:
+ return "HighReliability";
+ default:
+ std::stringstream str;
+ str << "TargetLatencyUnknown (" << std::hex << std::setw(2) << std::setfill('0')
+ << target_latency << ")";
+ return str.str();
+ }
+}
+
static std::string GenerateNameForConfig(
const ::bluetooth::le_audio::set_configurations::AudioSetConfiguration& config) {
auto namegen = [](const std::vector<::bluetooth::le_audio::set_configurations::AseConfiguration>&
@@ -550,7 +577,7 @@ static std::string GenerateNameForConfig(
cfg_str << "_" << current_codec.GetDataIntervalUs() << "us";
}
// QoS
- cfg_str << "-TargetLatency_" << +current_config->qos.target_latency;
+ cfg_str << "-" << StackTargetLatencyToString(current_config->qos.target_latency);
if (last_equal_config == configs.end()) {
break;
@@ -607,6 +634,18 @@ GetStackConfigSettingFromAidl(
}
}
+ if (aidl_ase_config.flags.has_value()) {
+ if (aidl_ase_config.flags->bitmask &
+ ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags::
+ ALLOW_ASYMMETRIC_CONFIGURATIONS) {
+ log::debug("Asymmetric configuration support flag set.");
+ }
+ if (aidl_ase_config.flags->bitmask &
+ ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags::LOW_LATENCY) {
+ log::debug("Low latency support flag set.");
+ }
+ }
+
cig_config.name = GenerateNameForConfig(cig_config);
return cig_config;
}
@@ -625,6 +664,87 @@ GetStackUnicastConfigurationFromAidlFormat(
return stack_config;
}
+static bool isAsymmetricConfigurationSupported(
+ IBluetoothAudioProviderFactory::ProviderInfo const& provider_info) {
+ for (auto& codec_info : provider_info.codecInfos) {
+ if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio) {
+ return false;
+ }
+
+ auto flags = codec_info.transport.get<CodecInfo::Transport::leAudio>().flags;
+
+ if (!flags) {
+ continue;
+ }
+
+ if (flags->bitmask & ConfigurationFlags::ALLOW_ASYMMETRIC_CONFIGURATIONS) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool isLowLatencyConfigurationSupported(
+ IBluetoothAudioProviderFactory::ProviderInfo const& provider_info) {
+ for (auto& codec_info : provider_info.codecInfos) {
+ if (codec_info.transport.getTag() != CodecInfo::Transport::leAudio) {
+ return false;
+ }
+
+ auto flags = codec_info.transport.get<CodecInfo::Transport::leAudio>().flags;
+
+ if (!flags) {
+ continue;
+ }
+
+ if (flags->bitmask & ConfigurationFlags::LOW_LATENCY) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::optional<bluetooth::le_audio::ProviderInfo> GetStackProviderInfoFromAidl(
+ std::optional<IBluetoothAudioProviderFactory::ProviderInfo> const& encoding_provider_info,
+ std::optional<IBluetoothAudioProviderFactory::ProviderInfo> const& decoding_provider_info) {
+ (void)encoding_provider_info;
+ (void)decoding_provider_info;
+
+ if (!encoding_provider_info.has_value() && !decoding_provider_info.has_value()) {
+ log::error("Neither the encoding or decoding provider info are correct.");
+ return std::nullopt;
+ }
+
+ std::optional<bluetooth::le_audio::ProviderInfo> result = bluetooth::le_audio::ProviderInfo();
+ if (encoding_provider_info.has_value()) {
+ log::debug("Encoding: {}", encoding_provider_info->toString());
+ if (isAsymmetricConfigurationSupported(encoding_provider_info.value())) {
+ result->allowAsymmetric = true;
+ }
+
+ if (isLowLatencyConfigurationSupported(encoding_provider_info.value())) {
+ result->lowLatency = true;
+ }
+ }
+
+ if (decoding_provider_info.has_value()) {
+ // Iterate and print for the debugging purpose
+ log::debug("Decoding: {}", decoding_provider_info->toString());
+ if (isAsymmetricConfigurationSupported(decoding_provider_info.value())) {
+ result->allowAsymmetric = true;
+ }
+
+ if (isLowLatencyConfigurationSupported(decoding_provider_info.value())) {
+ result->lowLatency = true;
+ }
+ }
+
+ log::debug("Stack: {}", result->toString());
+ return result;
+}
+
::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioBroadcastConfigurationRequirement
GetAidlLeAudioBroadcastConfigurationRequirementFromStackFormat(
@@ -664,7 +784,8 @@ GetStackUnicastConfigurationFromAidlFormat(
DeviceDirectionRequirements>>& sink_reqs,
const std::optional<std::vector<
::bluetooth::le_audio::CodecManager::UnicastConfigurationRequirements::
- DeviceDirectionRequirements>>& source_reqs) {
+ DeviceDirectionRequirements>>& source_reqs,
+ ::bluetooth::le_audio::CodecManager::Flags flags) {
auto aidl_reqs = ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioConfigurationRequirement();
@@ -720,7 +841,19 @@ GetStackUnicastConfigurationFromAidlFormat(
aidl_reqs.audioContext.bitmask = (uint32_t)context_type;
// TODO(b/341935895): Add the feature flags mechanism in the stack
- // aidl_reqs.flags
+ if (flags != ::bluetooth::le_audio::CodecManager::Flags::NONE) {
+ aidl_reqs.flags =
+ std::make_optional<::aidl::android::hardware::bluetooth::audio::ConfigurationFlags>();
+ if (flags & ::bluetooth::le_audio::CodecManager::Flags::ALLOW_ASYMMETRIC) {
+ aidl_reqs.flags->bitmask |= ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags::
+ ALLOW_ASYMMETRIC_CONFIGURATIONS;
+ }
+
+ if (flags & ::bluetooth::le_audio::CodecManager::Flags::LOW_LATENCY) {
+ aidl_reqs.flags->bitmask |=
+ ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags::LOW_LATENCY;
+ }
+ }
return aidl_reqs;
}
diff --git a/system/audio_hal_interface/aidl/le_audio_utils.h b/system/audio_hal_interface/aidl/le_audio_utils.h
index df9d2e044e..25efabdc28 100644
--- a/system/audio_hal_interface/aidl/le_audio_utils.h
+++ b/system/audio_hal_interface/aidl/le_audio_utils.h
@@ -63,7 +63,8 @@ GetAidlLeAudioDeviceCapabilitiesFromStackFormat(
DeviceDirectionRequirements>>& sink_reqs,
const std::optional<std::vector<
::bluetooth::le_audio::CodecManager::UnicastConfigurationRequirements::
- DeviceDirectionRequirements>>& source_reqs);
+ DeviceDirectionRequirements>>& source_reqs,
+ ::bluetooth::le_audio::CodecManager::Flags flags);
::bluetooth::le_audio::types::LeAudioLtvMap GetStackLeAudioLtvMapFromAidlFormat(
const std::vector<
::aidl::android::hardware::bluetooth::audio::CodecSpecificConfigurationLtv>&
@@ -94,6 +95,12 @@ GetStackUnicastConfigurationFromAidlFormat(
const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioAseConfigurationSetting& config);
+std::optional<bluetooth::le_audio::ProviderInfo> GetStackProviderInfoFromAidl(
+ std::optional<::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProviderFactory::
+ ProviderInfo> const& encoding_provider_info,
+ std::optional<::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProviderFactory::
+ ProviderInfo> const& decoding_provider_info);
+
} // namespace aidl
} // namespace audio
} // namespace bluetooth
diff --git a/system/audio_hal_interface/aidl/le_audio_utils_unittest.cc b/system/audio_hal_interface/aidl/le_audio_utils_unittest.cc
index 98fee01e5b..669c4fb4b0 100644
--- a/system/audio_hal_interface/aidl/le_audio_utils_unittest.cc
+++ b/system/audio_hal_interface/aidl/le_audio_utils_unittest.cc
@@ -919,8 +919,8 @@ TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackUnicastConfigurationFrom
ASSERT_EQ(stack_config->confs.source.size(), 2ul);
ASSERT_EQ(*stack_config, expected_stack_config);
ASSERT_EQ(stack_config->name,
- "AIDL-2-1chan-SinkAse-CodecId_6_0_0-48000hz_120oct_7500us-TargetLatency_2-"
- "2-1chan-SourceAse-CodecId_6_0_0-24000hz_80oct_7500us-TargetLatency_1");
+ "AIDL-2-1chan-SinkAse-CodecId_6_0_0-48000hz_120oct_7500us-BalancedReliability-"
+ "2-1chan-SourceAse-CodecId_6_0_0-24000hz_80oct_7500us-LowLatency");
}
TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackUnicastConfigurationFromAidlFormatMonoLoc) {
@@ -935,8 +935,8 @@ TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackUnicastConfigurationFrom
ASSERT_EQ(stack_config->confs.source.size(), 1ul);
ASSERT_EQ(*stack_config, expected_stack_config);
ASSERT_EQ(stack_config->name,
- "AIDL-2-1chan-SinkAse-CodecId_6_0_0-48000hz_120oct_7500us-TargetLatency_2-"
- "1-1chan-SourceAse-CodecId_6_0_0-24000hz_80oct_7500us-TargetLatency_1");
+ "AIDL-2-1chan-SinkAse-CodecId_6_0_0-48000hz_120oct_7500us-BalancedReliability-"
+ "1-1chan-SourceAse-CodecId_6_0_0-24000hz_80oct_7500us-LowLatency");
}
TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackBisConfigFromAidlFormat) {
@@ -1007,8 +1007,10 @@ TEST(BluetoothAudioClientInterfaceAidlTest,
aidl_req_l, aidl_req_r};
reference_aidl_requirements.sourceAseRequirement = reference_aidl_requirements.sinkAseRequirement;
+ ::bluetooth::le_audio::CodecManager::Flags flags =
+ ::bluetooth::le_audio::CodecManager::Flags::NONE;
auto aidl_requirements = GetAidlLeAudioUnicastConfigurationRequirementsFromStackFormat(
- stack_context, stack_sink_reqs, stack_source_reqs);
+ stack_context, stack_sink_reqs, stack_source_reqs, flags);
ASSERT_EQ(aidl_requirements.audioContext, reference_aidl_requirements.audioContext);
ASSERT_EQ(aidl_requirements.flags, reference_aidl_requirements.flags);
diff --git a/system/audio_hal_interface/le_audio_software.cc b/system/audio_hal_interface/le_audio_software.cc
index d58483e340..4d6cd88010 100644
--- a/system/audio_hal_interface/le_audio_software.cc
+++ b/system/audio_hal_interface/le_audio_software.cc
@@ -25,6 +25,7 @@
#include <vector>
#include "aidl/android/hardware/bluetooth/audio/AudioContext.h"
+#include "aidl/client_interface_aidl.h"
#include "aidl/le_audio_software_aidl.h"
#include "aidl/le_audio_utils.h"
#include "bta/le_audio/codec_manager.h"
@@ -36,10 +37,12 @@
namespace bluetooth {
namespace audio {
+using aidl::BluetoothAudioClientInterface;
using aidl::GetAidlLeAudioBroadcastConfigurationRequirementFromStackFormat;
using aidl::GetAidlLeAudioDeviceCapabilitiesFromStackFormat;
using aidl::GetAidlLeAudioUnicastConfigurationRequirementsFromStackFormat;
using aidl::GetStackBroadcastConfigurationFromAidlFormat;
+using aidl::GetStackProviderInfoFromAidl;
using aidl::GetStackUnicastConfigurationFromAidlFormat;
namespace le_audio {
@@ -53,6 +56,7 @@ using ::aidl::android::hardware::bluetooth::audio::AudioContext;
using ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider;
using ::aidl::android::hardware::bluetooth::audio::LatencyMode;
using ::aidl::android::hardware::bluetooth::audio::LeAudioCodecConfiguration;
+using ::aidl::android::hardware::bluetooth::audio::SessionType;
using ::bluetooth::le_audio::CodecManager;
using ::bluetooth::le_audio::set_configurations::AudioSetConfiguration;
@@ -329,7 +333,7 @@ LeAudioClientInterface::Sink::GetUnicastConfig(
std::vector<IBluetoothAudioProvider::LeAudioConfigurationRequirement> reqs;
reqs.push_back(GetAidlLeAudioUnicastConfigurationRequirementsFromStackFormat(
requirements.audio_context_type, requirements.sink_requirements,
- requirements.source_requirements));
+ requirements.source_requirements, requirements.flags));
log::debug("Making an AIDL call");
auto aidl_configs = get_aidl_client_interface(is_broadcaster_)
@@ -830,6 +834,26 @@ void LeAudioClientInterface::SetAllowedDsaModes(DsaModes dsa_modes) {
}
}
+std::optional<bluetooth::le_audio::ProviderInfo> LeAudioClientInterface::GetCodecConfigProviderInfo(
+ void) const {
+ if (HalVersionManager::GetHalTransport() != BluetoothAudioHalTransport::AIDL) {
+ log::error("Not using an AIDL HAL transport. Provider Info is not available.");
+ return std::nullopt;
+ }
+
+ auto encoding_provider_info = BluetoothAudioClientInterface::GetProviderInfo(
+ SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH, nullptr);
+
+ auto decoding_provider_info = BluetoothAudioClientInterface::GetProviderInfo(
+ SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH, nullptr);
+
+ if (!encoding_provider_info.has_value() && !decoding_provider_info.has_value()) {
+ log::info("LE Audio offload codec extensibility is enabled, but the provider info is empty");
+ return std::nullopt;
+ }
+
+ return GetStackProviderInfoFromAidl(encoding_provider_info, decoding_provider_info);
+}
} // namespace le_audio
} // namespace audio
} // namespace bluetooth
diff --git a/system/audio_hal_interface/le_audio_software.h b/system/audio_hal_interface/le_audio_software.h
index 67d804753a..fbc6f4400f 100644
--- a/system/audio_hal_interface/le_audio_software.h
+++ b/system/audio_hal_interface/le_audio_software.h
@@ -178,6 +178,9 @@ public:
// singleton.
static LeAudioClientInterface* Get();
+ // Get the Codec Configuration Provider info
+ std::optional<bluetooth::le_audio::ProviderInfo> GetCodecConfigProviderInfo(void) const;
+
private:
static LeAudioClientInterface* interface;
Sink* unicast_sink_ = nullptr;
diff --git a/system/audio_hal_interface/le_audio_software_host.cc b/system/audio_hal_interface/le_audio_software_host.cc
index 28f2592a6c..fba4c77716 100644
--- a/system/audio_hal_interface/le_audio_software_host.cc
+++ b/system/audio_hal_interface/le_audio_software_host.cc
@@ -570,6 +570,11 @@ LeAudioClientInterface* LeAudioClientInterface::Get() {
void LeAudioClientInterface::SetAllowedDsaModes(DsaModes /*dsa_modes*/) { return; }
+std::optional<bluetooth::le_audio::ProviderInfo> LeAudioClientInterface::GetCodecConfigProviderInfo(
+ void) const {
+ return std::nullopt;
+}
+
} // namespace le_audio
} // namespace audio
} // namespace bluetooth
diff --git a/system/audio_hal_interface/le_audio_software_unittest.cc b/system/audio_hal_interface/le_audio_software_unittest.cc
index fdf03dd65c..21db531e07 100644
--- a/system/audio_hal_interface/le_audio_software_unittest.cc
+++ b/system/audio_hal_interface/le_audio_software_unittest.cc
@@ -130,6 +130,10 @@ public:
IBluetoothAudioProvider::LeAudioDeviceCapabilities>>>&),
(const ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
LeAudioBroadcastConfigurationRequirement&)));
+ MOCK_METHOD(std::optional<bluetooth::audio::aidl::IBluetoothAudioProviderFactory::ProviderInfo>,
+ GetProviderInfo,
+ ((bluetooth::audio::aidl::SessionType),
+ (std::shared_ptr<bluetooth::audio::aidl::IBluetoothAudioProviderFactory>)));
static void SetInstance(MockBluetoothAudioClientInterfaceAidl* ptr) { instance_ptr = ptr; }
@@ -416,6 +420,17 @@ std::vector<AudioCapabilities> BluetoothAudioClientInterface::GetAudioCapabiliti
return std::vector<AudioCapabilities>(0);
}
+std::optional<bluetooth::audio::aidl::IBluetoothAudioProviderFactory::ProviderInfo>
+BluetoothAudioClientInterface::GetProviderInfo(
+ bluetooth::audio::aidl::SessionType session_type,
+ std::shared_ptr<bluetooth::audio::aidl::IBluetoothAudioProviderFactory> provider_factory) {
+ auto instance = MockBluetoothAudioClientInterfaceAidl::GetInstance();
+ if (instance) {
+ return instance->GetProviderInfo(session_type, provider_factory);
+ }
+ return std::nullopt;
+}
+
std::vector<IBluetoothAudioProvider::LeAudioAseConfigurationSetting>
BluetoothAudioClientInterface::GetLeAudioAseConfiguration(
std::optional<
diff --git a/system/audio_hearing_aid_hw/Android.bp b/system/audio_hearing_aid_hw/Android.bp
index e8e83e86c8..afc160d599 100644
--- a/system/audio_hearing_aid_hw/Android.bp
+++ b/system/audio_hearing_aid_hw/Android.bp
@@ -27,9 +27,7 @@ cc_library {
"src/audio_hearing_aid_hw.cc",
"src/audio_hearing_aid_hw_utils.cc",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
shared_libs: [
"libbase",
"liblog",
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index efa1d25fd1..57fd56b039 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -168,9 +168,7 @@ cc_library_static {
],
},
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -225,9 +223,7 @@ cc_library_static {
shared_libs: [
"libcrypto",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -816,7 +812,6 @@ cc_test {
"libbluetooth_crypto_toolbox",
"libbluetooth_gd",
"libbluetooth_log",
- "libbt-audio-hal-interface",
"libbt-common",
"libbt-platform-protos-lite",
"libchrome",
@@ -825,6 +820,7 @@ cc_test {
"libflatbuffers-cpp",
"libgmock",
"libosi",
+ "libudrv-uipc",
"server_configurable_flags",
],
sanitize: {
@@ -871,6 +867,7 @@ cc_test {
":TestMockStackL2cap",
":TestStubOsi",
"gmap/gmap_client.cc",
+ "gmap/gmap_server.cc",
"le_audio/audio_hal_client/audio_hal_client_test.cc",
"le_audio/audio_hal_client/audio_sink_hal_client.cc",
"le_audio/audio_hal_client/audio_source_hal_client.cc",
@@ -1066,6 +1063,95 @@ cc_test {
}
cc_test {
+ name: "bluetooth_ras_test",
+ test_suites: ["general-tests"],
+ defaults: [
+ "fluoride_bta_defaults",
+ "mts_defaults",
+ ],
+ host_supported: true,
+ isolated: false,
+ include_dirs: [
+ "packages/modules/Bluetooth/system",
+ "packages/modules/Bluetooth/system/bta/include",
+ "packages/modules/Bluetooth/system/bta/test/common",
+ "packages/modules/Bluetooth/system/stack/include",
+ ],
+ srcs: [
+ ":TestCommonMockFunctions",
+ ":TestMockBtaGatt",
+ ":TestMockMainShim",
+ ":TestMockMainShimEntry",
+ ":TestMockStackBtm",
+ ":TestMockStackBtmInterface",
+ ":TestMockStackBtmIso",
+ ":TestMockStackGatt",
+ ":TestMockStackL2cap",
+ ":TestStubOsi",
+ "gatt/database.cc",
+ "gatt/database_builder.cc",
+ "ras/ras_utils.cc",
+ "ras/ras_utils_test.cc",
+ "test/common/bta_gatt_queue_mock.cc",
+ "test/common/btif_storage_mock.cc",
+ "test/common/mock_device_groups.cc",
+ ],
+ shared_libs: [
+ "libaconfig_storage_read_api_cc",
+ "libbase",
+ "libcrypto",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ ],
+ static_libs: [
+ "bluetooth_flags_c_lib_for_test",
+ "libbluetooth-types",
+ "libbluetooth_crypto_toolbox",
+ "libbluetooth_gd",
+ "libbluetooth_log",
+ "libbt-audio-hal-interface",
+ "libbt-common",
+ "libbt-platform-protos-lite",
+ "libchrome",
+ "libcom.android.sysprop.bluetooth.wrapped",
+ "libevent",
+ "libflagtest",
+ "libflatbuffers-cpp",
+ "libgmock",
+ "libgtest",
+ "liblc3",
+ "libosi",
+ "server_configurable_flags",
+ ],
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "libPlatformProperties",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder_ndk",
+ ],
+ },
+ },
+ sanitize: {
+ cfi: true,
+ scs: true,
+ address: true,
+ all_undefined: true,
+ integer_overflow: true,
+ diag: {
+ undefined: true,
+ },
+ },
+}
+
+cc_test {
name: "bluetooth_test_broadcaster_state_machine",
test_suites: ["general-tests"],
defaults: [
diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h
index f6026815a6..f10cc716cc 100644
--- a/system/bta/ag/bta_ag_int.h
+++ b/system/bta/ag/bta_ag_int.h
@@ -134,6 +134,24 @@ typedef enum : uint8_t {
BTA_AG_SCO_SHUTTING_ST /* sco shutting down */
} tBTA_AG_SCO;
+inline std::string bta_ag_sco_state_text(const tBTA_AG_SCO& state) {
+ switch (state) {
+ CASE_RETURN_TEXT(BTA_AG_SCO_SHUTDOWN_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_LISTEN_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_CODEC_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_OPENING_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_OPEN_CL_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_OPEN_XFER_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_OPEN_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_CLOSING_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_CLOSE_OP_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_CLOSE_XFER_ST);
+ CASE_RETURN_TEXT(BTA_AG_SCO_SHUTTING_ST);
+ default:
+ return std::string("unknown_bta_ag_sco_state: ") +
+ std::to_string(static_cast<uint8_t>(state));
+ }
+}
/*****************************************************************************
* Data types
****************************************************************************/
@@ -245,7 +263,7 @@ struct formatter<tBTA_AG_SCO_LC3_SETTINGS> : enum_formatter<tBTA_AG_SCO_LC3_SETT
template <>
struct formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> : enum_formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> {};
template <>
-struct formatter<tBTA_AG_SCO> : enum_formatter<tBTA_AG_SCO> {};
+struct formatter<tBTA_AG_SCO> : string_formatter<tBTA_AG_SCO, &bta_ag_sco_state_text> {};
} // namespace std
/* state machine states */
diff --git a/system/bta/ag/bta_ag_sco.cc b/system/bta/ag/bta_ag_sco.cc
index 1c62678576..f2c2be8aeb 100644
--- a/system/bta/ag/bta_ag_sco.cc
+++ b/system/bta/ag/bta_ag_sco.cc
@@ -122,24 +122,6 @@ static const char* bta_ag_sco_evt_str(uint8_t event) {
}
}
-static const char* bta_ag_sco_state_str(uint8_t state) {
- switch (state) {
- CASE_RETURN_STR(BTA_AG_SCO_SHUTDOWN_ST)
- CASE_RETURN_STR(BTA_AG_SCO_LISTEN_ST)
- CASE_RETURN_STR(BTA_AG_SCO_CODEC_ST)
- CASE_RETURN_STR(BTA_AG_SCO_OPENING_ST)
- CASE_RETURN_STR(BTA_AG_SCO_OPEN_CL_ST)
- CASE_RETURN_STR(BTA_AG_SCO_OPEN_XFER_ST)
- CASE_RETURN_STR(BTA_AG_SCO_OPEN_ST)
- CASE_RETURN_STR(BTA_AG_SCO_CLOSING_ST)
- CASE_RETURN_STR(BTA_AG_SCO_CLOSE_OP_ST)
- CASE_RETURN_STR(BTA_AG_SCO_CLOSE_XFER_ST)
- CASE_RETURN_STR(BTA_AG_SCO_SHUTTING_ST)
- default:
- return "Unknown SCO State";
- }
-}
-
static int codec_uuid_to_sample_rate(tBTA_AG_UUID_CODEC codec) {
int sample_rate;
switch (codec) {
@@ -223,8 +205,7 @@ static void bta_ag_sco_conn_cback(uint16_t sco_idx) {
static void bta_ag_sco_disc_cback(uint16_t sco_idx) {
uint16_t handle = 0;
- log::debug("sco_idx: 0x{:x} sco.state:{}", sco_idx,
- sco_state_text(static_cast<tSCO_STATE>(bta_ag_cb.sco.state)));
+ log::debug("sco_idx: 0x{:x} sco.state:{}", sco_idx, bta_ag_cb.sco.state);
log::debug("scb[0] in_use:{} sco_idx: 0x{:x} ag state:{}", bta_ag_cb.scb[0].in_use,
bta_ag_cb.scb[0].sco_idx, bta_ag_state_str(bta_ag_cb.scb[0].state));
log::debug("scb[1] in_use:{} sco_idx:0x{:x} ag state:{}", bta_ag_cb.scb[1].in_use,
@@ -752,9 +733,10 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB* p_scb) {
static void bta_ag_sco_event(tBTA_AG_SCB* p_scb, uint8_t event) {
tBTA_AG_SCO_CB* p_sco = &bta_ag_cb.sco;
- uint8_t previous_state = p_sco->state;
- log::info("device:{} index:0x{:04x} state:{}[{}] event:{}[{}]", p_scb->peer_addr, p_scb->sco_idx,
- bta_ag_sco_state_str(p_sco->state), p_sco->state, bta_ag_sco_evt_str(event), event);
+ tBTA_AG_SCO previous_state = p_sco->state;
+ log::info("device:{} index:0x{:04x} state:{}[0x{:02x}] event:{}[{}]", p_scb->peer_addr,
+ p_scb->sco_idx, p_sco->state, static_cast<uint8_t>(p_sco->state),
+ bta_ag_sco_evt_str(event), event);
switch (p_sco->state) {
case BTA_AG_SCO_SHUTDOWN_ST:
@@ -1244,8 +1226,8 @@ static void bta_ag_sco_event(tBTA_AG_SCB* p_scb, uint8_t event) {
log::warn(
"SCO_state_change: [{}(0x{:02x})]->[{}(0x{:02x})] after event "
"[{}(0x{:02x})]",
- bta_ag_sco_state_str(previous_state), previous_state,
- bta_ag_sco_state_str(p_sco->state), p_sco->state, bta_ag_sco_evt_str(event), event);
+ previous_state, static_cast<uint8_t>(previous_state), p_sco->state,
+ static_cast<uint8_t>(p_sco->state), bta_ag_sco_evt_str(event), event);
}
}
diff --git a/system/bta/aics/Android.bp b/system/bta/aics/Android.bp
index 33bd649d13..acb6b4945b 100644
--- a/system/bta/aics/Android.bp
+++ b/system/bta/aics/Android.bp
@@ -4,7 +4,7 @@ cc_library_headers {
host_supported: true,
vendor_available: true, // remove when https://r.android.com/3302734 is merged
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
@@ -27,6 +27,6 @@ cc_library {
export_header_lib_headers: ["aics_headers"],
host_supported: true,
visibility: ["//packages/modules/Bluetooth:__subpackages__"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc
index 8cb73a4402..769cefc4d9 100644
--- a/system/bta/av/bta_av_aact.cc
+++ b/system/bta/av/bta_av_aact.cc
@@ -26,6 +26,7 @@
#define LOG_TAG "bluetooth-a2dp"
+#include <android_bluetooth_sysprop.h>
#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
@@ -877,17 +878,6 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) {
alarm_cancel(p_scb->accept_open_timer);
}
- /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION
- vendor_get_interface()->send_command(
- (vendor_opcode_t)BT_VND_OP_A2DP_OFFLOAD_STOP, (void*)&p_scb->l2c_cid);
- if (p_scb->offload_start_pending) {
- tBTA_AV_STATUS status = BTA_AV_FAIL_STREAM;
- tBTA_AV bta_av_data;
- bta_av_data.status = status;
- (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data);
- }
- */
-
if (p_scb->deregistering) {
/* remove stream */
for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
@@ -1134,7 +1124,11 @@ void bta_av_setconfig_rsp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
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,
+ const uint64_t accept_open_timeout =
+ android::sysprop::bluetooth::A2dp::avdt_accept_open_timeout_ms().value_or(
+ BTA_AV_ACCEPT_OPEN_TIMEOUT_MS);
+ log::debug("accept_open_timeout = {} ms", accept_open_timeout);
+ alarm_set_on_mloop(p_scb->accept_open_timer, accept_open_timeout,
bta_av_accept_open_timer_cback, UINT_TO_PTR(p_scb->hdi));
}
}
@@ -3180,67 +3174,32 @@ void bta_av_vendor_offload_stop() {
*
******************************************************************************/
void bta_av_offload_req(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /*p_data*/) {
- tBTA_AV_STATUS status = BTA_AV_FAIL_RESOURCES;
-
+ tBTA_AV bta_av_data = {};
tBT_A2DP_OFFLOAD offload_start;
log::verbose("stream {}, audio channels open {}", p_scb->started ? "STARTED" : "STOPPED",
bta_av_cb.audio_open_cnt);
- A2dpCodecConfig* codec_config = bta_av_get_a2dp_current_codec();
- log::assert_that(codec_config != nullptr, "assert failed: codec_config != nullptr");
+ if (!p_scb->started) {
+ log::warn("stream not started, start offload failed.");
+ bta_av_data.status = BTA_AV_FAIL_STREAM;
+ (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data);
+ return;
+ }
- /* Check if stream has already been started. */
- /* Support offload if only one audio source stream is open. */
- if (p_scb->started != true) {
- status = BTA_AV_FAIL_STREAM;
- } else if (bta_av_cb.offload_start_pending_hndl || bta_av_cb.offload_started_hndl) {
+ if (bta_av_cb.offload_start_pending_hndl || bta_av_cb.offload_started_hndl) {
log::warn("offload already started, ignore request");
return;
- } else if (::bluetooth::audio::a2dp::provider::supports_codec(codec_config->codecIndex())) {
+ }
+
+ A2dpCodecConfig* codec_config = bta_av_get_a2dp_current_codec();
+ log::assert_that(codec_config != nullptr, "assert failed: codec_config != nullptr");
+
+ if (::bluetooth::audio::a2dp::provider::supports_codec(codec_config->codecIndex())) {
bta_av_vendor_offload_start_v2(p_scb, static_cast<A2dpCodecConfigExt*>(codec_config));
} else {
bta_av_offload_codec_builder(p_scb, &offload_start);
bta_av_vendor_offload_start(p_scb, &offload_start);
- return;
- }
- if (status != BTA_AV_SUCCESS) {
- tBTA_AV bta_av_data;
- bta_av_data.status = status;
- (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data);
- }
- /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION
- else if (bta_av_cb.audio_open_cnt == 1 &&
- p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC &&
- p_scb->chnl == BTA_AV_CHNL_AUDIO) {
- bt_vendor_op_a2dp_offload_t a2dp_offload_start;
-
- if (L2CA_GetConnectionConfig(
- p_scb->l2c_cid, &a2dp_offload_start.acl_data_size,
- &a2dp_offload_start.remote_cid, &a2dp_offload_start.lm_handle)) {
- log::verbose("l2cmtu {} lcid 0x{:02X} rcid 0x{:02X} lm_handle 0x{:02X}",
- a2dp_offload_start.acl_data_size, p_scb->l2c_cid,
- a2dp_offload_start.remote_cid, a2dp_offload_start.lm_handle);
-
- a2dp_offload_start.bta_av_handle = p_scb->hndl;
- a2dp_offload_start.xmit_quota = BTA_AV_A2DP_OFFLOAD_XMIT_QUOTA;
- a2dp_offload_start.stream_mtu = p_scb->stream_mtu;
- a2dp_offload_start.local_cid = p_scb->l2c_cid;
- a2dp_offload_start.is_flushable = true;
- a2dp_offload_start.stream_source =
- ((uint32_t)(p_scb->cfg.codec_info[1] | p_scb->cfg.codec_info[2]));
-
- memcpy(a2dp_offload_start.codec_info, p_scb->cfg.codec_info,
- sizeof(a2dp_offload_start.codec_info));
-
- if (!vendor_get_interface()->send_command(
- (vendor_opcode_t)BT_VND_OP_A2DP_OFFLOAD_START,
- &a2dp_offload_start)) {
- status = BTA_AV_SUCCESS;
- p_scb->offload_start_pending = true;
- }
- }
}
- */
}
/*******************************************************************************
@@ -3402,4 +3361,4 @@ static void bta_av_accept_open_timer_cback(void* data) {
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
+}
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 916b1c0c56..1abf97356e 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -138,6 +138,7 @@ public:
CsisClientImpl(bluetooth::csis::CsisClientCallbacks* callbacks, Closure initCb)
: gatt_if_(0), callbacks_(callbacks) {
BTA_GATTC_AppRegister(
+ "csis",
[](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
if (instance && p_data) {
instance->GattcCallback(event, p_data);
@@ -1923,7 +1924,7 @@ private:
BtaGattQueue::Clean(evt.conn_id);
}
/* Verify bond */
- if (BTM_SecIsSecurityPending(device->addr)) {
+ if (BTM_SecIsLeSecurityPending(device->addr)) {
/* if security collision happened, wait for encryption done
* (BTA_GATTC_ENC_CMPL_CB_EVT) */
return;
diff --git a/system/bta/csis/csis_client_test.cc b/system/bta/csis/csis_client_test.cc
index 42ee0abb36..adc66066f2 100644
--- a/system/bta/csis/csis_client_test.cc
+++ b/system/bta/csis/csis_client_test.cc
@@ -437,8 +437,8 @@ protected:
void TestAppRegister(void) {
BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
CsisClient::Initialize(callbacks.get(), Bind(&btif_storage_load_bonded_csis_devices));
ASSERT_TRUE(gatt_callback);
ASSERT_TRUE(app_register_callback);
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 7a931a6248..eefec6d85b 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -25,6 +25,8 @@
#define LOG_TAG "bt_bta_dm"
+#include "bta/dm/bta_dm_act.h"
+
#include <android_bluetooth_sysprop.h>
#include <base/location.h>
#include <bluetooth/log.h>
@@ -39,12 +41,15 @@
#include "bta/dm/bta_dm_int.h"
#include "bta/dm/bta_dm_sec_int.h"
#include "bta/include/bta_api.h"
+#include "bta/include/bta_dm_acl.h"
+#include "bta/include/bta_dm_api.h"
#include "bta/include/bta_le_audio_api.h"
#include "bta/include/bta_sdp_api.h"
#include "bta/include/bta_sec_api.h"
#include "bta/sys/bta_sys.h"
#include "btif/include/btif_dm.h"
#include "btif/include/stack_manager_t.h"
+#include "gd/os/rand.h"
#include "hci/controller_interface.h"
#include "internal_include/bt_target.h"
#include "main/shim/acl_api.h"
@@ -54,6 +59,7 @@
#include "osi/include/properties.h"
#include "stack/connection_manager/connection_manager.h"
#include "stack/include/acl_api.h"
+#include "stack/include/ble_scanner.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/bt_types.h"
#include "stack/include/bt_uuid16.h"
@@ -66,20 +72,16 @@
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using bluetooth::Uuid;
using namespace bluetooth;
-bool ble_vnd_is_included();
-void btm_ble_scanner_init(void);
+static bool ble_vnd_is_included() {
+ // replace build time config BLE_VND_INCLUDED with runtime
+ return android::sysprop::bluetooth::Ble::vnd_included().value_or(true);
+}
static void bta_dm_check_av();
-void BTA_dm_update_policy(tBTA_SYS_CONN_STATUS status, uint8_t id, uint8_t app_id,
- const RawAddress& peer_addr);
-
/* Extended Inquiry Response */
static void bta_dm_set_eir(char* local_name);
@@ -87,7 +89,6 @@ static void bta_dm_disable_conn_down_timer_cback(void* data);
static void bta_dm_rm_cback(tBTA_SYS_CONN_STATUS status, tBTA_SYS_ID id, uint8_t app_id,
const RawAddress& peer_addr);
static void bta_dm_adjust_roles(bool delay_role_switch);
-tBTM_CONTRL_STATE bta_dm_pm_obtain_controller_state(void);
static void bta_dm_ctrl_features_rd_cmpl_cback(tHCI_STATUS result);
static const char kPropertySniffOffloadEnabled[] = "persist.bluetooth.sniff_offload.enabled";
@@ -116,6 +117,14 @@ static const char kPropertySniffOffloadEnabled[] = "persist.bluetooth.sniff_offl
#define BTA_DM_SWITCH_DELAY_TIMER_MS 500
#endif
+/* New swich delay values behind flag extend_and_randomize_role_switch_delay (in milliseconds) */
+#ifndef BTA_DM_MAX_SWITCH_DELAY_MS
+#define BTA_DM_MAX_SWITCH_DELAY_MS 1500
+#endif
+#ifndef BTA_DM_MIN_SWITCH_DELAY_MS
+#define BTA_DM_MIN_SWITCH_DELAY_MS 1000
+#endif
+
/* Sysprop path for page timeout */
#ifndef PROPERTY_PAGE_TIMEOUT
#define PROPERTY_PAGE_TIMEOUT "bluetooth.core.classic.page_timeout"
@@ -743,7 +752,7 @@ void BTA_dm_report_role_change(const RawAddress bd_addr, tHCI_ROLE new_role,
do_in_main_thread(base::BindOnce(handle_role_change, bd_addr, new_role, hci_status));
}
-void handle_remote_features_complete(const RawAddress& bd_addr) {
+static void handle_remote_features_complete(const RawAddress& bd_addr) {
tBTA_DM_PEER_DEVICE* p_dev = bta_dm_find_peer_device(bd_addr);
if (!p_dev) {
log::warn("Unable to find device peer:{}", bd_addr);
@@ -1174,8 +1183,15 @@ static void bta_dm_adjust_roles(bool delay_role_switch) {
break;
}
} else {
- alarm_set_on_mloop(bta_dm_cb.switch_delay_timer, BTA_DM_SWITCH_DELAY_TIMER_MS,
- bta_dm_delay_role_switch_cback, NULL);
+ uint64_t delay = BTA_DM_SWITCH_DELAY_TIMER_MS;
+ if (com::android::bluetooth::flags::extend_and_randomize_role_switch_delay()) {
+ delay = bluetooth::os::GenerateRandom() %
+ (BTA_DM_MAX_SWITCH_DELAY_MS - BTA_DM_MIN_SWITCH_DELAY_MS) +
+ BTA_DM_MIN_SWITCH_DELAY_MS;
+ }
+ log::debug("Set timer to delay role switch:{}", delay);
+ alarm_set_on_mloop(bta_dm_cb.switch_delay_timer, delay, bta_dm_delay_role_switch_cback,
+ NULL);
}
}
}
@@ -1902,7 +1918,6 @@ void bta_dm_acl_down(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
}
void bta_dm_init_cb() { ::bta_dm_init_cb(); }
void bta_dm_deinit_cb() { ::bta_dm_deinit_cb(); }
-void BTA_dm_on_hw_on() { ::BTA_dm_on_hw_on(); }
} // namespace testing
} // namespace legacy
diff --git a/system/bta/dm/bta_dm_act.h b/system/bta/dm/bta_dm_act.h
index 7dccefbb34..6bcd7db7be 100644
--- a/system/bta/dm/bta_dm_act.h
+++ b/system/bta/dm/bta_dm_act.h
@@ -21,6 +21,14 @@
#include "types/raw_address.h"
void bta_dm_process_remove_device_no_callback(const RawAddress& bd_addr);
+void bta_dm_process_remove_device(const RawAddress& bd_addr);
tBTA_DM_PEER_DEVICE* find_connected_device(const RawAddress& bd_addr,
tBT_TRANSPORT /* transport */);
+
+namespace bluetooth::legacy::testing {
+void bta_dm_init_cb(void);
+void bta_dm_acl_down(const RawAddress& bd_addr, tBT_TRANSPORT transport);
+void bta_dm_acl_up(const RawAddress& bd_addr, tBT_TRANSPORT transport, uint16_t acl_handle);
+tBTA_DM_PEER_DEVICE* allocate_device_for(const RawAddress& bd_addr, tBT_TRANSPORT transport);
+} // namespace bluetooth::legacy::testing
diff --git a/system/bta/dm/bta_dm_ci.cc b/system/bta/dm/bta_dm_ci.cc
index 16f6eb50c7..fb8cc0b18d 100644
--- a/system/bta/dm/bta_dm_ci.cc
+++ b/system/bta/dm/bta_dm_ci.cc
@@ -21,6 +21,8 @@
* This is the API implementation file for the BTA device manager.
*
******************************************************************************/
+#include "bta/include/bta_dm_ci.h"
+
#include <base/functional/bind.h>
#include <memory>
@@ -29,9 +31,6 @@
#include "stack/include/main_thread.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
/*******************************************************************************
*
* Function bta_dm_ci_rmt_oob
diff --git a/system/bta/dm/bta_dm_device_search.cc b/system/bta/dm/bta_dm_device_search.cc
index 0dded3c7db..a8a3e57a0a 100644
--- a/system/bta/dm/bta_dm_device_search.cc
+++ b/system/bta/dm/bta_dm_device_search.cc
@@ -29,6 +29,7 @@
#include <vector>
#include "bta/dm/bta_dm_device_search_int.h"
+#include "bta/dm/bta_dm_disc_int.h"
#include "common/circular_buffer.h"
#include "common/strings.h"
#include "device/include/interop.h"
@@ -45,9 +46,6 @@
#include "stack/include/rnr_interface.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using namespace bluetooth;
namespace {
@@ -888,8 +886,6 @@ static void bta_dm_search_reset() {
void bta_dm_search_stop() { bta_dm_search_reset(); }
-void bta_dm_disc_discover_next_device() { bta_dm_discover_next_device(); }
-
#define DUMPSYS_TAG "shim::legacy::bta::dm"
void DumpsysBtaDmSearch(int fd) {
auto copy = search_state_history_.Pull();
@@ -912,11 +908,6 @@ void bta_dm_disc_init_search_cb(tBTA_DM_SEARCH_CB& bta_dm_search_cb) {
}
void bta_dm_discover_next_device() { ::bta_dm_discover_next_device(); }
-tBTA_DM_SEARCH_CB bta_dm_disc_get_search_cb() {
- tBTA_DM_SEARCH_CB search_cb = {};
- ::bta_dm_disc_init_search_cb(search_cb);
- return search_cb;
-}
tBTA_DM_SEARCH_CB& bta_dm_disc_search_cb() { return ::bta_dm_search_cb; }
bool bta_dm_read_remote_device_name(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
return ::bta_dm_read_remote_device_name(bd_addr, transport);
diff --git a/system/bta/dm/bta_dm_device_search.h b/system/bta/dm/bta_dm_device_search.h
index 14da4d9d01..f20432a171 100644
--- a/system/bta/dm/bta_dm_device_search.h
+++ b/system/bta/dm/bta_dm_device_search.h
@@ -16,8 +16,11 @@
#pragma once
+#include "bta/dm/bta_dm_device_search_int.h"
#include "bta/include/bta_api.h" // tBTA_DM_SEARCH_CBACK
+#include "stack/btm/neighbor_inquiry.h"
#include "stack/include/bt_hdr.h"
+#include "stack/include/rnr_interface.h"
#include "types/bt_transport.h"
#include "types/raw_address.h"
@@ -39,3 +42,21 @@ bool bta_dm_is_search_request_queued();
// Provide data for the dumpsys procedure
void DumpsysBtaDmSearch(int fd);
+
+namespace bluetooth::legacy::testing {
+
+void bta_dm_disc_init_search_cb(tBTA_DM_SEARCH_CB& bta_dm_search_cb);
+bool bta_dm_read_remote_device_name(const RawAddress& bd_addr, tBT_TRANSPORT transport);
+tBTA_DM_SEARCH_CB& bta_dm_disc_search_cb();
+void bta_dm_discover_next_device();
+void bta_dm_inq_cmpl();
+void bta_dm_inq_cmpl_cb(void* p_result);
+void bta_dm_observe_cmpl_cb(void* p_result);
+void bta_dm_observe_results_cb(tBTM_INQ_RESULTS* p_inq, const uint8_t* p_eir, uint16_t eir_len);
+void bta_dm_opportunistic_observe_results_cb(tBTM_INQ_RESULTS* p_inq, const uint8_t* p_eir,
+ uint16_t eir_len);
+void bta_dm_queue_search(tBTA_DM_API_SEARCH& search);
+void bta_dm_remname_cback(const tBTM_REMOTE_DEV_NAME* p);
+void bta_dm_start_scan(uint8_t duration_sec);
+
+} // namespace bluetooth::legacy::testing
diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc
index 0ce2062ff8..c3977454f5 100644
--- a/system/bta/dm/bta_dm_disc.cc
+++ b/system/bta/dm/bta_dm_disc.cc
@@ -52,9 +52,6 @@
#include "stack/include/srvc_api.h"
#endif
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using bluetooth::Uuid;
using namespace bluetooth::legacy::stack::sdp;
using namespace bluetooth;
@@ -106,8 +103,8 @@ struct gatt_interface_t {
void (*BTA_GATTC_Refresh)(const RawAddress& remote_bda);
void (*BTA_GATTC_GetGattDb)(tCONN_ID conn_id, uint16_t start_handle, uint16_t end_handle,
btgatt_db_element_t** db, int* count);
- void (*BTA_GATTC_AppRegister)(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support);
+ void (*BTA_GATTC_AppRegister)(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support);
void (*BTA_GATTC_Close)(tCONN_ID conn_id);
void (*BTA_GATTC_ServiceSearchRequest)(tCONN_ID conn_id, const bluetooth::Uuid* p_srvc_uuid);
void (*BTA_GATTC_Open)(tGATT_IF client_if, const RawAddress& remote_bda,
@@ -125,8 +122,9 @@ struct gatt_interface_t {
BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, db, count);
},
.BTA_GATTC_AppRegister =
- [](tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb, bool eatt_support) {
- BTA_GATTC_AppRegister(p_client_cb, cb, eatt_support);
+ [](const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support) {
+ BTA_GATTC_AppRegister(name, p_client_cb, cb, eatt_support);
},
.BTA_GATTC_Close = [](tCONN_ID conn_id) { BTA_GATTC_Close(conn_id); },
.BTA_GATTC_ServiceSearchRequest =
@@ -466,7 +464,8 @@ void bta_dm_disc_gattc_register(void) {
return;
}
get_gatt_interface().BTA_GATTC_AppRegister(
- bta_dm_gattc_callback, base::Bind([](uint8_t client_id, uint8_t status) {
+ "bta_dm_disc_gatt", bta_dm_gattc_callback,
+ base::Bind([](uint8_t client_id, uint8_t status) {
tGATT_STATUS gatt_status = static_cast<tGATT_STATUS>(status);
if (static_cast<tGATT_STATUS>(status) == GATT_SUCCESS) {
log::info("Registered device discovery search gatt client tGATT_IF:{}", client_id);
@@ -512,7 +511,7 @@ static void bta_dm_gatt_disc_complete(tCONN_ID conn_id, tGATT_STATUS status) {
log::verbose("conn_id = {}, status = {}, sdp_pending = {}, le_pending = {}", conn_id, status,
sdp_pending, le_pending);
- if (com::android::bluetooth::flags::bta_dm_discover_both() && sdp_pending && !le_pending) {
+ if (sdp_pending && !le_pending) {
/* LE Service discovery finished, and services were reported, but SDP is not
* finished yet. gatt_close_timer closed the connection, and we received
* this callback because of disconnection */
@@ -716,10 +715,6 @@ tBT_TRANSPORT bta_dm_determine_discovery_transport(const RawAddress& bd_addr) {
return ::bta_dm_determine_discovery_transport(bd_addr);
}
-void bta_dm_sdp_result(tSDP_STATUS sdp_status, tBTA_DM_SDP_STATE* state) {
- ::bta_dm_sdp_result(sdp_status, state);
-}
-
} // namespace testing
} // namespace legacy
} // namespace bluetooth
@@ -789,8 +784,7 @@ static void bta_dm_disc_sm_execute(tBTA_DM_DISC_EVT event, std::unique_ptr<tBTA_
"bad message type: {}", msg->index());
auto req = std::get<tBTA_DM_API_DISCOVER>(*msg);
- if (com::android::bluetooth::flags::bta_dm_discover_both() &&
- is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) {
+ if (is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) {
bta_dm_discover_services(std::get<tBTA_DM_API_DISCOVER>(*msg));
} else {
bta_dm_queue_disc(std::get<tBTA_DM_API_DISCOVER>(*msg));
@@ -846,13 +840,3 @@ void DumpsysBtaDmDisc(int fd) {
bta_dm_state_text(bta_dm_discovery_get_state()).c_str());
}
#undef DUMPSYS_TAG
-
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-tBTA_DM_SERVICE_DISCOVERY_CB& bta_dm_discovery_cb() { return ::bta_dm_discovery_cb; }
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
diff --git a/system/bta/dm/bta_dm_disc.h b/system/bta/dm/bta_dm_disc.h
index a243b785d9..82b9787029 100644
--- a/system/bta/dm/bta_dm_disc.h
+++ b/system/bta/dm/bta_dm_disc.h
@@ -43,9 +43,6 @@ void bta_dm_ble_csis_observe(bool observe, tBTA_DM_SEARCH_CBACK* p_cback);
// Checks if there is a device discovery request queued
bool bta_dm_is_search_request_queued();
-// Proceed to execute service discovery on next device in queue
-void bta_dm_disc_discover_next_device();
-
// GATT service discovery
void bta_dm_disc_gattc_register();
void bta_dm_disc_gatt_cancel_open(const RawAddress& bd_addr);
diff --git a/system/bta/dm/bta_dm_disc_int.h b/system/bta/dm/bta_dm_disc_int.h
index 4714af997a..adfe3b3f5c 100644
--- a/system/bta/dm/bta_dm_disc_int.h
+++ b/system/bta/dm/bta_dm_disc_int.h
@@ -21,6 +21,7 @@
#include <queue>
#include <string>
+#include "bta/dm/bta_dm_device_search_int.h"
#include "bta/include/bta_api.h"
#include "bta/sys/bta_sys.h"
#include "macros.h"
@@ -137,3 +138,10 @@ template <>
struct formatter<tBTA_DM_SERVICE_DISCOVERY_STATE>
: enum_formatter<tBTA_DM_SERVICE_DISCOVERY_STATE> {};
} // namespace std
+
+namespace bluetooth::legacy::testing {
+
+tBT_TRANSPORT bta_dm_determine_discovery_transport(const RawAddress& bd_addr);
+void bta_dm_remote_name_cmpl(const tBTA_DM_REMOTE_NAME& remote_name_msg);
+
+} // namespace bluetooth::legacy::testing
diff --git a/system/bta/dm/bta_dm_disc_sdp.cc b/system/bta/dm/bta_dm_disc_sdp.cc
index dd467e210b..8c43a316cc 100644
--- a/system/bta/dm/bta_dm_disc_sdp.cc
+++ b/system/bta/dm/bta_dm_disc_sdp.cc
@@ -43,9 +43,6 @@
#include "stack/include/srvc_api.h"
#endif
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using bluetooth::Uuid;
using namespace bluetooth::legacy::stack::sdp;
using namespace bluetooth;
@@ -361,17 +358,3 @@ void bta_dm_sdp_find_services(tBTA_DM_SDP_STATE* sdp_state) {
}
sdp_state->service_index++;
}
-
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-void bta_dm_sdp_find_services(tBTA_DM_SDP_STATE* sdp_state) {
- ::bta_dm_sdp_find_services(sdp_state);
-}
-
-void store_avrcp_profile_feature(tSDP_DISC_REC* sdp_rec) { ::store_avrcp_profile_feature(sdp_rec); }
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
diff --git a/system/bta/dm/bta_dm_gatt_client.cc b/system/bta/dm/bta_dm_gatt_client.cc
index a78ed4ca33..9e511db855 100644
--- a/system/bta/dm/bta_dm_gatt_client.cc
+++ b/system/bta/dm/bta_dm_gatt_client.cc
@@ -30,9 +30,6 @@
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
namespace {
TimestampedStringCircularBuffer gatt_history_{50};
constexpr char kTimeFormatString[] = "%Y-%m-%d %H:%M:%S";
@@ -70,10 +67,11 @@ gatt_interface_t default_gatt_interface = {
BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, db, count);
},
.BTA_GATTC_AppRegister =
- [](tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb, bool eatt_support) {
+ [](const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support) {
gatt_history_.Push(std::format("{:<32s} eatt_support:{:c}", "GATTC_AppRegister",
eatt_support ? 'T' : 'F'));
- BTA_GATTC_AppRegister(p_client_cb, cb, eatt_support);
+ BTA_GATTC_AppRegister(name, p_client_cb, cb, eatt_support);
},
.BTA_GATTC_Close =
[](tCONN_ID conn_id) {
@@ -118,18 +116,10 @@ void DumpsysBtaDmGattClient(int fd) {
}
#undef DUMPSYS_TAG
-void bluetooth::testing::set_gatt_interface(const gatt_interface_t& interface) {
- *gatt_interface = interface;
-}
-
-namespace bluetooth {
-namespace legacy {
-namespace testing {
+namespace bluetooth::testing {
std::vector<bluetooth::common::TimestampedEntry<std::string>> PullCopyOfGattHistory() {
return gatt_history_.Pull();
}
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
+} // namespace bluetooth::testing
diff --git a/system/bta/dm/bta_dm_gatt_client.h b/system/bta/dm/bta_dm_gatt_client.h
index 7f5540af7d..caa1bd0800 100644
--- a/system/bta/dm/bta_dm_gatt_client.h
+++ b/system/bta/dm/bta_dm_gatt_client.h
@@ -17,8 +17,10 @@
#pragma once
#include <cstdint>
+#include <string>
#include "bta/include/bta_gatt_api.h"
+#include "gd/common/circular_buffer.h"
#include "include/hardware/bt_common_types.h"
#include "stack/include/btm_ble_api_types.h"
#include "types/bluetooth/uuid.h"
@@ -32,8 +34,8 @@ struct gatt_interface_t {
void (*BTA_GATTC_Refresh)(const RawAddress& remote_bda);
void (*BTA_GATTC_GetGattDb)(tCONN_ID conn_id, uint16_t start_handle, uint16_t end_handle,
btgatt_db_element_t** db, int* count);
- void (*BTA_GATTC_AppRegister)(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support);
+ void (*BTA_GATTC_AppRegister)(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support);
void (*BTA_GATTC_Close)(tCONN_ID conn_id);
void (*BTA_GATTC_ServiceSearchRequest)(tCONN_ID conn_id, const bluetooth::Uuid* p_srvc_uuid);
void (*BTA_GATTC_Open)(tGATT_IF client_if, const RawAddress& remote_bda,
@@ -56,13 +58,6 @@ void gatt_history_callback(const std::string& entry);
//
void DumpsysBtaDmGattClient(int fd);
-namespace bluetooth {
-namespace testing {
-
-//
-// TESTING: Sets a specialzed GATT client interface implementation for testing
-//
-void set_gatt_interface(const gatt_interface_t& interface);
-
-} // namespace testing
-} // namespace bluetooth
+namespace bluetooth::testing {
+std::vector<bluetooth::common::TimestampedEntry<std::string>> PullCopyOfGattHistory();
+} // namespace bluetooth::testing
diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h
index b5357bb06b..70deacff2f 100644
--- a/system/bta/dm/bta_dm_int.h
+++ b/system/bta/dm/bta_dm_int.h
@@ -21,8 +21,8 @@
* This is the private interface file for the BTA device manager.
*
******************************************************************************/
-#ifndef BTA_DM_INT_H
-#define BTA_DM_INT_H
+
+#pragma once
#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
@@ -323,6 +323,9 @@ extern tBTA_DM_ACL_CB bta_dm_acl_cb;
/* DI control block */
extern tBTA_DM_DI_CB bta_dm_di_cb;
+void BTA_dm_on_hw_on();
+void BTA_dm_on_hw_off();
+
void bta_dm_enable(tBTA_DM_SEC_CBACK*, tBTA_DM_ACL_CBACK*);
void bta_dm_disable();
void bta_dm_set_dev_name(const std::vector<uint8_t>&);
@@ -361,10 +364,19 @@ void bta_dm_eir_update_cust_uuid(const tBTA_CUSTOM_UUID& curr, bool adding);
void bta_dm_ble_subrate_request(const RawAddress& bd_addr, uint16_t subrate_min,
uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num,
uint16_t timeout);
+tBTM_CONTRL_STATE bta_dm_pm_obtain_controller_state(void);
namespace std {
template <>
struct formatter<tBTA_DM_CONN_STATE> : enum_formatter<tBTA_DM_CONN_STATE> {};
} // namespace std
-#endif /* BTA_DM_INT_H */
+namespace bluetooth::legacy::testing {
+
+tBTA_DM_PEER_DEVICE* allocate_device_for(const RawAddress& bd_addr, tBT_TRANSPORT transport);
+void bta_dm_acl_up(const RawAddress& bd_addr, tBT_TRANSPORT transport, uint16_t acl_handle);
+void bta_dm_acl_down(const RawAddress& bd_addr, tBT_TRANSPORT transport);
+void bta_dm_init_cb();
+void bta_dm_deinit_cb();
+
+} // namespace bluetooth::legacy::testing
diff --git a/system/bta/dm/bta_dm_pm.cc b/system/bta/dm/bta_dm_pm.cc
index 9c0b869d30..53812a27d4 100644
--- a/system/bta/dm/bta_dm_pm.cc
+++ b/system/bta/dm/bta_dm_pm.cc
@@ -46,9 +46,6 @@
#include "stack/include/main_thread.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using namespace bluetooth;
static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, const tBTA_SYS_ID id, uint8_t app_id,
@@ -1210,6 +1207,6 @@ tBTM_CONTRL_STATE bta_dm_pm_obtain_controller_state(void) {
tBTM_CONTRL_STATE cur_state = BTM_CONTRL_UNKNOWN;
cur_state = BTM_PM_ReadControllerState();
- log::verbose("bta_dm_pm_obtain_controller_state: {}", cur_state);
+ log::verbose("cur_state: {}", cur_state);
return cur_state;
}
diff --git a/system/bta/dm/bta_dm_sec.cc b/system/bta/dm/bta_dm_sec.cc
index 31ce27b772..cb97af5fd3 100644
--- a/system/bta/dm/bta_dm_sec.cc
+++ b/system/bta/dm/bta_dm_sec.cc
@@ -37,9 +37,6 @@
#include "types/bt_transport.h"
#include "types/raw_address.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using namespace bluetooth;
static tBTM_STATUS bta_dm_sp_cback(tBTM_SP_EVT event, tBTM_SP_EVT_DATA* p_data);
@@ -1016,7 +1013,6 @@ namespace testing {
tBTM_STATUS bta_dm_sp_cback(tBTM_SP_EVT event, tBTM_SP_EVT_DATA* p_data) {
return ::bta_dm_sp_cback(event, p_data);
}
-
} // namespace testing
} // namespace legacy
} // namespace bluetooth
diff --git a/system/bta/dm/bta_dm_sec_int.h b/system/bta/dm/bta_dm_sec_int.h
index 540205dd99..efd9140e69 100644
--- a/system/bta/dm/bta_dm_sec_int.h
+++ b/system/bta/dm/bta_dm_sec_int.h
@@ -83,9 +83,15 @@ void bta_dm_ci_rmt_oob_act(std::unique_ptr<tBTA_DM_CI_RMT_OOB> msg);
void bta_dm_confirm(const RawAddress& bd_addr, bool accept);
void bta_dm_consolidate(const RawAddress& identity_addr, const RawAddress& rpa);
void bta_dm_enable(tBTA_DM_SEC_CBACK* p_sec_cback);
-void bta_dm_encrypt_cback(const RawAddress* bd_addr, tBT_TRANSPORT transport,
- void* /* p_ref_data */, tBTM_STATUS result);
+void bta_dm_encrypt_cback(RawAddress bd_addr, tBT_TRANSPORT transport, void* /* p_ref_data */,
+ tBTM_STATUS result);
+void bta_dm_on_encryption_change(bt_encryption_change_evt encryption_change);
void bta_dm_pin_reply(std::unique_ptr<tBTA_DM_API_PIN_REPLY> msg);
void bta_dm_set_encryption(const RawAddress& bd_addr, tBT_TRANSPORT transport,
tBTA_DM_ENCRYPT_CBACK* p_callback, tBTM_BLE_SEC_ACT sec_act);
void btm_dm_sec_init();
+void bta_dm_remote_key_missing(const RawAddress bd_addr);
+
+namespace bluetooth::legacy::testing {
+tBTM_STATUS bta_dm_sp_cback(tBTM_SP_EVT event, tBTM_SP_EVT_DATA* p_data);
+} // namespace bluetooth::legacy::testing
diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc
index 9f5ad701c7..cca68b8c0d 100644
--- a/system/bta/gatt/bta_gattc_act.cc
+++ b/system/bta/gatt/bta_gattc_act.cc
@@ -180,8 +180,8 @@ static void bta_gattc_start_if(uint8_t client_if) {
}
/** Register a GATT client application with BTA */
-void bta_gattc_register(const Uuid& app_uuid, tBTA_GATTC_CBACK* p_cback, BtaAppRegisterCallback cb,
- bool eatt_support) {
+void bta_gattc_register(const Uuid& app_uuid, const std::string& name, tBTA_GATTC_CBACK* p_cback,
+ BtaAppRegisterCallback cb, bool eatt_support) {
tGATT_STATUS status = GATT_NO_RESOURCES;
uint8_t client_if = 0;
log::debug("state: {}, uuid={}", bta_gattc_cb.state, app_uuid.ToString());
@@ -193,7 +193,7 @@ void bta_gattc_register(const Uuid& app_uuid, tBTA_GATTC_CBACK* p_cback, BtaAppR
}
if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) {
- client_if = GATT_Register(app_uuid, "GattClient", &bta_gattc_cl_cback, eatt_support);
+ client_if = GATT_Register(app_uuid, name, &bta_gattc_cl_cback, eatt_support);
if (client_if == 0) {
log::error("Register with GATT stack failed");
status = GATT_ERROR;
diff --git a/system/bta/gatt/bta_gattc_api.cc b/system/bta/gatt/bta_gattc_api.cc
index 38b3691b4a..b08988d575 100644
--- a/system/bta/gatt/bta_gattc_api.cc
+++ b/system/bta/gatt/bta_gattc_api.cc
@@ -76,8 +76,8 @@ void BTA_GATTC_Disable(void) {
* module. |client_cb| pointer to the application callback function.
* |cb| one time callback when registration is finished
*/
-void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support) {
+void BTA_GATTC_AppRegister(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support) {
log::debug("eatt_support={}", eatt_support);
if (!bta_sys_is_register(BTA_ID_GATTC)) {
log::debug("BTA_ID_GATTC not registered in BTA, registering it");
@@ -86,8 +86,8 @@ void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback
Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>());
- do_in_main_thread(
- base::BindOnce(&bta_gattc_register, uuid, p_client_cb, std::move(cb), eatt_support));
+ do_in_main_thread(base::BindOnce(&bta_gattc_register, uuid, name, p_client_cb, std::move(cb),
+ eatt_support));
}
static void app_deregister_impl(tGATT_IF client_if) {
diff --git a/system/bta/gatt/bta_gattc_int.h b/system/bta/gatt/bta_gattc_int.h
index 4ceaff4062..b155260fb9 100644
--- a/system/bta/gatt/bta_gattc_int.h
+++ b/system/bta/gatt/bta_gattc_int.h
@@ -371,8 +371,8 @@ bool bta_gattc_sm_execute(tBTA_GATTC_CLCB* p_clcb, uint16_t event, const tBTA_GA
/* function processed outside SM */
void bta_gattc_disable();
-void bta_gattc_register(const bluetooth::Uuid& app_uuid, tBTA_GATTC_CBACK* p_data,
- BtaAppRegisterCallback cb, bool eatt_support);
+void bta_gattc_register(const bluetooth::Uuid& app_uuid, const std::string& name,
+ tBTA_GATTC_CBACK* p_data, BtaAppRegisterCallback cb, bool eatt_support);
void bta_gattc_process_api_open(const tBTA_GATTC_DATA* p_msg);
void bta_gattc_process_api_open_cancel(const tBTA_GATTC_DATA* p_msg);
void bta_gattc_deregister(tBTA_GATTC_RCB* p_clreg);
diff --git a/system/bta/gmap/gmap_client.cc b/system/bta/gmap/gmap_client.cc
index 0589b6a759..0a1210d413 100644
--- a/system/bta/gmap/gmap_client.cc
+++ b/system/bta/gmap/gmap_client.cc
@@ -36,10 +36,8 @@ using namespace bluetooth;
using bluetooth::le_audio::GmapClient;
bool GmapClient::is_offloader_support_gmap_ = false;
-void GmapClient::AddFromStorage(const RawAddress &addr, const uint8_t role,
- const uint16_t role_handle, const uint8_t UGT_feature,
- const uint16_t UGT_feature_handle) {
- addr_ = addr;
+void GmapClient::AddFromStorage(uint8_t role, uint16_t role_handle, uint8_t UGT_feature,
+ uint16_t UGT_feature_handle) {
role_ = role;
role_handle_ = role_handle;
UGT_feature_ = UGT_feature;
@@ -60,8 +58,8 @@ bool GmapClient::IsGmapClientEnabled() {
bool system_prop = osi_property_get_bool("bluetooth.profile.gmap.enabled", false);
bool result = flag && system_prop && is_offloader_support_gmap_;
- log::info("GmapClientEnabled={}, flag={}, system_prop={}, offloader_support={}", result,
- system_prop, flag, GmapClient::is_offloader_support_gmap_);
+ log::info("GmapClientEnabled={}, flag={}, system_prop={}, offloader_support={}", result, flag,
+ system_prop, GmapClient::is_offloader_support_gmap_);
return result;
}
@@ -90,14 +88,14 @@ bool GmapClient::parseAndSaveUGTFeature(uint16_t len, const uint8_t *value) {
return true;
}
-std::bitset<8> GmapClient::getRole() { return role_; }
+std::bitset<8> GmapClient::getRole() const { return role_; }
-uint16_t GmapClient::getRoleHandle() { return role_handle_; }
+uint16_t GmapClient::getRoleHandle() const { return role_handle_; }
void GmapClient::setRoleHandle(uint16_t handle) { role_handle_ = handle; }
-std::bitset<8> GmapClient::getUGTFeature() { return UGT_feature_; }
+std::bitset<8> GmapClient::getUGTFeature() const { return UGT_feature_; }
-uint16_t GmapClient::getUGTFeatureHandle() { return UGT_feature_handle_; }
+uint16_t GmapClient::getUGTFeatureHandle() const { return UGT_feature_handle_; }
void GmapClient::setUGTFeatureHandle(uint16_t handle) { UGT_feature_handle_ = handle; }
diff --git a/system/bta/gmap/gmap_client_test.cc b/system/bta/gmap/gmap_client_test.cc
index 68aec5caf5..bc497da0a9 100644
--- a/system/bta/gmap/gmap_client_test.cc
+++ b/system/bta/gmap/gmap_client_test.cc
@@ -68,7 +68,7 @@ TEST_F(GmapClientTest, test_add_from_storage) {
const uint16_t role_handle = 2;
const uint8_t UGT_feature = 0b0011;
const uint16_t UGT_feature_handle = 4;
- gmapClient.AddFromStorage(addr, role, role_handle, UGT_feature, UGT_feature_handle);
+ gmapClient.AddFromStorage(role, role_handle, UGT_feature, UGT_feature_handle);
ASSERT_EQ(gmapClient.getRole(), role);
ASSERT_EQ(gmapClient.getRoleHandle(), role_handle);
ASSERT_EQ(gmapClient.getUGTFeature(), UGT_feature);
diff --git a/system/bta/gmap/gmap_server.cc b/system/bta/gmap/gmap_server.cc
index 407e50147b..1851355fe1 100644
--- a/system/bta/gmap/gmap_server.cc
+++ b/system/bta/gmap/gmap_server.cc
@@ -60,8 +60,8 @@ bool GmapServer::IsGmapServerEnabled() {
bool system_prop = osi_property_get_bool("bluetooth.profile.gmap.enabled", false);
bool result = flag && system_prop && is_offloader_support_gmap_;
- log::info("GmapServerEnabled={}, flag={}, system_prop={}, offloader_support={}", result,
- system_prop, flag, GmapServer::is_offloader_support_gmap_);
+ log::info("GmapServerEnabled={}, flag={}, system_prop={}, offloader_support={}", result, flag,
+ system_prop, GmapServer::is_offloader_support_gmap_);
return result;
}
diff --git a/system/bta/has/has_client.cc b/system/bta/has/has_client.cc
index deb79bf103..15bdfc5bfc 100644
--- a/system/bta/has/has_client.cc
+++ b/system/bta/has/has_client.cc
@@ -138,6 +138,7 @@ public:
HasClientImpl(bluetooth::has::HasClientCallbacks* callbacks, base::Closure initCb)
: gatt_if_(0), callbacks_(callbacks) {
BTA_GATTC_AppRegister(
+ "has",
[](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
if (instance && p_data) {
instance->GattcCallback(event, p_data);
@@ -2014,7 +2015,7 @@ private:
if (com::android::bluetooth::flags::gatt_queue_cleanup_connected()) {
BtaGattQueue::Clean(evt.conn_id);
}
- if (BTM_SecIsSecurityPending(device->addr)) {
+ if (BTM_SecIsLeSecurityPending(device->addr)) {
/* if security collision happened, wait for encryption done
* (BTA_GATTC_ENC_CMPL_CB_EVT)
*/
diff --git a/system/bta/has/has_client_test.cc b/system/bta/has/has_client_test.cc
index 9c98258601..ddaed634a0 100644
--- a/system/bta/has/has_client_test.cc
+++ b/system/bta/has/has_client_test.cc
@@ -734,8 +734,8 @@ protected:
void TestAppRegister(void) {
BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
HasClient::Initialize(callbacks.get(), base::DoNothing());
ASSERT_TRUE(gatt_callback);
ASSERT_TRUE(app_register_callback);
diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc
index 245b095b07..58a4313638 100644
--- a/system/bta/hearing_aid/hearing_aid.cc
+++ b/system/bta/hearing_aid/hearing_aid.cc
@@ -328,7 +328,7 @@ public:
default_data_interval_ms, overwrite_min_ce_len, overwrite_max_ce_len);
BTA_GATTC_AppRegister(
- hearingaid_gattc_callback,
+ "asha", hearingaid_gattc_callback,
base::Bind(
[](Closure initCb, uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
@@ -559,7 +559,7 @@ public:
log::warn("Unable to set BLE data length peer:{} size:{}", address, 167);
}
- if (BTM_SecIsSecurityPending(address)) {
+ if (BTM_SecIsLeSecurityPending(address)) {
/* if security collision happened, wait for encryption done
* (BTA_GATTC_ENC_CMPL_CB_EVT) */
return;
diff --git a/system/bta/hh/bta_hh_headtracker.cc b/system/bta/hh/bta_hh_headtracker.cc
index c8b3e20f05..0282cb59d0 100644
--- a/system/bta/hh/bta_hh_headtracker.cc
+++ b/system/bta/hh/bta_hh_headtracker.cc
@@ -140,7 +140,10 @@ void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb, const gatt::Serv
bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) {
if (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_UNKNOWN) {
bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {};
- bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids), &remote_uuids};
+ bt_property_t remote_properties = {com::android::bluetooth::flags::separate_service_storage()
+ ? BT_PROPERTY_UUIDS_LE
+ : BT_PROPERTY_UUIDS,
+ sizeof(remote_uuids), &remote_uuids};
const RawAddress& bd_addr = p_dev_cb->link_spec.addrt.bda;
p_dev_cb->hid_srvc.headtracker_support = BTA_HH_UNAVAILABLE;
diff --git a/system/bta/hh/bta_hh_le.cc b/system/bta/hh/bta_hh_le.cc
index ea90ecfafc..5a5c261f98 100644
--- a/system/bta/hh/bta_hh_le.cc
+++ b/system/bta/hh/bta_hh_le.cc
@@ -202,7 +202,8 @@ void bta_hh_le_enable(void) {
bta_hh_cb.le_cb_index[xx] = BTA_HH_IDX_INVALID;
}
- BTA_GATTC_AppRegister(bta_hh_gattc_callback, base::Bind([](tGATT_IF client_id, uint8_t r_status) {
+ BTA_GATTC_AppRegister("hid", bta_hh_gattc_callback,
+ base::Bind([](tGATT_IF client_id, uint8_t r_status) {
tBTA_HH bta_hh;
bta_hh.status = BTA_HH_ERR;
@@ -1113,7 +1114,7 @@ void bta_hh_start_security(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* /* p_buf */
p_cb->status = BTA_HH_ERR_AUTH_FAILED;
BTM_SetEncryption(p_cb->link_spec.addrt.bda, BT_TRANSPORT_LE, bta_hh_le_encrypt_cback, NULL,
BTM_BLE_SEC_ENCRYPT);
- } else if (BTM_SecIsSecurityPending(p_cb->link_spec.addrt.bda)) {
+ } else if (BTM_SecIsLeSecurityPending(p_cb->link_spec.addrt.bda)) {
/* if security collision happened, wait for encryption done */
log::debug("addr:{} security collision", p_cb->link_spec.addrt.bda);
p_cb->security_pending = true;
@@ -1771,6 +1772,11 @@ void bta_hh_gatt_close(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) {
bta_hh_disc_cmpl();
} else {
+ if (com::android::bluetooth::flags::hogp_reconnection()) {
+ // reconnection is handled in btif_hh.cc:btif_hh_disconnected
+ return;
+ }
+
switch (le_close->reason) {
case GATT_CONN_FAILED_ESTABLISHMENT:
case GATT_CONN_TERMINATE_PEER_USER:
diff --git a/system/bta/include/bta_gatt_api.h b/system/bta/include/bta_gatt_api.h
index 6e93821e4a..5072ae63d2 100644
--- a/system/bta/include/bta_gatt_api.h
+++ b/system/bta/include/bta_gatt_api.h
@@ -466,8 +466,8 @@ using BtaAppRegisterCallback = base::Callback<void(uint8_t /* app_id */, uint8_t
*module.
* p_client_cb - pointer to the application callback function.
**/
-void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support);
+void BTA_GATTC_AppRegister(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support);
/*******************************************************************************
*
diff --git a/system/bta/include/bta_jv_api.h b/system/bta/include/bta_jv_api.h
index 8978ab00c2..08709e1900 100644
--- a/system/bta/include/bta_jv_api.h
+++ b/system/bta/include/bta_jv_api.h
@@ -698,7 +698,7 @@ tBTA_JV_STATUS BTA_JvL2capWrite(uint32_t handle, uint32_t req_id, BT_HDR* msg, u
******************************************************************************/
tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn,
const RawAddress& peer_bd_addr, tBTA_JV_RFCOMM_CBACK* p_cback,
- uint32_t rfcomm_slot_id, RfcommCfgInfo cfg);
+ uint32_t rfcomm_slot_id, RfcommCfgInfo cfg, uint32_t app_uid);
/*******************************************************************************
*
@@ -729,7 +729,7 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t handle, uint32_t rfcomm_slot_id);
******************************************************************************/
tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg);
+ RfcommCfgInfo cfg, uint32_t app_uid);
/*******************************************************************************
*
diff --git a/system/bta/include/bta_le_audio_api.h b/system/bta/include/bta_le_audio_api.h
index de11131bae..52a3f7d83c 100644
--- a/system/bta/include/bta_le_audio_api.h
+++ b/system/bta/include/bta_le_audio_api.h
@@ -86,8 +86,9 @@ public:
const std::vector<uint8_t>& handles,
const std::vector<uint8_t>& sink_pacs,
const std::vector<uint8_t>& source_pacs,
- const std::vector<uint8_t>& ases);
+ const std::vector<uint8_t>& ases, const std::vector<uint8_t>& gmap);
static bool GetHandlesForStorage(const RawAddress& addr, std::vector<uint8_t>& out);
+ static bool GetGmapForStorage(const RawAddress& addr, std::vector<uint8_t>& out);
static bool GetSinkPacsForStorage(const RawAddress& addr, std::vector<uint8_t>& out);
static bool GetSourcePacsForStorage(const RawAddress& addr, std::vector<uint8_t>& out);
static bool GetAsesForStorage(const RawAddress& addr, std::vector<uint8_t>& out);
diff --git a/system/bta/include/bta_ras_api.h b/system/bta/include/bta_ras_api.h
index ee6eccec4f..48113e5edf 100644
--- a/system/bta/include/bta_ras_api.h
+++ b/system/bta/include/bta_ras_api.h
@@ -25,6 +25,12 @@
namespace bluetooth {
namespace ras {
+enum class RasDisconnectReason {
+ GATT_DISCONNECT,
+ SERVER_NOT_AVAILABLE,
+ FATAL_ERROR,
+};
+
struct VendorSpecificCharacteristic {
bluetooth::Uuid characteristicUuid_;
std::vector<uint8_t> value_;
@@ -64,7 +70,8 @@ public:
const std::vector<VendorSpecificCharacteristic>& vendor_specific_characteristics,
uint16_t conn_interval) = 0;
virtual void OnConnIntervalUpdated(const RawAddress& address, uint16_t conn_interval) = 0;
- virtual void OnDisconnected(const RawAddress& address) = 0;
+ virtual void OnDisconnected(const RawAddress& address,
+ const RasDisconnectReason& ras_disconnect_reason) = 0;
virtual void OnWriteVendorSpecificReplyComplete(const RawAddress& address, bool success) = 0;
virtual void OnRemoteData(const RawAddress& address, const std::vector<uint8_t>& data) = 0;
virtual void OnRemoteDataTimeout(const RawAddress& address) = 0;
diff --git a/system/bta/jv/bta_jv_act.cc b/system/bta/jv/bta_jv_act.cc
index 7b6fa38ca8..ffe1756943 100644
--- a/system/bta/jv/bta_jv_act.cc
+++ b/system/bta/jv/bta_jv_act.cc
@@ -233,7 +233,7 @@ tBTA_JV_RFC_CB* bta_jv_alloc_rfc_cb(uint16_t port_handle, tBTA_JV_PCB** pp_pcb)
p_cb->rfc_hdl[j] = 0;
}
p_cb->rfc_hdl[0] = port_handle;
- log::verbose("port_handle={}, handle=0x{:x}", port_handle, p_cb->handle);
+ log::verbose("port_handle={}, jv_handle=0x{:x}", port_handle, p_cb->handle);
p_pcb = &bta_jv_cb.port_cb[port_handle - 1];
p_pcb->handle = p_cb->handle;
@@ -307,7 +307,7 @@ static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pc
log::error("p_cb or p_pcb cannot be null");
return tBTA_JV_STATUS::FAILURE;
}
- log::verbose("max_sess={}, curr_sess={}, p_pcb={}, user={}, state={}, jv handle=0x{:x}",
+ log::verbose("max_sess={}, curr_sess={}, p_pcb={}, user={}, state={}, jv_handle=0x{:x}",
p_cb->max_sess, p_cb->curr_sess, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id,
p_pcb->state, p_pcb->handle);
@@ -341,7 +341,7 @@ static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pc
break;
default:
log::warn(
- "failed, ignore port state= {}, scn={}, p_pcb= {}, jv handle=0x{:x}, "
+ "failed, ignore port state= {}, scn={}, p_pcb= {}, jv_handle=0x{:x}, "
"port_handle={}, user_data={}",
p_pcb->state, p_cb->scn, std::format_ptr(p_pcb), p_pcb->handle, p_pcb->port_handle,
p_pcb->rfcomm_slot_id);
@@ -359,7 +359,7 @@ static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pc
if (port_status != PORT_SUCCESS) {
status = tBTA_JV_STATUS::FAILURE;
log::warn(
- "Remove jv handle=0x{:x}, state={}, port_status={}, port_handle={}, close_pending={}",
+ "Remove jv_handle=0x{:x}, state={}, port_status={}, port_handle={}, close_pending={}",
p_pcb->handle, p_pcb->state, port_status, p_pcb->port_handle, close_pending);
}
}
@@ -470,8 +470,8 @@ static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(uint32_t jv_handle) {
}
}
- log::verbose("jv_handle=0x{:x}, idx={}app_id={}, bd_counter={}, appid_counter={}", jv_handle,
- i, bta_jv_cb.pm_cb[i].app_id, bd_counter, appid_counter);
+ log::verbose("jv_handle=0x{:x}, idx={}, app_id={}, bd_counter={}, appid_counter={}",
+ jv_handle, i, bta_jv_cb.pm_cb[i].app_id, bd_counter, appid_counter);
if (bd_counter > 1) {
bta_jv_pm_conn_idle(&bta_jv_cb.pm_cb[i]);
}
@@ -559,7 +559,7 @@ static tBTA_JV_PM_CB* bta_jv_alloc_set_pm_profile_cb(uint32_t jv_handle, tBTA_JV
}
}
}
- log::verbose("handle=0x{:x}, app_id={}, idx={}, BTA_JV_PM_MAX_NUM={}, pp_cb={}", jv_handle,
+ log::verbose("jv_handle=0x{:x}, app_id={}, idx={}, BTA_JV_PM_MAX_NUM={}, pp_cb={}", jv_handle,
app_id, i, BTA_JV_PM_MAX_NUM, std::format_ptr(pp_cb));
break;
}
@@ -573,7 +573,7 @@ static tBTA_JV_PM_CB* bta_jv_alloc_set_pm_profile_cb(uint32_t jv_handle, tBTA_JV
bta_jv_cb.pm_cb[i].state = BTA_JV_PM_IDLE_ST;
return &bta_jv_cb.pm_cb[i];
}
- log::warn("handle=0x{:x}, app_id={}, return NULL", jv_handle, app_id);
+ log::warn("jv_handle=0x{:x}, app_id={}, return NULL", jv_handle, app_id);
return NULL;
}
@@ -954,7 +954,7 @@ void bta_jv_delete_record(uint32_t handle) {
if (handle) {
/* this is a record created by btif layer*/
if (!get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) {
- log::warn("Unable to delete SDP record handle:{}", handle);
+ log::warn("Unable to delete SDP record handle:{}", handle);
}
}
}
@@ -997,7 +997,7 @@ static void bta_jv_l2cap_client_cback(uint16_t gap_handle, uint16_t event, tGAP_
if (GAP_GetLeChannelInfo(gap_handle, &remote_mtu, &local_mps, &remote_mps, &local_credit,
&remote_credit, &local_cid, &remote_cid,
&acl_handle) != PORT_SUCCESS) {
- log::warn("Unable to get GAP channel info handle:{}", gap_handle);
+ log::warn("Unable to get GAP channel info gap_handle:{}", gap_handle);
}
evt_data.l2c_open.tx_mtu = remote_mtu;
evt_data.l2c_open.local_coc_mps = local_mps;
@@ -1426,10 +1426,10 @@ static void bta_jv_port_mgmt_cl_cback(const tPORT_RESULT code, uint16_t port_han
return;
}
- log::verbose("code={}, port_handle={}, handle={}", code, port_handle, p_cb->handle);
+ log::verbose("code={}, port_handle={}, rfc_handle={}", code, port_handle, p_cb->handle);
if (PORT_CheckConnection(port_handle, &rem_bda, &lcid) != PORT_SUCCESS) {
- log::warn("Unable to check RFCOMM connection peer:{} handle:{}", rem_bda, port_handle);
+ log::warn("Unable to check RFCOMM connection peer:{} port_handle:{}", rem_bda, port_handle);
}
if (code == PORT_SUCCESS) {
@@ -1448,7 +1448,7 @@ static void bta_jv_port_mgmt_cl_cback(const tPORT_RESULT code, uint16_t port_han
&evt_data.rfc_open.dlci, &evt_data.rfc_open.max_frame_size,
&evt_data.rfc_open.acl_handle,
&evt_data.rfc_open.mux_initiator) != PORT_SUCCESS) {
- log::warn("Unable to get RFCOMM channel info peer:{} handle:{}", rem_bda, port_handle);
+ log::warn("Unable to get RFCOMM channel info peer:{} port_handle:{}", rem_bda, port_handle);
}
}
p_pcb->state = BTA_JV_ST_CL_OPEN;
@@ -1490,7 +1490,7 @@ static void bta_jv_port_event_cl_cback(uint32_t code, uint16_t port_handle) {
return;
}
- log::verbose("code=0x{:x}, port_handle={}, handle={}", code, port_handle, p_cb->handle);
+ log::verbose("code=0x{:x}, port_handle={}, rfc_handle={}", code, port_handle, p_cb->handle);
if (code & PORT_EV_RXCHAR) {
evt_data.data_ind.handle = p_cb->handle;
p_cb->p_cback(BTA_JV_RFCOMM_DATA_IND_EVT, &evt_data, p_pcb->rfcomm_slot_id);
@@ -1512,7 +1512,7 @@ static void bta_jv_port_event_cl_cback(uint32_t code, uint16_t port_handle) {
/* Client initiates an RFCOMM connection */
void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg) {
+ RfcommCfgInfo cfg, uint32_t app_uid) {
uint16_t handle = 0;
uint32_t event_mask = BTA_JV_RFC_EV_MASK;
PortSettings port_settings;
@@ -1527,14 +1527,12 @@ void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddre
},
};
- if (com::android::bluetooth::flags::rfcomm_always_use_mitm()) {
- // Update security service record for RFCOMM client so that
- // secure RFCOMM connection will be authenticated with MTIM protection
- // while creating the L2CAP connection.
- get_btm_client_interface().security.BTM_SetSecurityLevel(
- true, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM,
- 0);
- }
+ // Update security service record for RFCOMM client so that
+ // secure RFCOMM connection will be authenticated with MTIM protection
+ // while creating the L2CAP connection.
+ get_btm_client_interface().security.BTM_SetSecurityLevel(true, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX,
+ sec_mask, BT_PSM_RFCOMM,
+ BTM_SEC_PROTO_RFCOMM, 0);
if (RFCOMM_CreateConnectionWithSecurity(
UUID_SERVCLASS_SERIAL_PORT, remote_scn, false, BTA_JV_DEF_RFC_MTU, peer_bd_addr,
@@ -1551,21 +1549,24 @@ void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddre
p_pcb->rfcomm_slot_id = rfcomm_slot_id;
bta_jv.rfc_cl_init.use_co = true;
+ if (PORT_SetAppUid(handle, app_uid) != PORT_SUCCESS) {
+ log::warn("Unable to set app_uid for port_handle:{}", handle);
+ }
if (PORT_SetEventMaskAndCallback(handle, event_mask, bta_jv_port_event_cl_cback) !=
PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM client event mask and callback handle:{}", handle);
+ log::warn("Unable to set RFCOMM client event mask and callback port_handle:{}", handle);
}
if (PORT_SetDataCOCallback(handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM client data callback handle:{}", handle);
+ log::warn("Unable to set RFCOMM client data callback port_handle:{}", handle);
}
if (PORT_GetSettings(handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to get RFCOMM client state handle:{}", handle);
+ log::warn("Unable to get RFCOMM client state port_handle:{}", handle);
}
port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);
if (PORT_SetSettings(handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM client state handle:{}", handle);
+ log::warn("Unable to set RFCOMM client state port_handle:{}", handle);
}
bta_jv.rfc_cl_init.handle = p_cb->handle;
@@ -1579,7 +1580,7 @@ void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddre
if (bta_jv.rfc_cl_init.status == tBTA_JV_STATUS::FAILURE) {
if (handle) {
if (RFCOMM_RemoveConnection(handle) != PORT_SUCCESS) {
- log::warn("Unable to remove RFCOMM connection handle:{}", handle);
+ log::warn("Unable to remove RFCOMM connection port_handle:{}", handle);
}
}
}
@@ -1596,7 +1597,7 @@ static int find_rfc_pcb(uint32_t rfcomm_slot_id, tBTA_JV_RFC_CB** cb, tBTA_JV_PC
*pcb = &bta_jv_cb.port_cb[i];
*cb = &bta_jv_cb.rfc_cb[rfc_handle - 1];
log::verbose(
- "FOUND rfc_cb_handle=0x{:x}, port.jv_handle=0x{:x}, state={}, rfc_cb->handle=0x{:x}",
+ "FOUND rfc_handle=0x{:x}, port.jv_handle=0x{:x}, state={}, rfc_cb->handle=0x{:x}",
rfc_handle, (*pcb)->handle, (*pcb)->state, (*cb)->handle);
return 1;
}
@@ -1608,11 +1609,11 @@ static int find_rfc_pcb(uint32_t rfcomm_slot_id, tBTA_JV_RFC_CB** cb, tBTA_JV_PC
/* Close an RFCOMM connection */
void bta_jv_rfcomm_close(uint32_t handle, uint32_t rfcomm_slot_id) {
if (!handle) {
- log::error("rfc handle is null");
+ log::error("rfc_handle is null");
return;
}
- log::verbose("rfc handle={}", handle);
+ log::verbose("rfc_handle={}", handle);
tBTA_JV_RFC_CB* p_cb = NULL;
tBTA_JV_PCB* p_pcb = NULL;
@@ -1646,7 +1647,7 @@ static void bta_jv_port_mgmt_sr_cback(const tPORT_RESULT code, uint16_t port_han
return;
}
uint32_t rfcomm_slot_id = p_pcb->rfcomm_slot_id;
- log::verbose("code={}, port_handle=0x{:x}, handle=0x{:x}, p_pcb{}, user={}", code, port_handle,
+ log::verbose("code={}, port_handle=0x{:x}, jv_handle=0x{:x}, p_pcb{}, user={}", code, port_handle,
p_cb->handle, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id);
int status = PORT_CheckConnection(port_handle, &rem_bda, &lcid);
@@ -1667,7 +1668,7 @@ static void bta_jv_port_mgmt_sr_cback(const tPORT_RESULT code, uint16_t port_han
&evt_data.rfc_srv_open.dlci, &evt_data.rfc_srv_open.max_frame_size,
&evt_data.rfc_srv_open.acl_handle,
&evt_data.rfc_srv_open.mux_initiator) != PORT_SUCCESS) {
- log::warn("Unable to get RFCOMM channel info peer:{} handle:{}", rem_bda, port_handle);
+ log::warn("Unable to get RFCOMM channel info peer:{} port_handle:{}", rem_bda, port_handle);
}
}
tBTA_JV_PCB* p_pcb_new_listen = bta_jv_add_rfc_port(p_cb, p_pcb);
@@ -1728,7 +1729,7 @@ static void bta_jv_port_event_sr_cback(uint32_t code, uint16_t port_handle) {
return;
}
- log::verbose("code=0x{:x}, port_handle={}, handle={}", code, port_handle, p_cb->handle);
+ log::verbose("code=0x{:x}, port_handle={}, rfc_handle={}", code, port_handle, p_cb->handle);
uint32_t user_data = p_pcb->rfcomm_slot_id;
if (code & PORT_EV_RXCHAR) {
@@ -1778,7 +1779,8 @@ static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb
} else {
log::error(
- "open pcb not matching listen one, count={}, listen pcb handle={}, open pcb={}",
+ "open pcb not matching listen one, count={}, listen port_handle={}, open "
+ "pcb={}",
listen, p_pcb->port_handle, p_pcb_open->handle);
return NULL;
}
@@ -1808,24 +1810,25 @@ static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb
p_pcb->rfcomm_slot_id = p_pcb_open->rfcomm_slot_id;
if (PORT_ClearKeepHandleFlag(p_pcb->port_handle) != PORT_SUCCESS) {
- log::warn("Unable to clear RFCOMM server keep handle flag handle:{}", p_pcb->port_handle);
+ log::warn("Unable to clear RFCOMM server keep handle flag port_handle:{}",
+ p_pcb->port_handle);
}
if (PORT_SetEventMaskAndCallback(p_pcb->port_handle, event_mask,
bta_jv_port_event_sr_cback) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM server event mask and callback handle:{}",
+ log::warn("Unable to set RFCOMM server event mask and callback port_handle:{}",
p_pcb->port_handle);
}
if (PORT_SetDataCOCallback(p_pcb->port_handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM server data callback handle:{}", p_pcb->port_handle);
+ log::warn("Unable to set RFCOMM server data callback port_handle:{}", p_pcb->port_handle);
}
if (PORT_GetSettings(p_pcb->port_handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to get RFCOMM server state handle:{}", p_pcb->port_handle);
+ log::warn("Unable to get RFCOMM server state port_handle:{}", p_pcb->port_handle);
}
port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);
if (PORT_SetSettings(p_pcb->port_handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM server state handle:{}", p_pcb->port_handle);
+ log::warn("Unable to set RFCOMM server state port_handle:{}", p_pcb->port_handle);
}
p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si);
log::verbose("p_pcb->handle=0x{:x}, curr_sess={}", p_pcb->handle, p_cb->curr_sess);
@@ -1845,7 +1848,7 @@ static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb
/* waits for an RFCOMM client to connect */
void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg) {
+ RfcommCfgInfo cfg, uint32_t app_uid) {
uint16_t handle = 0;
uint32_t event_mask = BTA_JV_RFC_EV_MASK;
PortSettings port_settings;
@@ -1879,21 +1882,24 @@ void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t ma
evt_data.handle = p_cb->handle;
evt_data.use_co = true;
+ if (PORT_SetAppUid(handle, app_uid) != PORT_SUCCESS) {
+ log::warn("Unable to set app_uid for port_handle:{}", handle);
+ }
if (PORT_ClearKeepHandleFlag(handle) != PORT_SUCCESS) {
- log::warn("Unable to clear RFCOMM server keep handle flag handle:{}", handle);
+ log::warn("Unable to clear RFCOMM server keep handle flag port_handle:{}", handle);
}
if (PORT_SetEventMaskAndCallback(handle, event_mask, bta_jv_port_event_sr_cback) !=
PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM server event mask and callback handle:{}", handle);
+ log::warn("Unable to set RFCOMM server event mask and callback port_handle:{}", handle);
}
if (PORT_GetSettings(handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to get RFCOMM server state handle:{}", handle);
+ log::warn("Unable to get RFCOMM server state port_handle:{}", handle);
}
port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);
if (PORT_SetSettings(handle, &port_settings) != PORT_SUCCESS) {
- log::warn("Unable to set RFCOMM port state handle:{}", handle);
+ log::warn("Unable to set RFCOMM port state port_handle:{}", handle);
}
} while (0);
@@ -1902,12 +1908,12 @@ void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t ma
p_cback(BTA_JV_RFCOMM_START_EVT, &bta_jv, rfcomm_slot_id);
if (bta_jv.rfc_start.status == tBTA_JV_STATUS::SUCCESS) {
if (PORT_SetDataCOCallback(handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
- log::error("Unable to set RFCOMM server data callback handle:{}", handle);
+ log::error("Unable to set RFCOMM server data callback port_handle:{}", handle);
}
} else {
if (handle) {
if (RFCOMM_RemoveConnection(handle) != PORT_SUCCESS) {
- log::warn("Unable to remote RFCOMM server connection handle:{}", handle);
+ log::warn("Unable to remote RFCOMM server connection port_handle:{}", handle);
}
}
}
@@ -1916,7 +1922,7 @@ void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t ma
/* stops an RFCOMM server */
void bta_jv_rfcomm_stop_server(uint32_t handle, uint32_t rfcomm_slot_id) {
if (!handle) {
- log::error("jv handle is null");
+ log::error("jv_handle is null");
return;
}
@@ -1967,23 +1973,24 @@ void bta_jv_rfcomm_write(uint32_t handle, uint32_t req_id, tBTA_JV_RFC_CB* p_cb,
/* Set or free power mode profile for a JV application */
void bta_jv_set_pm_profile(uint32_t handle, tBTA_JV_PM_ID app_id, tBTA_JV_CONN_STATE init_st) {
- log::verbose("handle=0x{:x}, app_id={}, init_st={}", handle, app_id,
+ log::verbose("jv_handle=0x{:x}, app_id={}, init_st={}", handle, app_id,
bta_jv_conn_state_text(init_st));
/* clear PM control block */
if (app_id == BTA_JV_PM_ID_CLEAR) {
tBTA_JV_STATUS status = bta_jv_free_set_pm_profile_cb(handle);
if (status != tBTA_JV_STATUS::SUCCESS) {
- log::warn("Unable to free a power mode profile handle:0x:{:x} app_id:{} state:{} status:{}",
- handle, app_id, init_st, bta_jv_status_text(status));
+ log::warn(
+ "Unable to free a power mode profile jv_handle:0x:{:x} app_id:{} state:{} status:{}",
+ handle, app_id, init_st, bta_jv_status_text(status));
}
} else { /* set PM control block */
tBTA_JV_PM_CB* p_cb = bta_jv_alloc_set_pm_profile_cb(handle, app_id);
if (p_cb) {
bta_jv_pm_state_change(p_cb, init_st);
} else {
- log::warn("Unable to allocate a power mode profile handle:0x:{:x} app_id:{} state:{}", handle,
- app_id, init_st);
+ log::warn("Unable to allocate a power mode profile jv_handle:0x:{:x} app_id:{} state:{}",
+ handle, app_id, init_st);
}
}
}
@@ -2034,7 +2041,7 @@ static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB* p_cb) {
*
******************************************************************************/
static void bta_jv_pm_state_change(tBTA_JV_PM_CB* p_cb, const tBTA_JV_CONN_STATE state) {
- log::verbose("p_cb={}, handle=0x{:x}, busy/idle_state={}, app_id={}, conn_state={}",
+ log::verbose("p_cb={}, jv_handle=0x{:x}, busy/idle_state={}, app_id={}, conn_state={}",
std::format_ptr(p_cb), p_cb->handle, p_cb->state, p_cb->app_id,
bta_jv_conn_state_text(state));
diff --git a/system/bta/jv/bta_jv_api.cc b/system/bta/jv/bta_jv_api.cc
index bf240a9834..3110869bb1 100644
--- a/system/bta/jv/bta_jv_api.cc
+++ b/system/bta/jv/bta_jv_api.cc
@@ -415,7 +415,7 @@ tBTA_JV_STATUS BTA_JvL2capWrite(uint32_t handle, uint32_t req_id, BT_HDR* msg, u
******************************************************************************/
tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn,
const RawAddress& peer_bd_addr, tBTA_JV_RFCOMM_CBACK* p_cback,
- uint32_t rfcomm_slot_id, RfcommCfgInfo cfg) {
+ uint32_t rfcomm_slot_id, RfcommCfgInfo cfg, uint32_t app_uid) {
log::verbose("remote_scn:{}, peer_bd_addr:{}, rfcomm_slot_id:{}", remote_scn, peer_bd_addr,
rfcomm_slot_id);
@@ -424,7 +424,7 @@ tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn,
}
do_in_main_thread(Bind(&bta_jv_rfcomm_connect, sec_mask, remote_scn, peer_bd_addr, p_cback,
- rfcomm_slot_id, cfg));
+ rfcomm_slot_id, cfg, app_uid));
return tBTA_JV_STATUS::SUCCESS;
}
@@ -470,7 +470,7 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t handle, uint32_t rfcomm_slot_id) {
******************************************************************************/
tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg) {
+ RfcommCfgInfo cfg, uint32_t app_uid) {
log::verbose("local_scn:{}, rfcomm_slot_id:{}", local_scn, rfcomm_slot_id);
if (p_cback == NULL) {
@@ -486,7 +486,7 @@ tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uin
}
do_in_main_thread(Bind(&bta_jv_rfcomm_start_server, sec_mask, local_scn, max_session, p_cback,
- rfcomm_slot_id, cfg));
+ rfcomm_slot_id, cfg, app_uid));
return tBTA_JV_STATUS::SUCCESS;
}
diff --git a/system/bta/jv/bta_jv_int.h b/system/bta/jv/bta_jv_int.h
index 49ccd976b0..1295b88716 100644
--- a/system/bta/jv/bta_jv_int.h
+++ b/system/bta/jv/bta_jv_int.h
@@ -166,11 +166,11 @@ void bta_jv_l2cap_write(uint32_t handle, uint32_t req_id, BT_HDR* msg, uint32_t
tBTA_JV_L2C_CB* p_cb);
void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg);
+ RfcommCfgInfo cfg, uint32_t app_uid);
void bta_jv_rfcomm_close(uint32_t handle, uint32_t rfcomm_slot_id);
void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session,
tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id,
- RfcommCfgInfo cfg);
+ RfcommCfgInfo cfg, uint32_t app_uid);
void bta_jv_rfcomm_stop_server(uint32_t handle, uint32_t rfcomm_slot_id);
void bta_jv_rfcomm_write(uint32_t handle, uint32_t req_id, tBTA_JV_RFC_CB* p_cb,
tBTA_JV_PCB* p_pcb);
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index da8c3ddace..e72757a129 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -949,6 +949,17 @@ public:
log::info("Start queued broadcast.");
StartAudioBroadcast(broadcast_id);
}
+ } else {
+ // If audio resumes before ISO release, trigger broadcast start
+ if (audio_state_ == AudioState::ACTIVE) {
+ cancelBroadcastTimers();
+ UpdateAudioActiveStateInPublicAnnouncement();
+
+ for (auto& broadcast_pair : broadcasts_) {
+ auto& broadcast = broadcast_pair.second;
+ broadcast->ProcessMessage(BroadcastStateMachine::Message::START, nullptr);
+ }
+ }
}
if (queued_create_broadcast_request_) {
@@ -1394,10 +1405,19 @@ private:
return;
}
+ /* If there is ongoing ISO traffic, it might be not torn down unicast stream. Resume of
+ * broadcast stream would be triggered from IsoTrafficEventCb context, once ISO would be
+ * released.
+ */
+ if (!IsAnyoneStreaming() && instance->is_iso_running_) {
+ log::debug("iso is busy, skip resume request");
+ return;
+ }
+
instance->cancelBroadcastTimers();
instance->UpdateAudioActiveStateInPublicAnnouncement();
- /* In case of double call of resume when broadcast are already in streaming states */
+ /* In case of double call of resume when broadcasts are already in streaming states */
if (IsAnyoneStreaming()) {
log::debug("broadcasts are already streaming");
instance->le_audio_source_hal_client_->ConfirmStreamingRequest();
diff --git a/system/bta/le_audio/broadcaster/broadcaster_test.cc b/system/bta/le_audio/broadcaster/broadcaster_test.cc
index c228ac68ea..8a4acba946 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_test.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_test.cc
@@ -1454,6 +1454,78 @@ TEST_F(BroadcasterTest, AudioResumeWhileStreaming) {
Mock::VerifyAndClearExpectations(mock_codec_manager_);
}
+TEST_F(BroadcasterTest, AudioResumeAfterSuspend) {
+ com::android::bluetooth::flags::provider_->leaudio_big_depends_on_audio_state(true);
+
+ EXPECT_CALL(*mock_codec_manager_, UpdateActiveBroadcastAudioHalClient(mock_audio_source_, true))
+ .Times(1);
+ LeAudioSourceAudioHalClient::Callbacks* audio_receiver;
+ EXPECT_CALL(*mock_audio_source_, Start)
+ .WillOnce(DoAll(SaveArg<1>(&audio_receiver), Return(true)))
+ .WillRepeatedly(Return(false));
+ auto broadcast_id = InstantiateBroadcast();
+
+ ASSERT_NE(audio_receiver, nullptr);
+
+ // OnAudioResume cause state machine go to STREAMING state so BIG creation
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING))
+ .Times(1);
+ audio_receiver->OnAudioResume();
+ Mock::VerifyAndClearExpectations(mock_audio_source_);
+ Mock::VerifyAndClearExpectations(mock_codec_manager_);
+
+ // OnAudioSuspend cause starting the BIG termination timer
+ audio_receiver->OnAudioSuspend();
+ ASSERT_EQ(2, get_func_call_count("alarm_set_on_mloop"));
+ ASSERT_TRUE(big_terminate_timer_->cb != nullptr);
+ ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr);
+
+ // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED))
+ .Times(1);
+ // Imitate execution of BIG termination timer
+ big_terminate_timer_->cb(big_terminate_timer_->data);
+ Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_);
+
+ // OnAudioResume cause state machine go to STREAMING state so BIG creation
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING))
+ .Times(1);
+ audio_receiver->OnAudioResume();
+ Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_);
+
+ // OnAudioSuspend cause starting the BIG termination timer
+ audio_receiver->OnAudioSuspend();
+ ASSERT_EQ(4, get_func_call_count("alarm_set_on_mloop"));
+ ASSERT_TRUE(big_terminate_timer_->cb != nullptr);
+ ASSERT_TRUE(broadcast_stop_timer_->cb != nullptr);
+
+ // BIG termination timer execution, state machine go to CONFIGURED state so BIG terminated
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::CONFIGURED))
+ .Times(1);
+ // Imitate execution of BIG termination timer
+ big_terminate_timer_->cb(big_terminate_timer_->data);
+
+ // Imitate busy ISO
+ iso_active_callback(true);
+
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING))
+ .Times(0);
+ audio_receiver->OnAudioResume();
+ Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_);
+
+ // Verify if iso de-activation start streaming
+ EXPECT_CALL(mock_broadcaster_callbacks_,
+ OnBroadcastStateChanged(broadcast_id, BroadcastState::STREAMING))
+ .Times(1);
+ iso_active_callback(false);
+ Mock::VerifyAndClearExpectations(&mock_broadcaster_callbacks_);
+}
+
// TODO: Add tests for:
// ToRawPacket(BasicAudioAnnouncementData const& in, std::vector<uint8_t>& data)
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 7008676f9c..6dbb4ace85 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -249,7 +249,7 @@ public:
stream_setup_time_(0) {}
void Init(int group_id, LeAudioContextType context_type, int num_of_devices) {
- Reset();
+ Reset(bluetooth::groups::kGroupUnknown);
group_id_ = group_id;
context_type_ = context_type;
num_of_devices_ = num_of_devices;
@@ -257,7 +257,13 @@ public:
ToString(context_type_), num_of_devices);
}
- void Reset(void) {
+ void Reset(int group_id) {
+ if (group_id != bluetooth::groups::kGroupUnknown && group_id != group_id_) {
+ log::verbose("StreamSpeedTracker Reset called for invalid group_id: {} != {}", group_id,
+ group_id_);
+ return;
+ }
+
log::verbose("StreamSpeedTracker group_id: {}", group_id_);
is_started_ = false;
group_id_ = bluetooth::groups::kGroupUnknown;
@@ -300,14 +306,15 @@ public:
ToString(context_type_), total_time_);
}
- bool IsStarted(void) {
- if (is_started_) {
+ bool IsStarted(int group_id) {
+ if (is_started_ && group_id_ == group_id) {
log::verbose("StreamSpeedTracker group_id: {}, {} is_started_: {} ", group_id_,
ToString(context_type_), is_started_);
- } else {
- log::verbose("StreamSpeedTracker not started ");
+ return true;
}
- return is_started_;
+ log::verbose("StreamSpeedTracker not started {} or group_id does not match ({} ! = {}) ",
+ is_started_, group_id, group_id_);
+ return false;
}
void Dump(std::stringstream& stream) {
@@ -422,7 +429,7 @@ public:
leAudioHealthStatus_->RegisterCallback(base::BindRepeating(le_audio_health_status_callback));
BTA_GATTC_AppRegister(
- le_audio_gattc_callback,
+ "le_audio", le_audio_gattc_callback,
base::Bind(
[](base::Closure initCb, uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
@@ -1551,6 +1558,21 @@ public:
active_group_id_ = bluetooth::groups::kGroupUnknown;
}
+ void ConfigureStream(LeAudioDeviceGroup* group, bool up_to_qos_configured) {
+ log::debug("group_id: {}", group->group_id_);
+
+ BidirectionalPair<std::vector<uint8_t>> ccids = {
+ .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
+ local_metadata_context_types_.sink),
+ .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
+ local_metadata_context_types_.source)};
+ if (!groupStateMachine_->ConfigureStream(group, configuration_context_type_,
+ local_metadata_context_types_, ccids,
+ up_to_qos_configured)) {
+ log::info("Could not configure group {}", group->group_id_);
+ }
+ }
+
void PrepareStreamForAConversational(LeAudioDeviceGroup* group) {
if (!com::android::bluetooth::flags::leaudio_improve_switch_during_phone_call()) {
log::info("Flag leaudio_improve_switch_during_phone_call is not enabled");
@@ -1673,10 +1695,20 @@ public:
auto previous_active_group = active_group_id_;
log::info("Active group_id changed {} -> {}", previous_active_group, group_id);
+ bool prepare_for_a_call = IsInCall() || IsInVoipCall();
+
if (previous_active_group == bluetooth::groups::kGroupUnknown) {
/* Expose audio sessions if there was no previous active group */
StartAudioSession(group);
active_group_id_ = group_id;
+
+ /* For the fresh activated LeAudio device, do configuration ahead only when
+ * phone is in a call.
+ */
+ if (prepare_for_a_call) {
+ PrepareStreamForAConversational(group);
+ }
+
} else {
/* In case there was an active group. Stop the stream, but before that, set
* the new group so the group change is correctly handled in OnStateMachineStatusReportCb
@@ -1687,7 +1719,12 @@ public:
/* Note: On purpose we are not sending INACTIVE status up to Java, because previous active
* group will be provided in ACTIVE status. This is in order to have single call to audio
* framework
+ * If group become active while phone call, let's configure it right away up to
+ * the QoS configured state so when audio framework resumes the stream,
+ * only Enable will left.
+ * Otherwise, if there is group switch, let's move ASEs to Configured state.
*/
+ ConfigureStream(group, prepare_for_a_call);
}
/* Reset sink and source listener notified status */
@@ -1701,13 +1738,6 @@ public:
callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
SendAudioGroupSelectableCodecConfigChanged(group);
}
-
- /* If group become active while phone call, let's configure it right away,
- * so when audio framework resumes the stream, it will be almost there.
- */
- if (IsInCall()) {
- PrepareStreamForAConversational(group);
- }
}
void SetEnableState(const RawAddress& address, bool enabled) override {
@@ -1847,7 +1877,8 @@ public:
int source_audio_location, int sink_supported_context_types,
int source_supported_context_types, const std::vector<uint8_t>& handles,
const std::vector<uint8_t>& sink_pacs,
- const std::vector<uint8_t>& source_pacs, const std::vector<uint8_t>& ases) {
+ const std::vector<uint8_t>& source_pacs, const std::vector<uint8_t>& ases,
+ const std::vector<uint8_t>& gmap) {
LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(address);
if (leAudioDevice) {
@@ -1916,6 +1947,14 @@ public:
log::warn("Could not load ases");
}
+ if (gmap.size() != 0) {
+ leAudioDevice->gmap_client_ = std::make_unique<GmapClient>(leAudioDevice->address_);
+ if (!le_audio::DeserializeGmap(leAudioDevice->gmap_client_.get(), gmap)) {
+ leAudioDevice->gmap_client_.reset();
+ log::warn("Invalid GMAP storage for {}", leAudioDevice->address_);
+ }
+ }
+
leAudioDevice->autoconnect_flag_ = autoconnect;
/* When adding from storage, make sure that autoconnect is used
* by all the devices in the group.
@@ -1929,6 +1968,11 @@ public:
return SerializeHandles(leAudioDevice, out);
}
+ bool GetGmapForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
+ LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);
+ return SerializeGmap(leAudioDevice->gmap_client_.get(), out);
+ }
+
bool GetSinkPacsForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
LeAudioDevice* leAudioDevice = leAudioDevices_.FindByAddress(addr);
return SerializeSinkPacs(leAudioDevice, out);
@@ -2330,9 +2374,11 @@ public:
} else if (leAudioDevice->gmap_client_ != nullptr && GmapClient::IsGmapClientEnabled() &&
hdl == leAudioDevice->gmap_client_->getRoleHandle()) {
leAudioDevice->gmap_client_->parseAndSaveGmapRole(len, value);
+ btif_storage_leaudio_update_gmap_bin(leAudioDevice->address_);
} else if (leAudioDevice->gmap_client_ != nullptr && GmapClient::IsGmapClientEnabled() &&
hdl == leAudioDevice->gmap_client_->getUGTFeatureHandle()) {
leAudioDevice->gmap_client_->parseAndSaveUGTFeature(len, value);
+ btif_storage_leaudio_update_gmap_bin(leAudioDevice->address_);
} else {
log::error("Unknown attribute read: 0x{:x}", hdl);
}
@@ -2466,7 +2512,7 @@ public:
/* Check if the device is in allow list and update the flag */
leAudioDevice->UpdateDeviceAllowlistFlag();
- if (BTM_SecIsSecurityPending(address)) {
+ if (BTM_SecIsLeSecurityPending(address)) {
/* if security collision happened, wait for encryption done
* (BTA_GATTC_ENC_CMPL_CB_EVT) */
return;
@@ -5322,9 +5368,37 @@ public:
// Do not take the obsolete metadata
remote_metadata.get(remote_other_direction).clear();
} else {
- remote_metadata.get(remote_other_direction).unset_all(all_bidirectional_contexts);
- remote_metadata.get(remote_other_direction)
- .unset_all(single_direction_only_context_types);
+ // The other direction was opened when already in a bidirectional scenario that was not a
+ // VoIP or a regular Call. We need to figure out which direction metadata is the leading
+ // one.
+ // Note: We usually remove any bidirectional or the previous direction specific context
+ // from the previous direction metadata and replace it with the just-resumed
+ // direction (but still bidirectional) context. However when recording is started
+ // in a GAME scenario, we don't want to reconfigure to or mix the context with LIVE.
+ auto remote_game_uplink_available =
+ group->GetAvailableContexts(le_audio::types::kLeAudioDirectionSource)
+ .test(LeAudioContextType::GAME);
+ auto local_game_uplink_active =
+ (audio_sender_state_ == AudioState::STARTED) &&
+ remote_metadata.sink.test(LeAudioContextType::GAME) &&
+ remote_metadata.source.test_any(LeAudioContextType::LIVE |
+ LeAudioContextType::CONVERSATIONAL);
+ log::debug(
+ "Remote {} metadata change ({}) while having remote {} context ({}) in a "
+ "bidirectional scenario of {}, local_game_uplink_active: {}, "
+ "remote_game_uplink_available: {}",
+ remote_direction_str, ToString(remote_metadata.get(remote_direction)),
+ remote_other_direction_str, ToString(remote_metadata.get(remote_other_direction)),
+ ToString(configuration_context_type_), local_game_uplink_active,
+ remote_game_uplink_available);
+ if (local_game_uplink_active && remote_game_uplink_available) {
+ remote_metadata.source.clear();
+ remote_metadata.source.set(LeAudioContextType::GAME);
+ } else {
+ remote_metadata.get(remote_other_direction).unset_all(all_bidirectional_contexts);
+ remote_metadata.get(remote_other_direction)
+ .unset_all(single_direction_only_context_types);
+ }
}
remote_metadata.get(remote_other_direction)
@@ -5802,9 +5876,9 @@ public:
void speed_start_setup(int group_id, LeAudioContextType context_type, int num_of_connected,
bool is_reconfig = false) {
- log::verbose("is_started {} is_reconfig {} num_of_connected {}", speed_tracker_.IsStarted(),
- is_reconfig, num_of_connected);
- if (!speed_tracker_.IsStarted()) {
+ log::verbose("is_started {} is_reconfig {} num_of_connected {}",
+ speed_tracker_.IsStarted(group_id), is_reconfig, num_of_connected);
+ if (!speed_tracker_.IsStarted(group_id)) {
speed_tracker_.Init(group_id, context_type, num_of_connected);
}
if (is_reconfig) {
@@ -5814,26 +5888,27 @@ public:
}
}
- void speed_stop_reconfig(void) {
+ void speed_stop_reconfig(int group_id) {
log::verbose("");
- if (!speed_tracker_.IsStarted()) {
+ if (!speed_tracker_.IsStarted(group_id)) {
return;
}
+
speed_tracker_.ReconfigurationComplete();
}
- void speed_stream_created() {
+ void speed_stream_created(int group_id) {
log::verbose("");
- if (!speed_tracker_.IsStarted()) {
+ if (!speed_tracker_.IsStarted(group_id)) {
return;
}
speed_tracker_.StreamCreated();
}
- void speed_stop_setup() {
+ void speed_stop_setup(int group_id) {
log::verbose("");
- if (!speed_tracker_.IsStarted()) {
+ if (!speed_tracker_.IsStarted(group_id)) {
return;
}
@@ -5843,7 +5918,7 @@ public:
speed_tracker_.StopStreamSetup();
stream_speed_history_.emplace_front(speed_tracker_);
- speed_tracker_.Reset();
+ speed_tracker_.Reset(group_id);
}
void notifyGroupStreamStatus(int group_id, GroupStreamStatus groupStreamStatus) {
@@ -5898,26 +5973,36 @@ public:
*/
CancelStreamingRequest();
ReconfigurationComplete(previously_active_directions);
- speed_stop_reconfig();
+ speed_stop_reconfig(active_group_id_);
}
void OnStateMachineStatusReportCb(int group_id, GroupStreamStatus status) {
- log::info("status: {}, group_id: {}, audio_sender_state {}, audio_receiver_state {}",
- static_cast<int>(status), group_id, bluetooth::common::ToString(audio_sender_state_),
- bluetooth::common::ToString(audio_receiver_state_));
+ /* When switching stream between two group, it is important to keep track if given status is for
+ * active group or not in order to proper Audio HAL notifications.
+ * It means, we should update Audio HAL and clear common resources when group is an active group
+ * or active group is already cleared.
+ */
+ bool is_active_group_operation =
+ (group_id == active_group_id_ || active_group_id_ == bluetooth::groups::kGroupUnknown);
+
+ log::info(
+ "status: {}, group_id: {}, audio_sender_state {}, audio_receiver_state {}, "
+ "is_active_group_operation {}",
+ static_cast<int>(status), group_id, bluetooth::common::ToString(audio_sender_state_),
+ bluetooth::common::ToString(audio_receiver_state_), is_active_group_operation);
LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
notifyGroupStreamStatus(group_id, status);
switch (status) {
case GroupStreamStatus::STREAMING: {
- if (group_id != active_group_id_) {
+ if (!is_active_group_operation) {
log::error("Streaming group {} is no longer active. Stop the group.", group_id);
GroupStop(group_id);
return;
}
- speed_stream_created();
+ speed_stream_created(group_id);
bluetooth::le_audio::MetricsCollector::Get()->OnStreamStarted(active_group_id_,
configuration_context_type_);
@@ -5939,7 +6024,7 @@ public:
* Just stop streaming
*/
log::warn("Stopping stream for group {} as AF not interested.", group_id);
- speed_stop_setup();
+ speed_stop_setup(group_id);
groupStateMachine_->StopStream(group);
return;
}
@@ -5954,7 +6039,7 @@ public:
"reconfigure to {}",
ToString(group->GetConfigurationContextType()),
ToString(configuration_context_type_));
- speed_stop_setup();
+ speed_stop_setup(group_id);
initReconfiguration(group, group->GetConfigurationContextType());
return;
}
@@ -5976,16 +6061,21 @@ public:
StartReceivingAudio(group_id);
}
- speed_stop_setup();
+ speed_stop_setup(group_id);
break;
}
case GroupStreamStatus::SUSPENDED:
- speed_tracker_.Reset();
- /** Stop Audio but don't release all the Audio resources */
- SuspendAudio();
+ speed_tracker_.Reset(group_id);
+
+ if (is_active_group_operation) {
+ /** Stop Audio but don't release all the Audio resources */
+ SuspendAudio();
+ }
break;
case GroupStreamStatus::CONFIGURED_BY_USER:
- reconfigurationComplete();
+ if (is_active_group_operation) {
+ reconfigurationComplete();
+ }
break;
case GroupStreamStatus::CONFIGURED_AUTONOMOUS:
/* This state is notified only when
@@ -5994,19 +6084,21 @@ public:
* it is handled same as IDLE
*/
case GroupStreamStatus::IDLE: {
- if (sw_enc_left) {
- sw_enc_left.reset();
- }
- if (sw_enc_right) {
- sw_enc_right.reset();
- }
- if (sw_dec_left) {
- sw_dec_left.reset();
- }
- if (sw_dec_right) {
- sw_dec_right.reset();
+ if (is_active_group_operation) {
+ if (sw_enc_left) {
+ sw_enc_left.reset();
+ }
+ if (sw_enc_right) {
+ sw_enc_right.reset();
+ }
+ if (sw_dec_left) {
+ sw_dec_left.reset();
+ }
+ if (sw_dec_right) {
+ sw_dec_right.reset();
+ }
+ CleanCachedMicrophoneData();
}
- CleanCachedMicrophoneData();
if (group) {
handleAsymmetricPhyForUnicast(group);
@@ -6042,8 +6134,10 @@ public:
}
log::info("Clear pending configuration flag for group {}", group->group_id_);
group->ClearPendingConfiguration();
- reconfigurationComplete();
- } else {
+ if (is_active_group_operation) {
+ reconfigurationComplete();
+ }
+ } else if (is_active_group_operation) {
if (sink_monitor_mode_) {
notifyAudioLocalSink(UnicastMonitorModeStatus::STREAMING_SUSPENDED);
}
@@ -6054,8 +6148,10 @@ public:
}
}
- speed_tracker_.Reset();
- CancelStreamingRequest();
+ speed_tracker_.Reset(group_id);
+ if (is_active_group_operation) {
+ CancelStreamingRequest();
+ }
if (group) {
NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_);
@@ -6064,6 +6160,20 @@ public:
}
break;
}
+ case GroupStreamStatus::RELEASING_AUTONOMOUS:
+ /* Remote device releases all the ASEs autonomusly. This should not happen and not sure what
+ * is the remote device intention. If remote wants stop the stream then MCS shall be used to
+ * stop the stream in a proper way. For a phone call, GTBS shall be used. For now we assume
+ * this device has does not want to be used for streaming and mark it as Inactive.
+ */
+ log::warn("Group {} is doing autonomous release, make it inactive", group_id);
+ if (group) {
+ group->PrintDebugState();
+ groupSetAndNotifyInactive();
+ }
+ audio_sender_state_ = AudioState::IDLE;
+ audio_receiver_state_ = AudioState::IDLE;
+ break;
case GroupStreamStatus::RELEASING:
case GroupStreamStatus::SUSPENDING:
if (active_group_id_ != bluetooth::groups::kGroupUnknown &&
@@ -6076,23 +6186,29 @@ public:
* it means that it is some internal state machine error. This is very unlikely and
* for now just Inactivate the group.
*/
- log::error("Internal state machine error");
+ log::error("Internal state machine error for group {}", group_id);
group->PrintDebugState();
groupSetAndNotifyInactive();
+ audio_sender_state_ = AudioState::IDLE;
+ audio_receiver_state_ = AudioState::IDLE;
+ return;
}
- if (audio_sender_state_ != AudioState::IDLE) {
- audio_sender_state_ = AudioState::RELEASING;
- }
+ if (is_active_group_operation) {
+ if (audio_sender_state_ != AudioState::IDLE) {
+ audio_sender_state_ = AudioState::RELEASING;
+ }
- if (audio_receiver_state_ != AudioState::IDLE) {
- audio_receiver_state_ = AudioState::RELEASING;
- }
+ if (audio_receiver_state_ != AudioState::IDLE) {
+ audio_receiver_state_ = AudioState::RELEASING;
+ }
- if (group && group->IsPendingConfiguration()) {
- log::info("Releasing for reconfiguration, don't send anything on CISes");
- SuspendedForReconfiguration();
+ if (group && group->IsPendingConfiguration()) {
+ log::info("Releasing for reconfiguration, don't send anything on CISes");
+ SuspendedForReconfiguration();
+ }
}
+
break;
default:
break;
@@ -6238,14 +6354,14 @@ private:
* the session callbacks special action from this Module would be
* required e.g. to Unicast handover.
*/
- if (!com::android::bluetooth::flags::leaudio_use_audio_recording_listener()) {
- if (!sink_monitor_mode_) {
- local_metadata_context_types_.sink.clear();
- le_audio_sink_hal_client_->Stop();
- le_audio_sink_hal_client_.reset();
- }
+ if (com::android::bluetooth::flags::leaudio_use_audio_recording_listener() ||
+ !sink_monitor_mode_) {
+ local_metadata_context_types_.sink.clear();
+ le_audio_sink_hal_client_->Stop();
+ le_audio_sink_hal_client_.reset();
}
}
+
local_metadata_context_types_.source.clear();
configuration_context_type_ = LeAudioContextType::UNINITIALIZED;
@@ -6544,14 +6660,12 @@ DeviceGroupsCallbacksImpl deviceGroupsCallbacksImpl;
} // namespace
-void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect,
- int sink_audio_location, int source_audio_location,
- int sink_supported_context_types,
- int source_supported_context_types,
- const std::vector<uint8_t>& handles,
- const std::vector<uint8_t>& sink_pacs,
- const std::vector<uint8_t>& source_pacs,
- const std::vector<uint8_t>& ases) {
+void LeAudioClient::AddFromStorage(
+ const RawAddress& addr, bool autoconnect, int sink_audio_location,
+ int source_audio_location, int sink_supported_context_types,
+ int source_supported_context_types, const std::vector<uint8_t>& handles,
+ const std::vector<uint8_t>& sink_pacs, const std::vector<uint8_t>& source_pacs,
+ const std::vector<uint8_t>& ases, const std::vector<uint8_t>& gmap) {
if (!instance) {
log::error("Not initialized yet");
return;
@@ -6559,7 +6673,7 @@ void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect,
instance->AddFromStorage(addr, autoconnect, sink_audio_location, source_audio_location,
sink_supported_context_types, source_supported_context_types, handles,
- sink_pacs, source_pacs, ases);
+ sink_pacs, source_pacs, ases, gmap);
}
bool LeAudioClient::GetHandlesForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
@@ -6598,6 +6712,15 @@ bool LeAudioClient::GetAsesForStorage(const RawAddress& addr, std::vector<uint8_
return instance->GetAsesForStorage(addr, out);
}
+bool LeAudioClient::GetGmapForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
+ if (!instance) {
+ log::error("Not initialized yet");
+ return false;
+ }
+
+ return instance->GetGmapForStorage(addr, out);
+}
+
bool LeAudioClient::IsLeAudioClientRunning(void) { return instance != nullptr; }
bool LeAudioClient::IsLeAudioClientInStreaming(void) {
@@ -6669,6 +6792,7 @@ void LeAudioClient::Initialize(
void LeAudioClient::DebugDump(int fd) {
std::scoped_lock<std::mutex> lock(instance_mutex);
DeviceGroups::DebugDump(fd);
+ GmapServer::DebugDump(fd);
dprintf(fd, "LeAudio Manager: \n");
if (instance) {
diff --git a/system/bta/le_audio/client_linux.cc b/system/bta/le_audio/client_linux.cc
index ff56a555b8..9908ef0a61 100644
--- a/system/bta/le_audio/client_linux.cc
+++ b/system/bta/le_audio/client_linux.cc
@@ -48,14 +48,12 @@ void LeAudioClient::Initialize(
void LeAudioClient::Cleanup(void) {}
LeAudioClient* LeAudioClient::Get(void) { return nullptr; }
void LeAudioClient::DebugDump(int fd) {}
-void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect,
- int sink_audio_location, int source_audio_location,
- int sink_supported_context_types,
- int source_supported_context_types,
- const std::vector<uint8_t>& handles,
- const std::vector<uint8_t>& sink_pacs,
- const std::vector<uint8_t>& source_pacs,
- const std::vector<uint8_t>& ases) {}
+void LeAudioClient::AddFromStorage(
+ const RawAddress& addr, bool autoconnect, int sink_audio_location,
+ int source_audio_location, int sink_supported_context_types,
+ int source_supported_context_types, const std::vector<uint8_t>& handles,
+ const std::vector<uint8_t>& sink_pacs, const std::vector<uint8_t>& source_pacs,
+ const std::vector<uint8_t>& ases, const std::vector<uint8_t>& gmap) {}
bool LeAudioClient::GetHandlesForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
return false;
}
@@ -68,5 +66,8 @@ bool LeAudioClient::GetSourcePacsForStorage(const RawAddress& addr, std::vector<
bool LeAudioClient::GetAsesForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
return false;
}
+bool LeAudioClient::GetGmapForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
+ return false;
+}
bool LeAudioClient::IsLeAudioClientRunning() { return false; }
bool LeAudioClient::IsLeAudioClientInStreaming() { return false; }
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index 9977fec885..b956adec0e 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -25,6 +25,7 @@
#include <cstdint>
#include <functional>
#include <memory>
+#include <optional>
#include <ostream>
#include <sstream>
#include <string>
@@ -39,6 +40,8 @@
#include "broadcaster/broadcaster_types.h"
#include "bta_le_audio_api.h"
#include "btm_iso_api_types.h"
+#include "gmap_client.h"
+#include "gmap_server.h"
#include "hardware/bt_le_audio.h"
#include "hci/controller_interface.h"
#include "hci/hci_packets.h"
@@ -137,6 +140,19 @@ public:
osi_property_get_bool("bluetooth.leaudio.dual_bidirection_swb.supported", false);
bluetooth::le_audio::AudioSetConfigurationProvider::Initialize(GetCodecLocation());
UpdateOffloadCapability(offloading_preference);
+
+ if (IsUsingCodecExtensibility()) {
+ codec_provider_info_ =
+ audio::le_audio::LeAudioClientInterface::Get()->GetCodecConfigProviderInfo();
+ if (codec_provider_info_.has_value() && codec_provider_info_->allowAsymmetric &&
+ codec_provider_info_->lowLatency) {
+ GmapClient::UpdateGmapOffloaderSupport(true);
+ GmapServer::UpdateGmapOffloaderSupport(true);
+ log::debug("Asymmetric configuration supported. Enabling offloader GMAP support.");
+ } else {
+ log::debug("Asymmetric configurations not supported. Not enabling offloader GMAP support.");
+ }
+ }
}
~codec_manager_impl() {
if (GetCodecLocation() != CodecLocation::HOST) {
@@ -149,6 +165,10 @@ public:
}
CodecLocation GetCodecLocation(void) const { return codec_location_; }
+ std::optional<ProviderInfo> GetCodecConfigProviderInfo(void) const {
+ return codec_provider_info_;
+ }
+
bool IsDualBiDirSwbSupported(void) const {
if (GetCodecLocation() == CodecLocation::ADSP) {
// Whether dual bidirection swb is supported by property and for offload
@@ -1094,6 +1114,8 @@ private:
std::unordered_map<btle_audio_codec_index_t, uint8_t> btle_audio_codec_type_map_ = {
{::bluetooth::le_audio::LE_AUDIO_CODEC_INDEX_SOURCE_LC3, types::kLeAudioCodingFormatLC3}};
+ std::optional<ProviderInfo> codec_provider_info_;
+
std::vector<btle_audio_codec_config_t> codec_input_capa = {};
std::vector<btle_audio_codec_config_t> codec_output_capa = {};
int broadcast_target_config = -1;
@@ -1138,26 +1160,24 @@ std::ostream& operator<<(std::ostream& os,
if (req.sink_requirements.has_value()) {
for (auto const& sink_req : req.sink_requirements.value()) {
- os << "sink_req: {";
+ os << ", sink_req: {";
os << ", target_latency: " << +sink_req.target_latency;
os << ", target_Phy: " << +sink_req.target_Phy;
- // os << sink_req.params.GetAsCoreCodecCapabilities();
os << "}";
}
} else {
- os << "sink_req: None";
+ os << ", sink_req: None";
}
if (req.source_requirements.has_value()) {
for (auto const& source_req : req.source_requirements.value()) {
- os << "source_req: {";
+ os << ", source_req: {";
os << ", target_latency: " << +source_req.target_latency;
os << ", target_Phy: " << +source_req.target_Phy;
- // os << source_req.params.GetAsCoreCodecCapabilities();
os << "}";
}
} else {
- os << "source_req: None";
+ os << ", source_req: None";
}
os << "}";
@@ -1207,6 +1227,14 @@ types::CodecLocation CodecManager::GetCodecLocation(void) const {
return pimpl_->codec_manager_impl_->GetCodecLocation();
}
+std::optional<ProviderInfo> CodecManager::GetCodecConfigProviderInfo(void) const {
+ if (!pimpl_->IsRunning()) {
+ return std::nullopt;
+ }
+
+ return pimpl_->codec_manager_impl_->GetCodecConfigProviderInfo();
+}
+
bool CodecManager::IsDualBiDirSwbSupported(void) const {
if (!pimpl_->IsRunning()) {
return false;
diff --git a/system/bta/le_audio/codec_manager.h b/system/bta/le_audio/codec_manager.h
index 1a86a9d4b0..27c2534216 100644
--- a/system/bta/le_audio/codec_manager.h
+++ b/system/bta/le_audio/codec_manager.h
@@ -60,8 +60,28 @@ struct broadcast_offload_config {
uint16_t max_transport_latency;
};
+struct ProviderInfo {
+ bool allowAsymmetric = false;
+ bool lowLatency = false;
+
+ inline std::string toString() const {
+ std::ostringstream _aidl_os;
+ _aidl_os << "ProviderInfo{";
+ _aidl_os << "allowAsymmetric: " << allowAsymmetric;
+ _aidl_os << ", lowLatency: " << lowLatency;
+ _aidl_os << "}";
+ return _aidl_os.str();
+ }
+};
+
class CodecManager {
public:
+ enum Flags {
+ NONE = 0x00,
+ LOW_LATENCY,
+ ALLOW_ASYMMETRIC,
+ };
+
struct UnicastConfigurationRequirements {
::bluetooth::le_audio::types::LeAudioContextType audio_context_type;
std::optional<std::vector<types::acs_ac_record>> sink_pacs;
@@ -75,6 +95,8 @@ public:
std::optional<std::vector<DeviceDirectionRequirements>> sink_requirements;
std::optional<std::vector<DeviceDirectionRequirements>> source_requirements;
+
+ Flags flags;
};
/* The provider function checks each possible configuration (from the set of
@@ -103,6 +125,7 @@ public:
const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>& offloading_preference);
void Stop(void);
virtual types::CodecLocation GetCodecLocation(void) const;
+ virtual std::optional<ProviderInfo> GetCodecConfigProviderInfo(void) const;
virtual bool IsDualBiDirSwbSupported(void) const;
virtual bool UpdateCisConfiguration(const std::vector<struct types::cis>& cises,
const stream_parameters& stream_params, uint8_t direction);
diff --git a/system/bta/le_audio/codec_manager_test.cc b/system/bta/le_audio/codec_manager_test.cc
index 5770fdbea4..fe6a332ab8 100644
--- a/system/bta/le_audio/codec_manager_test.cc
+++ b/system/bta/le_audio/codec_manager_test.cc
@@ -25,6 +25,8 @@
#include "hci/controller_interface_mock.h"
#include "hci/hci_packets.h"
#include "internal_include/stack_config.h"
+#include "le_audio/gmap_client.h"
+#include "le_audio/gmap_server.h"
#include "le_audio/le_audio_types.h"
#include "le_audio_set_configuration_provider.h"
#include "test/mock/mock_legacy_hci_interface.h"
@@ -49,11 +51,6 @@ using bluetooth::le_audio::types::kLeAudioDirectionSource;
void osi_property_set_bool(const char* key, bool value);
-template <typename T>
-T& bluetooth::le_audio::types::BidirectionalPair<T>::get(uint8_t direction) {
- return (direction == bluetooth::le_audio::types::kLeAudioDirectionSink) ? sink : source;
-}
-
static const std::vector<AudioSetConfiguration> offload_capabilities_none(0);
const std::vector<AudioSetConfiguration>* offload_capabilities = &offload_capabilities_none;
@@ -95,18 +92,22 @@ stack_config_t mock_stack_config{
const stack_config_t* stack_config_get_interface(void) { return &mock_stack_config; }
-namespace bluetooth {
-namespace audio {
-namespace le_audio {
+namespace bluetooth::audio::le_audio {
OffloadCapabilities get_offload_capabilities() {
return {*offload_capabilities, *offload_capabilities};
}
-} // namespace le_audio
-} // namespace audio
-} // namespace bluetooth
+std::optional<bluetooth::le_audio::ProviderInfo> LeAudioClientInterface::GetCodecConfigProviderInfo(
+ void) const {
+ return std::nullopt;
+}
+LeAudioClientInterface* LeAudioClientInterface::Get() { return nullptr; }
+} // namespace bluetooth::audio::le_audio
namespace bluetooth::le_audio {
+void GmapClient::UpdateGmapOffloaderSupport(bool) {}
+void GmapServer::UpdateGmapOffloaderSupport(bool) {}
+
class MockLeAudioSourceHalClient;
MockLeAudioSourceHalClient* mock_le_audio_source_hal_client_;
std::unique_ptr<LeAudioSourceAudioHalClient> owned_mock_le_audio_source_hal_client_;
@@ -681,12 +682,16 @@ TEST_F(CodecManagerTestAdsp, test_capabilities_none) {
TEST_F(CodecManagerTestAdsp, test_capabilities) {
for (auto test_context : ::bluetooth::le_audio::types::kLeAudioContextAllTypesArray) {
// Build the offloader capabilities vector using the configuration provider
- // in HOST mode to get all the .json filce configuration entries.
+ // in HOST mode to get all the .json file configuration entries.
std::vector<AudioSetConfiguration> offload_capabilities;
AudioSetConfigurationProvider::Initialize(bluetooth::le_audio::types::CodecLocation::HOST);
- for (auto& cap : *AudioSetConfigurationProvider::Get()->GetConfigurations(test_context)) {
+ auto all_local_configs = AudioSetConfigurationProvider::Get()->GetConfigurations(test_context);
+ ASSERT_NE(0lu, all_local_configs->size());
+
+ for (auto& cap : *all_local_configs) {
offload_capabilities.push_back(*cap);
}
+
ASSERT_NE(0u, offload_capabilities.size());
set_mock_offload_capabilities(offload_capabilities);
// Clean up before the codec manager starts it in ADSP mode.
diff --git a/system/bta/le_audio/device_groups.cc b/system/bta/le_audio/device_groups.cc
index 85c230c0b0..c4000c16d7 100644
--- a/system/bta/le_audio/device_groups.cc
+++ b/system/bta/le_audio/device_groups.cc
@@ -34,6 +34,7 @@
#include "audio_hal_client/audio_hal_client.h"
#include "bta/include/bta_gatt_api.h"
+#include "bta/le_audio/gmap_server.h"
#include "bta_csis_api.h"
#include "bta_groups.h"
#include "btif/include/btif_profile_storage.h"
@@ -785,8 +786,8 @@ bool LeAudioDeviceGroup::GetPresentationDelay(uint32_t* delay, uint8_t direction
} while ((ase = leAudioDevice->GetNextActiveAseWithSameDirection(ase)));
} while ((leAudioDevice = GetNextActiveDevice(leAudioDevice)));
- if (preferred_delay_min <= preferred_delay_max && preferred_delay_min > delay_min &&
- preferred_delay_min < delay_max) {
+ if (preferred_delay_min <= preferred_delay_max && preferred_delay_min >= delay_min &&
+ preferred_delay_min <= delay_max) {
*delay = preferred_delay_min;
} else {
*delay = delay_min;
@@ -825,8 +826,11 @@ CodecManager::UnicastConfigurationRequirements
LeAudioDeviceGroup::GetAudioSetConfigurationRequirements(types::LeAudioContextType ctx_type) const {
auto new_req = CodecManager::UnicastConfigurationRequirements{
.audio_context_type = ctx_type,
+ .flags = CodecManager::Flags::NONE,
};
+ bool remote_has_gmap = false;
+
// Define a requirement for each location. Knowing codec specific
// capabilities (i.e. multiplexing capability) the config provider can
// determine the number of ASEs to activate.
@@ -933,6 +937,25 @@ LeAudioDeviceGroup::GetAudioSetConfigurationRequirements(types::LeAudioContextTy
}
}
}
+
+ if (device->gmap_client_) {
+ remote_has_gmap = true;
+ }
+ }
+
+ if ((ctx_type == ::bluetooth::le_audio::types::LeAudioContextType::GAME) &&
+ GmapClient::IsGmapClientEnabled() && GmapServer::IsGmapServerEnabled() && remote_has_gmap) {
+ // Allow asymmetric configurations for the low latency GAME scenarios
+ new_req.flags = CodecManager::Flags(CodecManager::Flags::ALLOW_ASYMMETRIC |
+ CodecManager::Flags::LOW_LATENCY);
+ log::debug(
+ "GMAP is enabled. Set asymmetric flag for the GAME audio context configuration "
+ "requests.");
+ } else {
+ log::debug(
+ "GMAP is disabled, remote_has_gmap: {}, gmap_client_enabled: {}, gmap_server_enabled: "
+ "{}",
+ remote_has_gmap, GmapClient::IsGmapClientEnabled(), GmapServer::IsGmapServerEnabled());
}
return new_req;
diff --git a/system/bta/le_audio/gmap_client.h b/system/bta/le_audio/gmap_client.h
index ec3d431afe..4b98abb868 100644
--- a/system/bta/le_audio/gmap_client.h
+++ b/system/bta/le_audio/gmap_client.h
@@ -26,8 +26,8 @@ namespace bluetooth::le_audio {
class GmapClient {
public:
- void AddFromStorage(const RawAddress& addr, const uint8_t role, const uint16_t role_handle,
- const uint8_t UGT_feature, const uint16_t UGT_feature_handle);
+ void AddFromStorage(uint8_t role, uint16_t role_handle, uint8_t UGT_feature,
+ uint16_t UGT_feature_handle);
void DebugDump(std::stringstream& stream);
@@ -41,15 +41,15 @@ public:
bool parseAndSaveUGTFeature(uint16_t len, const uint8_t* value);
- std::bitset<8> getRole();
+ std::bitset<8> getRole() const;
- uint16_t getRoleHandle();
+ uint16_t getRoleHandle() const;
void setRoleHandle(uint16_t handle);
- std::bitset<8> getUGTFeature();
+ std::bitset<8> getUGTFeature() const;
- uint16_t getUGTFeatureHandle();
+ uint16_t getUGTFeatureHandle() const;
void setUGTFeatureHandle(uint16_t handle);
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index 2891a9a8d4..693494f73c 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -1956,7 +1956,9 @@ protected:
unicast_source_hal_cb_));
SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&*mock_le_audio_source_hal_client_);
+ if (expected_confirmation || expected_cancel) {
+ Mock::VerifyAndClearExpectations(&*mock_le_audio_source_hal_client_);
+ }
}
void LocalAudioSinkSuspend(void) {
@@ -2693,6 +2695,7 @@ protected:
LeAudioSinkAudioHalClient::Callbacks* unicast_sink_hal_cb_ = nullptr;
uint8_t default_channel_cnt = 0x03;
+ uint8_t default_src_channel_cnt = default_channel_cnt;
uint8_t default_ase_cnt = 1;
NiceMock<MockCsisClient> mock_csis_client_module_;
@@ -2774,8 +2777,8 @@ protected:
std::vector<::bluetooth::le_audio::btle_audio_codec_config_t> framework_encode_preference;
BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ EXPECT_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
@@ -2969,8 +2972,8 @@ TEST_F(UnicastTestNoInit, InitializeNoHal_2_1) {
ON_CALL(mock_hal_2_1_verifier, Call()).WillByDefault([]() -> bool { return false; });
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
std::vector<::bluetooth::le_audio::btle_audio_codec_config_t> framework_encode_preference;
EXPECT_DEATH(
@@ -2989,7 +2992,7 @@ TEST_F(UnicastTest, CleanupWhenUserConnecting) {
uint16_t conn_id = 1;
SetSampleDatabaseEarbudsValid(1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
- default_channel_cnt, 0x0004,
+ default_src_channel_cnt, 0x0004,
/* source sample freq 16khz */ true, /*add_csis*/
true, /*add_cas*/
true, /*add_pacs*/
@@ -3728,17 +3731,20 @@ TEST_F(UnicastTestNoInit, ConnectFailedDueToInvalidParameters) {
std::vector<uint8_t> snk_pacs;
LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);
+ std::vector<uint8_t> gmap_data;
+ LeAudioClient::GetGmapForStorage(test_address0, gmap_data);
+
EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
do_in_main_thread(base::Bind(&LeAudioClient::AddFromStorage, test_address0, autoconnect,
codec_spec_conf::kLeAudioLocationFrontLeft,
codec_spec_conf::kLeAudioLocationFrontLeft, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
do_in_main_thread(base::Bind(&LeAudioClient::AddFromStorage, test_address1, autoconnect,
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationFrontRight, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
});
// Expect stored device0 to connect automatically (first directed connection )
@@ -3763,8 +3769,8 @@ TEST_F(UnicastTestNoInit, ConnectFailedDueToInvalidParameters) {
// Initialize
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
@@ -3835,16 +3841,17 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsBroakenStorage) {
std::vector<uint8_t> empty_buf;
EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
- do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address0, autoconnect,
- codec_spec_conf::kLeAudioLocationFrontLeft,
- codec_spec_conf::kLeAudioLocationFrontLeft, 0xff, 0xff,
- std::move(empty_buf), std::move(empty_buf),
- std::move(empty_buf), std::move(empty_buf)));
+ do_in_main_thread(base::BindOnce(
+ &LeAudioClient::AddFromStorage, test_address0, autoconnect,
+ codec_spec_conf::kLeAudioLocationFrontLeft, codec_spec_conf::kLeAudioLocationFrontLeft,
+ 0xff, 0xff, std::move(empty_buf), std::move(empty_buf), std::move(empty_buf),
+ std::move(empty_buf), std::move(empty_buf)));
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address1, autoconnect,
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationFrontRight, 0xff, 0xff,
std::move(empty_buf), std::move(empty_buf),
- std::move(empty_buf), std::move(empty_buf)));
+ std::move(empty_buf), std::move(empty_buf),
+ std::move(empty_buf)));
SyncOnMainLoop();
});
@@ -3870,8 +3877,8 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsBroakenStorage) {
// Initialize
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
@@ -3975,17 +3982,20 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGrouped) {
std::vector<uint8_t> snk_pacs;
LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);
+ std::vector<uint8_t> gmap_data;
+ LeAudioClient::GetGmapForStorage(test_address0, gmap_data);
+
EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address0, autoconnect,
codec_spec_conf::kLeAudioLocationFrontLeft,
codec_spec_conf::kLeAudioLocationFrontLeft, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address1, autoconnect,
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationFrontRight, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
SyncOnMainLoop();
});
@@ -4011,8 +4021,8 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGrouped) {
// Initialize
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
@@ -4098,6 +4108,9 @@ TEST_F(UnicastTest, LoadStoredBandedHeadphones) {
std::vector<uint8_t> snk_pacs;
LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);
+ std::vector<uint8_t> gmap_data;
+ LeAudioClient::GetGmapForStorage(test_address0, gmap_data);
+
/* Disconnect & Cleanup */
DisconnectLeAudioWithAclClose(test_address0, conn_id);
if (LeAudioClient::IsLeAudioClientRunning()) {
@@ -4116,7 +4129,7 @@ TEST_F(UnicastTest, LoadStoredBandedHeadphones) {
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationMonoAudio, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
SyncOnMainLoop();
});
@@ -4127,8 +4140,8 @@ TEST_F(UnicastTest, LoadStoredBandedHeadphones) {
// Re-Initialize & load from storage
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
std::vector<::bluetooth::le_audio::btle_audio_codec_config_t> framework_encode_preference;
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
@@ -4221,17 +4234,20 @@ TEST_F(UnicastTestNoInit, ServiceChangedBeforeServiceIsConnected) {
std::vector<uint8_t> snk_pacs;
LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);
+ std::vector<uint8_t> gmap_data;
+ LeAudioClient::GetGmapForStorage(test_address0, gmap_data);
+
EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address0, autoconnect,
codec_spec_conf::kLeAudioLocationFrontLeft,
codec_spec_conf::kLeAudioLocationFrontLeft, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address1, autoconnect,
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationFrontRight, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
SyncOnMainLoop();
});
@@ -4257,8 +4273,8 @@ TEST_F(UnicastTestNoInit, ServiceChangedBeforeServiceIsConnected) {
// Initialize
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
@@ -4347,18 +4363,21 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGroupedDifferently) {
std::vector<uint8_t> snk_pacs;
LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);
+ std::vector<uint8_t> gmap_data;
+ LeAudioClient::GetGmapForStorage(test_address0, gmap_data);
+
// Load devices from the storage when storage API is called
EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address0, autoconnect0,
codec_spec_conf::kLeAudioLocationFrontLeft,
codec_spec_conf::kLeAudioLocationFrontLeft, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address1, autoconnect1,
codec_spec_conf::kLeAudioLocationFrontRight,
codec_spec_conf::kLeAudioLocationFrontRight, 0xff, 0xff,
std::move(handles), std::move(snk_pacs), std::move(src_pacs),
- std::move(ases)));
+ std::move(ases), std::move(gmap_data)));
});
// Expect stored device0 to connect automatically
@@ -4388,8 +4407,8 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGroupedDifferently) {
// Initialize
BtaAppRegisterCallback app_register_callback;
- ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
- .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ ON_CALL(mock_gatt_interface_, AppRegister(_, _, _, _))
+ .WillByDefault(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
std::vector<::bluetooth::le_audio::btle_audio_codec_config_t> framework_encode_preference;
LeAudioClient::Initialize(
&mock_audio_hal_client_callbacks_,
@@ -4988,6 +5007,155 @@ TEST_F(UnicastTest, GroupSetActive_and_InactiveDuringStreamConfiguration) {
Mock::VerifyAndClearExpectations(&mock_state_machine_);
}
+TEST_F(UnicastTest, AnotherGroupSetActive_DuringMediaStream) {
+ com::android::bluetooth::flags::provider_->leaudio_improve_switch_during_phone_call(true);
+ const RawAddress test_address0 = GetTestAddress(0);
+ const RawAddress test_address1 = GetTestAddress(1);
+ int group_id_1 = 1;
+ int group_id_2 = 2;
+ int group_size = 1;
+
+ default_channel_cnt = 2;
+ default_src_channel_cnt = 1;
+
+ /**
+ * Scenario:
+ * 1. Voip is started
+ * 2. Group 1 is set active - no fast configuration is expected
+ * 3. Audio HAL resumse the stream and Group 1 is streaming
+ * 3. Group 2 is set to active - it is expected that stream to Group 1 is stopped and Group 2
+ * configuration goes to QoS configured
+ */
+ // Report working CSIS
+ ON_CALL(mock_csis_client_module_, IsCsisClientRunning()).WillByDefault(Return(true));
+ ON_CALL(mock_csis_client_module_, GetDesiredSize(_)).WillByDefault(Return(group_size));
+
+ log::info("Connecting first Group with device {}", test_address0);
+ ConnectCsisDevice(
+ test_address0, 1 /* conn_id*/,
+ codec_spec_conf::kLeAudioLocationFrontLeft | codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_1, 1 /* rank*/);
+
+ SyncOnMainLoop();
+
+ log::info("Connecting second Group with device {}", test_address1);
+ ConnectCsisDevice(
+ test_address1, 2 /* conn_id*/,
+ codec_spec_conf::kLeAudioLocationFrontLeft | codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_2, 1 /* rank*/);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+
+ EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
+ EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(0);
+
+ log::info("Group is getting Active");
+ LeAudioClient::Get()->GroupSetActive(group_id_1);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+
+ log::info("Audio HAL opens for Media");
+
+ constexpr int gmcs_ccid = 1;
+ LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, static_cast<int>(LeAudioContextType::MEDIA));
+
+ types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid}, .source = {}};
+ EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::MEDIA, _, ccids))
+ .Times(AtLeast(1));
+
+ StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id_1);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+
+ log::info("Set group 2 as active one ");
+ EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
+ EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
+ EXPECT_CALL(mock_state_machine_,
+ ConfigureStream(_, types::LeAudioContextType::MEDIA, _, _, false))
+ .Times(1);
+
+ LeAudioClient::Get()->GroupSetActive(group_id_2);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+}
+
+TEST_F(UnicastTest, AnotherGroupSetActive_DuringVoip) {
+ com::android::bluetooth::flags::provider_->leaudio_improve_switch_during_phone_call(true);
+ const RawAddress test_address0 = GetTestAddress(0);
+ const RawAddress test_address1 = GetTestAddress(1);
+ int group_id_1 = 1;
+ int group_id_2 = 2;
+ int group_size = 1;
+
+ default_channel_cnt = 2;
+ default_src_channel_cnt = 1;
+
+ /**
+ * Scenario:
+ * 1. Voip is started
+ * 2. Group 1 is set active - no fast configuration is expected
+ * 3. Audio HAL resumse the stream and Group 1 is streaming
+ * 3. Group 2 is set to active - it is expected that stream to Group 1 is stopped and Group 2
+ * configuration goes to QoS configured
+ */
+ // Report working CSIS
+ ON_CALL(mock_csis_client_module_, IsCsisClientRunning()).WillByDefault(Return(true));
+ ON_CALL(mock_csis_client_module_, GetDesiredSize(_)).WillByDefault(Return(group_size));
+
+ log::info("Connecting first Group with device {}", test_address0);
+ ConnectCsisDevice(
+ test_address0, 1 /* conn_id*/,
+ codec_spec_conf::kLeAudioLocationFrontLeft | codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_1, 1 /* rank*/);
+
+ SyncOnMainLoop();
+
+ log::info("Connecting second Group with device {}", test_address1);
+ ConnectCsisDevice(
+ test_address1, 2 /* conn_id*/,
+ codec_spec_conf::kLeAudioLocationFrontLeft | codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_2, 1 /* rank*/);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+
+ EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
+ EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(0);
+
+ log::info("Group is getting Active");
+ LeAudioClient::Get()->GroupSetActive(group_id_1);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+
+ log::info("Audio HAL opens for Converstational");
+
+ // VOIP not using Telecom API has no ccids.
+ types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {}, .source = {}};
+ EXPECT_CALL(mock_state_machine_,
+ StartStream(_, types::LeAudioContextType::CONVERSATIONAL, _, ccids))
+ .Times(AtLeast(1));
+
+ StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH, group_id_1);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+
+ log::info("Set group 2 as active one ");
+ EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
+ EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
+ EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, true)).Times(1);
+
+ LeAudioClient::Get()->GroupSetActive(group_id_2);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+}
+
TEST_F(UnicastTest, GroupSetActive_and_GroupSetInactive_DuringPhoneCall) {
com::android::bluetooth::flags::provider_->leaudio_improve_switch_during_phone_call(true);
const RawAddress test_address0 = GetTestAddress(0);
@@ -6649,11 +6817,18 @@ TEST_F(UnicastTest, SpeakerStreamingAutonomousRelease) {
Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
SyncOnMainLoop();
// Verify Data transfer on one audio source cis
TestAudioDataTransfer(group_id, 1 /* cis_count_out */, 0 /* cis_count_in */, 1920);
+ EXPECT_CALL(mock_audio_hal_client_callbacks_, OnGroupStatus(group_id, GroupStatus::INACTIVE))
+ .Times(1);
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id, GroupStreamStatus::IDLE))
+ .Times(1);
+
// Inject the IDLE state as if an autonomous release happened
ASSERT_NE(0lu, streaming_groups.count(group_id));
auto group = streaming_groups.at(group_id);
@@ -6667,9 +6842,14 @@ TEST_F(UnicastTest, SpeakerStreamingAutonomousRelease) {
InjectCisDisconnected(group_id, ase.cis_conn_hdl);
}
}
-
// Verify no Data transfer after the autonomous release
TestAudioDataTransfer(group_id, 0 /* cis_count_out */, 0 /* cis_count_in */, 1920);
+
+ // Inject Releasing
+ state_machine_callbacks_->StatusReportCb(group->group_id_,
+ GroupStreamStatus::RELEASING_AUTONOMOUS);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}
TEST_F(UnicastTest, TwoEarbudsStreaming) {
@@ -11953,6 +12133,16 @@ TEST_F(UnicastTest, GroupStreamStatus) {
EXPECT_CALL(mock_audio_hal_client_callbacks_,
OnGroupStreamStatus(group_id, GroupStreamStatus::IDLE))
.Times(1);
+ state_machine_callbacks_->StatusReportCb(group_id, GroupStreamStatus::RELEASING_AUTONOMOUS);
+
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id, GroupStreamStatus::STREAMING))
+ .Times(1);
+ state_machine_callbacks_->StatusReportCb(group_id, GroupStreamStatus::STREAMING);
+
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id, GroupStreamStatus::IDLE))
+ .Times(1);
state_machine_callbacks_->StatusReportCb(group_id, GroupStreamStatus::SUSPENDING);
EXPECT_CALL(mock_audio_hal_client_callbacks_,
@@ -12062,7 +12252,7 @@ TEST_F(UnicastTest, GroupStreamStatusManyGroups) {
.Times(1);
state_machine_callbacks_->StatusReportCb(group_id_2, GroupStreamStatus::IDLE);
- // Group 1 active and start streaming
+ log::info("Group 1 active and start streaming");
EXPECT_CALL(mock_audio_hal_client_callbacks_,
OnGroupStreamStatus(group_id_1, GroupStreamStatus::STREAMING))
.Times(1);
@@ -12072,7 +12262,7 @@ TEST_F(UnicastTest, GroupStreamStatusManyGroups) {
SyncOnMainLoop();
Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- // Group 2 active
+ log::info("Group 2 is getting active");
EXPECT_CALL(mock_audio_hal_client_callbacks_,
OnGroupStreamStatus(group_id_1, GroupStreamStatus::IDLE))
.Times(1);
@@ -12080,7 +12270,7 @@ TEST_F(UnicastTest, GroupStreamStatusManyGroups) {
SyncOnMainLoop();
Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- // Group 2 start streaming
+ log::info("Group 2 is starts streaming");
EXPECT_CALL(mock_audio_hal_client_callbacks_,
OnGroupStreamStatus(group_id_2, GroupStreamStatus::STREAMING))
.Times(1);
@@ -12089,6 +12279,106 @@ TEST_F(UnicastTest, GroupStreamStatusManyGroups) {
Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}
+TEST_F(UnicastTest, GroupStreamStatusManyGroups_GettingConfigWhileOtherGroupIsStreaming) {
+ uint8_t group_size = 2;
+ int group_id_1 = 1;
+ int group_id_2 = 2;
+
+ // Report working CSIS
+ ON_CALL(mock_csis_client_module_, IsCsisClientRunning()).WillByDefault(Return(true));
+
+ ON_CALL(mock_csis_client_module_, GetDesiredSize(_)).WillByDefault(Return(group_size));
+
+ // First group - First earbud
+ const RawAddress test_address0 = GetTestAddress(0);
+ EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true)).Times(1);
+ ConnectCsisDevice(test_address0, 1 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_1,
+ 1 /* rank*/);
+
+ // First group - Second earbud
+ const RawAddress test_address1 = GetTestAddress(1);
+ EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true)).Times(1);
+ ConnectCsisDevice(test_address1, 2 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id_1,
+ 2 /* rank*/, true /*connect_through_csis*/);
+
+ // Second group - First earbud
+ const RawAddress test_address2 = GetTestAddress(2);
+ EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address2, true)).Times(1);
+ ConnectCsisDevice(test_address2, 3 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft,
+ codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id_2,
+ 1 /* rank*/);
+
+ // Second group - Second earbud
+ const RawAddress test_address3 = GetTestAddress(3);
+ EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address3, true)).Times(1);
+ ConnectCsisDevice(test_address3, 4 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight,
+ codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id_2,
+ 2 /* rank*/, true /*connect_through_csis*/);
+
+ InSequence s;
+
+ // Group 1 IDLE
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id_1, GroupStreamStatus::IDLE))
+ .Times(1);
+ state_machine_callbacks_->StatusReportCb(group_id_1, GroupStreamStatus::IDLE);
+
+ // Group 2 IDLE
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id_2, GroupStreamStatus::IDLE))
+ .Times(1);
+ state_machine_callbacks_->StatusReportCb(group_id_2, GroupStreamStatus::IDLE);
+
+ log::info("Group 1 active and start streaming");
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id_1, GroupStreamStatus::STREAMING))
+ .Times(1);
+ LeAudioClient::Get()->GroupSetActive(group_id_1);
+ SyncOnMainLoop();
+ StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id_1);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+
+ log::info("Group 2 is getting active");
+ stay_at_releasing_stop_stream = true;
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id_1, GroupStreamStatus::IDLE))
+ .Times(1);
+ LeAudioClient::Get()->GroupSetActive(group_id_2);
+ SyncOnMainLoop();
+
+ log::info("Group 2 is starts streaming");
+ stay_at_qos_config_in_start_stream = true;
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupStreamStatus(group_id_2, GroupStreamStatus::STREAMING))
+ .Times(1);
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, ConfirmStreamingRequest()).Times(1);
+
+ StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id_2, AUDIO_SOURCE_INVALID,
+ false, false);
+
+ log::info("Group 1 going to IDLE");
+ do_in_main_thread(base::BindOnce(
+ [](int group_id,
+ bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks) {
+ state_machine_callbacks->StatusReportCb(group_id, GroupStreamStatus::IDLE);
+ },
+ group_id_1, base::Unretained(this->state_machine_callbacks_)));
+ SyncOnMainLoop();
+ log::info("Group 2 going to STREAMING");
+ do_in_main_thread(base::BindOnce(
+ [](int group_id,
+ bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks) {
+ state_machine_callbacks->StatusReportCb(group_id, GroupStreamStatus::STREAMING);
+ },
+ group_id_2, base::Unretained(this->state_machine_callbacks_)));
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+}
+
TEST_F(UnicastTest, GroupStreamStatusResendAfterRemove) {
uint8_t group_size = 2;
int group_id = 1;
@@ -12229,86 +12519,92 @@ TEST_F(UnicastTestHandoverMode, SetSinkMonitorModeWhileUnicastIsActive) {
// Stop
StopStreaming(group_id, true);
- // Check if cache configuration is still present
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSink)
- .size());
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSource)
- .size());
-
- // Release, Sink HAL client should remain in monitor mode
- EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
- EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
- LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
- SyncOnMainLoop();
+ if (com::android::bluetooth::flags::leaudio_use_audio_recording_listener()) {
+ // simulate suspend timeout passed, alarm executing
+ fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
+ SyncOnMainLoop();
+ } else {
+ // Check if cache configuration is still present
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSink)
+ .size());
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSource)
+ .size());
+
+ // Release, Sink HAL client should remain in monitor mode
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
+ LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+ SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
- Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
- // Re-initialize mock for destroyed hal client
- RegisterSourceHalClientMock();
+ // Re-initialize mock for destroyed hal client
+ RegisterSourceHalClientMock();
- // Setting group inactive, shall not change cached configuration
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSink)
- .size());
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSource)
- .size());
+ // Setting group inactive, shall not change cached configuration
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSink)
+ .size());
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSource)
+ .size());
- EXPECT_CALL(mock_audio_hal_client_callbacks_,
- OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSink,
- UnicastMonitorModeStatus::STREAMING_REQUESTED))
- .Times(1);
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSink,
+ UnicastMonitorModeStatus::STREAMING_REQUESTED))
+ .Times(1);
- // Start streaming to trigger next group going to IDLE state
- LocalAudioSinkResume();
+ // Start streaming to trigger next group going to IDLE state
+ LocalAudioSinkResume();
- EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _, _)).Times(1);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _, _)).Times(1);
- LeAudioClient::Get()->GroupSetActive(group_id);
- Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- SyncOnMainLoop();
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _, _)).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _, _)).Times(1);
+ LeAudioClient::Get()->GroupSetActive(group_id);
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
- StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH, group_id);
- SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH, group_id);
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
- // Stop streaming and expect Service to be informed about straming suspension
- EXPECT_CALL(mock_audio_hal_client_callbacks_,
- OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSink,
- UnicastMonitorModeStatus::STREAMING_SUSPENDED))
- .Times(1);
+ // Stop streaming and expect Service to be informed about straming suspension
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnUnicastMonitorModeStatus(bluetooth::le_audio::types::kLeAudioDirectionSink,
+ UnicastMonitorModeStatus::STREAMING_SUSPENDED))
+ .Times(1);
- // Stop
- StopStreaming(group_id, true);
+ // Stop
+ StopStreaming(group_id, true);
- // Release, Sink HAL client should remain in monitor mode
- EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
- EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
- LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
- SyncOnMainLoop();
+ // Release, Sink HAL client should remain in monitor mode
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
+ LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+ SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
- Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
- // De-activate monitoring mode
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(1);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
- do_in_main_thread(base::BindOnce(
- &LeAudioClient::SetUnicastMonitorMode, base::Unretained(LeAudioClient::Get()),
- bluetooth::le_audio::types::kLeAudioDirectionSink, false /* enable */));
- SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
+ // De-activate monitoring mode
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
+ do_in_main_thread(base::BindOnce(
+ &LeAudioClient::SetUnicastMonitorMode, base::Unretained(LeAudioClient::Get()),
+ bluetooth::le_audio::types::kLeAudioDirectionSink, false /* enable */));
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
+ }
}
TEST_F(UnicastTestHandoverMode, SetSinkMonitorModeWhileUnicastIsInactive) {
@@ -12376,33 +12672,39 @@ TEST_F(UnicastTestHandoverMode, SetSinkMonitorModeWhileUnicastIsInactive) {
// Stop
StopStreaming(group_id, true);
- // Check if cache configuration is still present
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSink)
- .size());
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSource)
- .size());
-
- // Release, Sink HAL client should remain in monitor mode
- EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
- EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
- EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
- LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
- SyncOnMainLoop();
+ if (com::android::bluetooth::flags::leaudio_use_audio_recording_listener()) {
+ // simulate suspend timeout passed, alarm executing
+ fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data);
+ SyncOnMainLoop();
+ } else {
+ // Check if cache configuration is still present
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSink)
+ .size());
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSource)
+ .size());
+
+ // Release, Sink HAL client should remain in monitor mode
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, Stop()).Times(1);
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(0);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(0);
+ LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+ SyncOnMainLoop();
- Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
- Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
- Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
- // Setting group inactive, shall not change cached configuration
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSink)
- .size());
- ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
- ->confs.get(le_audio::types::kLeAudioDirectionSource)
- .size());
+ // Setting group inactive, shall not change cached configuration
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSink)
+ .size());
+ ASSERT_TRUE(group->GetCachedConfiguration(types::LeAudioContextType::CONVERSATIONAL)
+ ->confs.get(le_audio::types::kLeAudioDirectionSource)
+ .size());
+ }
}
TEST_F(UnicastTestHandoverMode, ClearSinkMonitorModeWhileUnicastIsActive) {
diff --git a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
index 8e22ddb3ff..a76a58bb59 100644
--- a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
+++ b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
@@ -51,14 +51,14 @@ namespace bluetooth::le_audio {
#ifdef __ANDROID__
static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
- kLeAudioSetConfigs = {{"/apex/com.android.btservices/etc/bluetooth/le_audio/"
+ kLeAudioSetConfigs = {{"/apex/com.android.bt/etc/bluetooth/le_audio/"
"audio_set_configurations.bfbs",
- "/apex/com.android.btservices/etc/bluetooth/le_audio/"
+ "/apex/com.android.bt/etc/bluetooth/le_audio/"
"audio_set_configurations.json"}};
static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
- kLeAudioSetScenarios = {{"/apex/com.android.btservices/etc/bluetooth/"
+ kLeAudioSetScenarios = {{"/apex/com.android.bt/etc/bluetooth/"
"le_audio/audio_set_scenarios.bfbs",
- "/apex/com.android.btservices/etc/bluetooth/"
+ "/apex/com.android.bt/etc/bluetooth/"
"le_audio/audio_set_scenarios.json"}};
#elif defined(TARGET_FLOSS)
static const std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index b164649c50..55db925be4 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -773,22 +773,6 @@ std::ostream& operator<<(std::ostream& os, const AudioContexts& contexts) {
return os;
}
-template <typename T>
-const T& BidirectionalPair<T>::get(uint8_t direction) const {
- log::assert_that(direction < types::kLeAudioDirectionBoth,
- "Unsupported complex direction. Consider using "
- "get_bidirectional<>() instead.");
- return (direction == types::kLeAudioDirectionSink) ? sink : source;
-}
-
-template <typename T>
-T& BidirectionalPair<T>::get(uint8_t direction) {
- log::assert_that(direction < types::kLeAudioDirectionBoth,
- "Unsupported complex direction. Reference to a single "
- "complex direction value is not supported.");
- return (direction == types::kLeAudioDirectionSink) ? sink : source;
-}
-
/* Bidirectional getter trait for AudioContexts bidirectional pair */
template <>
AudioContexts get_bidirectional(BidirectionalPair<AudioContexts> p) {
@@ -874,23 +858,5 @@ std::ostream& operator<<(std::ostream& os, const LeAudioMetadata& config) {
return os;
}
-template struct BidirectionalPair<AudioContexts>;
-template struct BidirectionalPair<AudioLocations>;
-template struct BidirectionalPair<CisType>;
-template struct BidirectionalPair<LeAudioConfigurationStrategy>;
-template struct BidirectionalPair<ase*>;
-template struct BidirectionalPair<std::string>;
-template struct BidirectionalPair<std::vector<uint8_t>>;
-template struct BidirectionalPair<stream_configuration>;
-template struct BidirectionalPair<stream_parameters>;
-template struct BidirectionalPair<uint16_t>;
-template struct BidirectionalPair<uint8_t>;
-template struct BidirectionalPair<bool>;
-template struct BidirectionalPair<int>;
-template struct BidirectionalPair<std::vector<set_configurations::AseConfiguration>>;
-template struct BidirectionalPair<set_configurations::QosConfigSetting>;
-template struct BidirectionalPair<
- std::unique_ptr<const bluetooth::le_audio::btle_audio_codec_config_t>>;
-
} // namespace types
} // namespace bluetooth::le_audio
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index a0c5f364c7..906996a7c7 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -503,8 +503,19 @@ struct BidirectionalPair {
T sink;
T source;
- const T& get(uint8_t direction) const;
- T& get(uint8_t direction);
+ const T& get(uint8_t direction) const {
+ log::assert_that(direction < types::kLeAudioDirectionBoth,
+ "Unsupported complex direction. Consider using "
+ "get_bidirectional<>() instead.");
+ return (direction == types::kLeAudioDirectionSink) ? sink : source;
+ }
+
+ T& get(uint8_t direction) {
+ log::assert_that(direction < types::kLeAudioDirectionBoth,
+ "Unsupported complex direction. Reference to a single "
+ "complex direction value is not supported.");
+ return (direction == types::kLeAudioDirectionSink) ? sink : source;
+ }
};
template <typename T>
diff --git a/system/bta/le_audio/le_audio_types_test.cc b/system/bta/le_audio/le_audio_types_test.cc
index 70c29426ef..992a35d4c1 100644
--- a/system/bta/le_audio/le_audio_types_test.cc
+++ b/system/bta/le_audio/le_audio_types_test.cc
@@ -840,5 +840,45 @@ TEST(CodecConfigTest, test_invalid_codec_bits_per_sample) {
LE_AUDIO_BITS_PER_SAMPLE_INDEX_NONE);
}
+TEST(CodecConfigTest, test_tmap_and_gmap_target_latency) {
+ /* TMAP: Media -> Higher reliability */
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::MEDIA),
+ types::kTargetLatencyHigherReliability);
+
+ /* TMAP: Live performance -> Low Latency */
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::LIVE),
+ types::kTargetLatencyLower);
+
+ /* TMAP: Call -> Balanced reliability */
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::RINGTONE),
+ types::kTargetLatencyBalancedLatencyReliability);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::CONVERSATIONAL),
+ types::kTargetLatencyBalancedLatencyReliability);
+
+ /* GMAP: Game -> Low Latency */
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::GAME),
+ types::kTargetLatencyLower);
+
+ /* Undefined for the rest of contexts */
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::UNINITIALIZED),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::UNSPECIFIED),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::INSTRUCTIONAL),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::VOICEASSISTANTS),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::SOUNDEFFECTS),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::NOTIFICATIONS),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::ALERTS),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::EMERGENCYALARM),
+ types::kTargetLatencyUndefined);
+ ASSERT_EQ(utils::GetTargetLatencyForAudioContext(LeAudioContextType::RFU),
+ types::kTargetLatencyUndefined);
+}
+
} // namespace types
} // namespace bluetooth::le_audio
diff --git a/system/bta/le_audio/le_audio_utils.h b/system/bta/le_audio/le_audio_utils.h
index f7406a41a5..8feed29473 100644
--- a/system/bta/le_audio/le_audio_utils.h
+++ b/system/bta/le_audio/le_audio_utils.h
@@ -40,17 +40,19 @@ types::AudioContexts GetAudioContextsFromSinkMetadata(
const std::vector<struct record_track_metadata_v7>& sink_metadata);
inline uint8_t GetTargetLatencyForAudioContext(types::LeAudioContextType ctx) {
switch (ctx) {
- case types::LeAudioContextType::GAME:
- FALLTHROUGH_INTENDED;
- case types::LeAudioContextType::VOICEASSISTANTS:
- FALLTHROUGH_INTENDED;
+ case types::LeAudioContextType::MEDIA:
+ return types::kTargetLatencyHigherReliability;
+
case types::LeAudioContextType::LIVE:
FALLTHROUGH_INTENDED;
- case types::LeAudioContextType::CONVERSATIONAL:
- FALLTHROUGH_INTENDED;
- case types::LeAudioContextType::RINGTONE:
+ case types::LeAudioContextType::GAME:
return types::kTargetLatencyLower;
+ case types::LeAudioContextType::RINGTONE:
+ FALLTHROUGH_INTENDED;
+ case types::LeAudioContextType::CONVERSATIONAL:
+ return types::kTargetLatencyBalancedLatencyReliability;
+
default:
return types::kTargetLatencyUndefined;
}
diff --git a/system/bta/le_audio/mock_codec_manager.cc b/system/bta/le_audio/mock_codec_manager.cc
index addf45ba85..f1621e02c0 100644
--- a/system/bta/le_audio/mock_codec_manager.cc
+++ b/system/bta/le_audio/mock_codec_manager.cc
@@ -41,6 +41,13 @@ types::CodecLocation CodecManager::GetCodecLocation() const {
return pimpl_->GetCodecLocation();
}
+std::optional<ProviderInfo> CodecManager::GetCodecConfigProviderInfo(void) const {
+ if (!pimpl_) {
+ return std::nullopt;
+ }
+ return pimpl_->GetCodecConfigProviderInfo();
+}
+
bool CodecManager::IsDualBiDirSwbSupported(void) const {
if (!pimpl_) {
return false;
diff --git a/system/bta/le_audio/mock_codec_manager.h b/system/bta/le_audio/mock_codec_manager.h
index 3203f33257..27db5c399a 100644
--- a/system/bta/le_audio/mock_codec_manager.h
+++ b/system/bta/le_audio/mock_codec_manager.h
@@ -38,6 +38,8 @@ public:
virtual ~MockCodecManager() = default;
MOCK_METHOD((bluetooth::le_audio::types::CodecLocation), GetCodecLocation, (), (const));
+ MOCK_METHOD(std::optional<bluetooth::le_audio::ProviderInfo>, GetCodecConfigProviderInfo, (),
+ (const));
MOCK_METHOD((bool), IsDualBiDirSwbSupported, (), (const));
MOCK_METHOD((bool), UpdateActiveUnicastAudioHalClient,
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 2fb2963149..cc0a79690c 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -1084,7 +1084,7 @@ public:
/* Note, that this type is actually LONG WRITE.
* Meaning all the Prepare Writes plus Execute is handled in the stack
*/
- write_type = GATT_WRITE_PREPARE;
+ write_type = GATT_WRITE;
}
BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl,
@@ -1744,7 +1744,7 @@ private:
}
void SetAseState(LeAudioDevice* leAudioDevice, struct ase* ase, AseState state) {
- log::info("{}, ase_id: {}, {} -> {}", leAudioDevice->address_, ase->id, ToString(ase->state),
+ log::info("{} ({}), ase_id: {}, {} -> {}", leAudioDevice->address_, leAudioDevice->group_id_, ase->id, ToString(ase->state),
ToString(state));
log_history_->AddLogHistory(kLogStateMachineTag, leAudioDevice->group_id_,
@@ -3045,7 +3045,7 @@ private:
log::info("Group {} is doing autonomous release", group->group_id_);
SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
state_machine_callbacks_->StatusReportCb(group->group_id_,
- GroupStreamStatus::RELEASING);
+ GroupStreamStatus::RELEASING_AUTONOMOUS);
}
}
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 7fc2985b28..682bb9c722 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -4147,9 +4147,13 @@ TEST_F(StateMachineTest, testAutonomousReleaseMultiple) {
// Validate GroupStreamStatus
EXPECT_CALL(mock_callbacks_,
- StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING))
+ StatusReportCb(leaudio_group_id,
+ bluetooth::le_audio::GroupStreamStatus::RELEASING_AUTONOMOUS))
.Times(1);
EXPECT_CALL(mock_callbacks_,
+ StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING))
+ .Times(0);
+ EXPECT_CALL(mock_callbacks_,
StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::IDLE))
.Times(1);
EXPECT_CALL(mock_callbacks_,
diff --git a/system/bta/le_audio/storage_helper.cc b/system/bta/le_audio/storage_helper.cc
index 159580d456..d48ec25363 100644
--- a/system/bta/le_audio/storage_helper.cc
+++ b/system/bta/le_audio/storage_helper.cc
@@ -437,4 +437,94 @@ bool DeserializeHandles(LeAudioDevice* leAudioDevice, const std::vector<uint8_t>
leAudioDevice->known_service_handles_ = true;
return true;
}
+
+static constexpr uint8_t LEAUDIO_GMAP_STORAGE_V1_LAYOUT_MAGIC = 0x01;
+static constexpr uint8_t LEAUDIO_GMAP_STORAGE_V1_LAYOUT_SZ = 7;
+static constexpr uint8_t LEAUDIO_GMAP_STORAGE_CURRENT_LAYOUT_MAGIC =
+ LEAUDIO_GMAP_STORAGE_V1_LAYOUT_MAGIC;
+
+static bool SerializeGmapV1(const GmapClient* gmapClient, std::vector<uint8_t>& out) {
+ if (gmapClient == nullptr) {
+ log::warn("GMAP client not available");
+ return false;
+ }
+
+ /* The total size */
+ out.resize(LEAUDIO_GMAP_STORAGE_V1_LAYOUT_SZ);
+ auto* ptr = out.data();
+
+ /* header */
+ UINT8_TO_STREAM(ptr, LEAUDIO_GMAP_STORAGE_V1_LAYOUT_MAGIC);
+
+ /* handles */
+ UINT16_TO_STREAM(ptr, gmapClient->getRoleHandle());
+ UINT16_TO_STREAM(ptr, gmapClient->getUGTFeatureHandle());
+
+ /* role & features */
+ UINT8_TO_STREAM(ptr, gmapClient->getRole().to_ulong());
+ UINT8_TO_STREAM(ptr, gmapClient->getUGTFeature().to_ulong());
+
+ return true;
+}
+
+bool SerializeGmap(const GmapClient* gmapClient, std::vector<uint8_t>& out) {
+ if (gmapClient == nullptr) {
+ log::warn("GMAP client not available");
+ return false;
+ }
+
+ if (LEAUDIO_GMAP_STORAGE_CURRENT_LAYOUT_MAGIC == LEAUDIO_GMAP_STORAGE_V1_LAYOUT_MAGIC) {
+ return SerializeGmapV1(gmapClient, out);
+ }
+
+ log::warn("Invalid GMAP storage magic number {}", +LEAUDIO_GMAP_STORAGE_CURRENT_LAYOUT_MAGIC);
+}
+
+bool DeserializeGmapV1(GmapClient* gmapClient, const std::vector<uint8_t>& in) {
+ if (in.size() != LEAUDIO_GMAP_STORAGE_V1_LAYOUT_SZ) {
+ log::warn("Invalid storage size for GMAP data. Got {}, expected {}", in.size(),
+ +LEAUDIO_GMAP_STORAGE_V1_LAYOUT_SZ);
+ return false;
+ }
+
+ // Skip the magic number
+ auto* ptr = in.data() + 1;
+
+ /* handles */
+ uint16_t role_handle, ugt_feature_handle;
+ STREAM_TO_UINT16(role_handle, ptr);
+ STREAM_TO_UINT16(ugt_feature_handle, ptr);
+
+ uint8_t role, ugt_feature;
+ STREAM_TO_UINT8(role, ptr);
+ STREAM_TO_UINT8(ugt_feature, ptr);
+
+ gmapClient->AddFromStorage(role, role_handle, ugt_feature, ugt_feature_handle);
+ return true;
+}
+
+bool DeserializeGmap(GmapClient* gmapClient, const std::vector<uint8_t>& in) {
+ if (gmapClient == nullptr) {
+ log::warn("GMAP client not available");
+ return false;
+ }
+
+ if (in.size() < 1) {
+ log::warn("GMAP storage is not available");
+ return false;
+ }
+
+ auto* ptr = in.data();
+ uint8_t magic;
+ STREAM_TO_UINT8(magic, ptr);
+
+ if (magic == LEAUDIO_GMAP_STORAGE_V1_LAYOUT_MAGIC) {
+ return DeserializeGmapV1(gmapClient, in);
+ }
+
+ log::warn("Invalid GMAP storage magic number. Got {}, current {}", +magic,
+ +LEAUDIO_GMAP_STORAGE_CURRENT_LAYOUT_MAGIC);
+ return false;
+}
+
} // namespace bluetooth::le_audio
diff --git a/system/bta/le_audio/storage_helper.h b/system/bta/le_audio/storage_helper.h
index 85418457c9..78868fa744 100644
--- a/system/bta/le_audio/storage_helper.h
+++ b/system/bta/le_audio/storage_helper.h
@@ -21,6 +21,7 @@
#include <vector>
#include "devices.h"
+#include "le_audio/gmap_client.h"
namespace bluetooth::le_audio {
bool SerializeSinkPacs(const LeAudioDevice* leAudioDevice, std::vector<uint8_t>& out);
@@ -31,4 +32,6 @@ bool SerializeAses(const LeAudioDevice* leAudioDevice, std::vector<uint8_t>& out
bool DeserializeAses(LeAudioDevice* leAudioDevice, const std::vector<uint8_t>& in);
bool SerializeHandles(const LeAudioDevice* leAudioDevice, std::vector<uint8_t>& out);
bool DeserializeHandles(LeAudioDevice* leAudioDevice, const std::vector<uint8_t>& in);
+bool SerializeGmap(const GmapClient* gmap_server, std::vector<uint8_t>& out);
+bool DeserializeGmap(GmapClient* gmap_server, const std::vector<uint8_t>& in);
} // namespace bluetooth::le_audio
diff --git a/system/bta/le_audio/storage_helper_test.cc b/system/bta/le_audio/storage_helper_test.cc
index ef0a9ed172..9effb2b47f 100644
--- a/system/bta/le_audio/storage_helper_test.cc
+++ b/system/bta/le_audio/storage_helper_test.cc
@@ -309,4 +309,41 @@ TEST(StorageHelperTest, DeserializeHandles) {
ASSERT_FALSE(DeserializeHandles(&leAudioDevice, invalidHandlesMagic));
ASSERT_FALSE(DeserializeHandles(&leAudioDevice, invalidHandles));
}
+
+TEST(StorageHelperTest, DeserializeGmapV1) {
+ // clang-format off
+ const std::vector<uint8_t> validHandles {
+ 0x01, // V1 Layout Magic
+ 0x0e, 0x11, // Role Handle
+ 0x0f, 0x11, // Feature Handle
+ 0x05, // Role value
+ 0x06, // Feature value
+ };
+ const std::vector<uint8_t> invalidHandlesMagic {
+ 0x00, // Unknown Layout Magic
+ 0x0e, 0x11, // Role Handle
+ 0x0f, 0x11, // Feature Handle
+ 0x05, // Role value
+ 0x06, // Feature value
+ };
+ const std::vector<uint8_t> invalidHandles {
+ 0x01, // V1 Layout Magic
+ 0x0e, 0x11, // Role Handle
+ 0x0f, 0x11, // Feature Handle
+ 0x05, // Role value
+ 0x06, // Feature value
+ 0x06, // corrupted
+ };
+
+ // clang-format on
+ RawAddress test_address0 = GetTestAddress(0);
+ GmapClient gmap(test_address0);
+ ASSERT_TRUE(DeserializeGmap(&gmap, validHandles));
+ std::vector<uint8_t> serialize;
+ ASSERT_TRUE(SerializeGmap(&gmap, serialize));
+ ASSERT_TRUE(serialize == validHandles);
+
+ ASSERT_FALSE(DeserializeGmap(&gmap, invalidHandlesMagic));
+ ASSERT_FALSE(DeserializeGmap(&gmap, invalidHandles));
+}
} // namespace bluetooth::le_audio
diff --git a/system/bta/ras/ras_client.cc b/system/bta/ras/ras_client.cc
index 6dcf00c377..b052717d14 100644
--- a/system/bta/ras/ras_client.cc
+++ b/system/bta/ras/ras_client.cc
@@ -51,6 +51,7 @@ using namespace bluetooth;
using namespace ::ras;
using namespace ::ras::feature;
using namespace ::ras::uuid;
+using bluetooth::ras::RasDisconnectReason;
using bluetooth::ras::VendorSpecificCharacteristic;
namespace {
@@ -155,6 +156,7 @@ public:
return;
}
BTA_GATTC_AppRegister(
+ "ranging_service",
[](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
if (instance && p_data) {
instance->GattcCallback(event, p_data);
@@ -286,6 +288,8 @@ public:
if (evt.status != GATT_SUCCESS) {
log::error("Failed to connect to server device {}", evt.remote_bda);
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
return;
}
tracker->conn_id_ = evt.conn_id;
@@ -307,7 +311,7 @@ public:
BTA_GATTC_Close(evt.conn_id);
return;
}
- callbacks_->OnDisconnected(tracker->address_for_cs_);
+ callbacks_->OnDisconnected(tracker->address_for_cs_, RasDisconnectReason::GATT_DISCONNECT);
trackers_.remove(tracker);
}
@@ -337,6 +341,8 @@ public:
return;
} else if (!service_found) {
log::error("Can't find Ranging Service in the services list");
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
return;
} else {
log::info("Found Ranging Service");
@@ -346,7 +352,10 @@ public:
if (UseCachedData(tracker)) {
log::info("Use cached data for Ras features and vendor specific characteristic");
- SubscribeCharacteristic(tracker, kRasControlPointCharacteristic);
+ if (!SubscribeCharacteristic(tracker, kRasControlPointCharacteristic)) {
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
+ }
AllCharacteristicsReadComplete(tracker);
} else {
// Read Vendor Specific Uuid
@@ -369,6 +378,8 @@ public:
auto characteristic = tracker->FindCharacteristicByUuid(kRasFeaturesCharacteristic);
if (characteristic == nullptr) {
log::error("Can not find Characteristic for Ras Features");
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
return;
}
BTA_GATTC_ReadCharacteristic(
@@ -379,7 +390,9 @@ public:
},
&gatt_read_callback_data_);
- SubscribeCharacteristic(tracker, kRasControlPointCharacteristic);
+ if (!SubscribeCharacteristic(tracker, kRasControlPointCharacteristic)) {
+ callbacks_->OnDisconnected(tracker->address_for_cs_, RasDisconnectReason::FATAL_ERROR);
+ }
}
}
@@ -627,23 +640,23 @@ public:
}
}
- void SubscribeCharacteristic(std::shared_ptr<RasTracker> tracker, const Uuid uuid) {
+ bool SubscribeCharacteristic(std::shared_ptr<RasTracker> tracker, const Uuid uuid) {
auto characteristic = tracker->FindCharacteristicByUuid(uuid);
if (characteristic == nullptr) {
log::warn("Can't find characteristic 0x{:04x}", uuid.As16Bit());
- return;
+ return false;
}
uint16_t ccc_handle = FindCccHandle(characteristic);
if (ccc_handle == GAP_INVALID_HANDLE) {
log::warn("Can't find Client Characteristic Configuration descriptor");
- return;
+ return false;
}
tGATT_STATUS register_status = BTA_GATTC_RegisterForNotifications(gatt_if_, tracker->address_,
characteristic->value_handle);
if (register_status != GATT_SUCCESS) {
log::error("Fail to register, {}", gatt_status_text(register_status));
- return;
+ return false;
}
std::vector<uint8_t> value(2);
@@ -663,6 +676,7 @@ public:
}
},
nullptr);
+ return true;
}
void UnsubscribeCharacteristic(std::shared_ptr<RasTracker> tracker, const Uuid uuid) {
@@ -789,14 +803,22 @@ public:
if (tracker->remote_supported_features_ & feature::kRealTimeRangingData) {
log::info("Subscribe Real-time Ranging Data");
tracker->ranging_type_ = REAL_TIME;
- SubscribeCharacteristic(tracker, kRasRealTimeRangingDataCharacteristic);
+ if (!SubscribeCharacteristic(tracker, kRasRealTimeRangingDataCharacteristic)) {
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
+ return;
+ }
SetTimeOutAlarm(tracker, kFirstSegmentRangingDataTimeoutMs, TimeoutType::FIRST_SEGMENT);
} else {
log::info("Subscribe On-demand Ranging Data");
tracker->ranging_type_ = ON_DEMAND;
- SubscribeCharacteristic(tracker, kRasOnDemandDataCharacteristic);
- SubscribeCharacteristic(tracker, kRasRangingDataReadyCharacteristic);
- SubscribeCharacteristic(tracker, kRasRangingDataOverWrittenCharacteristic);
+ if (!SubscribeCharacteristic(tracker, kRasOnDemandDataCharacteristic) ||
+ !SubscribeCharacteristic(tracker, kRasRangingDataReadyCharacteristic) ||
+ !SubscribeCharacteristic(tracker, kRasRangingDataOverWrittenCharacteristic)) {
+ callbacks_->OnDisconnected(tracker->address_for_cs_,
+ RasDisconnectReason::SERVER_NOT_AVAILABLE);
+ return;
+ }
SetTimeOutAlarm(tracker, kRangingDataReadyTimeoutMs, TimeoutType::RANGING_DATA_READY);
}
auto characteristic = tracker->FindCharacteristicByUuid(kRasRealTimeRangingDataCharacteristic);
diff --git a/system/bta/ras/ras_utils_test.cc b/system/bta/ras/ras_utils_test.cc
new file mode 100644
index 0000000000..7acc658265
--- /dev/null
+++ b/system/bta/ras/ras_utils_test.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2025 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 <gtest/gtest.h>
+
+#include "bta/include/bta_ras_api.h"
+#include "bta/ras/ras_types.h"
+
+class RasUtilsTest : public ::testing::Test {};
+
+TEST(RasUtilsTest, GetUuidName) {
+ // Test known UUIDs
+ EXPECT_EQ(ras::uuid::getUuidName(bluetooth::Uuid::From16Bit(ras::uuid::kRangingService16Bit)),
+ "Ranging Service");
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasFeaturesCharacteristic16bit)),
+ "RAS Features");
+ EXPECT_EQ(ras::uuid::getUuidName(bluetooth::Uuid::From16Bit(
+ ras::uuid::kRasRealTimeRangingDataCharacteristic16bit)),
+ "Real-time Ranging Data");
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasOnDemandDataCharacteristic16bit)),
+ "On-demand Ranging Data");
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasControlPointCharacteristic16bit)),
+ "RAS Control Point (RAS-CP)");
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasRangingDataReadyCharacteristic16bit)),
+ "Ranging Data Ready");
+ EXPECT_EQ(ras::uuid::getUuidName(bluetooth::Uuid::From16Bit(
+ ras::uuid::kRasRangingDataOverWrittenCharacteristic16bit)),
+ "Ranging Data Overwritten");
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::From16Bit(ras::uuid::kClientCharacteristicConfiguration16bit)),
+ "Client Characteristic Configuration");
+
+ // Test unknown UUID
+ EXPECT_EQ(ras::uuid::getUuidName(
+ bluetooth::Uuid::FromString("00001101-0000-1000-8000-00805F9B34FB")),
+ "Unknown UUID");
+}
+
+TEST(RasUtilsTest, ParseControlPointCommand) {
+ // Test successful parsing of valid commands
+ uint8_t valid_data_get_ranging_data[] = {0x00, 0x01, 0x02};
+ ras::ControlPointCommand command_get_ranging_data;
+ ASSERT_TRUE(ras::ParseControlPointCommand(&command_get_ranging_data, valid_data_get_ranging_data,
+ sizeof(valid_data_get_ranging_data)));
+ ASSERT_EQ(command_get_ranging_data.opcode_, ras::Opcode::GET_RANGING_DATA);
+ ASSERT_EQ(command_get_ranging_data.parameter_[0], 0x01);
+ ASSERT_EQ(command_get_ranging_data.parameter_[1], 0x02);
+
+ uint8_t valid_data_ack_ranging_data[] = {0x01, 0x03, 0x04};
+ ras::ControlPointCommand command_ack_ranging_data;
+ ASSERT_TRUE(ras::ParseControlPointCommand(&command_ack_ranging_data, valid_data_ack_ranging_data,
+ sizeof(valid_data_ack_ranging_data)));
+ ASSERT_EQ(command_ack_ranging_data.opcode_, ras::Opcode::ACK_RANGING_DATA);
+ ASSERT_EQ(command_ack_ranging_data.parameter_[0], 0x03);
+ ASSERT_EQ(command_ack_ranging_data.parameter_[1], 0x04);
+
+ uint8_t valid_data_retrieve_lost_ranging_data_segments[] = {0x02, 0x05, 0x06, 0x07, 0x08};
+ ras::ControlPointCommand command_retrieve_lost_ranging_data_segments;
+ ASSERT_TRUE(
+ ras::ParseControlPointCommand(&command_retrieve_lost_ranging_data_segments,
+ valid_data_retrieve_lost_ranging_data_segments,
+ sizeof(valid_data_retrieve_lost_ranging_data_segments)));
+ ASSERT_EQ(command_retrieve_lost_ranging_data_segments.opcode_,
+ ras::Opcode::RETRIEVE_LOST_RANGING_DATA_SEGMENTS);
+ ASSERT_EQ(command_retrieve_lost_ranging_data_segments.parameter_[0], 0x05);
+ ASSERT_EQ(command_retrieve_lost_ranging_data_segments.parameter_[1], 0x06);
+ ASSERT_EQ(command_retrieve_lost_ranging_data_segments.parameter_[2], 0x07);
+ ASSERT_EQ(command_retrieve_lost_ranging_data_segments.parameter_[3], 0x08);
+
+ uint8_t valid_data_abort_operation[] = {0x03};
+ ras::ControlPointCommand command_abort_operation;
+ ASSERT_TRUE(ras::ParseControlPointCommand(&command_abort_operation, valid_data_abort_operation,
+ sizeof(valid_data_abort_operation)));
+ ASSERT_EQ(command_abort_operation.opcode_, ras::Opcode::ABORT_OPERATION);
+
+ uint8_t valid_data_filter[] = {0x04, 0x09, 0x0A};
+ ras::ControlPointCommand command_filter;
+ ASSERT_TRUE(ras::ParseControlPointCommand(&command_filter, valid_data_filter,
+ sizeof(valid_data_filter)));
+ ASSERT_EQ(command_filter.opcode_, ras::Opcode::FILTER);
+ ASSERT_EQ(command_filter.parameter_[0], 0x09);
+ ASSERT_EQ(command_filter.parameter_[1], 0x0A);
+
+ // Test failed parsing of invalid commands
+ uint8_t invalid_data_short_get_ranging_data[] = {0x00, 0x01};
+ ras::ControlPointCommand command_invalid_short_get_ranging_data;
+ ASSERT_FALSE(ras::ParseControlPointCommand(&command_invalid_short_get_ranging_data,
+ invalid_data_short_get_ranging_data,
+ sizeof(invalid_data_short_get_ranging_data)));
+
+ uint8_t invalid_data_long_get_ranging_data[] = {0x00, 0x01, 0x02, 0x03};
+ ras::ControlPointCommand command_invalid_long_get_ranging_data;
+ ASSERT_FALSE(ras::ParseControlPointCommand(&command_invalid_long_get_ranging_data,
+ invalid_data_long_get_ranging_data,
+ sizeof(invalid_data_long_get_ranging_data)));
+
+ uint8_t invalid_data_unknown_opcode[] = {0x05, 0x01, 0x02};
+ ras::ControlPointCommand command_invalid_unknown_opcode;
+ ASSERT_FALSE(ras::ParseControlPointCommand(&command_invalid_unknown_opcode,
+ invalid_data_unknown_opcode,
+ sizeof(invalid_data_unknown_opcode)));
+}
+
+TEST(RasUtilsTest, GetOpcodeText) {
+ // Test known opcodes
+ EXPECT_EQ(ras::GetOpcodeText(ras::Opcode::GET_RANGING_DATA), "GET_RANGING_DATA");
+ EXPECT_EQ(ras::GetOpcodeText(ras::Opcode::ACK_RANGING_DATA), "ACK_RANGING_DATA");
+ EXPECT_EQ(ras::GetOpcodeText(ras::Opcode::RETRIEVE_LOST_RANGING_DATA_SEGMENTS),
+ "RETRIEVE_LOST_RANGING_DATA_SEGMENTS");
+ EXPECT_EQ(ras::GetOpcodeText(ras::Opcode::ABORT_OPERATION), "ABORT_OPERATION");
+ EXPECT_EQ(ras::GetOpcodeText(ras::Opcode::FILTER), "FILTER");
+
+ // Test unknown opcode (casting an invalid value to Opcode)
+ EXPECT_EQ(ras::GetOpcodeText(static_cast<ras::Opcode>(0x05)), "Unknown Opcode");
+}
+
+TEST(RasUtilsTest, GetResponseOpcodeValueText) {
+ // Test known response code values
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::RESERVED_FOR_FUTURE_USE),
+ "RESERVED_FOR_FUTURE_USE");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::SUCCESS), "SUCCESS");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::OP_CODE_NOT_SUPPORTED),
+ "OP_CODE_NOT_SUPPORTED");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::INVALID_PARAMETER),
+ "INVALID_PARAMETER");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::PERSISTED), "PERSISTED");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::ABORT_UNSUCCESSFUL),
+ "ABORT_UNSUCCESSFUL");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::PROCEDURE_NOT_COMPLETED),
+ "PROCEDURE_NOT_COMPLETED");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::SERVER_BUSY), "SERVER_BUSY");
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(ras::ResponseCodeValue::NO_RECORDS_FOUND),
+ "NO_RECORDS_FOUND");
+
+ // Test unknown response code value (casting an invalid value to ResponseCodeValue)
+ EXPECT_EQ(ras::GetResponseOpcodeValueText(static_cast<ras::ResponseCodeValue>(0x09)),
+ "Reserved for Future Use");
+}
+
+TEST(RasUtilsTest, IsRangingServiceCharacteristic) {
+ // Test true cases for Ranging Service characteristics
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRangingService16Bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasFeaturesCharacteristic16bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasRealTimeRangingDataCharacteristic16bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasOnDemandDataCharacteristic16bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasControlPointCharacteristic16bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasRangingDataReadyCharacteristic16bit)));
+ EXPECT_TRUE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kRasRangingDataOverWrittenCharacteristic16bit)));
+
+ // Test false cases for non-Ranging Service characteristics
+ EXPECT_FALSE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::From16Bit(ras::uuid::kClientCharacteristicConfiguration16bit)));
+ EXPECT_FALSE(ras::IsRangingServiceCharacteristic(
+ bluetooth::Uuid::FromString("00001101-0000-1000-8000-00805F9B34FB"))); // Random UUID
+}
diff --git a/system/bta/test/bta_disc_test.cc b/system/bta/test/bta_disc_test.cc
index 72e446c122..3a1dbc9882 100644
--- a/system/bta/test/bta_disc_test.cc
+++ b/system/bta/test/bta_disc_test.cc
@@ -44,28 +44,6 @@ namespace {
const RawAddress kRawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
}
-// Test hooks
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-void bta_dm_disc_init_search_cb(tBTA_DM_SEARCH_CB& bta_dm_search_cb);
-bool bta_dm_read_remote_device_name(const RawAddress& bd_addr, tBT_TRANSPORT transport);
-tBTA_DM_SEARCH_CB& bta_dm_disc_search_cb();
-void bta_dm_discover_next_device();
-void bta_dm_sdp_find_services(tBTA_DM_SDP_STATE* state);
-void bta_dm_inq_cmpl();
-void bta_dm_inq_cmpl_cb(void* p_result);
-void bta_dm_observe_cmpl_cb(void* p_result);
-void bta_dm_observe_results_cb(tBTM_INQ_RESULTS* p_inq, const uint8_t* p_eir, uint16_t eir_len);
-void bta_dm_opportunistic_observe_results_cb(tBTM_INQ_RESULTS* p_inq, const uint8_t* p_eir,
- uint16_t eir_len);
-void bta_dm_queue_search(tBTA_DM_API_SEARCH& search);
-void bta_dm_start_scan(uint8_t duration_sec);
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
-
class BtaInitializedTest : public BtaWithContextTest {
protected:
void SetUp() override {
@@ -101,7 +79,9 @@ TEST_F(BtaInitializedTest, bta_dm_ble_scan) {
bta_dm_ble_scan(kStopLeScan, duration_in_seconds);
}
-TEST_F(BtaInitializedTest, bta_dm_disc_discover_next_device) { bta_dm_disc_discover_next_device(); }
+TEST_F(BtaInitializedTest, bta_dm_disc_discover_next_device) {
+ bluetooth::legacy::testing::bta_dm_discover_next_device();
+}
TEST_F(BtaInitializedTest, bta_dm_disc_remove_device) { bta_dm_disc_remove_device(kRawAddress); }
@@ -116,7 +96,7 @@ TEST_F(BtaInitializedTest, bta_dm_sdp_find_services) {
.services_found = 0,
.service_index = 0,
});
- bluetooth::legacy::testing::bta_dm_sdp_find_services(state.get());
+ bta_dm_sdp_find_services(state.get());
}
TEST_F(BtaInitializedTest, bta_dm_inq_cmpl) { bluetooth::legacy::testing::bta_dm_inq_cmpl(); }
@@ -239,61 +219,7 @@ int gatt_service_cb_both_call_cnt = 0;
/* This test exercises the usual service discovery flow when bonding to
* dual-mode, CTKD capable device on LE transport.
*/
-TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_disabled,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) {
- bta_dm_disc_start(true);
-
- std::promise<void> gatt_triggered;
- int gatt_call_cnt = 0;
- base::RepeatingCallback<void(const RawAddress&)> gatt_performer =
- base::BindLambdaForTesting([&](const RawAddress& /*bd_addr*/) {
- gatt_call_cnt++;
- gatt_triggered.set_value();
- });
- bta_dm_disc_override_gatt_performer_for_testing(gatt_performer);
-
- int sdp_call_cnt = 0;
- base::RepeatingCallback<void(tBTA_DM_SDP_STATE*)> sdp_performer =
- base::BindLambdaForTesting([&](tBTA_DM_SDP_STATE* /*sdp_state*/) { sdp_call_cnt++; });
- bta_dm_disc_override_sdp_performer_for_testing(sdp_performer);
-
- gatt_service_cb_both_call_cnt = 0;
- service_cb_both_call_cnt = 0;
-
- bta_dm_disc_start_service_discovery(
- {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) {}, nullptr,
- [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) {
- service_cb_both_call_cnt++;
- }},
- kRawAddress, BT_TRANSPORT_BR_EDR);
- EXPECT_EQ(sdp_call_cnt, 1);
-
- bta_dm_disc_start_service_discovery(
- {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) { gatt_service_cb_both_call_cnt++; },
- nullptr, [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) {}},
- kRawAddress, BT_TRANSPORT_LE);
-
- // GATT discovery is queued, until SDP finishes
- EXPECT_EQ(gatt_call_cnt, 0);
-
- bta_dm_sdp_finished(kRawAddress, BTA_SUCCESS, {}, {});
- EXPECT_EQ(service_cb_both_call_cnt, 1);
-
- // SDP finished, wait until GATT is triggered.
- EXPECT_EQ(std::future_status::ready,
- gatt_triggered.get_future().wait_for(std::chrono::seconds(1)));
- bta_dm_gatt_finished(kRawAddress, BTA_SUCCESS);
- EXPECT_EQ(gatt_service_cb_both_call_cnt, 1);
-
- bta_dm_disc_override_sdp_performer_for_testing({});
- bta_dm_disc_override_gatt_performer_for_testing({});
-}
-
-/* This test exercises the usual service discovery flow when bonding to
- * dual-mode, CTKD capable device on LE transport.
- */
-TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_enabled,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) {
+TEST_F(BtaInitializedTest, bta_dm_disc_both_transports) {
bta_dm_disc_start(true);
int gatt_call_cnt = 0;
diff --git a/system/bta/test/bta_dm_test.cc b/system/bta/test/bta_dm_test.cc
index 91a8390172..90d41d5e6f 100644
--- a/system/bta/test/bta_dm_test.cc
+++ b/system/bta/test/bta_dm_test.cc
@@ -25,8 +25,10 @@
#include <format>
#include <string>
+#include "bta/dm/bta_dm_device_search.h"
#include "bta/dm/bta_dm_device_search_int.h"
#include "bta/dm/bta_dm_disc.h"
+#include "bta/dm/bta_dm_disc_int.h"
#include "bta/dm/bta_dm_int.h"
#include "bta/dm/bta_dm_pm.cc"
#include "bta/dm/bta_dm_sec_int.h"
@@ -59,15 +61,6 @@ constexpr char kRemoteName[] = "TheRemoteName";
} // namespace
-namespace bluetooth::legacy::testing {
-
-tBTA_DM_SEARCH_CB& bta_dm_disc_search_cb();
-void bta_dm_deinit_cb();
-void bta_dm_init_cb();
-void bta_dm_remote_name_cmpl(const tBTA_DM_REMOTE_NAME& remote_name_msg);
-
-} // namespace bluetooth::legacy::testing
-
class BtaDmTest : public BtaWithContextTest {
protected:
void SetUp() override {
@@ -193,23 +186,6 @@ void BTA_DM_ENCRYPT_CBACK(const RawAddress& bd_addr, tBT_TRANSPORT transport, tB
} // namespace
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-tBTA_DM_PEER_DEVICE* allocate_device_for(const RawAddress& bd_addr, tBT_TRANSPORT transport);
-
-void bta_dm_remname_cback(const tBTM_REMOTE_DEV_NAME* p);
-
-tBT_TRANSPORT bta_dm_determine_discovery_transport(const RawAddress& remote_bd_addr);
-
-tBTM_STATUS bta_dm_sp_cback(tBTM_SP_EVT event, tBTM_SP_EVT_DATA* p_data);
-
-void BTA_dm_on_hw_on();
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
-
TEST_F(BtaDmTest, bta_dm_set_encryption) {
const tBT_TRANSPORT transport{BT_TRANSPORT_LE};
const tBTM_BLE_SEC_ACT sec_act{BTM_BLE_SEC_NONE};
@@ -268,9 +244,6 @@ TEST_F(BtaDmTest, bta_dm_set_encryption) {
BTA_DM_ENCRYPT_CBACK_queue = {};
}
-void bta_dm_encrypt_cback(RawAddress bd_addr, tBT_TRANSPORT transport, void* /* p_ref_data */,
- tBTM_STATUS result);
-
TEST_F(BtaDmTest, bta_dm_encrypt_cback) {
const tBT_TRANSPORT transport{BT_TRANSPORT_LE};
@@ -481,13 +454,13 @@ TEST_F(BtaDmCustomAlarmTest, sniff_offload_feature__test_sysprop) {
// Expect not to trigger bta_dm_init_pm due to sysprop enabled
// and reset the value of .srvc_id.
is_property_enabled = true;
- bluetooth::legacy::testing::BTA_dm_on_hw_on();
+ BTA_dm_on_hw_on();
ASSERT_EQ(0, bta_dm_cb.pm_timer[0].srvc_id[0]);
// Expect to trigger bta_dm_init_pm and init the value of .srvc_id to
// BTA_ID_MAX due to sysprop disabled.
is_property_enabled = false;
- bluetooth::legacy::testing::BTA_dm_on_hw_on();
+ BTA_dm_on_hw_on();
ASSERT_EQ((uint8_t)BTA_ID_MAX, bta_dm_cb.pm_timer[0].srvc_id[0]);
// Shouldn't crash even there's no active timer when calling
diff --git a/system/bta/test/bta_gatt_client_test.cc b/system/bta/test/bta_gatt_client_test.cc
index 3c19614768..c48dc02cf5 100644
--- a/system/bta/test/bta_gatt_client_test.cc
+++ b/system/bta/test/bta_gatt_client_test.cc
@@ -27,15 +27,6 @@
using namespace bluetooth::common;
// Test hooks
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-std::vector<TimestampedEntry<std::string>> PullCopyOfGattHistory();
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
class BtaDiscTest : public testing::Test {
protected:
@@ -61,7 +52,7 @@ TEST_F(BtaDiscTest, gatt_history_callback) {
gatt_history_callback(std::format("{}", a[2].c_str()));
std::vector<bluetooth::common::TimestampedEntry<std::string>> history =
- bluetooth::legacy::testing::PullCopyOfGattHistory();
+ bluetooth::testing::PullCopyOfGattHistory();
ASSERT_EQ(3UL, history.size());
ASSERT_STREQ(a[0].c_str(), history[0].entry.c_str());
ASSERT_STREQ(a[1].c_str(), history[1].entry.c_str());
diff --git a/system/bta/test/bta_sdp_test.cc b/system/bta/test/bta_sdp_test.cc
index 94d035d9a7..06e8ff86aa 100644
--- a/system/bta/test/bta_sdp_test.cc
+++ b/system/bta/test/bta_sdp_test.cc
@@ -22,24 +22,10 @@
#include "hci/controller_interface_mock.h"
#include "test/mock/mock_main_shim_entry.h"
-void BTA_dm_on_hw_on();
-void BTA_dm_on_hw_off();
-
namespace {
const char kName[] = "Hello";
}
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-tBTA_DM_SERVICE_DISCOVERY_CB& bta_dm_discovery_cb();
-void bta_dm_sdp_result(tSDP_STATUS sdp_status, tBTA_DM_SDP_STATE* state);
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
-
class BtaSdpTest : public BtaWithHwOnTest {
protected:
void SetUp() override {
@@ -69,5 +55,5 @@ TEST_F(BtaSdpTest, nop) {}
TEST_F(BtaSdpRegisteredTest, bta_dm_sdp_result_SDP_SUCCESS) {
std::unique_ptr<tBTA_DM_SDP_STATE> state = std::make_unique<tBTA_DM_SDP_STATE>(
tBTA_DM_SDP_STATE{.service_index = BTA_MAX_SERVICE_ID});
- bluetooth::legacy::testing::bta_dm_sdp_result(tSDP_STATUS::SDP_SUCCESS, state.get());
+ bta_dm_sdp_result(tSDP_STATUS::SDP_SUCCESS, state.get());
}
diff --git a/system/bta/test/bta_sec_test.cc b/system/bta/test/bta_sec_test.cc
index 20b82cc480..57952ece3f 100644
--- a/system/bta/test/bta_sec_test.cc
+++ b/system/bta/test/bta_sec_test.cc
@@ -38,17 +38,6 @@ constexpr char kRemoteName[] = "TheRemoteName";
} // namespace
-// Test hooks
-namespace bluetooth {
-namespace legacy {
-namespace testing {
-
-tBTM_STATUS bta_dm_sp_cback(tBTM_SP_EVT event, tBTM_SP_EVT_DATA* p_data);
-
-} // namespace testing
-} // namespace legacy
-} // namespace bluetooth
-
class BtaSecTest : public BtaWithHwOnTest {
protected:
void SetUp() override {
diff --git a/system/bta/test/bta_test_fixtures.h b/system/bta/test/bta_test_fixtures.h
index 373b846122..c259d0b916 100644
--- a/system/bta/test/bta_test_fixtures.h
+++ b/system/bta/test/bta_test_fixtures.h
@@ -36,9 +36,6 @@
constexpr tGATT_IF kGattRegisteredIf = 5;
-void BTA_dm_on_hw_on();
-void BTA_dm_on_hw_off();
-
extern tBTA_DM_CB bta_dm_cb;
// Set up base mocks and fakes
diff --git a/system/bta/test/common/bta_gatt_api_mock.cc b/system/bta/test/common/bta_gatt_api_mock.cc
index e5dee293fb..9494f96c48 100644
--- a/system/bta/test/common/bta_gatt_api_mock.cc
+++ b/system/bta/test/common/bta_gatt_api_mock.cc
@@ -31,10 +31,10 @@ void gatt::SetMockBtaGattInterface(MockBtaGattInterface* mock_bta_gatt_interface
gatt_interface = mock_bta_gatt_interface;
}
-void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support) {
+void BTA_GATTC_AppRegister(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support) {
log::assert_that(gatt_interface != nullptr, "Mock GATT interface not set!");
- gatt_interface->AppRegister(p_client_cb, cb, eatt_support);
+ gatt_interface->AppRegister(name, p_client_cb, cb, eatt_support);
}
void BTA_GATTC_AppDeregister(tGATT_IF client_if) {
diff --git a/system/bta/test/common/bta_gatt_api_mock.h b/system/bta/test/common/bta_gatt_api_mock.h
index 6b404526ed..dafec74eb7 100644
--- a/system/bta/test/common/bta_gatt_api_mock.h
+++ b/system/bta/test/common/bta_gatt_api_mock.h
@@ -27,8 +27,8 @@ namespace gatt {
class BtaGattInterface {
public:
- virtual void AppRegister(tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
- bool eatt_support) = 0;
+ virtual void AppRegister(const std::string& name, tBTA_GATTC_CBACK* p_client_cb,
+ BtaAppRegisterCallback cb, bool eatt_support) = 0;
virtual void AppDeregister(tGATT_IF client_if) = 0;
virtual void Open(tGATT_IF client_if, const RawAddress& remote_bda,
tBTM_BLE_CONN_TYPE connection_type, tBT_TRANSPORT transport, bool opportunistic,
@@ -52,7 +52,8 @@ public:
class MockBtaGattInterface : public BtaGattInterface {
public:
MOCK_METHOD((void), AppRegister,
- (tBTA_GATTC_CBACK * p_client_cb, BtaAppRegisterCallback cb, bool eatt_support),
+ (const std::string& name, tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
+ bool eatt_support),
(override));
MOCK_METHOD((void), AppDeregister, (tGATT_IF client_if), (override));
MOCK_METHOD((void), Open,
diff --git a/system/bta/test/common/btif_storage_mock.cc b/system/bta/test/common/btif_storage_mock.cc
index b74b7678ea..3b7ee7d06e 100644
--- a/system/bta/test/common/btif_storage_mock.cc
+++ b/system/bta/test/common/btif_storage_mock.cc
@@ -41,6 +41,12 @@ void btif_storage_leaudio_update_pacs_bin(const RawAddress& addr) {
btif_storage_interface->LeAudioUpdatePacs(addr);
}
+/** Store GMAP information */
+void btif_storage_leaudio_update_gmap_bin(const RawAddress& addr) {
+ log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!");
+ btif_storage_interface->LeAudioUpdateGmap(addr);
+}
+
void btif_storage_leaudio_update_ase_bin(const RawAddress& addr) {
log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!");
btif_storage_interface->LeAudioUpdateAses(addr);
diff --git a/system/bta/test/common/btif_storage_mock.h b/system/bta/test/common/btif_storage_mock.h
index a715b43b70..c756336dab 100644
--- a/system/bta/test/common/btif_storage_mock.h
+++ b/system/bta/test/common/btif_storage_mock.h
@@ -28,6 +28,7 @@ class BtifStorageInterface {
public:
virtual void AddLeaudioAutoconnect(RawAddress const& addr, bool autoconnect) = 0;
virtual void LeAudioUpdatePacs(RawAddress const& addr) = 0;
+ virtual void LeAudioUpdateGmap(RawAddress const& addr) = 0;
virtual void LeAudioUpdateAses(RawAddress const& addr) = 0;
virtual void LeAudioUpdateHandles(RawAddress const& addr) = 0;
virtual void SetLeAudioLocations(RawAddress const& addr, uint32_t sink_location,
@@ -57,6 +58,7 @@ public:
MOCK_METHOD((void), AddLeaudioAutoconnect, (RawAddress const& addr, bool autoconnect),
(override));
MOCK_METHOD((void), LeAudioUpdatePacs, (RawAddress const& addr), (override));
+ MOCK_METHOD((void), LeAudioUpdateGmap, (RawAddress const& addr), (override));
MOCK_METHOD((void), LeAudioUpdateAses, (RawAddress const& addr), (override));
MOCK_METHOD((void), LeAudioUpdateHandles, (RawAddress const& addr), (override));
MOCK_METHOD((void), SetLeAudioLocations,
diff --git a/system/bta/test/common/btm_api_mock.cc b/system/bta/test/common/btm_api_mock.cc
index b7208803a3..10540aea65 100644
--- a/system/bta/test/common/btm_api_mock.cc
+++ b/system/bta/test/common/btm_api_mock.cc
@@ -76,7 +76,7 @@ tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr, tBT_TRANSPORT transport
return btm_interface->SetEncryption(bd_addr, transport, p_callback, p_ref_data, sec_act);
}
-bool BTM_SecIsSecurityPending(const RawAddress& bd_addr) {
+bool BTM_SecIsLeSecurityPending(const RawAddress& bd_addr) {
log::assert_that(btm_interface != nullptr, "Mock btm interface not set!");
return btm_interface->SecIsSecurityPending(bd_addr);
}
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index c381f1210a..2acf1ae01d 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -20,6 +20,7 @@
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
#include <hardware/bt_gatt_types.h>
#include <hardware/bt_vc.h>
#include <stdio.h>
@@ -114,7 +115,7 @@ public:
VolumeControlImpl(bluetooth::vc::VolumeControlCallbacks* callbacks, const base::Closure& initCb)
: gatt_if_(0), callbacks_(callbacks), latest_operation_id_(0) {
BTA_GATTC_AppRegister(
- gattc_callback_static,
+ "volume_control", gattc_callback_static,
base::Bind(
[](const base::Closure& initCb, uint8_t client_id, uint8_t status) {
if (status != GATT_SUCCESS) {
@@ -421,7 +422,7 @@ public:
is_volume_change, is_mute_change);
if (!is_volume_change && !is_mute_change) {
- bluetooth::log::error("Autonomous change but volume and mute did not changed.");
+ bluetooth::log::warn("Autonomous change but volume and mute did not changed.");
return;
}
@@ -919,7 +920,7 @@ public:
[operation_id](auto& operation) { return operation.operation_id_ == operation_id; });
if (op == ongoing_operations_.end()) {
- bluetooth::log::error("Could not find operation id: {}", operation_id);
+ bluetooth::log::warn("Could not find operation id: {}", operation_id);
return;
}
@@ -934,6 +935,27 @@ public:
}
}
+ bool isPendingVolumeControlOperation(const RawAddress& addr) {
+ if (!com::android::bluetooth::flags::vcp_allow_set_same_volume_if_pending()) {
+ return false;
+ }
+
+ if (std::find_if(ongoing_operations_.begin(), ongoing_operations_.end(),
+ [&addr](const VolumeOperation& op) {
+ auto it = find(op.devices_.begin(), op.devices_.end(), addr);
+ if (it != op.devices_.end()) {
+ bluetooth::log::debug(
+ "There is a pending volume operation {} for device {}",
+ op.operation_id_, addr);
+ return true;
+ }
+ return false;
+ }) != ongoing_operations_.end()) {
+ return true;
+ }
+ return false;
+ }
+
void RemovePendingVolumeControlOperations(const std::vector<RawAddress>& devices, int group_id) {
bluetooth::log::debug("");
for (auto op = ongoing_operations_.begin(); op != ongoing_operations_.end();) {
@@ -1156,7 +1178,8 @@ public:
volume_control_devices_.FindByAddress(std::get<RawAddress>(addr_or_group_id));
if (dev != nullptr) {
bluetooth::log::debug("Address: {}: isReady: {}", dev->address, dev->IsReady());
- if (dev->IsReady() && (dev->volume != volume)) {
+ if (dev->IsReady() &&
+ ((dev->volume != volume) || isPendingVolumeControlOperation(dev->address))) {
std::vector<RawAddress> devices = {dev->address};
RemovePendingVolumeControlOperations(devices, bluetooth::groups::kGroupUnknown);
PrepareVolumeControlOperation(devices, bluetooth::groups::kGroupUnknown, false, opcode,
@@ -1189,7 +1212,7 @@ public:
continue;
}
- if (!dev->IsReady() || (dev->volume == volume)) {
+ if (!dev->IsReady() || ((dev->volume == volume) && !isPendingVolumeControlOperation(*it))) {
it = devices.erase(it);
volumeNotChanged = volumeNotChanged ? volumeNotChanged : (dev->volume == volume);
deviceNotReady = deviceNotReady ? deviceNotReady : !dev->IsReady();
diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc
index c9c6adc425..cd084fc75e 100644
--- a/system/bta/vc/vc_test.cc
+++ b/system/bta/vc/vc_test.cc
@@ -555,8 +555,8 @@ protected:
void TestAppRegister(void) {
BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
VolumeControl::Initialize(&callbacks, base::DoNothing());
ASSERT_TRUE(gatt_callback);
ASSERT_TRUE(app_register_callback);
@@ -765,8 +765,8 @@ TEST_F(VolumeControlTest, test_get_uninitialized) { ASSERT_DEATH(VolumeControl::
TEST_F(VolumeControlTest, test_initialize) {
bool init_cb_called = false;
BtaAppRegisterCallback app_register_callback;
- EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
- .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
+ EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
VolumeControl::Initialize(
&callbacks,
base::Bind([](bool* init_cb_called) { *init_cb_called = true; }, &init_cb_called));
@@ -1703,6 +1703,47 @@ TEST_F(VolumeControlValueSetTest, test_set_volume) {
VolumeControl::Get()->SetVolume(test_address, 0x20);
}
+TEST_F(VolumeControlValueSetTest, test_set_volume_to_previous_during_pending) {
+ com::android::bluetooth::flags::provider_->vcp_allow_set_same_volume_if_pending(true);
+ // In this test we simulate notification coming later and operations will be queued
+ ON_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, _, GATT_WRITE, _, _))
+ .WillByDefault([](uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value,
+ tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) {
+ uint8_t write_rsp;
+
+ switch (value[0]) {
+ case 0x04: // set abs. volume
+ break;
+ default:
+ break;
+ }
+ cb(conn_id, GATT_SUCCESS, handle, 0, &write_rsp, cb_data);
+ });
+
+ const std::vector<uint8_t> vol_x10({0x04, /*change_cnt*/ 0, 0x10});
+ std::vector<uint8_t> ntf_value_x10({0x10, 0, 1});
+ const std::vector<uint8_t> vol_x11({0x04, /*change_cnt*/ 1, 0x11});
+ std::vector<uint8_t> ntf_value_x11({0x11, 0, 2});
+ const std::vector<uint8_t> vol_x10_2({0x04, /*change_cnt*/ 2, 0x10});
+ std::vector<uint8_t> ntf_value_x10_2({0x10, 0, 3});
+
+ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1);
+
+ VolumeControl::Get()->SetVolume(test_address, 0x10);
+ GetNotificationEvent(0x0021, ntf_value_x10);
+
+ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x11, GATT_WRITE, _, _)).Times(1);
+ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10_2, GATT_WRITE, _, _))
+ .Times(1);
+
+ VolumeControl::Get()->SetVolume(test_address, 0x11);
+ VolumeControl::Get()->SetVolume(test_address, 0x10);
+ GetNotificationEvent(0x0021, ntf_value_x11);
+ GetNotificationEvent(0x0021, ntf_value_x10_2);
+
+ Mock::VerifyAndClearExpectations(&gatt_queue);
+}
+
TEST_F(VolumeControlValueSetTest, test_set_volume_stress) {
uint8_t n = 100;
uint8_t change_cnt = 0;
diff --git a/system/btcore/Android.bp b/system/btcore/Android.bp
index dc6df095ad..17d5c01905 100644
--- a/system/btcore/Android.bp
+++ b/system/btcore/Android.bp
@@ -24,9 +24,7 @@ cc_defaults {
],
header_libs: ["libbluetooth_headers"],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
target: {
host_linux: {
cflags: ["-D_GNU_SOURCE"],
@@ -47,9 +45,7 @@ cc_library_static {
],
header_libs: ["libbluetooth_headers"],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
target: {
host_linux: {
cflags: ["-D_GNU_SOURCE"],
@@ -80,7 +76,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "30",
}
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 75494590fd..498d0c1048 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -24,9 +24,7 @@ cc_library {
generated_sources: ["statslog_bt.cpp"],
generated_headers: ["statslog_bt.h"],
export_generated_headers: ["statslog_bt.h"],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
shared_libs: [
"libstatssocket",
@@ -64,9 +62,7 @@ cc_library_static {
shared_libs: [
"libchrome",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
@@ -164,9 +160,7 @@ cc_library_static {
shared_libs: [
"libbinder",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -253,9 +247,7 @@ cc_library_static {
/* we export all classes, so change default visibility, instead of having EXPORT_SYMBOL on each class*/
"-fvisibility=default",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
diff --git a/system/btif/include/btif_dm.h b/system/btif/include/btif_dm.h
index 15a5bfa1ae..9069c97b76 100644
--- a/system/btif/include/btif_dm.h
+++ b/system/btif/include/btif_dm.h
@@ -104,6 +104,7 @@ void btif_dm_clear_event_filter();
void btif_dm_clear_event_mask();
void btif_dm_clear_filter_accept_list();
void btif_dm_disconnect_all_acls();
+void btif_dm_disconnect_acl(const RawAddress& bd_addr, tBT_TRANSPORT transport);
void btif_dm_le_rand(bluetooth::hci::LeRandCallback callback);
void btif_dm_set_event_filter_connection_setup_all_devices();
@@ -150,6 +151,7 @@ void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask, Octet16*
tBTA_BLE_LOCAL_ID_KEYS* p_id_keys);
void btif_update_remote_properties(const RawAddress& bd_addr, BD_NAME bd_name, DEV_CLASS dev_class,
tBT_DEVICE_TYPE dev_type);
+bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid);
bool check_cod_hid(const RawAddress& bd_addr);
bool check_cod_hid_major(const RawAddress& bd_addr, uint32_t cod);
diff --git a/system/btif/include/btif_profile_storage.h b/system/btif/include/btif_profile_storage.h
index 6459c45b99..d6a3688741 100644
--- a/system/btif/include/btif_profile_storage.h
+++ b/system/btif/include/btif_profile_storage.h
@@ -102,6 +102,9 @@ void btif_storage_set_leaudio_autoconnect(const RawAddress& addr, bool autoconne
/** Store PACs information */
void btif_storage_leaudio_update_pacs_bin(const RawAddress& addr);
+/** Store GMAP information */
+void btif_storage_leaudio_update_gmap_bin(const RawAddress& addr);
+
/** Store ASEs information */
void btif_storage_leaudio_update_ase_bin(const RawAddress& addr);
diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h
index 0c1043faae..01432bade6 100644
--- a/system/btif/include/btif_storage.h
+++ b/system/btif/include/btif_storage.h
@@ -446,6 +446,7 @@ bt_status_t btif_storage_set_hid_connection_policy(const tAclLinkSpec& link_spec
bt_status_t btif_storage_get_hid_connection_policy(const tAclLinkSpec& link_spec,
bool* reconnect_allowed);
+void btif_storage_migrate_services();
/******************************************************************************
* Exported for unit tests
*****************************************************************************/
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 25a74ea47a..7bdf50781b 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -814,6 +814,16 @@ static int disconnect_all_acls() {
return BT_STATUS_SUCCESS;
}
+static int disconnect_acl(const RawAddress& bd_addr, int transport) {
+ log::verbose("{}", bd_addr);
+ if (!interface_ready()) {
+ return BT_STATUS_NOT_READY;
+ }
+
+ do_in_main_thread(base::BindOnce(btif_dm_disconnect_acl, bd_addr, to_bt_transport(transport)));
+ return BT_STATUS_SUCCESS;
+}
+
static void le_rand_btif_cb(uint64_t random_number) {
log::verbose("");
do_in_jni_thread(base::BindOnce(
@@ -1286,6 +1296,7 @@ EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
.clear_event_mask = clear_event_mask,
.clear_filter_accept_list = clear_filter_accept_list,
.disconnect_all_acls = disconnect_all_acls,
+ .disconnect_acl = disconnect_acl,
.le_rand = le_rand,
.set_event_filter_inquiry_result_all_devices = set_event_filter_inquiry_result_all_devices,
.set_default_event_mask_except = set_default_event_mask_except,
diff --git a/system/btif/src/btif_a2dp_sink.cc b/system/btif/src/btif_a2dp_sink.cc
index 02b92621ed..0328ba9cbc 100644
--- a/system/btif/src/btif_a2dp_sink.cc
+++ b/system/btif/src/btif_a2dp_sink.cc
@@ -731,6 +731,7 @@ uint8_t btif_a2dp_sink_enqueue_buf(BT_HDR* p_pkt) {
}
log::verbose("+");
+
/* Allocate and queue this buffer */
BT_HDR* p_msg = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(*p_msg) + p_pkt->len));
memcpy(p_msg, p_pkt, sizeof(*p_msg));
@@ -738,17 +739,18 @@ uint8_t btif_a2dp_sink_enqueue_buf(BT_HDR* p_pkt) {
memcpy(p_msg->data, p_pkt->data + p_pkt->offset, p_pkt->len);
fixed_queue_enqueue(btif_a2dp_sink_cb.rx_audio_queue, p_msg);
+ /* If the queue is full, pop the front off to make room for the new data */
if (fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) == MAX_INPUT_A2DP_FRAME_QUEUE_SZ) {
+ log::verbose("Audio data buffer has reached max size. Dropping front packet");
osi_free(fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue));
- uint8_t ret = fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue);
- return ret;
}
- // Avoid other checks if alarm has already been initialized.
+ /* Check to see if we need to start decoding */
if (btif_a2dp_sink_cb.decode_alarm == nullptr &&
fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) >= MAX_A2DP_DELAYED_START_FRAME_COUNT) {
- log::verbose("Initiate decoding. Current focus state:{}", btif_a2dp_sink_cb.rx_focus_state);
+ log::verbose("Can initiate decoding, focus_state={}", btif_a2dp_sink_cb.rx_focus_state);
if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_GRANTED) {
+ log::info("Request to begin decoding");
btif_a2dp_sink_audio_handle_start_decoding();
}
}
diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc
index d58e3de696..70634b9984 100644
--- a/system/btif/src/btif_a2dp_source.cc
+++ b/system/btif/src/btif_a2dp_source.cc
@@ -531,9 +531,15 @@ bool btif_a2dp_source_restart_session(const RawAddress& old_peer_address,
bool btif_a2dp_source_end_session(const RawAddress& peer_address) {
log::info("peer_address={} state={}", peer_address, btif_a2dp_source_cb.StateStr());
- local_thread()->DoInThread(FROM_HERE,
- base::BindOnce(&btif_a2dp_source_end_session_delayed, peer_address));
- btif_a2dp_source_cleanup_codec();
+ if (com::android::bluetooth::flags::a2dp_source_threading_fix()) {
+ btif_a2dp_source_cleanup_codec();
+ btif_a2dp_source_end_session_delayed(peer_address);
+ } else {
+
+ local_thread()->DoInThread(FROM_HERE,
+ base::BindOnce(&btif_a2dp_source_end_session_delayed, peer_address));
+ btif_a2dp_source_cleanup_codec();
+ }
return true;
}
diff --git a/system/btif/src/btif_bqr.cc b/system/btif/src/btif_bqr.cc
index 37ce06a935..eaaef220e6 100644
--- a/system/btif/src/btif_bqr.cc
+++ b/system/btif/src/btif_bqr.cc
@@ -725,30 +725,34 @@ static void CategorizeBqrEvent(uint8_t length, const uint8_t* p_bqr_event) {
break;
case QUALITY_REPORT_ID_ENERGY_MONITOR:
- if (length < kEnergyMonitorParamTotalLen) {
- log::fatal(
- "Parameter total length: {} is abnormal. It shall be not shorter "
- "than: {}",
- length, kEnergyMonitorParamTotalLen);
- return;
- }
-
if (com::android::bluetooth::flags::support_bluetooth_quality_report_v6()) {
- AddEnergyMonitorEventToQueue(length, p_bqr_event);
+ if (vendor_cap_supported_version >= kBqrVersion6_0) {
+ if (length < kEnergyMonitorParamTotalLen) {
+ log::fatal(
+ "Event {} Parameter total length: {} is abnormal. It shall be not shorter "
+ "than: {}",
+ quality_report_id, length, kEnergyMonitorParamTotalLen);
+ return;
+ }
+
+ AddEnergyMonitorEventToQueue(length, p_bqr_event);
+ }
}
break;
case QUALITY_REPORT_ID_RF_STATS:
- if (length < kRFStatsParamTotalLen) {
- log::fatal(
- "Parameter total length: {} is abnormal. It shall be not shorter "
- "than: {}",
- length, kRFStatsParamTotalLen);
- return;
- }
-
if (com::android::bluetooth::flags::support_bluetooth_quality_report_v6()) {
- AddRFStatsEventToQueue(length, p_bqr_event);
+ if (vendor_cap_supported_version >= kBqrVersion6_0) {
+ if (length < kRFStatsParamTotalLen) {
+ log::fatal(
+ "Event {} Parameter total length: {} is abnormal. It shall be not shorter "
+ "than: {}",
+ quality_report_id, length, kEnergyMonitorParamTotalLen);
+ return;
+ }
+
+ AddRFStatsEventToQueue(length, p_bqr_event);
+ }
}
break;
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index 9164e53ef7..8e481daed0 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -80,7 +80,7 @@ using namespace bluetooth;
#if defined(TARGET_FLOSS)
#define BTE_DID_CONF_FILE "/var/lib/bluetooth/bt_did.conf"
#elif defined(__ANDROID__)
-#define BTE_DID_CONF_FILE "/apex/com.android.btservices/etc/bluetooth/bt_did.conf"
+#define BTE_DID_CONF_FILE "/apex/com.android.bt/etc/bluetooth/bt_did.conf"
#else // !defined(__ANDROID__)
#define BTE_DID_CONF_FILE "bt_did.conf"
#endif // defined(__ANDROID__)
@@ -134,7 +134,12 @@ int btif_is_enabled(void) {
return (!btif_is_dut_mode()) && (stack_manager_get_interface()->get_stack_is_running());
}
-void btif_init_ok() { btif_dm_load_ble_local_keys(); }
+void btif_init_ok() {
+ btif_dm_load_ble_local_keys();
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ btif_storage_migrate_services();
+ }
+}
/*******************************************************************************
*
@@ -290,7 +295,7 @@ void btif_dut_mode_send(uint16_t opcode, uint8_t* buf, uint8_t len) {
****************************************************************************/
static bt_status_t btif_in_get_adapter_properties(void) {
- const static uint32_t NUM_ADAPTER_PROPERTIES = 5;
+ static const uint32_t NUM_ADAPTER_PROPERTIES = 5;
bt_property_t properties[NUM_ADAPTER_PROPERTIES];
uint32_t num_props = 0;
@@ -340,12 +345,13 @@ static bt_status_t btif_in_get_adapter_properties(void) {
}
static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) {
- bt_property_t remote_properties[8];
+ bt_property_t remote_properties[9];
uint32_t num_props = 0;
bt_bdname_t name, alias;
uint32_t cod, devtype;
Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ Uuid remote_uuids_le[BT_MAX_NUM_UUIDS];
memset(remote_properties, 0, sizeof(remote_properties));
BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_BDNAME, sizeof(name),
@@ -369,10 +375,17 @@ static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) {
num_props++;
BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS, sizeof(remote_uuids),
- remote_uuids);
+ &remote_uuids);
btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]);
num_props++;
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS_LE,
+ sizeof(remote_uuids_le), &remote_uuids_le);
+ btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]);
+ num_props++;
+ }
+
GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(
BT_STATUS_SUCCESS, *bd_addr, num_props, remote_properties);
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 5848ebba1c..6f83aee781 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -1517,15 +1517,16 @@ static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH*
}
/* Returns true if |uuid| should be passed as device property */
-static bool btif_is_interesting_le_service(bluetooth::Uuid uuid) {
+bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid) {
return uuid.As16Bit() == UUID_SERVCLASS_LE_HID || uuid == UUID_HEARING_AID || uuid == UUID_VC ||
uuid == UUID_CSIS || uuid == UUID_LE_AUDIO || uuid == UUID_LE_MIDI || uuid == UUID_HAS ||
uuid == UUID_BASS || uuid == UUID_BATTERY || uuid == ANDROID_HEADTRACKER_SERVICE_UUID;
}
-static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids) {
+static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids,
+ bt_property_type_t property_type = BT_PROPERTY_UUIDS) {
bt_property_t tmp_prop;
- BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, BT_PROPERTY_UUIDS, sizeof(*existing_uuids), existing_uuids);
+ BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, property_type, sizeof(*existing_uuids), existing_uuids);
return btif_storage_get_remote_device_property(bd_addr, &tmp_prop);
}
@@ -1537,9 +1538,10 @@ static bool btif_is_gatt_service_discovery_post_pairing(const RawAddress bd_addr
(pairing_cb.gatt_over_le == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED);
}
-static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) {
+static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids,
+ bt_property_type_t property_type = BT_PROPERTY_UUIDS) {
Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {};
- bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids);
+ bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids, property_type);
if (lookup_result == BT_STATUS_FAIL) {
return;
@@ -1550,18 +1552,14 @@ static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) {
if (btif_should_ignore_uuid(uuid)) {
continue;
}
- if (btif_is_interesting_le_service(uuid)) {
- log::info("interesting le service {} insert", uuid.ToString());
- uuids->insert(uuid);
- }
+
+ uuids->insert(uuid);
}
}
static void btif_on_service_discovery_results(RawAddress bd_addr,
const std::vector<bluetooth::Uuid>& uuids_param,
tBTA_STATUS result) {
- bt_property_t prop;
- std::vector<uint8_t> property_value;
std::set<Uuid> uuids;
bool a2dp_sink_capable = false;
@@ -1589,8 +1587,12 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
pairing_cb.sdp_over_classic = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED;
}
- prop.type = BT_PROPERTY_UUIDS;
- prop.len = 0;
+ std::vector<uint8_t> bredr_property_value;
+ std::vector<uint8_t> le_property_value;
+ bt_property_t uuid_props[2] = {};
+ bt_property_t& bredr_prop = uuid_props[0];
+ bt_property_t& le_prop = uuid_props[1];
+
if ((result == BTA_SUCCESS) && !uuids_param.empty()) {
log::info("New UUIDs for {}:", bd_addr);
for (const auto& uuid : uuids_param) {
@@ -1610,13 +1612,35 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
for (auto& uuid : uuids) {
auto uuid_128bit = uuid.To128BitBE();
- property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(),
+ uuid_128bit.end());
if (uuid == UUID_A2DP_SINK) {
a2dp_sink_capable = true;
}
}
- prop.val = (void*)property_value.data();
- prop.len = Uuid::kNumBytes128 * uuids.size();
+
+ bredr_prop = {BT_PROPERTY_UUIDS, static_cast<int>(Uuid::kNumBytes128 * uuids.size()),
+ (void*)bredr_property_value.data()};
+
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop);
+ ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote classic services failed", ret);
+
+ std::set<Uuid> le_uuids;
+ if (results_for_bonding_device) {
+ btif_merge_existing_uuids(pairing_cb.static_bdaddr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ btif_merge_existing_uuids(pairing_cb.bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ } else {
+ btif_merge_existing_uuids(bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE);
+ }
+
+ for (auto& uuid : le_uuids) {
+ auto uuid_128bit = uuid.To128BitBE();
+ le_property_value.insert(le_property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+ le_prop = {BT_PROPERTY_UUIDS_LE, static_cast<int>(Uuid::kNumBytes128 * le_uuids.size()),
+ (void*)le_property_value.data()};
+ }
}
bool skip_reporting_wait_for_le = false;
@@ -1649,17 +1673,18 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
log::info("SDP failed, send {} EIR UUIDs to unblock bonding {}", num_eir_uuids, bd_addr);
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());
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(),
+ uuid_128bit.end());
}
eir_uuids_cache.erase(uuids_iter);
}
if (num_eir_uuids > 0) {
- prop.val = (void*)property_value.data();
- prop.len = num_eir_uuids * Uuid::kNumBytes128;
+ bredr_prop.val = (void*)bredr_property_value.data();
+ bredr_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;
+ bredr_prop.val = &uuid;
+ bredr_prop.len = Uuid::kNumBytes128;
}
}
@@ -1677,9 +1702,10 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
uuids_param.size(), num_eir_uuids));
if (!uuids_param.empty() || num_eir_uuids != 0) {
- /* Also write this to the NVRAM */
- const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
- ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret);
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop);
+ ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret);
+ }
if (skip_reporting_wait_for_le) {
log::info(
@@ -1694,16 +1720,13 @@ static void btif_on_service_discovery_results(RawAddress bd_addr,
}
/* Send the event to the BTIF */
- GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
- 1, &prop);
+ GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(
+ BT_STATUS_SUCCESS, bd_addr, ARRAY_SIZE(uuid_props), uuid_props);
}
}
static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid>& services,
bool is_transport_le) {
- std::vector<bt_property_t> prop;
- std::vector<uint8_t> property_value;
- std::set<Uuid> uuids;
RawAddress static_addr_copy = pairing_cb.static_bdaddr;
bool lea_supported = is_le_audio_capable_during_service_discovery(bd_addr);
@@ -1739,6 +1762,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using SDP transport");
}
+ std::set<Uuid> uuids;
for (Uuid uuid : services) {
if (btif_is_interesting_le_service(uuid)) {
if (btif_should_ignore_uuid(uuid)) {
@@ -1767,46 +1791,28 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
log::info("Will return Classic SDP results, if done, to unblock bonding");
}
- Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {};
-
- // Look up UUIDs using pseudo address (either RPA or static address)
- bt_status_t existing_lookup_result = btif_get_existing_uuids(&bd_addr, existing_uuids);
-
- if (existing_lookup_result != BT_STATUS_FAIL) {
- log::info("Got some existing UUIDs by address {}", bd_addr);
-
- for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) {
- Uuid uuid = existing_uuids[i];
- if (uuid.IsEmpty()) {
- continue;
- }
- uuids.insert(uuid);
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ // Look up UUIDs using pseudo address (either RPA or static address)
+ btif_merge_existing_uuids(bd_addr, &uuids);
+ if (bd_addr != static_addr_copy) {
+ // Look up UUID using static address, if different than sudo address
+ btif_merge_existing_uuids(static_addr_copy, &uuids);
}
}
- if (bd_addr != static_addr_copy) {
- // Look up UUID using static address, if different than sudo address
- existing_lookup_result = btif_get_existing_uuids(&static_addr_copy, existing_uuids);
- if (existing_lookup_result != BT_STATUS_FAIL) {
- log::info("Got some existing UUIDs by static address {}", static_addr_copy);
- for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) {
- Uuid uuid = existing_uuids[i];
- if (uuid.IsEmpty()) {
- continue;
- }
- uuids.insert(uuid);
- }
- }
- }
+ std::vector<bt_property_t> prop;
+ std::vector<uint8_t> property_value;
for (auto& uuid : uuids) {
auto uuid_128bit = uuid.To128BitBE();
property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
}
- prop.push_back(bt_property_t{BT_PROPERTY_UUIDS,
- static_cast<int>(Uuid::kNumBytes128 * uuids.size()),
- (void*)property_value.data()});
+ prop.push_back(bt_property_t{
+ (com::android::bluetooth::flags::separate_service_storage() && is_transport_le)
+ ? BT_PROPERTY_UUIDS_LE
+ : BT_PROPERTY_UUIDS,
+ static_cast<int>(Uuid::kNumBytes128 * uuids.size()), (void*)property_value.data()});
/* Also write this to the NVRAM */
bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop[0]);
@@ -1817,8 +1823,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
* send them with rest of SDP results in on_service_discovery_results */
return;
} else {
- if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED &&
- com::android::bluetooth::flags::bta_dm_discover_both()) {
+ if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED) {
/* Don't report services yet, they will be reported together once SDP
* finishes. */
log::info("will report services later, with SDP results {}", bd_addr);
@@ -1826,6 +1831,32 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid
}
}
+ if (!com::android::bluetooth::flags::separate_service_storage()) {
+ /* Send the event to the BTIF */
+ GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
+ prop.size(), prop.data());
+ return;
+ }
+
+ std::set<Uuid> bredr_uuids;
+ // Look up UUIDs using pseudo address (either RPA or static address)
+ btif_merge_existing_uuids(bd_addr, &bredr_uuids);
+ if (bd_addr != static_addr_copy) {
+ // Look up UUID using static address, if different than sudo address
+ btif_merge_existing_uuids(static_addr_copy, &bredr_uuids);
+ }
+
+ std::vector<uint8_t> bredr_property_value;
+
+ for (auto& uuid : bredr_uuids) {
+ auto uuid_128bit = uuid.To128BitBE();
+ bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+
+ prop.push_back(bt_property_t{BT_PROPERTY_UUIDS,
+ static_cast<int>(Uuid::kNumBytes128 * bredr_uuids.size()),
+ (void*)bredr_property_value.data()});
+
/* Send the event to the BTIF */
GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr,
prop.size(), prop.data());
@@ -2517,6 +2548,9 @@ void btif_dm_cancel_bond(const RawAddress bd_addr) {
** 2. special handling for HID devices
*/
if (is_bonding_or_sdp()) {
+ // clear sdp_attempts
+ pairing_cb.sdp_attempts = 0;
+
if (com::android::bluetooth::flags::ignore_unrelated_cancel_bond() &&
(pairing_cb.bd_addr != bd_addr)) {
log::warn("Ignoring bond cancel for unrelated device: {} pairing: {}", bd_addr,
@@ -3896,6 +3930,30 @@ void btif_dm_clear_filter_accept_list() { BTA_DmClearFilterAcceptList(); }
void btif_dm_disconnect_all_acls() { BTA_DmDisconnectAllAcls(); }
+void btif_dm_disconnect_acl(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
+ log::debug(" {}, transport {}", bd_addr, transport);
+
+ if (transport == BT_TRANSPORT_LE || transport == BT_TRANSPORT_AUTO) {
+ uint16_t acl_handle =
+ get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_LE);
+
+ log::debug("{}, le_acl_handle: {:#x}", bd_addr, acl_handle);
+ if (acl_handle != HCI_INVALID_HANDLE) {
+ acl_disconnect_from_handle(acl_handle, HCI_ERR_PEER_USER, "bt_btif_dm disconnect");
+ }
+ }
+
+ if (transport == BT_TRANSPORT_BR_EDR || transport == BT_TRANSPORT_AUTO) {
+ uint16_t acl_handle =
+ get_btm_client_interface().peer.BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_BR_EDR);
+
+ log::debug("{}, bredr_acl_handle: {:#x}", bd_addr, acl_handle);
+ if (acl_handle != HCI_INVALID_HANDLE) {
+ acl_disconnect_from_handle(acl_handle, HCI_ERR_PEER_USER, "bt_btif_dm disconnect");
+ }
+ }
+}
+
void btif_dm_le_rand(bluetooth::hci::LeRandCallback callback) { BTA_DmLeRand(std::move(callback)); }
void btif_dm_set_event_filter_connection_setup_all_devices() {
diff --git a/system/btif/src/btif_gatt_client.cc b/system/btif/src/btif_gatt_client.cc
index 075ed1b20d..c77af0e13d 100644
--- a/system/btif/src/btif_gatt_client.cc
+++ b/system/btif/src/btif_gatt_client.cc
@@ -266,13 +266,13 @@ void btm_read_rssi_cb(void* p_void) {
* Client API Functions
******************************************************************************/
-static bt_status_t btif_gattc_register_app(const Uuid& uuid, bool eatt_support) {
+static bt_status_t btif_gattc_register_app(const Uuid& uuid, const char* name, bool eatt_support) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(
- [](const Uuid& uuid, bool eatt_support) {
+ [](const Uuid& uuid, const std::string& name, bool eatt_support) {
BTA_GATTC_AppRegister(
- bta_gattc_cback,
+ name, bta_gattc_cback,
base::Bind(
[](const Uuid& uuid, uint8_t client_id, uint8_t status) {
do_in_jni_thread(Bind(
@@ -286,7 +286,7 @@ static bt_status_t btif_gattc_register_app(const Uuid& uuid, bool eatt_support)
uuid),
eatt_support);
},
- uuid, eatt_support));
+ uuid, std::string(name), eatt_support));
}
static void btif_gattc_unregister_app_impl(int client_if) { BTA_GATTC_AppDeregister(client_if); }
diff --git a/system/btif/src/btif_hci_vs.cc b/system/btif/src/btif_hci_vs.cc
index 35b8b9cdfb..90af5cb05d 100644
--- a/system/btif/src/btif_hci_vs.cc
+++ b/system/btif/src/btif_hci_vs.cc
@@ -56,7 +56,7 @@ static void CommandStatusOrCompleteCallback(BluetoothHciVendorSpecificCallbacks*
static void EventCallback(BluetoothHciVendorSpecificCallbacks* callbacks,
VendorSpecificEventView view) {
- const uint8_t aosp_reserved_codes_range[] = {0x50, 0x60};
+ const uint8_t aosp_reserved_codes_range[] = {0x52, 0x60};
auto code = static_cast<uint8_t>(view.GetSubeventCode());
if (code >= aosp_reserved_codes_range[0] && code < aosp_reserved_codes_range[1]) {
return;
diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc
index 7e6fa5fef3..a8caa39452 100644
--- a/system/btif/src/btif_hf.cc
+++ b/system/btif/src/btif_hf.cc
@@ -67,6 +67,7 @@
#include "stack/include/bt_uuid16.h"
#include "stack/include/btm_client_interface.h"
#include "stack/include/btm_log_history.h"
+#include "stack/include/btm_sec_api.h"
#include "types/raw_address.h"
#define PRIVATE_CELL(number) \
@@ -471,7 +472,9 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) {
log_counter_metrics_btif(
android::bluetooth::CodePathCounterKeyEnum::HFP_SELF_INITIATED_AG_FAILED, 1);
btif_queue_advance();
- DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(connected_bda, IOT_CONF_KEY_HFP_SLC_CONN_FAIL_COUNT);
+ if (btm_sec_is_a_bonded_dev(connected_bda)) {
+ DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(connected_bda, IOT_CONF_KEY_HFP_SLC_CONN_FAIL_COUNT);
+ }
}
break;
case BTA_AG_CLOSE_EVT: {
diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc
index 5e4d2077cb..8fc0beed06 100644
--- a/system/btif/src/btif_hh.cc
+++ b/system/btif/src/btif_hh.cc
@@ -937,11 +937,10 @@ void btif_hh_disconnected(const RawAddress& addr, tBT_TRANSPORT transport) {
if (p_dev == nullptr) {
return;
}
- if (com::android::bluetooth::flags::allow_switching_hid_and_hogp()) {
- btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(link_spec);
- if (added_dev == nullptr || !added_dev->reconnect_allowed) {
- return;
- }
+
+ btif_hh_added_device_t* added_dev = btif_hh_find_added_dev(link_spec);
+ if (added_dev == nullptr || !added_dev->reconnect_allowed) {
+ return;
}
log::debug("Rearm HoGP reconnection for {}", addr);
diff --git a/system/btif/src/btif_profile_storage.cc b/system/btif/src/btif_profile_storage.cc
index e6d671a369..fba4aaf5cc 100644
--- a/system/btif/src/btif_profile_storage.cc
+++ b/system/btif/src/btif_profile_storage.cc
@@ -646,6 +646,21 @@ void btif_storage_leaudio_update_handles_bin(const RawAddress& addr) {
}
}
+/** Store GMAP information */
+void btif_storage_leaudio_update_gmap_bin(const RawAddress& addr) {
+ std::vector<uint8_t> gmap;
+
+ if (LeAudioClient::GetGmapForStorage(addr, gmap)) {
+ do_in_jni_thread(Bind(
+ [](const RawAddress& bd_addr, std::vector<uint8_t> gmap) {
+ auto bdstr = bd_addr.ToString();
+ btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_LEAUDIO_GMAP_BIN, gmap.data(),
+ gmap.size());
+ },
+ addr, std::move(gmap)));
+ }
+}
+
/** Store PACs information */
void btif_storage_leaudio_update_pacs_bin(const RawAddress& addr) {
std::vector<uint8_t> sink_pacs;
@@ -794,10 +809,16 @@ void btif_storage_load_bonded_leaudio() {
btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN, ases.data(), &buffer_size);
}
+ buffer_size = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_LEAUDIO_GMAP_BIN);
+ std::vector<uint8_t> gmap(buffer_size);
+ if (buffer_size > 0) {
+ btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_GMAP_BIN, gmap.data(), &buffer_size);
+ }
+
do_in_main_thread(Bind(&LeAudioClient::AddFromStorage, bd_addr, autoconnect,
sink_audio_location, source_audio_location, sink_supported_context_type,
source_supported_context_type, std::move(handles), std::move(sink_pacs),
- std::move(source_pacs), std::move(ases)));
+ std::move(source_pacs), std::move(ases), std::move(gmap)));
}
}
@@ -806,6 +827,7 @@ void btif_storage_leaudio_clear_service_data(const RawAddress& address) {
btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_HANDLES_BIN);
btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SINK_PACS_BIN);
btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN);
+ btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_GMAP_BIN);
}
/** Remove the Le Audio device from storage */
diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc
index de2188c5d8..d23f012249 100644
--- a/system/btif/src/btif_sock_rfc.cc
+++ b/system/btif/src/btif_sock_rfc.cc
@@ -196,7 +196,7 @@ static rfc_slot_t* find_rfc_slot_by_pending_sdp(void) {
static bool is_requesting_sdp(void) {
for (size_t i = 0; i < ARRAY_SIZE(rfc_slots); ++i) {
if (rfc_slots[i].id && rfc_slots[i].f.doing_sdp_request) {
- log::info("slot id {} is doing sdp request", rfc_slots[i].id);
+ log::info("slot_id {} is doing sdp request", rfc_slots[i].id);
return true;
}
}
@@ -458,7 +458,7 @@ bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const Uuid* service_uu
if (!service_uuid || service_uuid->IsEmpty()) {
tBTA_JV_STATUS ret = BTA_JvRfcommConnect(slot->security, slot->scn, slot->addr, rfcomm_cback,
- slot->id, RfcommCfgInfo{});
+ slot->id, RfcommCfgInfo{}, slot->app_uid);
if (ret != tBTA_JV_STATUS::SUCCESS) {
log::error("unable to initiate RFCOMM connection. status:{}, scn:{}, bd_addr:{}",
bta_jv_status_text(ret), slot->scn, slot->addr);
@@ -467,7 +467,7 @@ bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const Uuid* service_uu
}
if (!send_app_scn(slot)) {
- log::error("send_app_scn() failed, closing slot->id:{}", slot->id);
+ log::error("send_app_scn() failed, closing slot_id:{}", slot->id);
cleanup_rfc_slot(slot);
return BT_STATUS_SOCKET_ERROR;
}
@@ -536,7 +536,7 @@ static void cleanup_rfc_slot(rfc_slot_t* slot) {
close(slot->fd);
log::info(
"disconnected from RFCOMM socket connections for device: {}, scn: {}, "
- "app_uid: {}, id: {}, socket_id: {}",
+ "app_uid: {}, slot_id: {}, socket_id: {}",
slot->addr, slot->scn, slot->app_uid, slot->id, slot->socket_id);
btif_sock_connection_logger(
slot->addr, slot->id, BTSOCK_RFCOMM, SOCKET_CONNECTION_STATE_DISCONNECTED,
@@ -586,7 +586,7 @@ static bool send_app_scn(rfc_slot_t* slot) {
// already sent, just return success.
return true;
}
- log::debug("Sending scn for slot {}. bd_addr:{}", slot->id, slot->addr);
+ log::debug("Sending scn for slot_id {}. bd_addr:{}", slot->id, slot->addr);
slot->scn_notified = true;
return sock_send_all(slot->fd, (const uint8_t*)&slot->scn, sizeof(slot->scn)) ==
sizeof(slot->scn);
@@ -615,10 +615,10 @@ static void on_cl_rfc_init(tBTA_JV_RFCOMM_CL_INIT* p_init, uint32_t id) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found. p_init->status={}", id,
+ log::error("RFCOMM slot_id {} not found. p_init->status={}", id,
bta_jv_status_text(p_init->status));
} else if (p_init->status != tBTA_JV_STATUS::SUCCESS) {
- log::warn("INIT unsuccessful, status {}. Cleaning up slot with id {}",
+ log::warn("INIT unsuccessful, status {}. Cleaning up slot_id {}",
bta_jv_status_text(p_init->status), slot->id);
cleanup_rfc_slot(slot);
} else {
@@ -630,10 +630,10 @@ static void on_srv_rfc_listen_started(tBTA_JV_RFCOMM_START* p_start, uint32_t id
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found", id);
+ log::error("RFCOMM slot_id {} not found", id);
return;
} else if (p_start->status != tBTA_JV_STATUS::SUCCESS) {
- log::warn("START unsuccessful, status {}. Cleaning up slot with id {}",
+ log::warn("START unsuccessful, status {}. Cleaning up slot_id {}",
bta_jv_status_text(p_start->status), slot->id);
cleanup_rfc_slot(slot);
return;
@@ -702,7 +702,7 @@ static uint32_t on_srv_rfc_connect(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id)
rfc_slot_t* accept_rs;
rfc_slot_t* srv_rs = find_rfc_slot_by_id(id);
if (!srv_rs) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return 0;
}
@@ -719,7 +719,7 @@ static uint32_t on_srv_rfc_connect(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id)
log::info(
"connected to RFCOMM socket connections for device: {}, scn: {}, "
- "app_uid: {}, id: {}, socket_id: {}",
+ "app_uid: {}, slot_id: {}, socket_id: {}",
accept_rs->addr, accept_rs->scn, accept_rs->app_uid, id, accept_rs->socket_id);
btif_sock_connection_logger(accept_rs->addr, accept_rs->id, BTSOCK_RFCOMM,
SOCKET_CONNECTION_STATE_CONNECTED,
@@ -779,12 +779,12 @@ static void on_cli_rfc_connect(tBTA_JV_RFCOMM_OPEN* p_open, uint32_t id) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return;
}
if (p_open->status != tBTA_JV_STATUS::SUCCESS) {
- log::warn("CONNECT unsuccessful, status {}. Cleaning up slot with id {}",
+ log::warn("CONNECT unsuccessful, status {}. Cleaning up slot_id {}",
bta_jv_status_text(p_open->status), slot->id);
cleanup_rfc_slot(slot);
return;
@@ -906,7 +906,7 @@ static void on_rfc_close(tBTA_JV_RFCOMM_CLOSE* /* p_close */, uint32_t id) {
static void on_rfc_write_done(tBTA_JV_RFCOMM_WRITE* p, uint32_t id) {
if (p->status != tBTA_JV_STATUS::SUCCESS) {
- log::error("error writing to RFCOMM socket with slot {}.", p->req_id);
+ log::error("error writing to RFCOMM socket, req_id:{}.", p->req_id);
return;
}
@@ -915,7 +915,7 @@ static void on_rfc_write_done(tBTA_JV_RFCOMM_WRITE* p, uint32_t id) {
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return;
}
app_uid = slot->app_uid;
@@ -931,7 +931,7 @@ static void on_rfc_outgoing_congest(tBTA_JV_RFCOMM_CONG* p, uint32_t id) {
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return;
}
@@ -946,39 +946,39 @@ static uint32_t rfcomm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t rfcomm
switch (event) {
case BTA_JV_RFCOMM_START_EVT:
- log::info("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::info("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
on_srv_rfc_listen_started(&p_data->rfc_start, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_CL_INIT_EVT:
- log::info("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::info("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
on_cl_rfc_init(&p_data->rfc_cl_init, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_OPEN_EVT:
- log::info("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::info("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
BTA_JvSetPmProfile(p_data->rfc_open.handle, BTA_JV_PM_ID_1, BTA_JV_CONN_OPEN);
on_cli_rfc_connect(&p_data->rfc_open, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_SRV_OPEN_EVT:
- log::info("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::info("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
BTA_JvSetPmProfile(p_data->rfc_srv_open.handle, BTA_JV_PM_ALL, BTA_JV_CONN_OPEN);
id = on_srv_rfc_connect(&p_data->rfc_srv_open, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_CLOSE_EVT:
- log::info("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::info("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
on_rfc_close(&p_data->rfc_close, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_WRITE_EVT:
- log::verbose("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::verbose("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
on_rfc_write_done(&p_data->rfc_write, rfcomm_slot_id);
break;
case BTA_JV_RFCOMM_CONG_EVT:
- log::verbose("handling {}, rfcomm_slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::verbose("handling {}, slot_id:{}", bta_jv_event_text(event), rfcomm_slot_id);
on_rfc_outgoing_congest(&p_data->rfc_cong, rfcomm_slot_id);
break;
@@ -987,20 +987,20 @@ static uint32_t rfcomm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t rfcomm
break;
default:
- log::warn("unhandled event {}, slot id: {}", bta_jv_event_text(event), rfcomm_slot_id);
+ log::warn("unhandled event {}, slot_id: {}", bta_jv_event_text(event), rfcomm_slot_id);
break;
}
return id;
}
static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
- log::info("handling event:{}, id:{}", bta_jv_event_text(event), id);
+ log::info("handling event:{}, slot_id:{}", bta_jv_event_text(event), id);
switch (event) {
case BTA_JV_GET_SCN_EVT: {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* rs = find_rfc_slot_by_id(id);
if (!rs) {
- log::error("RFCOMM slot with id {} not found. event:{}", id, bta_jv_event_text(event));
+ log::error("RFCOMM slot with slot_id {} not found. event:{}", id, bta_jv_event_text(event));
break;
}
if (p_data->scn == 0) {
@@ -1044,7 +1044,8 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
}
}
// now start the rfcomm server after sdp & channel # assigned
- BTA_JvRfcommStartServer(rs->security, rs->scn, MAX_RFC_SESSION, rfcomm_cback, rs->id, cfg);
+ BTA_JvRfcommStartServer(rs->security, rs->scn, MAX_RFC_SESSION, rfcomm_cback, rs->id, cfg,
+ rs->app_uid);
}
break;
}
@@ -1060,7 +1061,7 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found. event:{}", id, bta_jv_event_text(event));
+ log::error("RFCOMM slot_id {} not found. event:{}", id, bta_jv_event_text(event));
break;
}
@@ -1085,7 +1086,7 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
}
// Start the rfcomm server after sdp & channel # assigned.
BTA_JvRfcommStartServer(slot->security, slot->scn, MAX_RFC_SESSION, rfcomm_cback, slot->id,
- cfg);
+ cfg, slot->app_uid);
break;
}
@@ -1103,7 +1104,7 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
}
default:
- log::debug("unhandled event:{}, slot id:{}", bta_jv_event_text(event), id);
+ log::debug("unhandled event:{}, slot_id:{}", bta_jv_event_text(event), id);
break;
}
}
@@ -1111,18 +1112,18 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) {
static void handle_discovery_comp(tBTA_JV_STATUS status, int scn, uint32_t id) {
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found. event: BTA_JV_DISCOVERY_COMP_EVT", id);
+ log::error("RFCOMM slot_id {} not found. event: BTA_JV_DISCOVERY_COMP_EVT", id);
return;
}
if (!slot->f.doing_sdp_request) {
- log::error("SDP response returned but RFCOMM slot {} did not request SDP record.", id);
+ log::error("SDP response returned but RFCOMM slot_id {} did not request SDP record.", id);
return;
}
if (status != tBTA_JV_STATUS::SUCCESS || !scn) {
log::error(
- "SDP service discovery completed for slot id: {} with the result "
+ "SDP service discovery completed for slot_id: {} with the result "
"status: {}, scn: {}",
id, bta_jv_status_text(status), scn);
cleanup_rfc_slot(slot);
@@ -1143,12 +1144,9 @@ static void handle_discovery_comp(tBTA_JV_STATUS status, int scn, uint32_t id) {
}
}
- if (BTA_JvRfcommConnect(slot->security, scn, slot->addr, rfcomm_cback, slot->id, cfg) !=
- tBTA_JV_STATUS::SUCCESS) {
- log::warn(
- "BTA_JvRfcommConnect() returned BTA_JV_FAILURE for RFCOMM slot with "
- "id: {}",
- id);
+ if (BTA_JvRfcommConnect(slot->security, scn, slot->addr, rfcomm_cback, slot->id, cfg,
+ slot->app_uid) != tBTA_JV_STATUS::SUCCESS) {
+ log::warn("BTA_JvRfcommConnect() returned BTA_JV_FAILURE for RFCOMM slot_id:{}", id);
cleanup_rfc_slot(slot);
return;
}
@@ -1157,7 +1155,7 @@ static void handle_discovery_comp(tBTA_JV_STATUS status, int scn, uint32_t id) {
slot->f.doing_sdp_request = false;
if (!send_app_scn(slot)) {
- log::warn("send_app_scn() failed, closing slot->id {}", slot->id);
+ log::warn("send_app_scn() failed, closing slot_id {}", slot->id);
cleanup_rfc_slot(slot);
return;
}
@@ -1232,7 +1230,7 @@ static bool flush_incoming_que_on_wr_signal(rfc_slot_t* slot) {
static bool btsock_rfc_read_signaled_on_connected_socket(int /* fd */, int flags, uint32_t /* id */,
rfc_slot_t* slot) {
if (!slot->f.connected) {
- log::error("socket signaled for read while disconnected, slot: {}, channel: {}", slot->id,
+ log::error("socket signaled for read while disconnected, slot_id: {}, channel: {}", slot->id,
slot->scn);
return false;
}
@@ -1259,7 +1257,7 @@ static bool btsock_rfc_read_signaled_on_listen_socket(int fd, int /* flags */, u
return false;
}
slot->is_accepting = accept_signal.is_accepting;
- log::info("Server socket: {}, is_accepting: {}", slot->id, slot->is_accepting);
+ log::info("Server socket slot_id: {}, is_accepting: {}", slot->id, slot->is_accepting);
}
return true;
}
@@ -1269,7 +1267,7 @@ static void btsock_rfc_signaled_flagged(int fd, int flags, uint32_t id) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::warn("RFCOMM slot with id {} not found.", id);
+ log::warn("RFCOMM slot_id {} not found.", id);
return;
}
@@ -1293,7 +1291,7 @@ static void btsock_rfc_signaled_flagged(int fd, int flags, uint32_t id) {
if (!slot->f.connected || !flush_incoming_que_on_wr_signal(slot)) {
log::error(
"socket signaled for write while disconnected (or write failure), "
- "slot: {}, channel: {}",
+ "slot_id: {}, channel: {}",
slot->id, slot->scn);
need_close = true;
}
@@ -1334,7 +1332,7 @@ void btsock_rfc_signaled(int fd, int flags, uint32_t id) {
BTA_JvRfcommWrite(slot->rfc_handle, slot->id);
}
} else {
- log::error("socket signaled for read while disconnected, slot: {}, channel: {}", slot->id,
+ log::error("socket signaled for read while disconnected, slot_id: {}, channel: {}", slot->id,
slot->scn);
need_close = true;
}
@@ -1345,7 +1343,7 @@ void btsock_rfc_signaled(int fd, int flags, uint32_t id) {
if (!slot->f.connected || !flush_incoming_que_on_wr_signal(slot)) {
log::error(
"socket signaled for write while disconnected (or write failure), "
- "slot: {}, channel: {}",
+ "slot_id: {}, channel: {}",
slot->id, slot->scn);
need_close = true;
}
@@ -1371,7 +1369,7 @@ int bta_co_rfc_data_incoming(uint32_t id, BT_HDR* p_buf) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return 0;
}
@@ -1411,7 +1409,7 @@ int bta_co_rfc_data_outgoing_size(uint32_t id, int* size) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return false;
}
@@ -1429,7 +1427,7 @@ int bta_co_rfc_data_outgoing(uint32_t id, uint8_t* buf, uint16_t size) {
std::unique_lock<std::recursive_mutex> lock(slot_lock);
rfc_slot_t* slot = find_rfc_slot_by_id(id);
if (!slot) {
- log::error("RFCOMM slot with id {} not found.", id);
+ log::error("RFCOMM slot_id {} not found.", id);
return false;
}
diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc
index e51756483c..4704362f2c 100644
--- a/system/btif/src/btif_storage.cc
+++ b/system/btif/src/btif_storage.cc
@@ -47,6 +47,7 @@
#include "btif/include/btif_api.h"
#include "btif/include/btif_config.h"
+#include "btif/include/btif_dm.h"
#include "btif/include/btif_util.h"
#include "btif/include/core_callbacks.h"
#include "btif/include/stack_manager_t.h"
@@ -179,15 +180,18 @@ static bool prop2cfg(const RawAddress* remote_bd_addr, bt_property_t* prop) {
case BT_PROPERTY_TYPE_OF_DEVICE:
btif_config_set_int(bdstr, BTIF_STORAGE_KEY_DEV_TYPE, *reinterpret_cast<int*>(prop->val));
break;
- case BT_PROPERTY_UUIDS: {
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_UUIDS_LE: {
std::string val;
size_t cnt = (prop->len) / sizeof(Uuid);
for (size_t i = 0; i < cnt; i++) {
val += (reinterpret_cast<Uuid*>(prop->val) + i)->ToString() + " ";
}
- btif_config_set_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, val);
- break;
- }
+ std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE
+ : BTIF_STORAGE_KEY_REMOTE_SERVICE;
+ btif_config_set_str(bdstr, key, val);
+ } break;
+
case BT_PROPERTY_REMOTE_VERSION_INFO: {
bt_remote_version_t* info = reinterpret_cast<bt_remote_version_t*>(prop->val);
@@ -300,10 +304,15 @@ static bool cfg2prop(const RawAddress* remote_bd_addr, bt_property_t* prop) {
reinterpret_cast<int*>(prop->val));
}
break;
- case BT_PROPERTY_UUIDS: {
+ case BT_PROPERTY_UUIDS:
+ case BT_PROPERTY_UUIDS_LE: {
char value[1280];
int size = sizeof(value);
- if (btif_config_get_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, value, &size)) {
+
+ std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE
+ : BTIF_STORAGE_KEY_REMOTE_SERVICE;
+
+ if (btif_config_get_str(bdstr, key, value, &size)) {
Uuid* p_uuid = reinterpret_cast<Uuid*>(prop->val);
size_t num_uuids = btif_split_uuids_string(value, p_uuid, BT_MAX_NUM_UUIDS);
prop->len = num_uuids * sizeof(Uuid);
@@ -938,13 +947,14 @@ bt_status_t btif_storage_load_bonded_devices(void) {
uint32_t i = 0;
bt_property_t adapter_props[6];
uint32_t num_props = 0;
- bt_property_t remote_properties[10];
+ bt_property_t remote_properties[11];
RawAddress addr;
bt_bdname_t name, alias, model_name;
bt_scan_mode_t mode;
uint32_t disc_timeout;
Uuid local_uuids[BT_MAX_NUM_UUIDS];
Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ Uuid remote_uuids_le[BT_MAX_NUM_UUIDS];
bt_status_t status;
remove_devices_with_sample_ltk();
@@ -1026,10 +1036,16 @@ bt_status_t btif_storage_load_bonded_devices(void) {
sizeof(devtype), &remote_properties[num_props]);
num_props++;
- btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, remote_uuids,
+ btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, &remote_uuids,
sizeof(remote_uuids), &remote_properties[num_props]);
num_props++;
+ if (com::android::bluetooth::flags::separate_service_storage()) {
+ btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS_LE, &remote_uuids_le,
+ sizeof(remote_uuids_le), &remote_properties[num_props]);
+ num_props++;
+ }
+
// Floss needs appearance for metrics purposes
uint16_t appearance = 0;
if (btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_APPEARANCE, &appearance,
@@ -1438,6 +1454,48 @@ void btif_storage_remove_gatt_cl_db_hash(const RawAddress& bd_addr) {
bd_addr));
}
+// TODO(b/369381361) Remove this function after all devices are migrated
+void btif_storage_migrate_services() {
+ for (const auto& mac_address : btif_config_get_paired_devices()) {
+ auto addr_str = mac_address.ToString();
+
+ int device_type = BT_DEVICE_TYPE_UNKNOWN;
+ btif_config_get_int(addr_str, BTIF_STORAGE_KEY_DEV_TYPE, &device_type);
+
+ if ((device_type == BT_DEVICE_TYPE_BREDR) ||
+ btif_config_exist(addr_str, BTIF_STORAGE_KEY_REMOTE_SERVICE_LE)) {
+ /* Classic only, or already migrated entries don't need migration */
+ continue;
+ }
+
+ bt_property_t remote_uuids_prop;
+ Uuid remote_uuids[BT_MAX_NUM_UUIDS];
+ BTIF_STORAGE_FILL_PROPERTY(&remote_uuids_prop, BT_PROPERTY_UUIDS, sizeof(remote_uuids),
+ remote_uuids);
+ btif_storage_get_remote_device_property(&mac_address, &remote_uuids_prop);
+
+ log::info("Will migrate Services => ServicesLe for {}", mac_address.ToStringForLogging());
+
+ std::vector<uint8_t> property_value;
+ for (auto& uuid : remote_uuids) {
+ if (!btif_is_interesting_le_service(uuid)) {
+ continue;
+ }
+
+ log::info("interesting LE service: {}", uuid);
+ auto uuid_128bit = uuid.To128BitBE();
+ property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end());
+ }
+
+ bt_property_t le_uuids_prop{BT_PROPERTY_UUIDS_LE, static_cast<int>(property_value.size()),
+ (void*)property_value.data()};
+
+ /* Write LE services to storage */
+ btif_storage_set_remote_device_property(&mac_address, &le_uuids_prop);
+ log::info("Migration finished for {}", mac_address.ToStringForLogging());
+ }
+}
+
void btif_debug_linkkey_type_dump(int fd) {
dprintf(fd, "\nLink Key Types:\n");
for (const auto& bd_addr : btif_config_get_paired_devices()) {
diff --git a/system/btif/src/btif_util.cc b/system/btif/src/btif_util.cc
index 85c0069eaa..c6cf544d4a 100644
--- a/system/btif/src/btif_util.cc
+++ b/system/btif/src/btif_util.cc
@@ -129,6 +129,7 @@ std::string dump_property_type(bt_property_type_t type) {
CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT);
CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_BONDED_DEVICES);
CASE_RETURN_STRING(BT_PROPERTY_REMOTE_FRIENDLY_NAME);
+ CASE_RETURN_STRING(BT_PROPERTY_UUIDS_LE);
default:
RETURN_UNKNOWN_TYPE_STRING(bt_property_type_t, type);
}
diff --git a/system/btif/src/stack_manager.cc b/system/btif/src/stack_manager.cc
index 288cbfcdf3..4ba403586e 100644
--- a/system/btif/src/stack_manager.cc
+++ b/system/btif/src/stack_manager.cc
@@ -102,9 +102,6 @@ static_assert(BTA_HH_INCLUDED,
// TODO(b/369381361) Enfore -Wmissing-prototypes
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-void BTA_dm_on_hw_on();
-void BTA_dm_on_hw_off();
-
using bluetooth::common::MessageLoopThread;
using bluetooth::log::error;
using bluetooth::log::fatal;
diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc
index 66881a92d8..1bffc9ce3a 100644
--- a/system/btif/test/btif_core_test.cc
+++ b/system/btif/test/btif_core_test.cc
@@ -320,6 +320,7 @@ TEST_F(BtifUtilsTest, dump_property_type) {
"BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT"),
std::make_pair(BT_PROPERTY_ADAPTER_BONDED_DEVICES, "BT_PROPERTY_ADAPTER_BONDED_DEVICES"),
std::make_pair(BT_PROPERTY_REMOTE_FRIENDLY_NAME, "BT_PROPERTY_REMOTE_FRIENDLY_NAME"),
+ std::make_pair(BT_PROPERTY_UUIDS_LE, "BT_PROPERTY_UUIDS_LE"),
};
for (const auto& type : types) {
EXPECT_TRUE(dump_property_type(type.first).starts_with(type.second));
diff --git a/system/btif/test/btif_dm_test.cc b/system/btif/test/btif_dm_test.cc
index 5d7595b1c5..068740472f 100644
--- a/system/btif/test/btif_dm_test.cc
+++ b/system/btif/test/btif_dm_test.cc
@@ -23,8 +23,9 @@
#include <memory>
#include "bta/include/bta_api_data_types.h"
-#include "btif/include/btif_dm.h"
#include "btif/include/mock_core_callbacks.h"
+#include "main/shim/entry.h"
+#include "main/shim/shim.h"
#include "main/shim/stack.h"
#include "module.h"
#include "stack/include/bt_dev_class.h"
@@ -116,21 +117,47 @@ TEST_F(BtifDmWithUidTest, bta_energy_info_cb__with_uid) {
ASSERT_TRUE(invoke_energy_info_cb_entered);
}
+// Mock implementation for GetStorage()
+static bluetooth::storage::StorageModule* s_StorageModule = nullptr;
+bluetooth::storage::StorageModule* bluetooth::shim::GetStorage() { return s_StorageModule; }
+
+bluetooth::os::Handler* bluetooth::shim::GetGdShimHandler() { return nullptr; }
+bluetooth::hci::LeAdvertisingManager* bluetooth::shim::GetAdvertising() { return nullptr; }
+bluetooth::hci::ControllerInterface* bluetooth::shim::GetController() { return nullptr; }
+bluetooth::hci::HciInterface* bluetooth::shim::GetHciLayer() { return nullptr; }
+bluetooth::hci::RemoteNameRequestModule* bluetooth::shim::GetRemoteNameRequest() { return nullptr; }
+bluetooth::hci::LeScanningManager* bluetooth::shim::GetScanning() { return nullptr; }
+bluetooth::hci::DistanceMeasurementManager* bluetooth::shim::GetDistanceMeasurementManager() {
+ return nullptr;
+}
+bluetooth::hal::SnoopLogger* bluetooth::shim::GetSnoopLogger() { return nullptr; }
+bluetooth::lpp::LppOffloadInterface* bluetooth::shim::GetLppOffloadManager() { return nullptr; }
+bluetooth::hci::AclManager* bluetooth::shim::GetAclManager() { return nullptr; }
+bluetooth::metrics::CounterMetrics* bluetooth::shim::GetCounterMetrics() { return nullptr; }
+bluetooth::hci::MsftExtensionManager* bluetooth::shim::GetMsftExtensionManager() { return nullptr; }
+
+bool bluetooth::shim::is_gd_stack_started_up() { return s_StorageModule != nullptr; }
+
class BtifDmWithStackTest : public BtifDmTest {
protected:
void SetUp() override {
BtifDmTest::SetUp();
- modules_.add<bluetooth::storage::StorageModule>();
- bluetooth::shim::Stack::GetInstance()->StartModuleStack(
- &modules_,
- new bluetooth::os::Thread("gd_stack_thread", bluetooth::os::Thread::Priority::NORMAL));
+ thread_ = new bluetooth::os::Thread("gd_stack_thread", bluetooth::os::Thread::Priority::NORMAL);
+ storage_module_ = new bluetooth::storage::StorageModule(new bluetooth::os::Handler(thread_));
+ storage_module_->Start();
+ s_StorageModule = storage_module_;
}
void TearDown() override {
- bluetooth::shim::Stack::GetInstance()->Stop();
+ storage_module_->Stop();
+ s_StorageModule = nullptr;
+ delete storage_module_;
+ delete thread_;
BtifDmTest::TearDown();
}
- bluetooth::ModuleList modules_;
+
+ bluetooth::os::Thread* thread_;
+ bluetooth::storage::StorageModule* storage_module_;
};
TEST_F_WITH_FLAGS(BtifDmWithStackTest, btif_dm_search_services_evt__BTA_DM_NAME_READ_EVT) {
diff --git a/system/common/Android.bp b/system/common/Android.bp
index f3f2a619ca..6fe25539d9 100644
--- a/system/common/Android.bp
+++ b/system/common/Android.bp
@@ -49,9 +49,7 @@ cc_library_static {
"libcrypto",
"libcutils",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
static_libs: [
diff --git a/system/device/Android.bp b/system/device/Android.bp
index eb71e3f414..d5176640c0 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -26,9 +26,7 @@ cc_library_static {
"src/esco_parameters.cc",
"src/interop.cc",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
static_libs: [
@@ -97,17 +95,14 @@ cc_test {
"liblog",
],
static_libs: [
- "bluetooth_flags_c_lib",
"libbluetooth-types",
"libbluetooth_log",
"libbt-platform-protos-lite",
"libbtcore",
"libbtdevice",
"libchrome",
- "libflagtest",
"libgmock",
"libosi",
- "server_configurable_flags",
],
header_libs: ["libbluetooth_headers"],
}
diff --git a/system/device/src/device_iot_config.cc b/system/device/src/device_iot_config.cc
index 4513e6840a..f247eff0e6 100644
--- a/system/device/src/device_iot_config.cc
+++ b/system/device/src/device_iot_config.cc
@@ -21,7 +21,6 @@
#include "device/include/device_iot_config.h"
#include <bluetooth/log.h>
-#include <com_android_bluetooth_flags.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
@@ -49,10 +48,6 @@ alarm_t* config_timer;
using namespace bluetooth;
bool device_iot_config_has_section(const std::string& section) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -60,10 +55,6 @@ bool device_iot_config_has_section(const std::string& section) {
}
bool device_iot_config_exist(const std::string& section, const std::string& key) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -71,10 +62,6 @@ bool device_iot_config_exist(const std::string& section, const std::string& key)
}
bool device_iot_config_get_int(const std::string& section, const std::string& key, int& value) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -87,10 +74,6 @@ bool device_iot_config_get_int(const std::string& section, const std::string& ke
}
bool device_iot_config_set_int(const std::string& section, const std::string& key, int value) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -107,10 +90,6 @@ bool device_iot_config_set_int(const std::string& section, const std::string& ke
}
bool device_iot_config_int_add_one(const std::string& section, const std::string& key) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
int result = 0;
@@ -128,10 +107,6 @@ bool device_iot_config_int_add_one(const std::string& section, const std::string
}
bool device_iot_config_get_hex(const std::string& section, const std::string& key, int& value) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -159,10 +134,6 @@ bool device_iot_config_get_hex(const std::string& section, const std::string& ke
bool device_iot_config_set_hex(const std::string& section, const std::string& key, int value,
int byte_num) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
char value_str[32] = {0};
@@ -189,10 +160,6 @@ bool device_iot_config_set_hex(const std::string& section, const std::string& ke
bool device_iot_config_set_hex_if_greater(const std::string& section, const std::string& key,
int value, int byte_num) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
int stored_value = 0;
bool ret = device_iot_config_get_hex(section, key, stored_value);
if (ret && stored_value >= value) {
@@ -204,10 +171,6 @@ bool device_iot_config_set_hex_if_greater(const std::string& section, const std:
bool device_iot_config_get_str(const std::string& section, const std::string& key, char* value,
int* size_bytes) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(value != NULL, "assert failed: value != NULL");
log::assert_that(size_bytes != NULL, "assert failed: size_bytes != NULL");
@@ -227,10 +190,6 @@ bool device_iot_config_get_str(const std::string& section, const std::string& ke
bool device_iot_config_set_str(const std::string& section, const std::string& key,
const std::string& value) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -246,10 +205,6 @@ bool device_iot_config_set_str(const std::string& section, const std::string& ke
bool device_iot_config_get_bin(const std::string& section, const std::string& key, uint8_t* value,
size_t* length) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(value != NULL, "assert failed: value != NULL");
log::assert_that(length != NULL, "assert failed: length != NULL");
@@ -293,10 +248,6 @@ bool device_iot_config_get_bin(const std::string& section, const std::string& ke
}
size_t device_iot_config_get_bin_length(const std::string& section, const std::string& key) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return 0;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -312,10 +263,6 @@ size_t device_iot_config_get_bin_length(const std::string& section, const std::s
bool device_iot_config_set_bin(const std::string& section, const std::string& key,
const uint8_t* value, size_t length) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
const char* lookup = "0123456789abcdef";
log::assert_that(config != NULL, "assert failed: config != NULL");
@@ -350,10 +297,6 @@ bool device_iot_config_set_bin(const std::string& section, const std::string& ke
}
bool device_iot_config_remove(const std::string& section, const std::string& key) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return false;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
std::unique_lock<std::mutex> lock(config_lock);
@@ -361,10 +304,6 @@ bool device_iot_config_remove(const std::string& section, const std::string& key
}
void device_iot_config_flush(void) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(config_timer != NULL, "assert failed: config_timer != NULL");
@@ -376,10 +315,6 @@ void device_iot_config_flush(void) {
}
bool device_iot_config_clear(void) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return true;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(config_timer != NULL, "assert failed: config_timer != NULL");
@@ -400,10 +335,6 @@ bool device_iot_config_clear(void) {
}
void device_debug_iot_config_dump(int fd) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return;
- }
-
dprintf(fd, "\nBluetooth Iot Config:\n");
dprintf(fd, " Config Source: ");
diff --git a/system/device/src/device_iot_config_int.cc b/system/device/src/device_iot_config_int.cc
index 33b5f8106d..fa65e13096 100644
--- a/system/device/src/device_iot_config_int.cc
+++ b/system/device/src/device_iot_config_int.cc
@@ -21,7 +21,6 @@
#include "device_iot_config_int.h"
#include <bluetooth/log.h>
-#include <com_android_bluetooth_flags.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
@@ -184,10 +183,6 @@ EXPORT_SYMBOL module_t device_iot_config_module = {.name = DEVICE_IOT_CONFIG_MOD
.clean_up = device_iot_config_module_clean_up};
void device_iot_config_write(uint16_t event, UNUSED_ATTR char* p_param) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(config_timer != NULL, "assert failed: config_timer != NULL");
@@ -223,10 +218,6 @@ bool device_iot_config_has_key_value(const std::string& section, const std::stri
}
void device_iot_config_save_async(void) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return;
- }
-
log::assert_that(config != NULL, "assert failed: config != NULL");
log::assert_that(config_timer != NULL, "assert failed: config_timer != NULL");
@@ -235,10 +226,6 @@ void device_iot_config_save_async(void) {
}
int device_iot_config_get_device_num(const config_t& conf) {
- if (!com::android::bluetooth::flags::device_iot_config_logging()) {
- return 0;
- }
-
int devices = 0;
for (const auto& entry : conf.sections) {
diff --git a/system/device/src/interop.cc b/system/device/src/interop.cc
index 99c7941aa5..354a9e1a65 100644
--- a/system/device/src/interop.cc
+++ b/system/device/src/interop.cc
@@ -55,7 +55,7 @@ using namespace bluetooth;
#ifdef __ANDROID__
static const char* INTEROP_DYNAMIC_FILE_PATH = "/data/misc/bluedroid/interop_database_dynamic.conf";
static const char* INTEROP_STATIC_FILE_PATH =
- "/apex/com.android.btservices/etc/bluetooth/interop_database.conf";
+ "/apex/com.android.bt/etc/bluetooth/interop_database.conf";
#elif TARGET_FLOSS
#include <base/files/file_util.h>
diff --git a/system/device/test/device_iot_config_test.cc b/system/device/test/device_iot_config_test.cc
index 4693c741d5..958a76bd7a 100644
--- a/system/device/test/device_iot_config_test.cc
+++ b/system/device/test/device_iot_config_test.cc
@@ -18,8 +18,6 @@
#include "device/include/device_iot_config.h"
-#include <com_android_bluetooth_flags.h>
-#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <sys/mman.h>
@@ -34,8 +32,6 @@
#include "test/mock/mock_osi_future.h"
#include "test/mock/mock_osi_properties.h"
-#define TEST_BT com::android::bluetooth::flags
-
using namespace bluetooth;
using namespace testing;
@@ -132,8 +128,7 @@ protected:
}
};
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_is_factory_reset,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_is_factory_reset) {
bool is_factory_reset = false;
config_t* config_new_return_value = NULL;
config_t* config_new_empty_return_value = NULL;
@@ -192,8 +187,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_no_config,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_no_config) {
test::mock::osi_config::config_new.body = [&](const char* /*filename*/) {
return std::unique_ptr<config_t>(nullptr);
};
@@ -217,8 +211,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_original,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_original) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -268,8 +261,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_backup,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_backup) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -322,8 +314,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_new_file,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_new_file) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -367,8 +358,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_version_invalid,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_version_invalid) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -418,9 +408,8 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
- test_device_iot_config_module_init_version_new_config_new_empty_success,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest,
+ test_device_iot_config_module_init_version_new_config_new_empty_success) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -495,9 +484,8 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
- test_device_iot_config_module_init_version_new_config_new_empty_fail,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest,
+ test_device_iot_config_module_init_version_new_config_new_empty_fail) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -572,9 +560,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
- test_device_iot_config_module_init_original_timestamp_null,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_original_timestamp_null) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -622,8 +608,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest,
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_alarm_new_fail,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_init_alarm_new_fail) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
config_t* config_new_return_value = NULL;
@@ -675,8 +660,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_init_
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_start_up,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_start_up) {
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
@@ -699,8 +683,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_start
test::mock::osi_config::config_new_empty.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_shutdown,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_shutdown) {
bool return_value;
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
@@ -745,8 +728,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_shutd
test::mock::osi_alarm::alarm_is_scheduled.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigModuleTest, test_device_iot_config_module_clean_up,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigModuleTest, test_device_iot_config_module_clean_up) {
bool return_value;
std::string enable_logging_property_get_value;
std::string factory_reset_property_get_value;
@@ -867,8 +849,7 @@ protected:
}
};
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_sections_sort_by_entry_key,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_sections_sort_by_entry_key) {
{
config_t conf;
device_iot_config_sections_sort_by_entry_key(conf, NULL);
@@ -925,8 +906,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_sections_sort_by_e
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_has_section,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_has_section) {
std::string actual_section, expected_section = "abc";
bool return_value = false;
@@ -958,8 +938,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_has_section,
test::mock::osi_config::config_has_section.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_exist,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_exist) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
bool return_value = false;
@@ -993,8 +972,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_exist,
test::mock::osi_config::config_has_key.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_has_key_value,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_has_key_value) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
std::string expected_value_str = "xyz", actual_value_str;
const std::string* actual_def_value = NULL;
@@ -1080,8 +1058,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_has_key_value,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_int,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_int) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
bool return_value = false;
int int_value = 0, new_value = 0xff;
@@ -1126,8 +1103,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_int,
test::mock::osi_config::config_get_int.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_get_int,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_get_int) {
const RawAddress peer_addr{};
std::string actual_section, actual_key, expected_section = "00:00:00:00:00:00",
expected_key = "def";
@@ -1174,8 +1150,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_get_int,
test::mock::osi_config::config_get_int.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_int,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_set_int) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
std::string string_return_value = "123456789";
std::string old_string_value = string_return_value;
@@ -1229,8 +1204,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_int,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_int,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_set_int) {
const RawAddress peer_addr{};
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
@@ -1286,8 +1260,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_int,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_int_add_one,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_int_add_one) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
int int_value = 0, get_default_value, set_value;
@@ -1393,8 +1366,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_int_add_one,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_int_add_one,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_int_add_one) {
const RawAddress peer_addr{};
std::string actual_section, actual_key, expected_section = "00:00:00:00:00:00",
expected_key = "def";
@@ -1501,8 +1473,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_int_add_one,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_hex,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_hex) {
std::string actual_section, actual_key, expected_section = "00:00:00:00:00:00",
expected_key = "def";
int int_value = 0;
@@ -1661,8 +1632,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_hex,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_get_hex,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_get_hex) {
const RawAddress peer_addr{};
std::string actual_section, actual_key, expected_section = "00:00:00:00:00:00",
expected_key = "def";
@@ -1818,8 +1788,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_get_hex,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_hex,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_set_hex) {
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
std::string string_return_value;
@@ -1966,8 +1935,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_hex,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_hex,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_set_hex) {
const RawAddress peer_addr{};
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
@@ -2119,8 +2087,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_hex,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_hex_if_greater,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_set_hex_if_greater) {
const RawAddress peer_addr{};
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
@@ -2205,8 +2172,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_hex_if_gr
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_str,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_str) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
std::string actual_value_str;
const std::string* actual_def_value = NULL;
@@ -2260,8 +2226,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_str,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_str,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_set_str) {
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
std::string input_value;
@@ -2324,8 +2289,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_str,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_str,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_set_str) {
const RawAddress peer_addr{};
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
@@ -2389,8 +2353,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_str,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_bin,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_bin) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
std::string actual_value_str;
const std::string* actual_def_value = NULL;
@@ -2496,8 +2459,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_bin,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_bin_length,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_bin_length) {
std::string actual_section, actual_key, expected_section = "abc", expected_key = "def";
std::string actual_value_str;
const std::string* actual_def_value = NULL;
@@ -2573,8 +2535,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_bin_length,
test::mock::osi_config::config_get_string.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_bin,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_set_bin) {
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
std::string string_return_value;
@@ -2680,8 +2641,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_bin,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_bin,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_addr_set_bin) {
const RawAddress peer_addr{};
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
@@ -2788,8 +2748,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_addr_set_bin,
test::mock::osi_alarm::alarm_set.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_remove,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_remove) {
std::string actual_key, expected_key = "def";
std::string actual_section, expected_section = "00:00:00:00:00:00";
bool return_value;
@@ -2828,8 +2787,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_remove,
test::mock::osi_config::config_remove_key.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_save_async,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_save_async) {
{
reset_mock_function_count_map();
@@ -2839,8 +2797,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_save_async,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_flush,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_flush) {
bool return_value;
test::mock::osi_alarm::alarm_is_scheduled.body = [&](const alarm_t* /*alarm*/) -> bool {
@@ -2876,8 +2833,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_flush,
test::mock::osi_alarm::alarm_is_scheduled.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_clear,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_clear) {
config_t* config_new_empty_return_value;
bool config_save_return_value;
@@ -2935,8 +2891,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_clear,
test::mock::osi_config::config_save.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_timer_save_cb,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_timer_save_cb) {
{
reset_mock_function_count_map();
@@ -2946,8 +2901,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_timer_save_cb,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_modified_time,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_set_modified_time) {
{
reset_mock_function_count_map();
@@ -2957,8 +2911,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_set_modified_time,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_device_num,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_get_device_num) {
{
config_t config;
auto num = device_iot_config_get_device_num(config);
@@ -2980,8 +2933,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_get_device_num,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_restrict_device_num,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_restrict_device_num) {
section_t section = {.name = "00:01:02:03:04:05"};
{
@@ -3032,8 +2984,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_restrict_device_nu
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_compare_key,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_compare_key) {
{
entry_t first =
{
@@ -3095,8 +3046,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_compare_key,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_write,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_write) {
test::mock::osi_config::config_save.body =
[&](const config_t& /*config*/, const std::string& /*filename*/) -> bool { return true; };
@@ -3122,8 +3072,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_write,
test::mock::osi_config::config_save.body = {};
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_debug_iot_config_dump,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_debug_iot_config_dump) {
{
errno = 0;
int fd = -1;
@@ -3152,8 +3101,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_debug_iot_config_dump,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_is_factory_reset,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_iot_config_is_factory_reset) {
bool return_value;
test::mock::osi_properties::osi_property_get_bool.body =
[&](const char* /*key*/, bool /*default_value*/) -> bool { return return_value; };
@@ -3169,8 +3117,7 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_iot_config_is_factory_reset,
}
}
-TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_debug_iot_config_delete_files,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
+TEST_F(DeviceIotConfigTest, test_device_debug_iot_config_delete_files) {
{
errno = 0;
int file_fd = -1;
@@ -3199,248 +3146,3 @@ TEST_F_WITH_FLAGS(DeviceIotConfigTest, test_device_debug_iot_config_delete_files
EXPECT_EQ(errno, ENOENT);
}
}
-class DeviceIotConfigDisabledTest : public testing::Test {
-protected:
- void SetUp() override {
- test::mock::osi_alarm::alarm_new.body = [&](const char* /*name*/) -> alarm_t* {
- return &placeholder_alarm;
- };
-
- test::mock::osi_alarm::alarm_set.body = [&](alarm_t* /*alarm*/, uint64_t /*interval_ms*/,
- alarm_callback_t /*cb*/,
- void* /*data*/) { return; };
-
- test::mock::osi_alarm::alarm_free.body = [](alarm_t* /*alarm*/) {};
-
- test::mock::osi_alarm::alarm_is_scheduled.body = [&](const alarm_t* /*alarm*/) -> bool {
- return false;
- };
-
- test::mock::osi_future::future_new_immediate.body = [&](void* /*value*/) -> future_t* {
- return &placeholder_future;
- };
-
- test::mock::osi_config::config_new_empty.body = [&]() -> std::unique_ptr<config_t> {
- return std::make_unique<config_t>();
- };
-
- test::mock::osi_config::config_new.body =
- [&](const char* /*filename*/) -> std::unique_ptr<config_t> {
- return std::make_unique<config_t>();
- };
-
- test::mock::osi_config::config_get_int.body =
- [&](const config_t& /*config*/, const std::string& /*section*/,
- const std::string& /*key*/, int def_value) { return def_value; };
-
- test::mock::osi_config::config_set_int.body =
- [&](config_t* /*config*/, const std::string& /*section*/, const std::string& /*key*/,
- int /*value*/) { return; };
-
- test::mock::osi_config::config_get_string.body =
- [&](const config_t& /*config*/, const std::string& /*section*/,
- const std::string& /*key*/, const std::string* def_value) { return def_value; };
-
- test::mock::osi_config::config_set_string.body =
- [&](config_t* /*config*/, const std::string& /*section*/, const std::string& /*key*/,
- const std::string& /*value*/) { return; };
-
- test::mock::osi_allocator::osi_free.body = [&](void* /*ptr*/) {};
-
- device_iot_config_module_init();
- device_iot_config_module_start_up();
-
- reset_mock_function_count_map();
- }
-
- void TearDown() override {
- test::mock::osi_alarm::alarm_new = {};
- test::mock::osi_alarm::alarm_set = {};
- test::mock::osi_alarm::alarm_free = {};
- test::mock::osi_alarm::alarm_is_scheduled = {};
- test::mock::osi_future::future_new_immediate = {};
- test::mock::osi_properties::osi_property_get = {};
- test::mock::osi_config::config_new_empty = {};
- test::mock::osi_config::config_new = {};
- test::mock::osi_config::config_get_int = {};
- test::mock::osi_config::config_set_int = {};
- test::mock::osi_config::config_get_string = {};
- test::mock::osi_config::config_set_string = {};
- test::mock::osi_allocator::osi_free = {};
- }
-};
-
-TEST_F_WITH_FLAGS(DeviceIotConfigDisabledTest, test_device_iot_config_disabled,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_BT, device_iot_config_logging))) {
- const RawAddress peer_addr{};
- std::string section, key, value_str;
- int value_int{};
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_has_section(section));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_exist(section, key));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_get_int(section, key, value_int));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_GET_INT(peer_addr, key, value_int));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_set_int(section, key, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_SET_INT(peer_addr, key, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_int_add_one(section, key));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_addr, key));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_get_hex(section, key, value_int));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_GET_HEX(peer_addr, key, value_int));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_set_hex(section, key, 0, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_SET_HEX(peer_addr, key, 0, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(peer_addr, key, 0, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_get_str(section, key, NULL, NULL));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_set_str(section, key, value_str));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_SET_STR(peer_addr, key, value_str));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_get_bin(section, key, NULL, NULL));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_set_bin(section, key, NULL, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(DEVICE_IOT_CONFIG_ADDR_SET_BIN(peer_addr, key, NULL, 0));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_FALSE(device_iot_config_remove(section, key));
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_EQ(device_iot_config_get_bin_length(section, key), 0u);
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- device_iot_config_flush();
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- EXPECT_TRUE(device_iot_config_clear());
- EXPECT_EQ(get_func_call_size(), 0);
- }
-
- {
- reset_mock_function_count_map();
-
- device_debug_iot_config_dump(0);
- EXPECT_EQ(get_func_call_size(), 0);
- }
-}
diff --git a/system/embdrv/encoder_for_aptx/Android.bp b/system/embdrv/encoder_for_aptx/Android.bp
index 406f8e302f..1b421089d7 100644
--- a/system/embdrv/encoder_for_aptx/Android.bp
+++ b/system/embdrv/encoder_for_aptx/Android.bp
@@ -42,9 +42,7 @@ cc_library_static {
tidy_checks: tidy_errors,
tidy_checks_as_errors: tidy_errors,
min_sdk_version: "Tiramisu",
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
visibility: [
"//packages/modules/Bluetooth:__subpackages__",
],
diff --git a/system/embdrv/encoder_for_aptxhd/Android.bp b/system/embdrv/encoder_for_aptxhd/Android.bp
index 553d87547c..04dd120c40 100644
--- a/system/embdrv/encoder_for_aptxhd/Android.bp
+++ b/system/embdrv/encoder_for_aptxhd/Android.bp
@@ -42,9 +42,7 @@ cc_library_static {
tidy_checks: tidy_errors,
tidy_checks_as_errors: tidy_errors,
min_sdk_version: "Tiramisu",
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
visibility: [
"//packages/modules/Bluetooth:__subpackages__",
],
diff --git a/system/embdrv/g722/Android.bp b/system/embdrv/g722/Android.bp
index 63af8f58ea..1eb2c59a1b 100644
--- a/system/embdrv/g722/Android.bp
+++ b/system/embdrv/g722/Android.bp
@@ -21,8 +21,6 @@ cc_library_static {
"g722_encode.cc",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/system/embdrv/sbc/decoder/Android.bp b/system/embdrv/sbc/decoder/Android.bp
index 84184fece7..2bce45cf98 100644
--- a/system/embdrv/sbc/decoder/Android.bp
+++ b/system/embdrv/sbc/decoder/Android.bp
@@ -35,9 +35,7 @@ cc_library_static {
"srce",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/system/embdrv/sbc/encoder/Android.bp b/system/embdrv/sbc/encoder/Android.bp
index 74fe1ec965..c583277f41 100644
--- a/system/embdrv/sbc/encoder/Android.bp
+++ b/system/embdrv/sbc/encoder/Android.bp
@@ -32,8 +32,6 @@ cc_library_static {
"packages/modules/Bluetooth/system/stack/include",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index aae5f1deb2..fd01e82eb2 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -177,9 +177,7 @@ cc_library_static {
include_dirs: [
"packages/modules/Bluetooth/system",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "31",
static_libs: [
"bluetooth_flags_c_lib",
@@ -263,11 +261,11 @@ cc_test {
],
static_libs: [
"bluetooth_flags_c_lib_for_test",
+ "libaconfig_storage_read_api_cc",
"libbluetooth_log",
"libchrome",
"libgmock",
"server_configurable_flags",
- "libaconfig_storage_read_api_cc",
],
shared_libs: [
"libbase",
diff --git a/system/gd/AndroidTestTemplate.xml b/system/gd/AndroidTestTemplate.xml
index 8332422b34..4083baac14 100644
--- a/system/gd/AndroidTestTemplate.xml
+++ b/system/gd/AndroidTestTemplate.xml
@@ -39,7 +39,7 @@
<!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/system/gd/crypto_toolbox/Android.bp b/system/gd/crypto_toolbox/Android.bp
index 05e44b7bc2..2bac8bc985 100644
--- a/system/gd/crypto_toolbox/Android.bp
+++ b/system/gd/crypto_toolbox/Android.bp
@@ -19,7 +19,7 @@ cc_library {
name: "libbluetooth_crypto_toolbox",
defaults: ["fluoride_defaults"],
host_supported: true,
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "29",
include_dirs: [
"packages/modules/Bluetooth/system/gd",
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index e00554c923..65c1671c8d 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -328,7 +328,6 @@ public:
protected:
void ListDependencies(ModuleList* list) const {
list->add<LinkClocker>();
- list->add<metrics::CounterMetrics>();
list->add<SnoopLogger>();
}
diff --git a/system/gd/hal/hci_hal_host_rootcanal.cc b/system/gd/hal/hci_hal_host_rootcanal.cc
index 05d898228d..f4866a5115 100644
--- a/system/gd/hal/hci_hal_host_rootcanal.cc
+++ b/system/gd/hal/hci_hal_host_rootcanal.cc
@@ -163,10 +163,7 @@ public:
}
protected:
- void ListDependencies(ModuleList* list) const {
- list->add<metrics::CounterMetrics>();
- list->add<SnoopLogger>();
- }
+ void ListDependencies(ModuleList* list) const { list->add<SnoopLogger>(); }
void Start() override {
std::lock_guard<std::mutex> lock(api_mutex_);
diff --git a/system/gd/hal/ranging_hal.h b/system/gd/hal/ranging_hal.h
index 5a37900bc5..b22378970e 100644
--- a/system/gd/hal/ranging_hal.h
+++ b/system/gd/hal/ranging_hal.h
@@ -281,9 +281,15 @@ struct ProcedureDataV2 {
struct RangingResult {
double result_meters_;
+ double error_meters_;
+
// A normalized value from 0 (low confidence) to 100 (high confidence) representing the confidence
// of estimated distance. The value is -1 when unavailable.
int8_t confidence_level_;
+ double delay_spread_meters_;
+ uint8_t detected_attack_level_;
+ double velocity_meters_per_second_;
+ int64_t elapsed_timestamp_nanos_;
};
class RangingHalCallback {
diff --git a/system/gd/hal/ranging_hal_android.cc b/system/gd/hal/ranging_hal_android.cc
index d093685863..1a82eb2062 100644
--- a/system/gd/hal/ranging_hal_android.cc
+++ b/system/gd/hal/ranging_hal_android.cc
@@ -73,10 +73,11 @@ class BluetoothChannelSoundingSessionTracker : public BnBluetoothChannelSounding
public:
BluetoothChannelSoundingSessionTracker(uint16_t connection_handle,
RangingHalCallback* ranging_hal_callback,
- bool for_vendor_specific_reply)
+ bool for_vendor_specific_reply, RangingHalVersion hal_ver)
: connection_handle_(connection_handle),
ranging_hal_callback_(ranging_hal_callback),
- for_vendor_specific_reply_(for_vendor_specific_reply) {}
+ for_vendor_specific_reply_(for_vendor_specific_reply),
+ hal_ver_(hal_ver) {}
::ndk::ScopedAStatus onOpened(::aidl::android::hardware::bluetooth::ranging::Reason in_reason) {
log::info("connection_handle 0x{:04x}, reason {}", connection_handle_, (uint16_t)in_reason);
@@ -103,8 +104,15 @@ public:
log::verbose("resultMeters {}", in_result.resultMeters);
hal::RangingResult ranging_result = {
.result_meters_ = in_result.resultMeters,
+ .error_meters_ = in_result.errorMeters,
.confidence_level_ = in_result.confidenceLevel,
+ .delay_spread_meters_ = in_result.delaySpreadMeters,
+ .detected_attack_level_ = static_cast<uint8_t>(in_result.detectedAttackLevel),
+ .velocity_meters_per_second_ = in_result.velocityMetersPerSecond,
};
+ if (hal_ver_ == V_2) {
+ ranging_result.elapsed_timestamp_nanos_ = in_result.timestampNanos;
+ }
ranging_hal_callback_->OnResult(connection_handle_, ranging_result);
return ::ndk::ScopedAStatus::ok();
}
@@ -129,6 +137,7 @@ private:
uint16_t connection_handle_;
RangingHalCallback* ranging_hal_callback_;
bool for_vendor_specific_reply_;
+ RangingHalVersion hal_ver_;
};
class RangingHalAndroid : public RangingHal {
@@ -167,7 +176,7 @@ public:
connection_handle, att_handle, vendor_specific_data.size());
session_trackers_[connection_handle] =
ndk::SharedRefBase::make<BluetoothChannelSoundingSessionTracker>(
- connection_handle, ranging_hal_callback_, false);
+ connection_handle, ranging_hal_callback_, false, hal_ver_);
BluetoothChannelSoundingParameters parameters;
parameters.aclHandle = connection_handle;
parameters.role = aidl::android::hardware::bluetooth::ranging::Role::INITIATOR;
@@ -200,7 +209,7 @@ public:
log::info("connection_handle 0x{:04x}", connection_handle);
session_trackers_[connection_handle] =
ndk::SharedRefBase::make<BluetoothChannelSoundingSessionTracker>(
- connection_handle, ranging_hal_callback_, true);
+ connection_handle, ranging_hal_callback_, true, hal_ver_);
BluetoothChannelSoundingParameters parameters;
parameters.aclHandle = connection_handle;
parameters.role = aidl::android::hardware::bluetooth::ranging::Role::REFLECTOR;
diff --git a/system/gd/hal/snoop_logger.cc b/system/gd/hal/snoop_logger.cc
index 2e587cbfd6..326ec3e82e 100644
--- a/system/gd/hal/snoop_logger.cc
+++ b/system/gd/hal/snoop_logger.cc
@@ -29,6 +29,7 @@
#include <algorithm>
#include <bitset>
#include <chrono>
+#include <filesystem>
#include <sstream>
#include "common/circular_buffer.h"
@@ -322,6 +323,21 @@ std::string get_btsnoop_log_path(std::string log_dir, bool filtered) {
std::string get_last_log_path(std::string log_file_path) { return log_file_path.append(".last"); }
+#ifdef __ANDROID__
+static bool create_log_directories() {
+ std::filesystem::path default_path = os::ParameterProvider::SnoopLogFilePath();
+ std::filesystem::path default_dir_path = default_path.parent_path();
+
+ if (std::filesystem::exists(default_dir_path)) {
+ log::info("Directory {} already exists", default_dir_path.string());
+ return true;
+ }
+
+ log::info("Creating directory: {}", default_dir_path.string());
+ return std::filesystem::create_directories(default_dir_path);
+}
+#endif // __ANDROID__
+
void delete_btsnoop_files(const std::string& log_path) {
log::info("Deleting logs if they exist");
if (os::FileExists(log_path)) {
@@ -531,6 +547,13 @@ void SnoopLogger::OpenNextSnoopLogFile() {
auto last_file_path = get_last_log_path(snoop_log_path_);
+#ifdef __ANDROID__
+ if (com::android::bluetooth::flags::snoop_logger_recreate_logs_directory() &&
+ !create_log_directories()) {
+ log::error("Could not recreate log directory");
+ }
+#endif // __ANDROID__
+
if (os::FileExists(snoop_log_path_)) {
if (!os::RenameFile(snoop_log_path_, last_file_path)) {
log::error("Unabled to rename existing snoop log from \"{}\" to \"{}\"", snoop_log_path_,
@@ -547,21 +570,17 @@ void SnoopLogger::OpenNextSnoopLogFile() {
file_creation_time = fake_timerfd_get_clock();
#endif
if (!btsnoop_ostream_.good()) {
- log::error("Unable to open snoop log at \"{}\", error: \"{}\"", snoop_log_path_,
+ log::fatal("Unable to open snoop log at \"{}\", error: \"{}\"", snoop_log_path_,
strerror(errno));
- return;
}
umask(prevmask);
if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
sizeof(SnoopLoggerCommon::FileHeaderType))) {
- log::error("Unable to write file header to \"{}\", error: \"{}\"", snoop_log_path_,
+ log::fatal("Unable to write file header to \"{}\", error: \"{}\"", snoop_log_path_,
strerror(errno));
- btsnoop_ostream_.close();
- return;
}
if (!btsnoop_ostream_.flush()) {
log::error("Failed to flush, error: \"{}\"", strerror(errno));
- return;
}
}
@@ -725,21 +744,30 @@ uint32_t SnoopLogger::PayloadStrip(profile_type_t current_profile, uint8_t* pack
uint32_t SnoopLogger::FilterProfilesHandleHfp(uint8_t* packet, uint32_t length, uint32_t totlen,
uint32_t offset) {
- if ((totlen - offset) > cpbr_pat_len) {
- if (memcmp(&packet[offset], cpbr_pattern, cpbr_pat_len) == 0) {
- length = offset + cpbr_pat_len + 1;
- packet[L2CAP_PDU_LENGTH_OFFSET] = offset + cpbr_pat_len - BASIC_L2CAP_HEADER_LENGTH;
- packet[L2CAP_PDU_LENGTH_OFFSET] =
- offset + cpbr_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
- return length;
- }
+ // CPBR packet
+ if ((totlen - offset) > cpbr_pat_len &&
+ memcmp(&packet[offset], cpbr_pattern, cpbr_pat_len) == 0) {
+ length = offset + cpbr_pat_len + 1;
+ packet[ACL_LENGTH_OFFSET] = offset + cpbr_pat_len - BASIC_L2CAP_HEADER_LENGTH;
+ packet[ACL_LENGTH_OFFSET + 1] = (offset + cpbr_pat_len - BASIC_L2CAP_HEADER_LENGTH) >> 8;
- if (memcmp(&packet[offset], clcc_pattern, clcc_pat_len) == 0) {
- length = offset + cpbr_pat_len + 1;
- packet[L2CAP_PDU_LENGTH_OFFSET] = offset + clcc_pat_len - BASIC_L2CAP_HEADER_LENGTH;
- packet[L2CAP_PDU_LENGTH_OFFSET] =
- offset + clcc_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
- }
+ packet[L2CAP_PDU_LENGTH_OFFSET] =
+ offset + cpbr_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
+ packet[L2CAP_PDU_LENGTH_OFFSET + 1] =
+ (offset + cpbr_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8;
+ return length;
+ }
+ // CLCC packet
+ if ((totlen - offset) > clcc_pat_len &&
+ memcmp(&packet[offset], clcc_pattern, clcc_pat_len) == 0) {
+ length = offset + cpbr_pat_len + 1;
+ packet[ACL_LENGTH_OFFSET] = offset + clcc_pat_len - BASIC_L2CAP_HEADER_LENGTH;
+ packet[ACL_LENGTH_OFFSET + 1] = (offset + clcc_pat_len - BASIC_L2CAP_HEADER_LENGTH) >> 8;
+
+ packet[L2CAP_PDU_LENGTH_OFFSET] =
+ offset + clcc_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH);
+ packet[L2CAP_PDU_LENGTH_OFFSET + 1] =
+ (offset + clcc_pat_len - (ACL_HEADER_LENGTH + BASIC_L2CAP_HEADER_LENGTH)) >> 8;
}
return length;
@@ -1194,9 +1222,6 @@ void SnoopLogger::Capture(const HciPacket& immutable_packet, Direction direction
if (packet_counter_ > max_packets_per_file_) {
OpenNextSnoopLogFile();
}
- if (!btsnoop_ostream_.is_open() || !btsnoop_ostream_.good()) {
- return;
- }
if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
log::error("Failed to write packet header for btsnoop, error: \"{}\"", strerror(errno));
}
diff --git a/system/gd/hal/snoop_logger_test.cc b/system/gd/hal/snoop_logger_test.cc
index bfe81f3194..a64db3cff5 100644
--- a/system/gd/hal/snoop_logger_test.cc
+++ b/system/gd/hal/snoop_logger_test.cc
@@ -17,6 +17,7 @@
#include "hal/snoop_logger.h"
#include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <netinet/in.h>
@@ -28,6 +29,7 @@
#include "hal/snoop_logger_common.h"
#include "hal/syscall_wrapper_impl.h"
#include "os/fake_timer/fake_timerfd.h"
+#include "os/parameter_provider.h"
#include "os/system_properties.h"
#include "os/utils.h"
@@ -161,6 +163,7 @@ protected:
}
void TearDown() override {
+ com::android::bluetooth::flags::provider_->reset_flags();
DeleteSnoopLogFiles();
fake_timerfd_reset();
test_registry->StopAll();
@@ -389,7 +392,10 @@ TEST_F(SnoopLoggerModuleTest, delete_old_snooz_log_files) {
handler->Post(bluetooth::common::BindOnce(fake_timerfd_advance, 15));
sync_handler(handler);
handler->Post(bluetooth::common::BindOnce(
- [](std::filesystem::path path) { ASSERT_FALSE(std::filesystem::exists(path)); },
+ [](std::filesystem::path path) {
+ log::info("path: {}", path.string());
+ ASSERT_FALSE(std::filesystem::exists(path));
+ },
temp_snooz_log_));
sync_handler(handler);
@@ -1392,7 +1398,7 @@ TEST_F(SnoopLoggerModuleTest, custom_socket_profiles_filtered_hfp_hf_test) {
uint16_t conn_handle = 0x000b;
uint16_t local_cid = 0x0043;
uint16_t remote_cid = 0x3040;
- uint8_t dlci = 0x06;
+ uint8_t dlci = 0x02;
uint16_t psm = 0x0003;
uint16_t profile_uuid_hfp_hf = 0x111f;
bool flow = true;
@@ -1400,18 +1406,22 @@ TEST_F(SnoopLoggerModuleTest, custom_socket_profiles_filtered_hfp_hf_test) {
const uint16_t HEADER_SIZE = 12;
size_t expected_data_size = HEADER_SIZE + strlen(clcc_pattern.c_str());
std::vector<uint8_t> kPhoneNumber = {
- 0x0b, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x40, 0x30, 0x19, 0xff, 0x4f, 0x01, 0x0d,
- 0x0a, 0x2b, 0x43, 0x4c, 0x43, 0x43, 0x3a, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x34,
- 0x2c, 0x30, 0x2c, 0x30, 0x2c, 0x22, 0x2b, 0x39, 0x39, 0x31, 0x32, 0x33, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x22, 0x2c, 0x31, 0x34, 0x35, 0x0d, 0x0a, 0x49,
+ 0x0b, 0x00, 0x30, 0x00, // ACL Header (Handle: 0x000b, PB flag: 0x00, Length: 48)
+ 0x2c, 0x00, 0x40, 0x30, // L2CAP Header (Length: 44, CID: 0x3040)
+ 0x0b, 0xff, 0x4f, 0x01, // RFCOMM
+ // "\r\n+CLCC: 1,1,4,0,0,"+99123456789",145\r\n"
+ 0x0d, 0x0a, 0x2b, 0x43, 0x4c, 0x43, 0x43, 0x3a, 0x20, 0x31, 0x2c, 0x31, 0x2c, 0x34, 0x2c,
+ 0x30, 0x2c, 0x30, 0x2c, 0x22, 0x2b, 0x39, 0x39, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x22, 0x2c, 0x31, 0x34, 0x35, 0x0d, 0x0a,
+ 0x86 // RFCOMM
};
std::vector<uint8_t> kExpectedPhoneNumber = {
- 0x0b, 0x00, 0x30, 0x00, 0x0c, 0x00, 0x40, 0x30, 0x19, 0xff, 0x4f, 0x01, 0x0d,
- 0x0a, 0x2b, 0x43, 0x4c, 0x43, 0x43, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
+ 0x0b, 0x00, 0x10, 0x00, // ACL Header (Handle: 0x000b, PB flag: 0x00, Length: 16)
+ 0x0c, 0x00, 0x40, 0x30, // L2CAP Header (Length: 12, CID: 0x3040)
+ 0x0b, 0xff, 0x4f, 0x01, // RFCOMM
+ // "\r\n+CLCC:"
+ 0x0d, 0x0a, 0x2b, 0x43, 0x4c, 0x43, 0x43, 0x3a};
// Set pbap and map filtering modes
ASSERT_TRUE(
@@ -1498,4 +1508,86 @@ TEST_F(SnoopLoggerModuleTest, custom_socket_profiles_filtered_hfp_hf_test) {
test_registry->StopAll();
close(socket_fd);
}
+
+#ifdef __ANDROID__
+TEST_F(SnoopLoggerModuleTest, recreate_log_directory_when_enabled_test) {
+ com::android::bluetooth::flags::provider_->snoop_logger_recreate_logs_directory(true);
+ // Actual test
+ const testing::TestInfo* const test_info = testing::UnitTest::GetInstance()->current_test_info();
+ const std::filesystem::path os_btsnoop_file_path_ = os::ParameterProvider::SnoopLogFilePath();
+ std::filesystem::path temp_dir_path_ = os_btsnoop_file_path_.parent_path();
+
+ const std::filesystem::path temp_log_btsnoop_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnoop_hci.log");
+ const std::filesystem::path temp_log_btsnooz_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnooz_hci.log");
+
+ if (std::filesystem::exists(temp_dir_path_)) {
+ std::filesystem::remove_all(temp_dir_path_);
+ }
+
+ ASSERT_FALSE(std::filesystem::exists(temp_dir_path_));
+
+ auto* snoop_logger = new TestSnoopLoggerModule(temp_log_btsnoop_file_.string(),
+ temp_log_btsnooz_file_.string(), 10,
+ SnoopLogger::kBtSnoopLogModeFull, false, false);
+ test_registry->InjectTestModule(&SnoopLogger::Factory, snoop_logger);
+
+ ASSERT_TRUE(std::filesystem::exists(temp_dir_path_));
+
+ test_registry->StopAll();
+
+ // btsnoop file should exist
+ ASSERT_TRUE(std::filesystem::exists(temp_log_btsnoop_file_));
+ // btsnooz file should be removed as snoop_log_persists is false
+ ASSERT_FALSE(std::filesystem::exists(temp_log_btsnooz_file_));
+ // remove after test
+ if (std::filesystem::exists(temp_dir_path_)) {
+ std::filesystem::remove_all(temp_dir_path_);
+ }
+}
+
+TEST_F(SnoopLoggerModuleTest, recreate_log_directory_when_filtered_test) {
+ com::android::bluetooth::flags::provider_->snoop_logger_recreate_logs_directory(true);
+ // Actual test
+ const testing::TestInfo* const test_info = testing::UnitTest::GetInstance()->current_test_info();
+ const std::filesystem::path os_btsnoop_file_path_ = os::ParameterProvider::SnoopLogFilePath();
+ std::filesystem::path temp_dir_path_ = os_btsnoop_file_path_.parent_path();
+
+ const std::filesystem::path temp_log_btsnoop_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnoop_hci.log");
+ const std::filesystem::path temp_log_btsnooz_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnooz_hci.log");
+
+ if (std::filesystem::exists(temp_dir_path_)) {
+ std::filesystem::remove_all(temp_dir_path_);
+ }
+
+ ASSERT_FALSE(std::filesystem::exists(temp_dir_path_));
+
+ auto* snoop_logger = new TestSnoopLoggerModule(
+ temp_log_btsnoop_file_.string(), temp_log_btsnooz_file_.string(), 10,
+ SnoopLogger::kBtSnoopLogModeFiltered, false, false);
+ test_registry->InjectTestModule(&SnoopLogger::Factory, snoop_logger);
+
+ ASSERT_TRUE(std::filesystem::exists(temp_dir_path_));
+
+ test_registry->StopAll();
+
+ const std::filesystem::path temp_log_btsnoop_filtered_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnoop_hci.log.filtered");
+ const std::filesystem::path temp_log_btsnooz_filtered_file_ =
+ temp_dir_path_ / (std::string(test_info->name()) + "_btsnooz_hci.log.filtered");
+
+ // btsnoop file should exist
+ ASSERT_TRUE(std::filesystem::exists(temp_log_btsnoop_filtered_file_));
+ // btsnooz file should be removed as snoop_log_persists is false
+ ASSERT_FALSE(std::filesystem::exists(temp_log_btsnooz_filtered_file_));
+ // remove after test
+ if (std::filesystem::exists(temp_dir_path_)) {
+ std::filesystem::remove_all(temp_dir_path_);
+ }
+}
+#endif // __ANDROID__
+
} // namespace testing
diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc
index 04c3955204..696f33e621 100644
--- a/system/gd/hci/distance_measurement_manager.cc
+++ b/system/gd/hci/distance_measurement_manager.cc
@@ -45,6 +45,13 @@ namespace bluetooth {
namespace hci {
const ModuleFactory DistanceMeasurementManager::Factory =
ModuleFactory([]() { return new DistanceMeasurementManager(); });
+// valid azimuth angle degree value is from 0 to 360.
+static constexpr int kInvalidAzimuthAngleDegree = -1;
+// valid altitude angle degree value is from -90 to 90
+static constexpr int kInvalidAltitudeAngleDegree = -91;
+static constexpr double kInvalidDelayedSpreadMeters = -1.0;
+static constexpr int8_t kInvalidConfidenceLevel = -1;
+static constexpr double kInvalidVelocityMetersPerSecond = -1.0;
static constexpr uint16_t kIllegalConnectionHandle = 0xffff;
static constexpr uint8_t kTxPowerNotAvailable = 0xfe;
static constexpr int8_t kRSSIDropOffAt1M = 41;
@@ -283,10 +290,18 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
using namespace std::chrono;
uint64_t elapsedRealtimeNanos =
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
+ if (is_hal_v2()) {
+ elapsedRealtimeNanos = ranging_result.elapsed_timestamp_nanos_;
+ }
distance_measurement_callbacks_->OnDistanceMeasurementResult(
cs_requester_trackers_[connection_handle].address, ranging_result.result_meters_ * 100,
- 0.0, -1, -1, -1, -1, elapsedRealtimeNanos, ranging_result.confidence_level_,
- DistanceMeasurementMethod::METHOD_CS);
+ ranging_result.error_meters_ * 100, kInvalidAzimuthAngleDegree,
+ kInvalidAzimuthAngleDegree, kInvalidAltitudeAngleDegree, kInvalidAltitudeAngleDegree,
+ elapsedRealtimeNanos, ranging_result.confidence_level_,
+ ranging_result.delay_spread_meters_,
+ static_cast<DistanceMeasurementDetectedAttackLevel>(
+ ranging_result.detected_attack_level_),
+ ranging_result.velocity_meters_per_second_, DistanceMeasurementMethod::METHOD_CS);
}
~impl() {}
@@ -527,7 +542,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
}
}
- void handle_ras_client_disconnected_event(const Address address) {
+ void handle_ras_client_disconnected_event(const Address address,
+ const ras::RasDisconnectReason& ras_disconnect_reason) {
log::info("address:{}", address);
for (auto it = cs_requester_trackers_.begin(); it != cs_requester_trackers_.end();) {
if (it->second.address == address) {
@@ -535,8 +551,13 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
it->second.repeating_alarm->Cancel();
it->second.repeating_alarm.reset();
}
- distance_measurement_callbacks_->OnDistanceMeasurementStopped(
- address, REASON_NO_LE_CONNECTION, METHOD_CS);
+ DistanceMeasurementErrorCode reason = REASON_NO_LE_CONNECTION;
+ if (ras_disconnect_reason == ras::RasDisconnectReason::SERVER_NOT_AVAILABLE) {
+ reason = REASON_FEATURE_NOT_SUPPORTED_REMOTE;
+ } else if (ras_disconnect_reason == ras::RasDisconnectReason::FATAL_ERROR) {
+ reason = REASON_INTERNAL_ERROR;
+ }
+ distance_measurement_callbacks_->OnDistanceMeasurementStopped(address, reason, METHOD_CS);
gatt_mtus_.erase(it->first);
it = cs_requester_trackers_.erase(it); // erase and get the next iterator
} else {
@@ -915,9 +936,6 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
req_it->second.remote_support_phase_based_ranging =
event_view.GetOptionalSubfeaturesSupported().phase_based_ranging_ == 0x01;
req_it->second.remote_num_antennas_supported_ = event_view.GetNumAntennasSupported();
- req_it->second.setup_complete = true;
- log::info("Setup phase complete, connection_handle: {}, address: {}", connection_handle,
- req_it->second.address);
req_it->second.retry_counter_for_create_config = 0;
req_it->second.remote_supported_sw_time_ = event_view.GetTSwTimeSupported();
send_le_cs_create_config(connection_handle, req_it->second.requesting_config_id);
@@ -1016,6 +1034,10 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
if (!live_tracker->local_start) {
// reset the responder state, as no other event to set the state.
live_tracker->state = CsTrackerState::WAIT_FOR_CONFIG_COMPLETE;
+ } else {
+ live_tracker->setup_complete = true;
+ log::info("connection_handle: {}, address: {}, config_id: {}", connection_handle,
+ live_tracker->address, config_id);
}
live_tracker->used_config_id = config_id;
@@ -2477,8 +2499,11 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback {
uint64_t elapsedRealtimeNanos =
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
distance_measurement_callbacks_->OnDistanceMeasurementResult(
- address, distance * 100, distance * 100, -1, -1, -1, -1, elapsedRealtimeNanos, -1,
- DistanceMeasurementMethod::METHOD_RSSI);
+ address, distance * 100, distance * 100, kInvalidAzimuthAngleDegree,
+ kInvalidAzimuthAngleDegree, kInvalidAltitudeAngleDegree, kInvalidAltitudeAngleDegree,
+ elapsedRealtimeNanos, kInvalidConfidenceLevel, kInvalidDelayedSpreadMeters,
+ DistanceMeasurementDetectedAttackLevel::NADM_ATTACK_UNKNOWN,
+ kInvalidVelocityMetersPerSecond, DistanceMeasurementMethod::METHOD_RSSI);
}
std::vector<uint8_t> builder_to_bytes(std::unique_ptr<PacketBuilder<true>> builder) {
@@ -2578,8 +2603,9 @@ void DistanceMeasurementManager::HandleConnIntervalUpdated(const Address& addres
conn_interval);
}
-void DistanceMeasurementManager::HandleRasClientDisconnectedEvent(const Address& address) {
- CallOn(pimpl_.get(), &impl::handle_ras_client_disconnected_event, address);
+void DistanceMeasurementManager::HandleRasClientDisconnectedEvent(
+ const Address& address, const ras::RasDisconnectReason& ras_disconnect_reason) {
+ CallOn(pimpl_.get(), &impl::handle_ras_client_disconnected_event, address, ras_disconnect_reason);
}
void DistanceMeasurementManager::HandleVendorSpecificReply(
diff --git a/system/gd/hci/distance_measurement_manager.h b/system/gd/hci/distance_measurement_manager.h
index da26425c49..d8c3d49aea 100644
--- a/system/gd/hci/distance_measurement_manager.h
+++ b/system/gd/hci/distance_measurement_manager.h
@@ -18,6 +18,7 @@
#include <bluetooth/log.h>
#include "address.h"
+#include "bta/include/bta_ras_api.h"
#include "hal/ranging_hal.h"
#include "hci/hci_packets.h"
#include "module.h"
@@ -42,11 +43,15 @@ enum DistanceMeasurementErrorCode {
REASON_INTERNAL_ERROR,
};
-struct DistanceMeasurementResult {
- Address address;
- uint32_t centimeter;
- uint32_t error_centimeter;
- DistanceMeasurementMethod method;
+enum DistanceMeasurementDetectedAttackLevel {
+ NADM_ATTACK_IS_EXTREMELY_UNLIKELY = 0,
+ NADM_ATTACK_IS_VERY_UNLIKELY = 1,
+ NADM_ATTACK_IS_UNLIKELY = 2,
+ NADM_ATTACK_IS_POSSIBLE = 3,
+ NADM_ATTACK_IS_LIKELY = 4,
+ NADM_ATTACK_IS_VERY_LIKELY = 5,
+ NADM_ATTACK_IS_EXTREMELY_LIKELY = 6,
+ NADM_ATTACK_UNKNOWN = 0xFF,
};
class DistanceMeasurementCallbacks {
@@ -55,12 +60,12 @@ public:
virtual void OnDistanceMeasurementStarted(Address address, DistanceMeasurementMethod method) = 0;
virtual void OnDistanceMeasurementStopped(Address address, DistanceMeasurementErrorCode reason,
DistanceMeasurementMethod method) = 0;
- virtual void OnDistanceMeasurementResult(Address address, uint32_t centimeter,
- uint32_t error_centimeter, int azimuth_angle,
- int error_azimuth_angle, int altitude_angle,
- int error_altitude_angle, uint64_t elapsedRealtimeNanos,
- int8_t confidence_level,
- DistanceMeasurementMethod method) = 0;
+ virtual void OnDistanceMeasurementResult(
+ Address address, uint32_t centimeter, uint32_t error_centimeter, int azimuth_angle,
+ int error_azimuth_angle, int altitude_angle, int error_altitude_angle,
+ uint64_t elapsed_realtime_nanos, int8_t confidence_level, double delayed_spread_meters,
+ DistanceMeasurementDetectedAttackLevel detected_attack_level,
+ double velocity_meters_per_second, DistanceMeasurementMethod method) = 0;
virtual void OnRasFragmentReady(Address address, uint16_t procedure_counter, bool is_last,
std::vector<uint8_t> raw_data) = 0;
virtual void OnVendorSpecificCharacteristics(
@@ -88,7 +93,8 @@ public:
const Address& address, uint16_t connection_handle, uint16_t att_handle,
const std::vector<hal::VendorSpecificCharacteristic>& vendor_specific_data,
uint16_t conn_interval);
- void HandleRasClientDisconnectedEvent(const Address& address);
+ void HandleRasClientDisconnectedEvent(const Address& address,
+ const ras::RasDisconnectReason& ras_disconnect_reason);
void HandleVendorSpecificReply(
const Address& address, uint16_t connection_handle,
const std::vector<hal::VendorSpecificCharacteristic>& vendor_specific_reply);
diff --git a/system/gd/hci/distance_measurement_manager_mock.h b/system/gd/hci/distance_measurement_manager_mock.h
index 0aef34d2af..827c096670 100644
--- a/system/gd/hci/distance_measurement_manager_mock.h
+++ b/system/gd/hci/distance_measurement_manager_mock.h
@@ -35,8 +35,8 @@ class MockDistanceMeasurementCallbacks : public DistanceMeasurementCallbacks {
MOCK_METHOD(void, OnDistanceMeasurementStopped,
(Address, DistanceMeasurementErrorCode, DistanceMeasurementMethod));
MOCK_METHOD(void, OnDistanceMeasurementResult,
- (Address, uint32_t, uint32_t, int, int, int, int, uint64_t, int8_t,
- DistanceMeasurementMethod));
+ (Address, uint32_t, uint32_t, int, int, int, int, uint64_t, int8_t, double,
+ DistanceMeasurementDetectedAttackLevel, double, DistanceMeasurementMethod));
};
class MockDistanceMeasurementManager : public DistanceMeasurementManager {
diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc
index 5213569243..acf3da1188 100644
--- a/system/gd/hci/le_advertising_manager.cc
+++ b/system/gd/hci/le_advertising_manager.cc
@@ -250,6 +250,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
auto advertiser_id = view.GetAdvertisingInstance();
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
log::info("Instance: 0x{:x} StateChangeReason: {} Handle: 0x{:x} Address: {}", advertiser_id,
VseStateChangeReasonText(view.GetStateChangeReason()), view.GetConnectionHandle(),
advertising_sets_[view.GetAdvertisingInstance()].current_address.ToString());
@@ -312,6 +319,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
uint8_t advertiser_id = event_view.GetAdvertisingHandle();
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
bool was_rotating_address = false;
if (advertising_sets_[advertiser_id].address_rotation_wake_alarm_ != nullptr) {
was_rotating_address = true;
@@ -450,7 +464,8 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
}
- /// Generates an address for the advertiser
+ // Generates an address for the advertiser
+ // Before calling this method, ensure the id exists in advertising_sets_.
AddressWithType new_advertiser_address(AdvertiserId id) {
switch (advertising_sets_[id].address_type) {
case AdvertiserAddressType::PUBLIC:
@@ -771,6 +786,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
void rotate_advertiser_address(AdvertiserId advertiser_id) {
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
if (advertising_api_type_ == AdvertisingApiType::EXTENDED) {
AddressWithType address_with_type = new_advertiser_address(advertiser_id);
le_advertising_interface_->EnqueueCommand(
@@ -784,6 +806,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
void set_advertising_set_random_address_on_timer(AdvertiserId advertiser_id) {
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
// This function should only be trigger by enabled advertising set or IRK rotation
if (enabled_sets_[advertiser_id].advertising_handle_ == kInvalidHandle) {
if (advertising_sets_[advertiser_id].address_rotation_wake_alarm_ != nullptr) {
@@ -892,6 +921,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
void set_parameters(AdvertiserId advertiser_id, AdvertisingConfig config) {
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
config.tx_power = get_tx_power_after_calibration(static_cast<int8_t>(config.tx_power));
advertising_sets_[advertiser_id].is_legacy = config.legacy_pdus;
advertising_sets_[advertiser_id].connectable = config.connectable;
@@ -1054,6 +1090,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
void set_data(AdvertiserId advertiser_id, bool set_scan_rsp, std::vector<GapData> data) {
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
+
// The Flags data type shall be included when any of the Flag bits are non-zero and the
// advertising packet is connectable and discoverable.
if (!set_scan_rsp && advertising_sets_[advertiser_id].connectable &&
@@ -1578,12 +1621,19 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
return;
}
for (EnabledSet enabled_set : enabled_sets) {
- bool started = advertising_sets_[enabled_set.advertising_handle_].started;
uint8_t id = enabled_set.advertising_handle_;
if (id == kInvalidHandle) {
continue;
}
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(id)) {
+ log::warn("Unknown advertiser id {}", id);
+ continue;
+ }
+ }
+
+ bool started = advertising_sets_[id].started;
int reg_id = id_map_[id];
if (reg_id == kIdLocal) {
if (!advertising_sets_[enabled_set.advertising_handle_].status_callback.is_null()) {
@@ -1626,13 +1676,21 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
}
for (EnabledSet enabled_set : enabled_sets) {
- int8_t tx_power = advertising_sets_[enabled_set.advertising_handle_].tx_power;
- bool started = advertising_sets_[enabled_set.advertising_handle_].started;
uint8_t id = enabled_set.advertising_handle_;
if (id == kInvalidHandle) {
continue;
}
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(id)) {
+ log::warn("Unknown advertiser id {}", id);
+ continue;
+ }
+ }
+
+ int8_t tx_power = advertising_sets_[enabled_set.advertising_handle_].tx_power;
+ bool started = advertising_sets_[enabled_set.advertising_handle_].started;
+
int reg_id = id_map_[id];
if (reg_id == kIdLocal) {
if (!advertising_sets_[enabled_set.advertising_handle_].status_callback.is_null()) {
@@ -1665,6 +1723,14 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus()));
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
}
+
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(id)) {
+ log::warn("Unknown advertiser id {}", id);
+ return;
+ }
+ }
+
advertising_sets_[id].tx_power = complete_view.GetSelectedTxPower();
if (advertising_sets_[id].started && id_map_[id] != kIdLocal) {
@@ -1686,6 +1752,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
}
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(id)) {
+ log::warn("Unknown advertiser id {}", id);
+ return;
+ }
+ }
+
if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started ||
id_map_[id] == kIdLocal) {
return;
@@ -1706,6 +1779,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
} else {
log::info("update random address for advertising set {} : {}", advertiser_id,
address_with_type.GetAddress());
+
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(advertiser_id)) {
+ log::warn("Unknown advertiser id {}", advertiser_id);
+ return;
+ }
+ }
advertising_sets_[advertiser_id].current_address = address_with_type;
}
}
@@ -1726,6 +1806,13 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
}
+ if (com::android::bluetooth::flags::fix_unusable_adv_slot_due_to_map_access()) {
+ if (!advertising_sets_.contains(id)) {
+ log::warn("Unknown advertiser id {}", id);
+ return;
+ }
+ }
+
// Do not trigger callback if the advertiser not stated yet, or the advertiser is not register
// from Java layer
if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started ||
diff --git a/system/gd/hci/le_advertising_manager.h b/system/gd/hci/le_advertising_manager.h
index 55245a5cfa..fe614861e2 100644
--- a/system/gd/hci/le_advertising_manager.h
+++ b/system/gd/hci/le_advertising_manager.h
@@ -47,8 +47,8 @@ class AdvertisingConfig {
public:
std::vector<GapData> advertisement;
std::vector<GapData> scan_response;
- uint16_t interval_min;
- uint16_t interval_max;
+ uint32_t interval_min;
+ uint32_t interval_max;
AdvertisingType advertising_type;
AdvertiserAddressType requested_advertiser_address_type;
PeerAddressType peer_address_type;
diff --git a/system/gd/metrics/counter_metrics.cc b/system/gd/metrics/counter_metrics.cc
index 1315a87df1..99ba652bf9 100644
--- a/system/gd/metrics/counter_metrics.cc
+++ b/system/gd/metrics/counter_metrics.cc
@@ -27,8 +27,6 @@ namespace metrics {
const int COUNTER_METRICS_PERDIOD_MINUTES = 360; // Drain counters every 6 hours
-const ModuleFactory CounterMetrics::Factory = ModuleFactory([]() { return new CounterMetrics(); });
-
void CounterMetrics::ListDependencies(ModuleList* /* list */) const {}
void CounterMetrics::Start() {
diff --git a/system/gd/metrics/counter_metrics.h b/system/gd/metrics/counter_metrics.h
index a677041d16..e5a63f6394 100644
--- a/system/gd/metrics/counter_metrics.h
+++ b/system/gd/metrics/counter_metrics.h
@@ -25,14 +25,16 @@ namespace metrics {
class CounterMetrics : public bluetooth::Module {
public:
+ CounterMetrics(os::Handler* handler) : Module(handler) {}
+
bool CacheCount(int32_t key, int64_t value);
virtual bool Count(int32_t key, int64_t count);
void Stop() override;
- static const ModuleFactory Factory;
+ void Start() override;
protected:
+ CounterMetrics() = default;
void ListDependencies(ModuleList* list) const override;
- void Start() override;
std::string ToString() const override { return std::string("BluetoothCounterMetrics"); }
void DrainBufferedCounters();
virtual bool IsInitialized() { return initialized_; }
diff --git a/system/gd/module.h b/system/gd/module.h
index e708035a77..0e703ba817 100644
--- a/system/gd/module.h
+++ b/system/gd/module.h
@@ -84,6 +84,9 @@ public:
virtual ~Module() = default;
protected:
+ Module() = default;
+ Module(os::Handler* handler) : handler_(handler) {}
+
// Populate the provided list with modules that must start before yours
virtual void ListDependencies(ModuleList* list) const = 0;
@@ -120,7 +123,7 @@ private:
::bluetooth::os::Handler* handler_ = nullptr;
ModuleList dependencies_;
- const ModuleRegistry* registry_;
+ const ModuleRegistry* registry_ = nullptr;
};
class ModuleRegistry {
diff --git a/system/gd/proto/Android.bp b/system/gd/proto/Android.bp
index 6136cf84f6..1f7e72687f 100644
--- a/system/gd/proto/Android.bp
+++ b/system/gd/proto/Android.bp
@@ -16,9 +16,7 @@ java_library_static {
srcs: [
"bluetooth/metrics/bluetooth.proto",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
sdk_version: "current",
}
@@ -41,8 +39,6 @@ cc_library_static {
srcs: [
"bluetooth/metrics/bluetooth.proto",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
}
diff --git a/system/gd/rust/common/Android.bp b/system/gd/rust/common/Android.bp
index a1e8c4a977..26b724c900 100644
--- a/system/gd/rust/common/Android.bp
+++ b/system/gd/rust/common/Android.bp
@@ -35,9 +35,7 @@ rust_library {
],
},
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
}
diff --git a/system/gd/rust/topshim/le_audio/le_audio_shim.cc b/system/gd/rust/topshim/le_audio/le_audio_shim.cc
index 77d7a5c60d..53a0f7d98f 100644
--- a/system/gd/rust/topshim/le_audio/le_audio_shim.cc
+++ b/system/gd/rust/topshim/le_audio/le_audio_shim.cc
@@ -162,6 +162,8 @@ static BtLeAudioGroupStreamStatus to_rust_btle_audio_group_stream_status(
return BtLeAudioGroupStreamStatus::Streaming;
case le_audio::GroupStreamStatus::RELEASING:
return BtLeAudioGroupStreamStatus::Releasing;
+ case le_audio::GroupStreamStatus::RELEASING_AUTONOMOUS:
+ return BtLeAudioGroupStreamStatus::ReleasingAutonomous;
case le_audio::GroupStreamStatus::SUSPENDING:
return BtLeAudioGroupStreamStatus::Suspending;
case le_audio::GroupStreamStatus::SUSPENDED:
diff --git a/system/gd/rust/topshim/src/profiles/gatt.rs b/system/gd/rust/topshim/src/profiles/gatt.rs
index 7bed7bdf78..6bb9714852 100644
--- a/system/gd/rust/topshim/src/profiles/gatt.rs
+++ b/system/gd/rust/topshim/src/profiles/gatt.rs
@@ -15,6 +15,8 @@ use num_traits::cast::{FromPrimitive, ToPrimitive};
use std::fmt::{Display, Formatter, Result};
use std::sync::{Arc, Mutex};
+use std::ffi::CString;
+
use topshim_macros::{cb_variant, gen_cxx_extern_trivial};
pub type BtGattNotifyParams = bindings::btgatt_notify_params_t;
@@ -1165,7 +1167,8 @@ pub struct GattClient {
impl GattClient {
pub fn register_client(&self, uuid: &Uuid, eatt_support: bool) -> BtStatus {
- BtStatus::from(ccall!(self, register_client, uuid, eatt_support))
+ let cname = CString::new("rust_client").expect("CString::new failed");
+ BtStatus::from(ccall!(self, register_client, uuid, cname.as_ptr(), eatt_support))
}
pub fn unregister_client(&self, client_if: i32) -> BtStatus {
diff --git a/system/gd/rust/topshim/src/profiles/le_audio.rs b/system/gd/rust/topshim/src/profiles/le_audio.rs
index 266f24f7fb..651ad82ecd 100644
--- a/system/gd/rust/topshim/src/profiles/le_audio.rs
+++ b/system/gd/rust/topshim/src/profiles/le_audio.rs
@@ -109,6 +109,7 @@ pub mod ffi {
Idle = 0,
Streaming,
Releasing,
+ ReleasingAutonomous,
Suspending,
Suspended,
ConfiguredAutonomous,
@@ -413,11 +414,12 @@ impl From<BtLeAudioGroupStreamStatus> for i32 {
BtLeAudioGroupStreamStatus::Idle => 0,
BtLeAudioGroupStreamStatus::Streaming => 1,
BtLeAudioGroupStreamStatus::Releasing => 2,
- BtLeAudioGroupStreamStatus::Suspending => 3,
- BtLeAudioGroupStreamStatus::Suspended => 4,
- BtLeAudioGroupStreamStatus::ConfiguredAutonomous => 5,
- BtLeAudioGroupStreamStatus::ConfiguredByUser => 6,
- BtLeAudioGroupStreamStatus::Destroyed => 7,
+ BtLeAudioGroupStreamStatus::ReleasingAutonomous => 3,
+ BtLeAudioGroupStreamStatus::Suspending => 4,
+ BtLeAudioGroupStreamStatus::Suspended => 5,
+ BtLeAudioGroupStreamStatus::ConfiguredAutonomous => 6,
+ BtLeAudioGroupStreamStatus::ConfiguredByUser => 7,
+ BtLeAudioGroupStreamStatus::Destroyed => 8,
_ => panic!("Invalid value {:?} to BtLeAudioGroupStreamStatus", value),
}
}
@@ -429,11 +431,12 @@ impl From<i32> for BtLeAudioGroupStreamStatus {
0 => BtLeAudioGroupStreamStatus::Idle,
1 => BtLeAudioGroupStreamStatus::Streaming,
2 => BtLeAudioGroupStreamStatus::Releasing,
- 3 => BtLeAudioGroupStreamStatus::Suspending,
- 4 => BtLeAudioGroupStreamStatus::Suspended,
- 5 => BtLeAudioGroupStreamStatus::ConfiguredAutonomous,
- 6 => BtLeAudioGroupStreamStatus::ConfiguredByUser,
- 7 => BtLeAudioGroupStreamStatus::Destroyed,
+ 3 => BtLeAudioGroupStreamStatus::ReleasingAutonomous,
+ 4 => BtLeAudioGroupStreamStatus::Suspending,
+ 5 => BtLeAudioGroupStreamStatus::Suspended,
+ 6 => BtLeAudioGroupStreamStatus::ConfiguredAutonomous,
+ 7 => BtLeAudioGroupStreamStatus::ConfiguredByUser,
+ 8 => BtLeAudioGroupStreamStatus::Destroyed,
_ => panic!("Invalid value {} to BtLeAudioGroupStreamStatus", value),
}
}
diff --git a/system/gd/storage/config_keys.h b/system/gd/storage/config_keys.h
index c3e6573c78..d3659b6a96 100644
--- a/system/gd/storage/config_keys.h
+++ b/system/gd/storage/config_keys.h
@@ -99,6 +99,7 @@
#define BTIF_STORAGE_KEY_LEAUDIO_SOURCE_AUDIOLOCATION "SourceAudioLocation"
#define BTIF_STORAGE_KEY_LEAUDIO_SOURCE_PACS_BIN "SourcePacsBin"
#define BTIF_STORAGE_KEY_LEAUDIO_SOURCE_SUPPORTED_CONTEXT_TYPE "SourceSupportedContextType"
+#define BTIF_STORAGE_KEY_LEAUDIO_GMAP_BIN "LeAudioGmap"
#define BTIF_STORAGE_KEY_LINK_KEY "LinkKey"
#define BTIF_STORAGE_KEY_LINK_KEY_TYPE "LinkKeyType"
#define BTIF_STORAGE_KEY_LOCAL_IO_CAPS "LocalIOCaps"
@@ -110,6 +111,7 @@
#define BTIF_STORAGE_KEY_PIN_LENGTH "PinLength"
#define BTIF_STORAGE_KEY_PRODUCT_ID "ProductId"
#define BTIF_STORAGE_KEY_REMOTE_SERVICE "Service"
+#define BTIF_STORAGE_KEY_REMOTE_SERVICE_LE "ServiceLe"
#define BTIF_STORAGE_KEY_REMOTE_VER_MFCT "Manufacturer"
#define BTIF_STORAGE_KEY_REMOTE_VER_SUBVER "LmpSubVer"
#define BTIF_STORAGE_KEY_REMOTE_VER_VER "LmpVer"
diff --git a/system/gd/storage/storage_module.cc b/system/gd/storage/storage_module.cc
index ce5a89c6f7..0a7c9a7ef1 100644
--- a/system/gd/storage/storage_module.cc
+++ b/system/gd/storage/storage_module.cc
@@ -63,11 +63,20 @@ const std::string StorageModule::kTimeCreatedFormat = "%Y-%m-%d %H:%M:%S";
const std::string StorageModule::kAdapterSection = BTIF_STORAGE_SECTION_ADAPTER;
-StorageModule::StorageModule(std::string config_file_path,
+StorageModule::StorageModule()
+ : StorageModule(nullptr, os::ParameterProvider::ConfigFilePath(), kDefaultConfigSaveDelay,
+ kDefaultTempDeviceCapacity, false, false) {}
+
+StorageModule::StorageModule(os::Handler* handler)
+ : StorageModule(handler, os::ParameterProvider::ConfigFilePath(), kDefaultConfigSaveDelay,
+ kDefaultTempDeviceCapacity, false, false) {}
+
+StorageModule::StorageModule(os::Handler* handler, std::string config_file_path,
std::chrono::milliseconds config_save_delay,
size_t temp_devices_capacity, bool is_restricted_mode,
bool is_single_user_mode)
- : config_file_path_(std::move(config_file_path)),
+ : Module(handler),
+ config_file_path_(std::move(config_file_path)),
config_save_delay_(config_save_delay),
temp_devices_capacity_(temp_devices_capacity),
is_restricted_mode_(is_restricted_mode),
@@ -84,10 +93,7 @@ StorageModule::~StorageModule() {
pimpl_.reset();
}
-const ModuleFactory StorageModule::Factory = ModuleFactory([]() {
- return new StorageModule(os::ParameterProvider::ConfigFilePath(), kDefaultConfigSaveDelay,
- kDefaultTempDeviceCapacity, false, false);
-});
+const ModuleFactory StorageModule::Factory = ModuleFactory([]() { return new StorageModule(); });
struct StorageModule::impl {
explicit impl(Handler* handler, ConfigCache cache, size_t in_memory_cache_size_limit)
@@ -144,9 +150,7 @@ void StorageModule::Clear() {
pimpl_->cache_.Clear();
}
-void StorageModule::ListDependencies(ModuleList* list) const {
- list->add<metrics::CounterMetrics>();
-}
+void StorageModule::ListDependencies(ModuleList* /*list*/) const {}
void StorageModule::Start() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
diff --git a/system/gd/storage/storage_module.h b/system/gd/storage/storage_module.h
index 12f5750196..b94251a0dc 100644
--- a/system/gd/storage/storage_module.h
+++ b/system/gd/storage/storage_module.h
@@ -55,12 +55,17 @@ public:
static const std::string kAdapterSection;
+ StorageModule();
+ StorageModule(os::Handler*);
StorageModule(const StorageModule&) = delete;
StorageModule& operator=(const StorageModule&) = delete;
~StorageModule();
static const ModuleFactory Factory;
+ void Start() override;
+ void Stop() override;
+
// Methods to access the storage layer via Device abstraction
// - Devices will be lazily created when methods below are called. Hence, no std::optional<> nor
// nullptr is used in
@@ -121,8 +126,6 @@ public:
protected:
void ListDependencies(ModuleList* list) const override;
- void Start() override;
- void Stop() override;
std::string ToString() const override;
friend shim::BtifConfigInterface;
@@ -147,8 +150,9 @@ protected:
// - temp_devices_capacity is the number of temporary, typically unpaired devices to hold in a
// memory based LRU
// - is_restricted_mode and is_single_user_mode are flags from upper layer
- StorageModule(std::string config_file_path, std::chrono::milliseconds config_save_delay,
- size_t temp_devices_capacity, bool is_restricted_mode, bool is_single_user_mode);
+ StorageModule(os::Handler* handler, std::string config_file_path,
+ std::chrono::milliseconds config_save_delay, size_t temp_devices_capacity,
+ bool is_restricted_mode, bool is_single_user_mode);
bool HasSection(const std::string& section) const;
bool HasProperty(const std::string& section, const std::string& property) const;
diff --git a/system/gd/storage/storage_module_test.cc b/system/gd/storage/storage_module_test.cc
index 2f600944ae..e58c210609 100644
--- a/system/gd/storage/storage_module_test.cc
+++ b/system/gd/storage/storage_module_test.cc
@@ -53,8 +53,8 @@ class TestStorageModule : public StorageModule {
public:
TestStorageModule(std::string config_file_path, std::chrono::milliseconds config_save_delay,
bool is_restricted_mode, bool is_single_user_mode)
- : StorageModule(std::move(config_file_path), config_save_delay, kTestTempDevicesCapacity,
- is_restricted_mode, is_single_user_mode) {}
+ : StorageModule(nullptr, std::move(config_file_path), config_save_delay,
+ kTestTempDevicesCapacity, is_restricted_mode, is_single_user_mode) {}
ConfigCache* GetMemoryOnlyConfigCachePublic() {
return StorageModule::GetMemoryOnlyConfigCache();
diff --git a/system/hci/Android.bp b/system/hci/Android.bp
index e4e0106606..1f0a51435a 100644
--- a/system/hci/Android.bp
+++ b/system/hci/Android.bp
@@ -26,9 +26,7 @@ cc_library_static {
"packages/modules/Bluetooth/system/stack/include",
"system/libhwbinder/include",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
diff --git a/system/include/Android.bp b/system/include/Android.bp
index 2482effbf3..c7cec2e726 100644
--- a/system/include/Android.bp
+++ b/system/include/Android.bp
@@ -22,7 +22,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "30",
}
@@ -49,7 +49,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "30",
}
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index a8a169a58d..fa15353d9f 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -299,7 +299,7 @@ typedef enum {
*/
BT_PROPERTY_TYPE_OF_DEVICE,
/**
- * Description - Bluetooth Service Record
+ * Description - Bluetooth Service Record, UUIDs on BREDR transport
* Access mode - Only GET.
* Data type - bt_service_record_t
*/
@@ -427,6 +427,14 @@ typedef enum {
*/
BT_PROPERTY_LPP_OFFLOAD_FEATURES,
+ /**
+ * Description - Bluetooth Service 128-bit UUIDs on LE transport
+ * Access mode - Only GET.
+ * Data type - Array of bluetooth::Uuid (Array size inferred from property
+ * length).
+ */
+ BT_PROPERTY_UUIDS_LE,
+
BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
} bt_property_type_t;
@@ -915,6 +923,11 @@ typedef struct {
int (*disconnect_all_acls)();
/**
+ * Call to disconnect ACL connection to device
+ */
+ int (*disconnect_acl)(const RawAddress& bd_addr, int transport);
+
+ /**
* Call to retrieve a generated random
*/
int (*le_rand)();
diff --git a/system/include/hardware/bt_gatt_client.h b/system/include/hardware/bt_gatt_client.h
index 5fbbd5f7c7..94bb4c7c24 100644
--- a/system/include/hardware/bt_gatt_client.h
+++ b/system/include/hardware/bt_gatt_client.h
@@ -203,7 +203,7 @@ typedef struct {
typedef struct {
/** Registers a GATT client application with the stack */
- bt_status_t (*register_client)(const bluetooth::Uuid& uuid, bool eatt_support);
+ bt_status_t (*register_client)(const bluetooth::Uuid& uuid, const char* name, bool eatt_support);
/** Unregister a client application from the stack */
bt_status_t (*unregister_client)(int client_if);
diff --git a/system/include/hardware/bt_le_audio.h b/system/include/hardware/bt_le_audio.h
index 91ce17c388..94c3762596 100644
--- a/system/include/hardware/bt_le_audio.h
+++ b/system/include/hardware/bt_le_audio.h
@@ -70,6 +70,7 @@ enum class GroupStreamStatus {
IDLE = 0,
STREAMING,
RELEASING,
+ RELEASING_AUTONOMOUS,
SUSPENDING,
SUSPENDED,
CONFIGURED_AUTONOMOUS,
diff --git a/system/include/hardware/distance_measurement_interface.h b/system/include/hardware/distance_measurement_interface.h
index e5de171c39..65532034d9 100644
--- a/system/include/hardware/distance_measurement_interface.h
+++ b/system/include/hardware/distance_measurement_interface.h
@@ -34,7 +34,9 @@ public:
uint32_t error_centimeter, int azimuth_angle,
int error_azimuth_angle, int altitude_angle,
int error_altitude_angle, uint64_t elapsedRealtimeNanos,
- int8_t confidence_level, uint8_t method) = 0;
+ int8_t confidence_level, double delayedSpreadCentimeters,
+ uint8_t detectedAttackLevel,
+ double velocityCentimetersPerSecond, uint8_t method) = 0;
};
class DistanceMeasurementInterface {
diff --git a/system/internal_include/Android.bp b/system/internal_include/Android.bp
index 7d66847ebf..06a56b06fa 100644
--- a/system/internal_include/Android.bp
+++ b/system/internal_include/Android.bp
@@ -14,7 +14,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "30",
}
diff --git a/system/log/Android.bp b/system/log/Android.bp
index ebfd81d1fb..649a039aff 100644
--- a/system/log/Android.bp
+++ b/system/log/Android.bp
@@ -2,9 +2,7 @@ cc_library {
name: "libbluetooth_log",
host_supported: true,
min_sdk_version: "33",
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
export_include_dirs: [
"include",
],
diff --git a/system/main/Android.bp b/system/main/Android.bp
index 5272905831..15b1351bc3 100644
--- a/system/main/Android.bp
+++ b/system/main/Android.bp
@@ -41,9 +41,7 @@ cc_library_static {
"packages/modules/Bluetooth/system/udrv/include",
"system/security/keystore/include",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
static_libs: [
@@ -102,9 +100,7 @@ cc_library {
sanitize: {
scs: true,
},
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "30",
}
diff --git a/system/main/shim/distance_measurement_manager.cc b/system/main/shim/distance_measurement_manager.cc
index 61150f3b07..fd7eb3591d 100644
--- a/system/main/shim/distance_measurement_manager.cc
+++ b/system/main/shim/distance_measurement_manager.cc
@@ -25,6 +25,7 @@
#include "stack/include/acl_api.h"
#include "stack/include/main_thread.h"
+using bluetooth::hci::DistanceMeasurementDetectedAttackLevel;
using bluetooth::hci::DistanceMeasurementErrorCode;
using bluetooth::hci::DistanceMeasurementMethod;
using namespace bluetooth;
@@ -117,15 +118,18 @@ public:
void OnDistanceMeasurementResult(bluetooth::hci::Address address, uint32_t centimeter,
uint32_t error_centimeter, int azimuth_angle,
int error_azimuth_angle, int altitude_angle,
- int error_altitude_angle, uint64_t elapsedRealtimeNanos,
- int8_t confidence_level,
+ int error_altitude_angle, uint64_t elapsed_realtime_nanos,
+ int8_t confidence_level, double delay_spread_meters,
+ DistanceMeasurementDetectedAttackLevel detected_attack_level,
+ double velocity_meters_per_second,
DistanceMeasurementMethod method) override {
- do_in_jni_thread(base::BindOnce(&::DistanceMeasurementCallbacks::OnDistanceMeasurementResult,
- base::Unretained(distance_measurement_callbacks_),
- bluetooth::ToRawAddress(address), centimeter, error_centimeter,
- azimuth_angle, error_azimuth_angle, altitude_angle,
- error_altitude_angle, elapsedRealtimeNanos, confidence_level,
- static_cast<uint8_t>(method)));
+ do_in_jni_thread(base::BindOnce(
+ &::DistanceMeasurementCallbacks::OnDistanceMeasurementResult,
+ base::Unretained(distance_measurement_callbacks_), bluetooth::ToRawAddress(address),
+ centimeter, error_centimeter, azimuth_angle, error_azimuth_angle, altitude_angle,
+ error_altitude_angle, elapsed_realtime_nanos, confidence_level, delay_spread_meters,
+ static_cast<uint8_t>(detected_attack_level), velocity_meters_per_second,
+ static_cast<uint8_t>(method)));
}
void OnRasFragmentReady(bluetooth::hci::Address address, uint16_t procedure_counter, bool is_last,
@@ -248,9 +252,10 @@ public:
bluetooth::ToGdAddress(address), GetConnectionHandleAndRole(address), conn_interval);
}
- void OnDisconnected(const RawAddress& address) {
+ void OnDisconnected(const RawAddress& address,
+ const ras::RasDisconnectReason& ras_disconnect_reason) {
bluetooth::shim::GetDistanceMeasurementManager()->HandleRasClientDisconnectedEvent(
- bluetooth::ToGdAddress(address));
+ bluetooth::ToGdAddress(address), ras_disconnect_reason);
}
// Must be called from main_thread
diff --git a/system/main/shim/entry.cc b/system/main/shim/entry.cc
index ff0014b481..c1014a8a09 100644
--- a/system/main/shim/entry.cc
+++ b/system/main/shim/entry.cc
@@ -27,6 +27,7 @@
#include "hci/msft.h"
#include "hci/remote_name_request.h"
#include "lpp/lpp_offload_manager.h"
+#include "main/shim/shim.h"
#include "main/shim/stack.h"
#include "metrics/counter_metrics.h"
#include "os/handler.h"
@@ -71,13 +72,13 @@ storage::StorageModule* GetStorage() {
hci::AclManager* GetAclManager() { return Stack::GetInstance()->GetInstance<hci::AclManager>(); }
-metrics::CounterMetrics* GetCounterMetrics() {
- return Stack::GetInstance()->GetInstance<metrics::CounterMetrics>();
-}
+metrics::CounterMetrics* GetCounterMetrics() { return Stack::GetInstance()->GetCounterMetrics(); }
hci::MsftExtensionManager* GetMsftExtensionManager() {
return Stack::GetInstance()->GetInstance<hci::MsftExtensionManager>();
}
+bool is_gd_stack_started_up() { return Stack::GetInstance()->IsRunning(); }
+
} // namespace shim
} // namespace bluetooth
diff --git a/system/main/shim/shim.cc b/system/main/shim/shim.cc
index 69c70cd9ba..81bd0b6bf3 100644
--- a/system/main/shim/shim.cc
+++ b/system/main/shim/shim.cc
@@ -54,7 +54,3 @@ EXPORT_SYMBOL extern const module_t gd_shim_module = {.name = GD_SHIM_MODULE,
.shut_down = GeneralShutDown,
.clean_up = kUnusedModuleApi,
.dependencies = {kUnusedModuleDependencies}};
-
-bool bluetooth::shim::is_gd_stack_started_up() {
- return bluetooth::shim::Stack::GetInstance()->IsRunning();
-}
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 6da9117ae4..d45d65d7cd 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -67,6 +67,7 @@ namespace shim {
struct Stack::impl {
Acl* acl_ = nullptr;
+ metrics::CounterMetrics* counter_metrics_ = nullptr;
};
Stack::Stack() { pimpl_ = std::make_shared<Stack::impl>(); }
@@ -82,6 +83,11 @@ void Stack::StartEverything() {
log::info("Starting Gd stack");
ModuleList modules;
+ stack_thread_ = new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME);
+ stack_handler_ = new os::Handler(stack_thread_);
+
+ pimpl_->counter_metrics_ = new metrics::CounterMetrics(new Handler(stack_thread_));
+
#if TARGET_FLOSS
modules.add<sysprops::SyspropsModule>();
#else
@@ -89,7 +95,6 @@ void Stack::StartEverything() {
modules.add<lpp::LppOffloadManager>();
}
#endif
- modules.add<metrics::CounterMetrics>();
modules.add<hal::HciHal>();
modules.add<hci::HciLayer>();
modules.add<storage::StorageModule>();
@@ -103,14 +108,30 @@ void Stack::StartEverything() {
modules.add<hci::LeScanningManager>();
modules.add<hci::DistanceMeasurementManager>();
- stack_thread_ = new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME);
- StartUp(&modules, stack_thread_);
+ management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
+ management_handler_ = new Handler(management_thread_);
- stack_handler_ = new os::Handler(stack_thread_);
+ WakelockManager::Get().Acquire();
+
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ management_handler_->Post(common::BindOnce(&Stack::handle_start_up, common::Unretained(this),
+ &modules, std::move(promise)));
+
+ auto init_status = future.wait_for(
+ std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ true)));
+
+ WakelockManager::Get().Release();
+
+ log::info("init_status == {}", int(init_status));
+
+ log::assert_that(init_status == std::future_status::ready, "Can't start stack, last instance: {}",
+ registry_.last_instance_);
log::info("Successfully toggled Gd stack");
is_running_ = true;
+
// Make sure the leaf modules are started
log::assert_that(GetInstance<storage::StorageModule>() != nullptr,
"assert failed: GetInstance<storage::StorageModule>() != nullptr");
@@ -127,19 +148,6 @@ void Stack::StartEverything() {
bluetooth::shim::init_distance_measurement_manager();
}
-void Stack::StartModuleStack(const ModuleList* modules, const os::Thread* thread) {
- std::lock_guard<std::recursive_mutex> lock(mutex_);
- log::assert_that(!is_running_, "Gd stack already running");
- stack_thread_ = const_cast<os::Thread*>(thread);
- log::info("Starting Gd stack");
-
- StartUp(const_cast<ModuleList*>(modules), stack_thread_);
- stack_handler_ = new os::Handler(stack_thread_);
-
- num_modules_ = modules->NumModules();
- is_running_ = true;
-}
-
void Stack::Stop() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
bluetooth::shim::hci_on_shutting_down();
@@ -192,13 +200,19 @@ bool Stack::IsRunning() {
return is_running_;
}
-Acl* Stack::GetAcl() {
+Acl* Stack::GetAcl() const {
std::lock_guard<std::recursive_mutex> lock(mutex_);
log::assert_that(is_running_, "assert failed: is_running_");
log::assert_that(pimpl_->acl_ != nullptr, "Acl shim layer has not been created");
return pimpl_->acl_;
}
+metrics::CounterMetrics* Stack::GetCounterMetrics() const {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ log::assert_that(is_running_, "assert failed: is_running_");
+ return pimpl_->counter_metrics_;
+}
+
os::Handler* Stack::GetHandler() {
std::lock_guard<std::recursive_mutex> lock(mutex_);
log::assert_that(is_running_, "assert failed: is_running_");
@@ -222,32 +236,9 @@ void Stack::Dump(int fd, std::promise<void> promise) const {
}
}
-void Stack::StartUp(ModuleList* modules, Thread* stack_thread) {
- management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
- management_handler_ = new Handler(management_thread_);
-
- WakelockManager::Get().Acquire();
-
- std::promise<void> promise;
- auto future = promise.get_future();
- management_handler_->Post(common::BindOnce(&Stack::handle_start_up, common::Unretained(this),
- modules, stack_thread, std::move(promise)));
-
- auto init_status = future.wait_for(
- std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ true)));
-
- WakelockManager::Get().Release();
-
- log::info("init_status == {}", int(init_status));
-
- log::assert_that(init_status == std::future_status::ready, "Can't start stack, last instance: {}",
- registry_.last_instance_);
-
- log::info("init complete");
-}
-
-void Stack::handle_start_up(ModuleList* modules, Thread* stack_thread, std::promise<void> promise) {
- registry_.Start(modules, stack_thread);
+void Stack::handle_start_up(ModuleList* modules, std::promise<void> promise) {
+ pimpl_->counter_metrics_->Start();
+ registry_.Start(modules, stack_thread_);
promise.set_value();
}
diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h
index 0dfd3fe3ab..e65a0ef528 100644
--- a/system/main/shim/stack.h
+++ b/system/main/shim/stack.h
@@ -25,10 +25,13 @@
// The shim layer implementation on the Gd stack side.
namespace bluetooth {
+namespace metrics {
+class CounterMetrics;
+}
+
namespace shim {
class Acl;
-class Btm;
// GD shim stack, having modes corresponding to legacy stack
class Stack {
@@ -39,7 +42,7 @@ public:
Stack(const Stack&) = delete;
Stack& operator=(const Stack&) = delete;
- ~Stack() = default;
+ virtual ~Stack() = default;
// Running mode, everything is up
void StartEverything();
@@ -57,17 +60,13 @@ public:
return registry_.IsStarted(&T::Factory);
}
- Acl* GetAcl();
+ virtual Acl* GetAcl() const;
+ virtual metrics::CounterMetrics* GetCounterMetrics() const;
os::Handler* GetHandler();
void Dump(int fd, std::promise<void> promise) const;
- // Start the list of modules with the given stack manager thread
- void StartModuleStack(const ModuleList* modules, const os::Thread* thread);
-
- size_t NumModules() const { return num_modules_; }
-
private:
struct impl;
std::shared_ptr<impl> pimpl_;
@@ -76,15 +75,12 @@ private:
bool is_running_ = false;
os::Thread* stack_thread_ = nullptr;
os::Handler* stack_handler_ = nullptr;
- size_t num_modules_{0};
-
- void StartUp(ModuleList* modules, os::Thread* stack_thread);
os::Thread* management_thread_ = nullptr;
os::Handler* management_handler_ = nullptr;
ModuleRegistry registry_;
- void handle_start_up(ModuleList* modules, os::Thread* stack_thread, std::promise<void> promise);
+ void handle_start_up(ModuleList* modules, std::promise<void> promise);
void handle_shut_down(std::promise<void> promise);
static std::chrono::milliseconds get_gd_stack_timeout_ms(bool is_start);
};
diff --git a/system/main/stack_config.cc b/system/main/stack_config.cc
index 9b04e7f16c..20db9be13d 100644
--- a/system/main/stack_config.cc
+++ b/system/main/stack_config.cc
@@ -60,7 +60,7 @@ static future_t* init() {
#if defined(TARGET_FLOSS)
const char* path = "/etc/bluetooth/bt_stack.conf";
#elif defined(__ANDROID__)
- const char* path = "/apex/com.android.btservices/etc/bluetooth/bt_stack.conf";
+ const char* path = "/apex/com.android.bt/etc/bluetooth/bt_stack.conf";
#else // !defined(__ANDROID__)
const char* path = "bt_stack.conf";
#endif // defined(__ANDROID__)
diff --git a/system/osi/Android.bp b/system/osi/Android.bp
index 4806d61985..23055416f2 100644
--- a/system/osi/Android.bp
+++ b/system/osi/Android.bp
@@ -83,9 +83,7 @@ cc_library_static {
"-DLIB_OSI_INTERNAL",
],
min_sdk_version: "Tiramisu",
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
header_libs: ["libbluetooth_headers"],
static_libs: [
"bluetooth_flags_c_lib",
diff --git a/system/packet/Android.bp b/system/packet/Android.bp
index d3ba6dfd71..05c2c8cc5b 100644
--- a/system/packet/Android.bp
+++ b/system/packet/Android.bp
@@ -20,9 +20,7 @@ cc_library_static {
"lib-bt-packets-base",
"libbluetooth_log",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
}
diff --git a/system/packet/avrcp/Android.bp b/system/packet/avrcp/Android.bp
index 1808112072..ee51ed340a 100644
--- a/system/packet/avrcp/Android.bp
+++ b/system/packet/avrcp/Android.bp
@@ -48,8 +48,6 @@ cc_library_static {
"libbluetooth_log",
"libchrome",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
}
diff --git a/system/packet/base/Android.bp b/system/packet/base/Android.bp
index 732d597c34..f81a511374 100644
--- a/system/packet/base/Android.bp
+++ b/system/packet/base/Android.bp
@@ -18,9 +18,7 @@ cc_library_static {
"packet.cc",
"packet_builder.cc",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "30",
shared_libs: [
"libbase",
diff --git a/system/pdl/hci/Android.bp b/system/pdl/hci/Android.bp
index 354d500300..832598aaff 100644
--- a/system/pdl/hci/Android.bp
+++ b/system/pdl/hci/Android.bp
@@ -19,9 +19,7 @@ cc_library_headers {
"BluetoothGeneratedPacketsHci_h",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
@@ -48,9 +46,7 @@ cc_library_static {
"//hardware/interfaces/bluetooth/aidl/vts",
"//packages/modules/Bluetooth/system:__subpackages__",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
diff --git a/system/pdl/l2cap/Android.bp b/system/pdl/l2cap/Android.bp
index d55935995c..bbd499612e 100644
--- a/system/pdl/l2cap/Android.bp
+++ b/system/pdl/l2cap/Android.bp
@@ -15,9 +15,7 @@ cc_library_headers {
"BluetoothGeneratedPacketsL2cap_h",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
@@ -30,8 +28,6 @@ cc_library_static {
"libbluetooth_l2cap_pdl_header",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
diff --git a/system/pdl/ras/Android.bp b/system/pdl/ras/Android.bp
index d54d753b77..84e516c64f 100644
--- a/system/pdl/ras/Android.bp
+++ b/system/pdl/ras/Android.bp
@@ -15,9 +15,7 @@ cc_library_headers {
"BluetoothGeneratedPacketsRas_h",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
@@ -30,8 +28,6 @@ cc_library_static {
"libbluetooth_ras_pdl_header",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
diff --git a/system/pdl/security/Android.bp b/system/pdl/security/Android.bp
index ebd5a3d8db..fbbc2b7339 100644
--- a/system/pdl/security/Android.bp
+++ b/system/pdl/security/Android.bp
@@ -15,9 +15,7 @@ cc_library_headers {
"BluetoothGeneratedPacketsSmp_h",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
@@ -30,9 +28,7 @@ cc_library_static {
"libbluetooth_smp_pdl_header",
],
host_supported: true,
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "33",
}
diff --git a/system/profile/avrcp/Android.bp b/system/profile/avrcp/Android.bp
index 8db8f11b2a..6ea0ea1abb 100644
--- a/system/profile/avrcp/Android.bp
+++ b/system/profile/avrcp/Android.bp
@@ -38,9 +38,7 @@ cc_library_static {
shared_libs: [
"liblog",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
}
diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc
index ef4fcda929..d094108d8a 100644
--- a/system/profile/avrcp/device.cc
+++ b/system/profile/avrcp/device.cc
@@ -766,6 +766,7 @@ void Device::GetElementAttributesResponse(uint8_t label,
SongInfo info) {
auto get_element_attributes_pkt = pkt;
auto attributes_requested = get_element_attributes_pkt->GetAttributesRequested();
+ bool all_attributes_flag = com::android::bluetooth::flags::get_all_element_attributes_empty();
auto response = GetElementAttributesResponseBuilder::MakeBuilder(ctrl_mtu_);
@@ -780,10 +781,12 @@ void Device::GetElementAttributesResponse(uint8_t label,
for (const auto& attribute : attributes_requested) {
if (info.attributes.find(attribute) != info.attributes.end()) {
response->AddAttributeEntry(*info.attributes.find(attribute));
+ } else if (all_attributes_flag) {
+ response->AddAttributeEntry(attribute, std::string());
}
}
} else { // zero attributes requested which means all attributes requested
- if (!com::android::bluetooth::flags::get_all_element_attributes_empty()) {
+ if (!all_attributes_flag) {
for (const auto& attribute : info.attributes) {
response->AddAttributeEntry(attribute);
}
diff --git a/system/rust/Android.bp b/system/rust/Android.bp
index 592a686216..59ef358330 100644
--- a/system/rust/Android.bp
+++ b/system/rust/Android.bp
@@ -58,7 +58,7 @@ rust_defaults {
],
},
},
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
}
rust_ffi_static {
@@ -123,7 +123,7 @@ cc_library_static {
],
host_supported: true,
generated_sources: ["libbluetooth_core_rs_bridge_codegen"],
- apex_available: ["com.android.btservices"],
+ apex_available: ["com.android.bt"],
min_sdk_version: "Tiramisu",
// Bug: 286537287 this library gets linked with Rust objects but cross-language LTO
// isn't supported yet.
diff --git a/system/rust/src/core/shared_box.rs b/system/rust/src/core/shared_box.rs
index 5dd1118185..accd1ae6ec 100644
--- a/system/rust/src/core/shared_box.rs
+++ b/system/rust/src/core/shared_box.rs
@@ -79,7 +79,7 @@ impl<T: ?Sized> Clone for WeakBox<T> {
/// A strong reference to the contents within a SharedBox<>.
pub struct WeakBoxRef<'a, T: ?Sized>(&'a T, Weak<T>);
-impl<'a, T: ?Sized> WeakBoxRef<'a, T> {
+impl<T: ?Sized> WeakBoxRef<'_, T> {
/// Downgrade to a weak reference (with static lifetime) to the contents
/// within the underlying SharedBox<>
pub fn downgrade(&self) -> WeakBox<T> {
@@ -87,7 +87,7 @@ impl<'a, T: ?Sized> WeakBoxRef<'a, T> {
}
}
-impl<'a, T: ?Sized> Deref for WeakBoxRef<'a, T> {
+impl<T: ?Sized> Deref for WeakBoxRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@@ -95,7 +95,7 @@ impl<'a, T: ?Sized> Deref for WeakBoxRef<'a, T> {
}
}
-impl<'a, T: ?Sized> Clone for WeakBoxRef<'a, T> {
+impl<T: ?Sized> Clone for WeakBoxRef<'_, T> {
fn clone(&self) -> Self {
Self(self.0, self.1.clone())
}
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 2d690133f3..841e7a93ef 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -24,9 +24,7 @@ cc_library_static {
shared_libs: [
"libchrome",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -150,9 +148,7 @@ cc_library_static {
"libldacBT_abr",
"libldacBT_enc",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -342,9 +338,7 @@ cc_library_static {
"libPlatformProperties",
"libcrypto",
],
- apex_available: [
- "com.android.btservices",
- ],
+ apex_available: ["com.android.bt"],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -355,6 +349,10 @@ cc_defaults {
defaults: [
"bluetooth_cflags",
],
+ cflags: [
+ "-Wno-missing-prototypes",
+ "-Wno-unused-parameter",
+ ],
include_dirs: [
"packages/modules/Bluetooth/system",
"packages/modules/Bluetooth/system/gd",
@@ -1105,6 +1103,7 @@ cc_test {
":TestMockRustFfi",
":TestMockStackArbiter",
":TestMockStackBtm",
+ ":TestMockStackConnMgr",
":TestMockStackSdp",
"gatt/gatt_utils.cc",
"test/common/mock_eatt.cc",
@@ -1639,6 +1638,7 @@ cc_test {
],
static_libs: [
"bluetooth_flags_c_lib_for_test",
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbluetooth-types",
"libbluetooth_gd",
@@ -1655,7 +1655,6 @@ cc_test {
"libprotobuf-cpp-lite",
"libstatslog_bt",
"server_configurable_flags",
- "libaconfig_storage_read_api_cc",
],
target: {
android: {
@@ -1887,6 +1886,7 @@ cc_test {
"test/hid/stack_hid_test.cc",
],
static_libs: [
+ "bluetooth_flags_c_lib_for_test",
"libbase",
"libbluetooth-types",
"libbluetooth_crypto_toolbox",
@@ -1905,6 +1905,7 @@ cc_test {
"libprotobuf-cpp-lite",
],
shared_libs: [
+ "libaconfig_storage_read_api_cc",
"libcrypto",
],
target: {
@@ -1962,6 +1963,7 @@ cc_test {
],
static_libs: [
"bluetooth_flags_c_lib_for_test",
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbluetooth-types",
"libbluetooth_gd",
@@ -1977,7 +1979,6 @@ cc_test {
"liblog",
"libosi",
"server_configurable_flags",
- "libaconfig_storage_read_api_cc",
],
aidl: {
libs: ["bluetooth_constants"],
diff --git a/system/stack/acl/ble_acl.cc b/system/stack/acl/ble_acl.cc
index 683e4801cd..0aeb9ceb91 100644
--- a/system/stack/acl/ble_acl.cc
+++ b/system/stack/acl/ble_acl.cc
@@ -50,9 +50,6 @@ static bool acl_ble_common_connection(const tBLE_BD_ADDR& address_with_type, uin
btm_ble_clear_topology_mask(BTM_BLE_STATE_INIT_BIT);
}
- // Inform any applications that a connection has completed.
- connection_manager::on_connection_complete(address_with_type.bda);
-
// Allocate or update the security device record for this device
btm_ble_connected(address_with_type.bda, handle, HCI_ENCRYPT_MODE_DISABLED, role,
address_with_type.type, is_in_security_db,
@@ -108,8 +105,6 @@ void acl_ble_enhanced_connection_complete_from_shim(
uint16_t conn_interval, uint16_t conn_latency, uint16_t conn_timeout,
const RawAddress& local_rpa, const RawAddress& peer_rpa, tBLE_ADDR_TYPE peer_addr_type,
bool can_read_discoverable_characteristics) {
- connection_manager::on_connection_complete(address_with_type.bda);
-
tBLE_BD_ADDR resolved_address_with_type;
const bool is_in_security_db =
maybe_resolve_received_address(address_with_type, &resolved_address_with_type);
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index 4b9cc79e32..6e16d43c17 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -1511,9 +1511,8 @@ static void btm_ble_scan_filt_param_cfg_evt(uint8_t /* avbl_space */,
* If the duration is zero, the periodic inquiry mode is
* cancelled.
*
- * Parameters: duration - Duration of inquiry in seconds. With flag
- * le_inquiry_duration duration is a multiplier for
- * 1.28 seconds.
+ * Parameters: duration - Duration of inquiry as a multiplier for 1.28
+ * seconds.
*
* Returns tBTM_STATUS::BTM_CMD_STARTED if successfully started
* tBTM_STATUS::BTM_BUSY - if an inquiry is already active
@@ -1559,10 +1558,6 @@ tBTM_STATUS btm_ble_start_inquiry(uint8_t duration) {
} else if ((btm_cb.ble_ctr_cb.inq_var.scan_interval != scan_interval) ||
(btm_cb.ble_ctr_cb.inq_var.scan_window != scan_window)) {
log::verbose("restart LE scan with low latency scan params");
- if (!com::android::bluetooth::flags::le_inquiry_duration()) {
- btm_cb.ble_ctr_cb.inq_var.scan_interval = scan_interval;
- btm_cb.ble_ctr_cb.inq_var.scan_window = scan_window;
- }
btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
btm_send_hci_set_scan_params(BTM_BLE_SCAN_MODE_ACTI, scan_interval, scan_window, scan_phy,
btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, SP_ADV_ALL);
@@ -1576,8 +1571,7 @@ tBTM_STATUS btm_ble_start_inquiry(uint8_t duration) {
if (duration != 0) {
/* start inquiry timer */
- uint64_t duration_ms =
- duration * (com::android::bluetooth::flags::le_inquiry_duration() ? 1280 : 1000);
+ uint64_t duration_ms = duration * 1280;
alarm_set_on_mloop(btm_cb.ble_ctr_cb.inq_var.inquiry_timer, duration_ms,
btm_ble_inquiry_timer_timeout, NULL);
}
@@ -1924,7 +1918,7 @@ static DEV_CLASS btm_ble_appearance_to_cod(uint16_t appearance) {
dev_class[1] = BTM_COD_MAJOR_PERIPHERAL;
dev_class[2] = BTM_COD_MINOR_DIGITAL_PAN;
break;
- case BTM_BLE_APPEARANCE_UKNOWN:
+ case BTM_BLE_APPEARANCE_UNKNOWN:
case BTM_BLE_APPEARANCE_GENERIC_CLOCK:
case BTM_BLE_APPEARANCE_GENERIC_TAG:
case BTM_BLE_APPEARANCE_GENERIC_KEYRING:
diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc
index 2ab87a0573..e05091f656 100644
--- a/system/stack/btm/btm_ble_sec.cc
+++ b/system/stack/btm/btm_ble_sec.cc
@@ -1595,7 +1595,7 @@ void btm_ble_connection_established(const RawAddress& bda) {
}
// Encrypt the link if device is bonded
- if (com::android::bluetooth::flags::le_enc_on_reconnection() &&
+ if (com::android::bluetooth::flags::le_enc_on_reconnect() &&
p_dev_rec->sec_rec.is_le_link_key_known()) {
btm_ble_set_encryption(bda, BTM_BLE_SEC_ENCRYPT,
p_dev_rec->role_central ? HCI_ROLE_CENTRAL : HCI_ROLE_PERIPHERAL);
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 47dda744d7..63be75bffd 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -35,6 +35,8 @@
#include <cstdint>
#include <string>
+#include "bta/dm/bta_dm_act.h"
+#include "bta/dm/bta_dm_sec_int.h"
#include "btif/include/btif_storage.h"
#include "common/metrics.h"
#include "common/time_util.h"
@@ -103,9 +105,6 @@ extern tBTM_CB btm_cb;
bool btm_ble_init_pseudo_addr(tBTM_SEC_DEV_REC* p_dev_rec, const RawAddress& new_pseudo_addr);
void bta_dm_remove_device(const RawAddress& bd_addr);
-void bta_dm_on_encryption_change(bt_encryption_change_evt encryption_change);
-void bta_dm_remote_key_missing(const RawAddress bd_addr);
-void bta_dm_process_remove_device(const RawAddress& bd_addr);
static tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec);
@@ -1029,7 +1028,7 @@ tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr, tBT_TRANSPORT transport
: p_dev_rec->sec_rec.classic_link;
/* Enqueue security request if security is active */
- if (!com::android::bluetooth::flags::le_enc_on_reconnection()) {
+ if (!com::android::bluetooth::flags::le_enc_on_reconnect()) {
if (p_dev_rec->sec_rec.p_callback ||
(p_dev_rec->sec_rec.le_link != tSECURITY_STATE::IDLE &&
p_dev_rec->sec_rec.classic_link != tSECURITY_STATE::IDLE)) {
@@ -1099,9 +1098,9 @@ tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr, tBT_TRANSPORT transport
return rc;
}
-bool BTM_SecIsSecurityPending(const RawAddress& bd_addr) {
+bool BTM_SecIsLeSecurityPending(const RawAddress& bd_addr) {
tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
- return p_dev_rec && (p_dev_rec->sec_rec.is_security_state_encrypting() ||
+ return p_dev_rec && (p_dev_rec->sec_rec.is_security_state_le_encrypting() ||
p_dev_rec->sec_rec.le_link == tSECURITY_STATE::AUTHENTICATING);
}
@@ -4974,7 +4973,7 @@ static void btm_sec_check_pending_enc_req(tBTM_SEC_DEV_REC* p_dev_rec, tBT_TRANS
node = list_next(node);
log::debug("btm_sec_check_pending_enc_req : sec_act=0x{:x}", p_e->sec_act);
if (p_e->bd_addr == p_dev_rec->bd_addr && p_e->psm == 0 && p_e->transport == transport) {
- if (!com::android::bluetooth::flags::le_enc_on_reconnection()) {
+ if (!com::android::bluetooth::flags::le_enc_on_reconnect()) {
if (encr_enable == 0 || transport == BT_TRANSPORT_BR_EDR ||
p_e->sec_act == BTM_BLE_SEC_ENCRYPT || p_e->sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM ||
(p_e->sec_act == BTM_BLE_SEC_ENCRYPT_MITM &&
diff --git a/system/stack/btm/btm_sec.h b/system/stack/btm/btm_sec.h
index 9646ada250..117edc0c3c 100644
--- a/system/stack/btm/btm_sec.h
+++ b/system/stack/btm/btm_sec.h
@@ -243,7 +243,7 @@ tBTM_STATUS BTM_SetEncryption(const RawAddress& bd_addr, tBT_TRANSPORT transport
tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
tBTM_BLE_SEC_ACT sec_act);
-bool BTM_SecIsSecurityPending(const RawAddress& bd_addr);
+bool BTM_SecIsLeSecurityPending(const RawAddress& bd_addr);
/*******************************************************************************
*
diff --git a/system/stack/btm/btm_security_client_interface.cc b/system/stack/btm/btm_security_client_interface.cc
index f2d397a9a7..3adda4d0f8 100644
--- a/system/stack/btm/btm_security_client_interface.cc
+++ b/system/stack/btm/btm_security_client_interface.cc
@@ -54,7 +54,7 @@ static SecurityClientInterface security = {
.BTM_SecClearSecurityFlags = BTM_SecClearSecurityFlags,
.BTM_SetEncryption = BTM_SetEncryption,
.BTM_IsEncrypted = BTM_IsEncrypted,
- .BTM_SecIsSecurityPending = BTM_SecIsSecurityPending,
+ .BTM_SecIsLeSecurityPending = BTM_SecIsLeSecurityPending,
.BTM_IsLinkKeyKnown = BTM_IsLinkKeyKnown,
.BTM_SetSecurityLevel = BTM_SetSecurityLevel,
diff --git a/system/stack/connection_manager/connection_manager.cc b/system/stack/connection_manager/connection_manager.cc
index 25c9ee57ac..ed223b92a8 100644
--- a/system/stack/connection_manager/connection_manager.cc
+++ b/system/stack/connection_manager/connection_manager.cc
@@ -52,6 +52,8 @@ struct closure_data {
base::Location posted_from;
};
+extern std::string get_client_name(uint8_t gatt_if);
+
static void alarm_closure_cb(void* p) {
closure_data* data = (closure_data*)p;
log::verbose("executing timer scheduled at {}", data->posted_from.ToString());
@@ -367,7 +369,7 @@ bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
}
if (is_anyone_connecting(it)) {
- log::debug("some device is still connecting, app_id={}, address={}", static_cast<int>(app_id),
+ log::debug("some app is still connecting, app_id={}, address={}", static_cast<int>(app_id),
address);
/* Check which method should be used now.*/
if (!accept_list_enabled) {
@@ -627,14 +629,14 @@ void dump(int fd) {
if (!entry.second.doing_direct_conn.empty()) {
dprintf(fd, "\n\t\tapps doing direct connect: ");
for (const auto& id : entry.second.doing_direct_conn) {
- dprintf(fd, "%d, ", id.first);
+ dprintf(fd, "%s (%d), ", get_client_name(id.first).c_str(), id.first);
}
}
if (!entry.second.doing_bg_conn.empty()) {
dprintf(fd, "\n\t\tapps doing background connect: ");
for (const auto& id : entry.second.doing_bg_conn) {
- dprintf(fd, "%d, ", id);
+ dprintf(fd, "%s (%d), ", get_client_name(id).c_str(), id);
}
}
}
diff --git a/system/stack/connection_manager/connection_manager.h b/system/stack/connection_manager/connection_manager.h
index 0f029fbe60..5328b4e7cb 100644
--- a/system/stack/connection_manager/connection_manager.h
+++ b/system/stack/connection_manager/connection_manager.h
@@ -23,6 +23,9 @@
#include "types/ble_address_with_type.h"
#include "types/raw_address.h"
+/* Must be provided by stack to connection manager, so it can dump nice client names in dumpsys */
+std::string get_client_name(uint8_t gatt_if);
+
/* connection_manager takes care of all the low-level details of LE connection
* initiation. It accept requests from multiple subsystems to connect to
* devices, and multiplex them into acceptlist add/remove, and scan parameter
diff --git a/system/stack/fuzzers/avrc_fuzzer.cc b/system/stack/fuzzers/avrc_fuzzer.cc
index be0c80f32c..d2c4acf4f8 100644
--- a/system/stack/fuzzers/avrc_fuzzer.cc
+++ b/system/stack/fuzzers/avrc_fuzzer.cc
@@ -33,10 +33,6 @@
#include "test/mock/mock_stack_l2cap_api.h"
#include "types/bluetooth/uuid.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
using bluetooth::Uuid;
using namespace bluetooth;
diff --git a/system/stack/fuzzers/bnep_fuzzer.cc b/system/stack/fuzzers/bnep_fuzzer.cc
index 79a70556e4..6976ccd602 100644
--- a/system/stack/fuzzers/bnep_fuzzer.cc
+++ b/system/stack/fuzzers/bnep_fuzzer.cc
@@ -31,10 +31,6 @@
#include "test/mock/mock_stack_l2cap_ble.h"
#include "types/bluetooth/uuid.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
using bluetooth::Uuid;
namespace {
diff --git a/system/stack/fuzzers/gatt_fuzzer.cc b/system/stack/fuzzers/gatt_fuzzer.cc
index 3a9fb81276..064a203a81 100644
--- a/system/stack/fuzzers/gatt_fuzzer.cc
+++ b/system/stack/fuzzers/gatt_fuzzer.cc
@@ -33,11 +33,8 @@
#include "test/mock/mock_stack_l2cap_api.h"
#include "test/mock/mock_stack_l2cap_ble.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
using bluetooth::Uuid;
+
bt_status_t do_in_main_thread(base::OnceCallback<void()>) {
// this is not properly mocked, so we use abort to catch if this is used in
// any test cases
diff --git a/system/stack/fuzzers/l2cap_fuzzer.cc b/system/stack/fuzzers/l2cap_fuzzer.cc
index 004e5b0920..6cb4d5170f 100644
--- a/system/stack/fuzzers/l2cap_fuzzer.cc
+++ b/system/stack/fuzzers/l2cap_fuzzer.cc
@@ -42,9 +42,6 @@
#include "test/mock/mock_stack_acl.h"
#include "test/mock/mock_stack_btm_devctl.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
using bluetooth::Uuid;
using testing::Return;
using namespace bluetooth;
diff --git a/system/stack/fuzzers/rfcomm_fuzzer.cc b/system/stack/fuzzers/rfcomm_fuzzer.cc
index 1445156aec..3418f3a6ed 100644
--- a/system/stack/fuzzers/rfcomm_fuzzer.cc
+++ b/system/stack/fuzzers/rfcomm_fuzzer.cc
@@ -39,10 +39,6 @@
#include "test/mock/mock_stack_l2cap_interface.h"
#include "test/rfcomm/stack_rfcomm_test_utils.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
diff --git a/system/stack/fuzzers/sdp_fuzzer.cc b/system/stack/fuzzers/sdp_fuzzer.cc
index bd5b6f77c2..de994d415e 100644
--- a/system/stack/fuzzers/sdp_fuzzer.cc
+++ b/system/stack/fuzzers/sdp_fuzzer.cc
@@ -31,10 +31,6 @@
#include "test/mock/mock_stack_l2cap_api.h"
#include "types/bluetooth/uuid.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
namespace {
#define SDP_DB_SIZE 0x10000
diff --git a/system/stack/fuzzers/smp_fuzzer.cc b/system/stack/fuzzers/smp_fuzzer.cc
index a99010254a..b4dad542ea 100644
--- a/system/stack/fuzzers/smp_fuzzer.cc
+++ b/system/stack/fuzzers/smp_fuzzer.cc
@@ -33,13 +33,10 @@
#include "test/mock/mock_stack_l2cap_api.h"
#include "test/mock/mock_stack_l2cap_ble.h"
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
bluetooth::common::MessageLoopThread* main_thread_ptr = nullptr;
bluetooth::common::MessageLoopThread* get_main_thread() { return main_thread_ptr; }
+
namespace {
#define SDP_DB_SIZE 0x10000
diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc
index 19ea3a3090..3e10a8b660 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -1486,14 +1486,7 @@ bool GATT_Connect(tGATT_IF gatt_if, const RawAddress& bd_addr, tBLE_ADDR_TYPE ad
/* Consider to remove gatt_act_connect at all */
ret = gatt_act_connect(p_reg, bd_addr, addr_type, transport, initiating_phys);
} else {
- log::verbose("Connecting without tcb address: {}", bd_addr);
-
- if (p_reg->direct_connect_request.count(bd_addr) == 0) {
- p_reg->direct_connect_request.insert(bd_addr);
- } else {
- log::warn("{} already added to gatt_if {} direct conn list", bd_addr, gatt_if);
- }
-
+ log::verbose("Connecting without tcb to: {}", bd_addr);
ret = connection_manager::direct_connect_add(gatt_if, bd_addr, addr_type);
}
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 70ff37546f..4ba6d7d127 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -193,7 +193,6 @@ typedef struct {
uint8_t listening{0}; /* if adv for all has been enabled */
bool eatt_support{false};
std::string name;
- std::set<RawAddress> direct_connect_request;
std::map<RawAddress, uint16_t> mtu_prefs;
} tGATT_REG;
@@ -490,7 +489,6 @@ extern bluetooth::common::TimestampedCircularBuffer<tTCB_STATE_HISTORY> tcb_stat
/* from gatt_main.cc */
bool gatt_disconnect(tGATT_TCB* p_tcb);
-void gatt_cancel_connect(const RawAddress& bd_addr, tBT_TRANSPORT transport);
bool gatt_act_connect(tGATT_REG* p_reg, const RawAddress& bd_addr, tBT_TRANSPORT transport,
int8_t initiating_phys);
bool gatt_act_connect(tGATT_REG* p_reg, const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
@@ -613,7 +611,7 @@ bool gatt_is_pending_mtu_exchange(tGATT_TCB* p_tcb);
void gatt_set_conn_id_waiting_for_mtu_exchange(tGATT_TCB* p_tcb, tCONN_ID conn_id);
void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB& p_tcb);
-bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& p_tcb);
+bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& p_tcb, uint16_t cid);
bool gatt_sr_is_prep_cnt_zero(tGATT_TCB& p_tcb);
void gatt_sr_reset_cback_cnt(tGATT_TCB& p_tcb, uint16_t cid);
void gatt_sr_reset_prep_cnt(tGATT_TCB& tcb);
diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc
index 0d8d51d846..2688cfa582 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -240,32 +240,6 @@ static bool gatt_connect(const RawAddress& rem_bda, tBLE_ADDR_TYPE addr_type, tG
/*******************************************************************************
*
- * Function gatt_cancel_connect
- *
- * Description This will remove device from allow list and cancel connection
- *
- * Parameter bd_addr: peer device address.
- * transport: transport
- *
- *
- ******************************************************************************/
-void gatt_cancel_connect(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
- /* This shall be call only when device is not connected */
- log::debug("{}, transport {}", bd_addr, transport);
-
- if (!connection_manager::direct_connect_remove(CONN_MGR_ID_L2CAP, bd_addr)) {
- bluetooth::shim::ACL_IgnoreLeConnectionFrom(BTM_Sec_GetAddressWithType(bd_addr));
- log::info(
- "GATT connection manager has no record but removed filter "
- "acceptlist gatt_if:{} peer:{}",
- static_cast<uint8_t>(CONN_MGR_ID_L2CAP), bd_addr);
- }
-
- gatt_cleanup_upon_disc(bd_addr, GATT_CONN_TERMINATE_LOCAL_HOST, transport);
-}
-
-/*******************************************************************************
- *
* Function gatt_disconnect
*
* Description This function is called to disconnect to an ATT device.
@@ -290,29 +264,42 @@ bool gatt_disconnect(tGATT_TCB* p_tcb) {
return true;
}
- if (p_tcb->att_lcid == L2CAP_ATT_CID) {
- if (ch_state == GATT_CH_OPEN) {
- if (com::android::bluetooth::flags::gatt_disconnect_fix() && p_tcb->eatt) {
- /* ATT is fixed channel and it is expected to drop ACL.
- * Make sure all EATT channels are disconnected before doing that.
- */
- EattExtension::GetInstance()->Disconnect(p_tcb->peer_bda);
- }
- if (!stack::l2cap::get_interface().L2CA_RemoveFixedChnl(L2CAP_ATT_CID, p_tcb->peer_bda)) {
- log::warn("Unable to remove L2CAP ATT fixed channel peer:{}", p_tcb->peer_bda);
- }
- gatt_set_ch_state(p_tcb, GATT_CH_CLOSING);
- } else {
- gatt_cancel_connect(p_tcb->peer_bda, p_tcb->transport);
- }
- } else {
+ if (p_tcb->att_lcid != L2CAP_ATT_CID) {
if ((ch_state == GATT_CH_OPEN) || (ch_state == GATT_CH_CFG)) {
gatt_l2cif_disconnect(p_tcb->att_lcid);
} else {
log::verbose("gatt_disconnect channel not opened");
}
+ return true;
+ }
+
+ /* att_lcid == L2CAP_ATT_CID */
+
+ if (ch_state != GATT_CH_OPEN) {
+ if (!connection_manager::direct_connect_remove(CONN_MGR_ID_L2CAP, p_tcb->peer_bda)) {
+ bluetooth::shim::ACL_IgnoreLeConnectionFrom(BTM_Sec_GetAddressWithType(p_tcb->peer_bda));
+ log::info(
+ "GATT connection manager has no record but removed filter "
+ "acceptlist gatt_if:{} peer:{}",
+ static_cast<uint8_t>(CONN_MGR_ID_L2CAP), p_tcb->peer_bda);
+ }
+
+ gatt_cleanup_upon_disc(p_tcb->peer_bda, GATT_CONN_TERMINATE_LOCAL_HOST, p_tcb->transport);
+ return true;
+ }
+
+ if (com::android::bluetooth::flags::gatt_disconnect_fix() && p_tcb->eatt) {
+ /* ATT is fixed channel and it is expected to drop ACL.
+ * Make sure all EATT channels are disconnected before doing that.
+ */
+ EattExtension::GetInstance()->Disconnect(p_tcb->peer_bda);
+ }
+
+ if (!stack::l2cap::get_interface().L2CA_RemoveFixedChnl(L2CAP_ATT_CID, p_tcb->peer_bda)) {
+ log::warn("Unable to remove L2CAP ATT fixed channel peer:{}", p_tcb->peer_bda);
}
+ gatt_set_ch_state(p_tcb, GATT_CH_CLOSING);
return true;
}
@@ -1004,13 +991,6 @@ static void gatt_send_conn_cback(tGATT_TCB* p_tcb) {
gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true);
}
- if (p_reg->direct_connect_request.count(p_tcb->peer_bda) > 0) {
- gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true);
- log::info("Removing device {} from the direct connect list of gatt_if {}", p_tcb->peer_bda,
- p_reg->gatt_if);
- p_reg->direct_connect_request.erase(p_tcb->peer_bda);
- }
-
if (p_reg->app_cb.p_conn_cb) {
conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if);
(*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, kGattConnected,
@@ -1027,13 +1007,6 @@ static void gatt_send_conn_cback(tGATT_TCB* p_tcb) {
gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true);
}
- if (p_reg->direct_connect_request.count(p_tcb->peer_bda) > 0) {
- gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true);
- log::info("Removing device {} from the direct connect list of gatt_if {}", p_tcb->peer_bda,
- p_reg->gatt_if);
- p_reg->direct_connect_request.erase(p_tcb->peer_bda);
- }
-
if (p_reg->app_cb.p_conn_cb) {
conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if);
(*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, kGattConnected,
diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc
index 49c1ae452d..133080c1db 100644
--- a/system/stack/gatt/gatt_sr.cc
+++ b/system/stack/gatt/gatt_sr.cc
@@ -338,7 +338,7 @@ tGATT_STATUS gatt_sr_process_app_rsp(tGATT_TCB& tcb, tGATT_IF gatt_if, uint32_t
sr_res_p->status = status;
- if (gatt_sr_is_cback_cnt_zero(tcb) && status == GATT_SUCCESS) {
+ if (gatt_sr_is_cback_cnt_zero(tcb, sr_res_p->cid) && 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, payload_size);
@@ -347,7 +347,7 @@ tGATT_STATUS gatt_sr_process_app_rsp(tGATT_TCB& tcb, tGATT_IF gatt_if, uint32_t
}
}
}
- if (gatt_sr_is_cback_cnt_zero(tcb)) {
+ if (gatt_sr_is_cback_cnt_zero(tcb, sr_res_p->cid)) {
if ((sr_res_p->status == GATT_SUCCESS) && (sr_res_p->p_rsp_msg)) {
ret_code = attp_send_sr_msg(tcb, sr_res_p->cid, sr_res_p->p_rsp_msg);
sr_res_p->p_rsp_msg = NULL;
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index 88ecfe2d53..b0d18f892e 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -430,16 +430,26 @@ tGATT_TCB* gatt_find_tcb_by_addr(const RawAddress& bda, tBT_TRANSPORT transport)
return p_tcb;
}
+/* This is for connection manager */
+std::string get_client_name(uint8_t gatt_if) {
+ if (gatt_if == CONN_MGR_ID_L2CAP) {
+ return "L2CAP";
+ }
+
+ tGATT_REG* reg = gatt_get_regcb(gatt_if);
+ return (reg == nullptr) ? "" : reg->name;
+}
+
std::string gatt_tcb_get_holders_info_string(const tGATT_TCB* p_tcb) {
std::stringstream stream;
if (p_tcb->app_hold_link.size() == 0) {
stream << "No ACL holders";
} else {
- stream << "ACL holders gatt_if:";
+ stream << "ACL holders gatt_if: ";
for (auto gatt_if : p_tcb->app_hold_link) {
- stream << static_cast<int>(gatt_if) << ",";
+ stream << get_client_name(gatt_if) << " (" << +gatt_if << "), ";
}
}
return stream.str();
@@ -1362,17 +1372,30 @@ tGATT_SR_CMD* gatt_sr_get_cmd_by_trans_id(tGATT_TCB* p_tcb, uint32_t trans_id) {
* Returns True if thetotal application callback count is zero
*
******************************************************************************/
-bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& tcb) {
- if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) {
- return tcb.sr_cmd.cback_cnt_map.empty();
+bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& tcb, uint16_t cid) {
+ tGATT_SR_CMD* sr_cmd_p;
+ if (cid == tcb.att_lcid) {
+ sr_cmd_p = &tcb.sr_cmd;
} else {
- for (uint8_t i = 0; i < GATT_MAX_APPS; i++) {
- if (tcb.sr_cmd.cback_cnt[i]) {
- return false;
- }
+ EattChannel* channel = EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
+ if (channel == nullptr) {
+ log::warn("{}, cid 0x{:02x} already disconnected", tcb.peer_bda, cid);
+ return true;
}
- return true;
+ sr_cmd_p = &channel->server_outstanding_cmd_;
+ }
+
+ if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) {
+ return sr_cmd_p->cback_cnt_map.empty();
}
+
+ for (uint8_t i = 0; i < GATT_MAX_APPS; i++) {
+ if (sr_cmd_p->cback_cnt[i] != 0) {
+ return false;
+ }
+ }
+
+ return true;
}
/*******************************************************************************
@@ -1581,46 +1604,25 @@ void gatt_sr_update_prep_cnt(tGATT_TCB& tcb, tGATT_IF gatt_if, bool is_inc, bool
}
}
-static bool gatt_is_anybody_interested_in_connection(const RawAddress& bda) {
- if (connection_manager::is_background_connection(bda)) {
- log::debug("{} is in background connection", bda);
- return true;
+/** Cancel LE Create Connection request */
+bool gatt_cancel_open(tGATT_IF gatt_if, const RawAddress& bda) {
+ if (connection_manager::direct_connect_remove(gatt_if, bda)) {
+ log::info("{} was doing direct connect to {}, canceled", gatt_if, bda);
}
-
- for (size_t i = 1; i <= GATT_MAX_APPS; i++) {
- tGATT_REG* p_reg = &gatt_cb.cl_rcb[i - 1];
- if (p_reg->in_use && p_reg->direct_connect_request.count(bda) > 0) {
- log::debug("gatt_if {} interested in connection to {}", i, bda);
- return true;
- }
+ if (connection_manager::background_connect_remove(gatt_if, bda)) {
+ log::info("{} was doing background connect to {}, canceled", gatt_if, bda);
}
- return false;
-}
-/** Cancel LE Create Connection request */
-bool gatt_cancel_open(tGATT_IF gatt_if, const RawAddress& bda) {
tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(bda, BT_TRANSPORT_LE);
if (!p_tcb) {
- /* TCB is not allocated when trying to connect under this flag.
- * but device address is storred in the tGATT_REG. Make sure to remove
- * the address from the list when cancel is called.
- */
-
- tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
- if (!p_reg) {
- log::error("Unable to find registered app gatt_if={}", gatt_if);
- } else {
- log::info("Removing {} from direct list", bda);
- p_reg->direct_connect_request.erase(bda);
- }
- if (!gatt_is_anybody_interested_in_connection(bda)) {
- gatt_cancel_connect(bda, static_cast<tBT_TRANSPORT>(BT_TRANSPORT_LE));
+ if (connection_manager::get_apps_connecting_to(bda).empty()) {
+ gatt_cleanup_upon_disc(bda, GATT_CONN_TERMINATE_LOCAL_HOST, BT_TRANSPORT_LE);
}
return true;
}
if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) {
- log::error("link connected Too late to cancel");
+ log::error("link connected too late to cancel {}", bda);
return false;
}
@@ -1632,24 +1634,6 @@ bool gatt_cancel_open(tGATT_IF gatt_if, const RawAddress& bda) {
gatt_disconnect(p_tcb);
}
- if (!connection_manager::direct_connect_remove(gatt_if, bda)) {
- if (!connection_manager::is_background_connection(bda)) {
- if (!com::android::bluetooth::flags::gatt_fix_multiple_direct_connect() ||
- p_tcb->app_hold_link.empty()) {
- bluetooth::shim::ACL_IgnoreLeConnectionFrom(BTM_Sec_GetAddressWithType(bda));
- }
- log::info(
- "Gatt connection manager has no background record but removed "
- "filter acceptlist gatt_if:{} peer:{}",
- gatt_if, bda);
- } else {
- log::info(
- "Gatt connection manager maintains a background record preserving "
- "filter acceptlist gatt_if:{} peer:{}",
- gatt_if, bda);
- }
- }
-
return true;
}
@@ -1830,12 +1814,6 @@ static void gatt_disconnect_complete_notify_user(const RawAddress& bda, tGATT_DI
(*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, kGattDisconnected, reason,
transport);
}
-
- if (p_reg->direct_connect_request.count(bda) > 0) {
- log::info("Removing device {} from the direct connect list of gatt_if {}", bda,
- p_reg->gatt_if);
- p_reg->direct_connect_request.erase(bda);
- }
}
} else {
for (uint8_t i = 0; i < GATT_MAX_APPS; i++) {
@@ -1846,12 +1824,6 @@ static void gatt_disconnect_complete_notify_user(const RawAddress& bda, tGATT_DI
(*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, kGattDisconnected, reason,
transport);
}
-
- if (p_reg->direct_connect_request.count(bda) > 0) {
- log::info("Removing device {} from the direct connect list of gatt_if {}", bda,
- p_reg->gatt_if);
- p_reg->direct_connect_request.erase(bda);
- }
}
}
}
@@ -1906,6 +1878,9 @@ void gatt_cleanup_upon_disc(const RawAddress& bda, tGATT_DISCONN_REASON reason,
gatt_free_pending_ind(p_tcb);
fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL);
p_tcb->sr_cmd.multi_rsp_q = NULL;
+ if (p_tcb->sr_cmd.p_rsp_msg) {
+ osi_free_and_reset((void**)&p_tcb->sr_cmd.p_rsp_msg);
+ }
gatt_disconnect_complete_notify_user(bda, reason, transport);
diff --git a/system/stack/hid/hidh_conn.cc b/system/stack/hid/hidh_conn.cc
index 83bb039173..d658c1ab29 100644
--- a/system/stack/hid/hidh_conn.cc
+++ b/system/stack/hid/hidh_conn.cc
@@ -24,6 +24,7 @@
#include <base/functional/callback.h>
#include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h>
#include <string.h>
@@ -70,6 +71,8 @@ static void hidh_l2cif_connect_cfm(uint16_t l2cap_cid, tL2CAP_CONN result);
static void hidh_l2cif_config_ind(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg);
static void hidh_l2cif_config_cfm(uint16_t l2cap_cid, uint16_t result, tL2CAP_CFG_INFO* p_cfg);
static void hidh_l2cif_disconnect_ind(uint16_t l2cap_cid, bool ack_needed);
+static void hidh_l2cif_disconnect_cfm(uint16_t l2cap_cid, uint16_t result);
+static void hidh_l2cif_disconnect_cfm_actual(uint16_t l2cap_cid, uint16_t result);
static void hidh_l2cif_data_ind(uint16_t l2cap_cid, BT_HDR* p_msg);
static void hidh_l2cif_disconnect(uint16_t l2cap_cid);
static void hidh_l2cif_cong_ind(uint16_t l2cap_cid, bool congested);
@@ -81,6 +84,7 @@ static const tL2CAP_APPL_INFO hst_reg_info = {
.pL2CA_ConfigInd_Cb = hidh_l2cif_config_ind,
.pL2CA_ConfigCfm_Cb = hidh_l2cif_config_cfm,
.pL2CA_DisconnectInd_Cb = hidh_l2cif_disconnect_ind,
+ .pL2CA_DisconnectCfm_Cb = hidh_l2cif_disconnect_cfm,
.pL2CA_DataInd_Cb = hidh_l2cif_data_ind,
.pL2CA_CongestionStatus_Cb = hidh_l2cif_cong_ind,
.pL2CA_TxComplete_Cb = nullptr,
@@ -160,7 +164,7 @@ tHID_STATUS hidh_conn_disconnect(uint8_t dhandle) {
BT_TRANSPORT_BR_EDR)) {
log::warn("Unable to set L2CAP idle timeout peer:{}", hh_cb.devices[dhandle].addr);
}
- /* Disconnect both interrupt and control channels */
+ /* Disconnect channels one by one */
if (p_hcon->intr_cid) {
hidh_l2cif_disconnect(p_hcon->intr_cid);
} else if (p_hcon->ctrl_cid) {
@@ -553,11 +557,38 @@ static void hidh_l2cif_disconnect_ind(uint16_t l2cap_cid, bool ack_needed) {
}
}
+// TODO: after disconnect_hid_channels_serially aflags is the default,
+// remove this function and call L2CA_DisconnectReq directly.
static void hidh_l2cif_disconnect(uint16_t l2cap_cid) {
if (!stack::l2cap::get_interface().L2CA_DisconnectReq(l2cap_cid)) {
log::warn("Unable to send L2CAP disconnect request cid:{}", l2cap_cid);
}
+ if (!com::android::bluetooth::flags::disconnect_hid_channels_serially()) {
+ hidh_l2cif_disconnect_cfm_actual(l2cap_cid, 0);
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function hidh_l2cif_disconnect_cfm
+ *
+ * Description This function handles a disconnect confirm from L2CAP. This
+ * means our disconnection request has been acknowledged, so we
+ * can disconnect the other channel, if required.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+static void hidh_l2cif_disconnect_cfm(uint16_t l2cap_cid, uint16_t result) {
+ if (com::android::bluetooth::flags::disconnect_hid_channels_serially()) {
+ hidh_l2cif_disconnect_cfm_actual(l2cap_cid, result);
+ }
+}
+
+// TODO: after disconnect_hid_channels_serially aflags is the default,
+// copy the body to hidh_l2cif_disconnect_cfm and remove this.
+static void hidh_l2cif_disconnect_cfm_actual(uint16_t l2cap_cid, uint16_t /* result */) {
/* Find CCB based on CID */
const uint8_t dhandle = find_conn_by_cid(l2cap_cid);
if (dhandle == kHID_HOST_MAX_DEVICES) {
diff --git a/system/stack/include/ble_appearance.h b/system/stack/include/ble_appearance.h
new file mode 100644
index 0000000000..6dbb32996c
--- /dev/null
+++ b/system/stack/include/ble_appearance.h
@@ -0,0 +1,446 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2025 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 BLE_APPEARANCE_H
+#define BLE_APPEARANCE_H
+
+/* BLE appearance values as per BT spec assigned numbers */
+/* Category[15:6] 0x000 */
+#define BLE_APPEARANCE_UNKNOWN 0x0000
+
+/* Category[15:6] 0x001 */
+#define BLE_APPEARANCE_GENERIC_PHONE 0x0040
+
+/* Category[15:6] 0x002 */
+#define BLE_APPEARANCE_GENERIC_COMPUTER 0x0080
+
+#define BLE_APPEARANCE_DESKTOP_WORKSTATION 0x81
+#define BLE_APPEARANCE_SERVER_CLASS_COMPUTER 0x82
+#define BLE_APPEARANCE_LAPTOP 0x83
+#define BLE_APPEARANCE_HANDHELD_PC_PDA 0x84
+#define BLE_APPEARANCE_PALM_SIZE_PC_PDA 0x85
+#define BLE_APPEARANCE_WEARABLE_COMPUTER_WATCH_SIZE 0x86
+#define BLE_APPEARANCE_TABLET 0x87
+#define BLE_APPEARANCE_DOCKING_STATION 0x88
+#define BLE_APPEARANCE_ALL_IN_ONE 0x89
+#define BLE_APPEARANCE_BLADE_SERVER 0x8A
+#define BLE_APPEARANCE_CONVERTIBLE 0x8B
+#define BLE_APPEARANCE_DETACHABLE 0x8C
+#define BLE_APPEARANCE_IOT_GATEWAY 0x8D
+#define BLE_APPEARANCE_MINI_PC 0x8E
+#define BLE_APPEARANCE_STICK_PC 0x8F
+
+/* Category[15:6] 0x003 */
+#define BLE_APPEARANCE_GENERIC_WATCH 0x00C0
+#define BLE_APPEARANCE_SPORTS_WATCH 0x00C1
+#define BLE_APPEARANCE_SMART_WATCH 0x00C2
+
+/* Category[15:6] 0x004 */
+#define BLE_APPEARANCE_GENERIC_CLOCK 0x0100
+
+/* Category[15:6] 0x005 */
+#define BLE_APPEARANCE_GENERIC_DISPLAY 0x0140
+
+/* Category[15:6] 0x006 */
+#define BLE_APPEARANCE_GENERIC_REMOTE 0x0180
+
+/* Category[15:6] 0x007 */
+#define BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0
+
+/* Category[15:6] 0x008 */
+#define BLE_APPEARANCE_GENERIC_TAG 0x0200
+
+/* Category[15:6] 0x009 */
+#define BLE_APPEARANCE_GENERIC_KEYRING 0x0240
+
+/* Category[15:6] 0x00A */
+#define BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280
+
+/* Category[15:6] 0x00B */
+#define BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0
+
+/* Category[15:6] 0x00C */
+#define BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300
+#define BLE_APPEARANCE_THERMOMETER_EAR 0x0301
+
+/* Category[15:6] 0x00D */
+#define BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340
+#define BLE_APPEARANCE_HEART_RATE_BELT 0x0341
+
+/* Category[15:6] 0x00E */
+#define BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380
+#define BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381
+#define BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382
+
+/* Category[15:6] 0x00F */
+#define BLE_APPEARANCE_GENERIC_HID 0x03C0
+#define BLE_APPEARANCE_HID_KEYBOARD 0x03C1
+#define BLE_APPEARANCE_HID_MOUSE 0x03C2
+#define BLE_APPEARANCE_HID_JOYSTICK 0x03C3
+#define BLE_APPEARANCE_HID_GAMEPAD 0x03C4
+#define BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5
+#define BLE_APPEARANCE_HID_CARD_READER 0x03C6
+#define BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7
+#define BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8
+#define BLE_APPEARANCE_HID_TOUCHPAD 0x03C9
+#define BLE_APPEARANCE_HID_PRESENTATION_REMOTE 0x03CA
+
+/* Category[15:6] 0x010 */
+#define BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400
+
+/* Category[15:6] 0x011 */
+#define BLE_APPEARANCE_GENERIC_WALKING 0x0440
+#define BLE_APPEARANCE_WALKING_IN_SHOE 0x0441
+#define BLE_APPEARANCE_WALKING_ON_SHOE 0x0442
+#define BLE_APPEARANCE_WALKING_ON_HIP 0x0443
+
+/* Category[15:6] 0x012 */
+#define BLE_APPEARANCE_GENERIC_CYCLING 0x0480
+#define BLE_APPEARANCE_CYCLING_COMPUTER 0x0481
+#define BLE_APPEARANCE_CYCLING_SPEED 0x0482
+#define BLE_APPEARANCE_CYCLING_CADENCE 0x0483
+#define BLE_APPEARANCE_CYCLING_POWER 0x0484
+#define BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485
+
+/* Category[15:6] 0x013 */
+#define BLE_APPEARANCE_GENERIC_CONTROL_DEVICE 0x04C0
+#define BLE_APPEARANCE_SWITCH 0x04C1
+#define BLE_APPEARANCE_MULTI_SWITCH 0x04C2
+#define BLE_APPEARANCE_SWITCH_BUTTON 0x04C3
+#define BLE_APPEARANCE_SWITCH_SLIDER 0x04C4
+#define BLE_APPEARANCE_ROTARY_SWITCH 0x04C5
+#define BLE_APPEARANCE_TOUCH_PANEL 0x04C6
+#define BLE_APPEARANCE_SINGLE_SWITCH 0x04C7
+#define BLE_APPEARANCE_DOUBLE_SWITCH 0x04C8
+#define BLE_APPEARANCE_TRIPLE_SWITCH 0x04C9
+#define BLE_APPEARANCE_BATTERY_SWITCH 0x04CA
+#define BLE_APPEARANCE_ENERGY_HARVESTING_SWITCH 0x04CB
+#define BLE_APPEARANCE_SWITCH_PUSH_BUTTON 0x04CC
+#define BLE_APPEARANCE_SWITCH_DIAL 0x04CD
+
+/* Category[15:6] 0x014 */
+#define BLE_APPEARANCE_GENERIC_NETWORK_DEVICE 0x0500
+#define BLE_APPEARANCE_NETWORK_DEVICE_ACCESS_POINT 0x0501
+#define BLE_APPEARANCE_NETWORK_DEVICE_MESH_DEVICE 0x0502
+#define BLE_APPEARANCE_NETWORK_DEVICE_MESH_NETWORK_PROXY 0x0503
+
+/* Category[15:6] 0x015 */
+#define BLE_APPEARANCE_GENERIC_SENSOR 0x0540
+#define BLE_APPEARANCE_MOTION_SENSOR 0x0541
+#define BLE_APPEARANCE_AIR_QUALITY_SENSOR 0x0542
+#define BLE_APPEARANCE_TEMPERATURE_SENSOR 0x0543
+#define BLE_APPEARANCE_HUMIDITY_SENSOR 0x0544
+#define BLE_APPEARANCE_LEAK_SENSOR 0x05
+#define BLE_APPEARANCE_SMOKE_SENSOR 0x0546
+#define BLE_APPEARANCE_OCCUPANCY_SENSOR 0x0547
+#define BLE_APPEARANCE_CONTACT_SENSOR 0x0548
+#define BLE_APPEARANCE_CARBON_MONOXIDE_SENSOR 0x0549
+#define BLE_APPEARANCE_CARBON_DIOXIDE_SENSOR 0x054A
+#define BLE_APPEARANCE_AMBIENT_LIGHT_SENSOR 0x054B
+#define BLE_APPEARANCE_ENERGY_SENSOR 0x054C
+#define BLE_APPEARANCE_COLOR_LIGHT_SENSOR 0x054D
+#define BLE_APPEARANCE_RAIN_SENSOR 0x054E
+#define BLE_APPEARANCE_FIRE_SENSOR 0x054F
+#define BLE_APPEARANCE_WIND_SENSOR 0x0550
+#define BLE_APPEARANCE_PROXIMITY_SENSOR 0x0551
+#define BLE_APPEARANCE_MULTI_SENSOR 0x0552
+#define BLE_APPEARANCE_FLUSH_MOUNTED_SENSOR 0x0553
+#define BLE_APPEARANCE_CEILING_MOUNTED_SENSOR 0x0554
+#define BLE_APPEARANCE_WALL_MOUNTED_SENSOR 0x0555
+#define BLE_APPEARANCE_MULTISENSOR 0x0556
+#define BLE_APPEARANCE_SENSOR_ENERGY_METER 0x0557
+#define BLE_APPEARANCE_SENSOR_FLAME_DETECTOR 0x0558
+#define BLE_APPEARANCE_VEHICLE_TIRE_PRESSURE_SENSOR 0x0559
+
+/* Category[15:6] 0x016 */
+#define BLE_APPEARANCE_GENERIC_LIGHT_FIXTURE 0x0580
+#define BLE_APPEARANCE_WALL_LIGHT 0x0581
+#define BLE_APPEARANCE_CEILING_LIGHT 0x0582
+#define BLE_APPEARANCE_FLOOR_LIGHT 0x0583
+#define BLE_APPEARANCE_CABINET_LIGHT 0x0584
+#define BLE_APPEARANCE_DESK_LIGHT 0x0585
+#define BLE_APPEARANCE_TROFFER_LIGHT 0x0586
+#define BLE_APPEARANCE_PENDANT_LIGHT 0x0587
+#define BLE_APPEARANCE_IN_GROUND_LIGHT 0x0588
+#define BLE_APPEARANCE_FLOOD_LIGHT 0x0589
+#define BLE_APPEARANCE_UNDERWATER_LIGHT 0x058A
+#define BLE_APPEARANCE_BOLLARD_WITH_LIGHT 0x058B
+#define BLE_APPEARANCE_PATHWAY_LIGHT 0x058C
+#define BLE_APPEARANCE_GARDEN_LIGHT 0x058D
+#define BLE_APPEARANCE_POLE_TOP_LIGHT 0x058E
+#define BLE_APPEARANCE_SPOTLIGHT 0x058F
+#define BLE_APPEARANCE_LINEAR_LIGHT 0x0590
+#define BLE_APPEARANCE_STREET_LIGHT 0x0591
+#define BLE_APPEARANCE_SHELVES_LIGHT 0x0592
+#define BLE_APPEARANCE_BAY_LIGHT 0x0593
+#define BLE_APPEARANCE_EMERGENCY_EXIT_LIGHT 0x0594
+#define BLE_APPEARANCE_LIGHT_CONTROLLER 0x0595
+#define BLE_APPEARANCE_LIGHT_DRIVER 0x0596
+#define BLE_APPEARANCE_BULB 0x0597
+#define BLE_APPEARANCE_LOW_BAY_LIGHT 0x0598
+#define BLE_APPEARANCE_HIGH_BAY_LIGHT 0x0599
+
+/* Category[15:6] 0x017 */
+#define BLE_APPEARANCE_GENERIC_FAN 0x05C0
+#define BLE_APPEARANCE_CEILING_FAN 0x05C1
+#define BLE_APPEARANCE_AXIAL_FAN 0x05C2
+#define BLE_APPEARANCE_EXHAUST_FAN 0x05C3
+#define BLE_APPEARANCE_PEDESTAL_FAN 0x05C4
+#define BLE_APPEARANCE_DESK_FAN 0x05C5
+#define BLE_APPEARANCE_WALL_FAN 0x05C6
+
+/* Category[15:6] 0x018 */
+#define BLE_APPEARANCE_GENERIC_HVAC 0x0600
+#define BLE_APPEARANCE_HVAC_THERMOSTAT 0x0601
+#define BLE_APPEARANCE_HVAC_HUMIDIFIER 0x0602
+#define BLE_APPEARANCE_HVAC_DEHUMIDIFIER 0x0603
+#define BLE_APPEARANCE_HVAC_HEATER 0x0604
+#define BLE_APPEARANCE_HVAC_RADIATOR 0x0605
+#define BLE_APPEARANCE_HVAC_BOILER 0x0606
+#define BLE_APPEARANCE_HVAC_HEAT_PUMP 0x0607
+#define BLE_APPEARANCE_HVAC_INFRARED_HEATER 0x0608
+#define BLE_APPEARANCE_HVAC_RADIANT_PANEL_HEATER 0x0609
+#define BLE_APPEARANCE_HVAC_FAN_HEATER 0x060A
+#define BLE_APPEARANCE_HVAC_AIR_CURTAIN 0x060B
+
+/* Category[15:6] 0x019 */
+#define BLE_APPEARANCE_GENERIC_AIR_CONDITIONING 0x0640
+
+/* Category[15:6] 0x01A */
+#define BLE_APPEARANCE_GENERIC_HUMIDIFIER 0x0680
+
+/* Category[15:6] 0x01B */
+#define BLE_APPEARANCE_GENERIC_HEATING 0x06C0
+#define BLE_APPEARANCE_HEATING_RADIATOR 0x06C1
+#define BLE_APPEARANCE_HEATING_BOILER 0x06C2
+#define BLE_APPEARANCE_HEATING_HEAT_PUMP 0x06C3
+#define BLE_APPEARANCE_HEATING_INFRARED_HEATER 0x06C4
+#define BLE_APPEARANCE_HEATING_RADIANT_PANEL_HEATER 0x06C5
+#define BLE_APPEARANCE_HEATING_FAN_HEATER 0x06C6
+#define BLE_APPEARANCE_HEATING_AIR_CURTAIN 0x06C7
+
+/* Category[15:6] 0x01C */
+#define BLE_APPEARANCE_GENERIC_ACCESS_CONTROL 0x0700
+#define BLE_APPEARANCE_ACCESS_DOOR 0x0701
+#define BLE_APPEARANCE_ACCESS_CONTROL_GARAGE_DOOR 0x0702
+#define BLE_APPEARANCE_ACCESS_CONTROL_EMERGENCY_EXIT_DOOR 0x0703
+#define BLE_APPEARANCE_ACCESS_CONTROL_ACCESS_LOCK 0x0704
+#define BLE_APPEARANCE_ACCESS_CONTROL_ELEVATOR 0x0705
+#define BLE_APPEARANCE_ACCESS_CONTROL_WINDOW 0x0706
+#define BLE_APPEARANCE_ACCESS_CONTROL_ENTRANCE_GATE 0x0707
+#define BLE_APPEARANCE_ACCESS_CONTROL_DOOR_LOCK 0x0708
+#define BLE_APPEARANCE_ACCESS_CONTROL_LOCKER 0x0709
+
+/* Category[15:6] 0x01D */
+#define BLE_APPEARANCE_GENERIC_MOTORIZED_DEVICE 0x0740
+#define BLE_APPEARANCE_MOTORIZED_GATE 0x0741
+#define BLE_APPEARANCE_MOTORIZED_AWNING 0x0742
+#define BLE_APPEARANCE_MOTORIZED_BLINDS_OR_SHADES 0x0743
+#define BLE_APPEARANCE_MOTORIZED_CURTAINS 0x0744
+#define BLE_APPEARANCE_MOTORIZED_SCREEN 0x0745
+
+/* Category[15:6] 0x01E */
+#define BLE_APPEARANCE_GENERIC_POWER_DEVICE 0x0780
+#define BLE_APPEARANCE_POWER_OUTLET 0x0781
+#define BLE_APPEARANCE_POWER_STRIP 0x0782
+#define BLE_APPEARANCE_POWER_PLUG 0x0783
+#define BLE_APPEARANCE_POWER_SUPPLY 0x0784
+
+/* Category[15:6] 0x01F */
+#define BLE_APPEARANCE_GENERIC_LIGHT_SOURCE 0x07C0
+#define BLE_APPEARANCE_LIGHT_SOURCE_INCANDESCENT_LIGHT_BULB 0x07C1
+#define BLE_APPEARANCE_LIGHT_SOURCE_LED_LAMP 0x07C2
+#define BLE_APPEARANCE_LIGHT_SOURCE_HID_LAMP 0x07C3
+#define BLE_APPEARANCE_LIGHT_SOURCE_FLUORESCENT_LAMP 0x07C4
+#define BLE_APPEARANCE_LIGHT_SOURCE_LED_ARRAY 0x07C5
+#define BLE_APPEARANCE_LIGHT_SOURCE_MULTI_COLOR_LED_ARRAY 0x07C6
+#define BLE_APPEARANCE_LIGHT_SOURCE_LOW_VOLTAGE_HALOGEN 0x07C7
+#define BLE_APPEARANCE_LIGHT_SOURCE_ORGANIC_LIGHT_EMITTING_DIODE_OLED 0x07C8
+
+/* Category[15:6] 0x020 */
+#define BLE_APPEARANCE_GENERIC_WINDOW_COVERING 0x0800
+#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_SHADES 0x0801
+#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_BLINDS 0x0802
+#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_AWNING 0x0803
+#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_CURTAIN 0x0804
+#define BLE_APPEARANCE_WINDOW_COVERING_EXTERIOR_SHUTTER 0x0805
+#define BLE_APPEARANCE_WINDOW_COVERING_EXTERIOR_SCREEN 0x0806
+
+/* Category[15:6] 0x021 */
+#define BLE_APPEARANCE_GENERIC_AUDIO_SINK 0x0840
+#define BLE_APPEARANCE_AUDIO_SINK_STANDALONE_SPEAKER 0x0841
+#define BLE_APPEARANCE_AUDIO_SINK_SOUNDBAR 0x0842
+#define BLE_APPEARANCE_AUDIO_SINK_BOOKSHELF_SPEAKER 0x0843
+#define BLE_APPEARANCE_AUDIO_SINK_STANDMOUNTED_SPEAKER 0x0844
+#define BLE_APPEARANCE_AUDIO_SINK_SPEAKERPHONE 0x0845
+
+/* Category[15:6] 0x022 */
+#define BLE_APPEARANCE_GENERIC_AUDIO_SOURCE 0x0880
+#define BLE_APPEARANCE_AUDIO_SOURCE_MICROPHONE 0x0881
+#define BLE_APPEARANCE_AUDIO_SOURCE_ALARM 0x0882
+#define BLE_APPEARANCE_AUDIO_SOURCE_BELL 0x0883
+#define BLE_APPEARANCE_AUDIO_SOURCE_HORN 0x0884
+#define BLE_APPEARANCE_AUDIO_SOURCE_BROADCASTING_DEVICE 0x0885
+#define BLE_APPEARANCE_AUDIO_SOURCE_SERVICE_DESK 0x0886
+#define BLE_APPEARANCE_AUDIO_SOURCE_KIOSK 0x0887
+#define BLE_APPEARANCE_AUDIO_SOURCE_BROADCASTING_ROOM 0x0888
+#define BLE_APPEARANCE_AUDIO_SOURCE_AUDITORIUM 0x0889
+
+/* Category[15:6] 0x023 */
+#define BLE_APPEARANCE_GENERIC_MOTORIZED_VEHICLE 0x08C0
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_CAR 0x08C1
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_LARGE_GOODS 0x08C2
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_2_WHEELED 0x08C3
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MOTORBIKE 0x08C4
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_SCOOTER 0x08C5
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MOPED 0x08C6
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_3_WHEELED 0x08C7
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_LIGHT_VEHICLE 0x08C8
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_QUAD_BIKE 0x08C9
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MINIBUS 0x08CA
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_BUS 0x08CB
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_TROLLEY 0x08CC
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_AGRICULTURAL_VEHICLE 0x08CD
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_CAMPER_CARAVAN 0x08CE
+#define BLE_APPEARANCE_MOTORIZED_VEHICLE_RECREATIONAL_VEHICLE_MOTOR_HOME 0x08CF
+
+/* Category[15:6] 0x024 */
+#define BLE_APPEARANCE_GENERIC_DOMESTIC_APPLIANCE 0x0900
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_REFRIGERATOR 0x0901
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_FREEZER 0x0902
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_OVEN 0x0903
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_MICROWAVE 0x0904
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_TOASTER 0x0905
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_WASHING_MACHINE 0x0906
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_DRYER 0x0907
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_COFFEE_MAKER 0x0908
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CLOTHES_IRON 0x0909
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CURLING_IRON 0x090A
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_HAIR_DRYER 0x090B
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_VACUUM_CLEANER 0x090C
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_ROBOTIC_VACUUM_CLEANER 0x090D
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_RICE_COOKER 0x090E
+#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CLOTHES_STEAMER 0x090F
+
+/* Category[15:6] 0x025 */
+#define BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE 0x0940
+#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD 0x0941
+#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET 0x0942
+#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES 0x0943
+#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND 0x0944
+
+/* Category[15:6] 0x026 */
+#define BLE_APPEARANCE_GENERIC_AIRCRAFT 0x0980
+#define BLE_APPEARANCE_AIRCRAFT_LIGHT 0x0981
+#define BLE_APPEARANCE_AIRCRAFT_MICROLIGHT 0x0982
+#define BLE_APPEARANCE_AIRCRAFT_PARAGLIDER 0x0983
+#define BLE_APPEARANCE_AIRCRAFT_LARGE_PASSENGER 0x0984
+
+/* Category[15:6] 0x027 */
+#define BLE_APPEARANCE_GENERIC_AV_EQUIPMENT 0x09C0
+#define BLE_APPEARANCE_AV_EQUIPMENT_AMPLIFIER 0x09C1
+#define BLE_APPEARANCE_AV_EQUIPMENT_RECEIVER 0x09C2
+#define BLE_APPEARANCE_AV_EQUIPMENT_RADIO 0x09C3
+#define BLE_APPEARANCE_AV_EQUIPMENT_TUNER 0x09C4
+#define BLE_APPEARANCE_AV_EQUIPMENT_TURNTABLE 0x09C5
+#define BLE_APPEARANCE_AV_EQUIPMENT_CD_PLAYER 0x09C6
+#define BLE_APPEARANCE_AV_EQUIPMENT_DVD_PLAYER 0x09C7
+#define BLE_APPEARANCE_AV_EQUIPMENT_BLU_RAY_PLAYER 0x09C8
+#define BLE_APPEARANCE_AV_EQUIPMENT_OPTICAL_DISC_PLAYER 0x09C9
+#define BLE_APPEARANCE_AV_EQUIPMENT_SET_TOP_BOX 0x09CA
+
+/* Category[15:6] 0x028 */
+#define BLE_APPEARANCE_GENERIC_DISPLAY_EQUIPMENT 0x0A00
+#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_TELEVISION 0x0A01
+#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_MONITOR 0x0A02
+#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_PROJECTOR 0x0A03
+
+/* Category[15:6] 0x029 */
+#define BLE_APPEARANCE_GENERIC_HEARING_AID 0x0A40
+#define BLE_APPEARANCE_HEARING_AID_IN_EAR 0x0A41
+#define BLE_APPEARANCE_HEARING_AID_BEHIND_EAR 0x0A42
+#define BLE_APPEARANCE_HEARING_AID_COCHLLEAR_IMPLANT 0x0A43
+
+/* Category[15:6] 0x02A */
+#define BLE_APPEARANCE_GENERIC_GAMING 0x0A80
+#define BLE_APPEARANCE_GAMING_HOME_VIDEO_GAME_CONSOLE 0x0A81
+#define BLE_APPEARANCE_GAMING_PORTABLE_HANDHELD_CONSOLE 0x0A82
+
+/* Category[15:6] 0x02B */
+#define BLE_APPEARANCE_GENERIC_SIGNAGE 0x0AC0
+#define BLE_APPEARANCE_SIGNAGE_DIGITAL 0x0AC1
+#define BLE_APPEARANCE_SIGNAGE_ELECTRONIC_LABEL 0x0AC2
+
+/* Category[15:6] 0x031 */
+#define BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40
+#define BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41
+#define BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42
+
+/* Category[15:6] 0x032 */
+#define BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80
+
+/* Category[15:6] 0x033 */
+#define BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE 0x0CC0
+#define BLE_APPEARANCE_PERSONAL_MOBILITY_DEVICE_POWERED_WHEELCHAIR 0x0CC1
+#define BLE_APPEARANCE_PERSONAL_MOBILITY_DEVICE_MOBILITY_SCOOTER 0x0CC2
+
+/* Category[15:6] 0x034 */
+#define BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR 0x0D00
+
+/* Category[15:6] 0x035 */
+#define BLE_APPEARANCE_GENERIC_INSULIN_PUMP 0x0D40
+#define BLE_APPEARANCE_INSULIN_PUMP_DURABLE 0x0D41
+#define BLE_APPEARANCE_INSULIN_PUMP_PATCH 0x0D44
+#define BLE_APPEARANCE_INSULIN_PUMP_PEN 0x0D48
+
+/* Category[15:6] 0x036 */
+#define BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY 0x0D80
+
+/* Category[15:6] 0x037 */
+#define BLE_APPEARANCE_GENERIC_SPIROMETER 0x0DC0
+#define BLE_APPEARANCE_SPIROMETER_HANDHELD 0x0DC1
+
+/* Category[15:6] 0x051 */
+#define BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440
+#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441
+#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442
+#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443
+#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444
+
+/* Category[15:6] 0x052 */
+#define BLE_APPEARANCE_GENERIC_INDUSTRIAL_MEASUREMENT_DEVICE 0x1480
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_TORQUE_TESTING 0x1481
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_CALIPER 0x1482
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_DIAL_INDICATOR 0x1483
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_MICROMETER 0x1484
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_HEIGHT_GAUGE 0x1485
+#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_FORCE_GAUGE 0x1486
+
+/* Category[15:6] 0x053 */
+#define BLE_APPEARANCE_GENERIC_INDUSTRIAL_TOOLS 0x14C0
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_MACHINE_TOOL_HOLDER 0x14C1
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_GENERIC_CLAMPING_DEVICE 0x14C2
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_JAWS_JAWS_CHUCK 0x14C3
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_COLLET_CHUCK 0x14C4
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_MANDREL 0x14C5
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_VISE 0x14C6
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_ZERO_POINT_CLAMPING_SYSTEM 0x14C7
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_TORQUE_WRENCH 0x14C8
+#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_TORQUE_SCREWDRIVER 0x14C9
+
+#endif // BLE_APPEARANCE_H \ No newline at end of file
diff --git a/system/stack/include/bt_dev_class.h b/system/stack/include/bt_dev_class.h
index 1455f66fd2..ca951532ce 100644
--- a/system/stack/include/bt_dev_class.h
+++ b/system/stack/include/bt_dev_class.h
@@ -26,72 +26,222 @@ typedef std::array<uint8_t, kDevClassLength> DEV_CLASS; /* Device class */
inline constexpr DEV_CLASS kDevClassEmpty = {};
+/***************************
+ * major device class field
+ * Note: All values are deduced by basing BIT_X to BIT_8, values as per
+ * BT-spec assigned-numbers.
+ ***************************/
+#define COD_MAJOR_MISC 0x00
+#define COD_MAJOR_COMPUTER 0x01 // BIT8
+#define COD_MAJOR_PHONE 0x02 // BIT9
+#define COD_MAJOR_LAN_NAP 0x03 // BIT8 | BIT9
+#define COD_MAJOR_AUDIO 0x04 // BIT10
+#define COD_MAJOR_PERIPHERAL 0x05 // BIT8 | BIT10
+#define COD_MAJOR_IMAGING 0x06 // BIT9 | BIT10
+#define COD_MAJOR_WEARABLE 0x07 // BIT8 | BIT9 | BIT10
+#define COD_MAJOR_TOY 0x08 // BIT11
+#define COD_MAJOR_HEALTH 0x09 // BIT8 | BIT11
+#define COD_MAJOR_UNCLASSIFIED 0x1F // BIT8 | BIT9 | BIT10 | BIT11 | BIT12
+
+/***************************
+ * service class fields
+ * Note: All values are deduced by basing BIT_X to BIT_8, values as per
+ * BT-spec assigned-numbers.
+ ***************************/
+#define COD_SERVICE_LMTD_DISCOVER 0x0020 // BIT13 (eg. 13-8 = BIT5 = 0x0020)
+#define COD_SERVICE_LE_AUDIO 0x0040 // BIT14
+#define COD_SERVICE_POSITIONING 0x0100 // BIT16
+#define COD_SERVICE_NETWORKING 0x0200 // BIT17
+#define COD_SERVICE_RENDERING 0x0400 // BIT18
+#define COD_SERVICE_CAPTURING 0x0800 // BIT19
+#define COD_SERVICE_OBJ_TRANSFER 0x1000 // BIT20
+#define COD_SERVICE_AUDIO 0x2000 // BIT21
+#define COD_SERVICE_TELEPHONY 0x4000 // BIT22
+#define COD_SERVICE_INFORMATION 0x8000 // BIT23
+
+/***************************
+ * minor device class field
+ * Note: LSB[1:0] (2 bits) is don't care for minor device class.
+ ***************************/
+/* Minor Device class field - Computer Major Class (COD_MAJOR_COMPUTER) */
+#define COD_MAJOR_COMPUTER_MINOR_UNCATEGORIZED 0x00
+#define COD_MAJOR_COMPUTER_MINOR_DESKTOP_WORKSTATION 0x04 // BIT2
+#define COD_MAJOR_COMPUTER_MINOR_SERVER_CLASS_COMPUTER 0x08 // BIT3
+#define COD_MAJOR_COMPUTER_MINOR_LAPTOP 0x0C // BIT2 | BIT3
+#define COD_MAJOR_COMPUTER_MINOR_HANDHELD_PC_PDA 0x10 // BIT4
+#define COD_MAJOR_COMPUTER_MINOR_PALM_SIZE_PC_PDA 0x14 // BIT2 | BIT4
+#define COD_MAJOR_COMPUTER_MINOR_WEARABLE_COMPUTER_WATCH_SIZE 0x18 // BIT3 | BIT4
+#define COD_MAJOR_COMPUTER_MINOR_TABLET 0x1C // BIT2 | BIT3 | BIT4
+
+/* Minor Device class field - Phone Major Class (COD_MAJOR_PHONE) */
+#define COD_MAJOR_PHONE_MINOR_UNCATEGORIZED 0x00
+#define COD_MAJOR_PHONE_MINOR_CELLULAR 0x04 // BIT2
+#define COD_MAJOR_PHONE_MINOR_CORDLESS 0x08 // BIT3
+#define COD_MAJOR_PHONE_MINOR_SMARTPHONE 0x0C // BIT2 | BIT3
+#define COD_MAJOR_PHONE_MINOR_WIRED_MODEM_OR_VOICE_GATEWAY 0x10 // BIT4
+#define COD_MAJOR_PHONE_MINOR_COMMON_ISDN_ACCESS 0x14 // BIT2 | BIT4
+
+/*
+ * Minor Device class field -
+ * LAN/Network Access Point Major Class (COD_MAJOR_LAN_NAP)
+ */
+#define COD_MAJOR_LAN_NAP_MINOR_FULLY_AVAILABLE 0x00
+#define COD_MAJOR_LAN_NAP_MINOR_1_TO_17_PER_UTILIZED 0x20 // BIT5
+#define COD_MAJOR_LAN_NAP_MINOR_17_TO_33_PER_UTILIZED 0x40 // BIT6
+#define COD_MAJOR_LAN_NAP_MINOR_33_TO_50_PER_UTILIZED 0x60 // BIT5 | BIT6
+#define COD_MAJOR_LAN_NAP_MINOR_50_TO_67_PER_UTILIZED 0x80 // BIT7
+#define COD_MAJOR_LAN_NAP_MINOR_67_TO_83_PER_UTILIZED 0xA0 // BIT5 | BIT7
+#define COD_MAJOR_LAN_NAP_MINOR_83_TO_99_PER_UTILIZED 0xC0 // BIT6 | BIT7
+#define COD_MAJOR_LAN_NAP_MINOR_NO_SERVICE_AVAILABLE 0xE0 // BIT5 | BIT6 | BIT7
+
+/* Minor Device class field - Audio/Video Major Class (COD_MAJOR_AUDIO) */
+/* 0x00 is used as unclassified for all minor device classes */
+#define COD_MINOR_UNCATEGORIZED 0x00
+#define COD_MAJOR_AUDIO_MINOR_WEARABLE_HEADSET 0x04 // BIT2
+#define COD_MAJOR_AUDIO_MINOR_CONFM_HANDSFREE 0x08 // BIT3
+#define COD_MAJOR_AUDIO_MINOR_MICROPHONE 0x10 // BIT4
+#define COD_MAJOR_AUDIO_MINOR_LOUDSPEAKER 0x14 // BIT2 | BIT4
+#define COD_MAJOR_AUDIO_MINOR_HEADPHONES 0x18 // BIT3 | BIT4
+#define COD_MAJOR_AUDIO_MINOR_PORTABLE_AUDIO 0x1C // BIT2 | BIT3 | BIT4
+#define COD_MAJOR_AUDIO_MINOR_CAR_AUDIO 0x20 // BIT5
+#define COD_MAJOR_AUDIO_MINOR_SET_TOP_BOX 0x24 // BIT2 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_HIFI_AUDIO 0x28 // BIT3 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_VCR 0x2C // BIT2 | BIT3 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_VIDEO_CAMERA 0x30 // BIT4 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_CAMCORDER 0x34 // BIT2 | BIT4 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_VIDEO_MONITOR 0x38 // BIT3 | BIT4 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_VIDEO_DISPLAY_AND_LOUDSPEAKER 0x3C // BIT2 |
+ // BIT3 | BIT4 | BIT5
+#define COD_MAJOR_AUDIO_MINOR_VIDEO_CONFERENCING 0x40 // BIT6
+#define COD_MAJOR_AUDIO_MINOR_GAMING_OR_TOY 0x48 // BIT3 | BIT6
+
+/* Minor Device class field - Peripheral Major Class (COD_MAJOR_PERIPHERAL) */
+/* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */
+#define COD_MAJOR_PERIPH_MINOR_KEYBOARD 0x40 // BIT6
+#define COD_MAJOR_PERIPH_MINOR_POINTING 0x80 // BIT7
+#define COD_MAJOR_PERIPH_MINOR_KEYBOARD_AND_POINTING_DEVICE 0xC0 // BIT6 | BIT7
+
+/* Bits 2-5 OR'd with selection from bits 6-7 */
+#define COD_MAJOR_PERIPH_MINOR_JOYSTICK 0x04 // BIT2
+#define COD_MAJOR_PERIPH_MINOR_GAMEPAD 0x08 // BIT3
+#define COD_MAJOR_PERIPH_MINOR_REMOTE_CONTROL 0x0C // BIT2 | BIT3
+#define COD_MAJOR_PERIPH_MINOR_SENSING_DEVICE 0x10 // BIT4
+#define COD_MAJOR_PERIPH_MINOR_DIGITIZING_TABLET 0x14 // BIT2 | BIT4
+#define COD_MAJOR_PERIPH_MINOR_CARD_READER 0x18 /* e.g. SIM card reader, BIT3 | BIT4 */
+#define COD_MAJOR_PERIPH_MINOR_DIGITAL_PEN 0x1C // Pen, BIT2 | BIT3 | BIT4
+#define COD_MAJOR_PERIPH_MINOR_HANDHELD_SCANNER 0x20 // e.g. Barcode, RFID, BIT5
+#define COD_MAJOR_PERIPH_MINOR_HANDHELD_GESTURAL_INP_DEVICE 0x24
+ // e.g. "wand" form factor, BIT2 | BIT5
+
+/* Minor Device class field - Imaging Major Class (COD_MAJOR_IMAGING)
+ *
+ * Bits 5-7 independently specify display, camera, scanner, or printer
+ * Note: Apart from the set bit, all other bits are don't care.
+ */
+#define COD_MAJOR_IMAGING_MINOR_DISPLAY 0x10 // BIT4
+#define COD_MAJOR_IMAGING_MINOR_CAMERA 0x20 // BIT5
+#define COD_MAJOR_IMAGING_MINOR_SCANNER 0x40 // BIT6
+#define COD_MAJOR_IMAGING_MINOR_PRINTER 0x80 // BIT7
+
+/* Minor Device class field - Wearable Major Class (COD_MAJOR_WEARABLE) */
+#define COD_MAJOR_WEARABLE_MINOR_WRIST_WATCH 0x04 // BIT2
+#define COD_MAJOR_WEARABLE_MINOR_PAGER 0x08 // BIT3
+#define COD_MJAOR_WEARABLE_MINOR_JACKET 0x0C // BIT2 | BIT3
+#define COD_MAJOR_WEARABLE_MINOR_HELMET 0x10 // BIT4
+#define COD_MAJOR_WEARABLE_MINOR_GLASSES 0x14 // BIT2 | BIT4
+#define COD_MAJOR_WEARABLE_MINOR_PIN 0x18
+ // e.g. Label pin, broach, badge BIT3 | BIT4
+
+/* Minor Device class field - Toy Major Class (COD_MAJOR_TOY) */
+#define COD_MAJOR_TOY_MINOR_ROBOT 0x04 // BIT2
+#define COD_MAJOR_TOY_MINOR_VEHICLE 0x08 // BIT3
+#define COD_MAJOR_TOY_MINOR_DOLL_OR_ACTION_FIGURE 0x0C // BIT2 | BIT3
+#define COD_MAJOR_TOY_MINOR_CONTROLLER 0x10 // BIT4
+#define COD_MAJOR_TOY_MINOR_GAME 0x14 // BIT2 | BIT4
+
+/* Minor Device class field - Health Major Class (COD_MAJOR_HEALTH) */
+#define COD_MAJOR_HEALTH_MINOR_BLOOD_MONITOR 0x04 // Blood pressure monitor, BIT2
+#define COD_MAJOR_HEALTH_MINOR_THERMOMETER 0x08 // BIT3
+#define COD_MAJOR_HEALTH_MINOR_WEIGHING_SCALE 0x0C // BIT2 | BIT3
+#define COD_MAJOR_HEALTH_MINOR_GLUCOSE_METER 0x10 // BIT4
+#define COD_MAJOR_HEALTH_MINOR_PULSE_OXIMETER 0x14 // BIT2 | BIT4
+#define COD_MAJOR_HEALTH_MINOR_HEART_PULSE_MONITOR 0x18 // BIT3 | BIT4
+#define COD_MAJOR_HEALTH_MINOR_HEALTH_DATA_DISPLAY 0x1C // BIT2 | BIT3 | BIT4
+#define COD_MAJO_HEALTH_MINOR_STEP_COUNTER 0x20 // BIT5
+#define COD_MAJOR_HEALTH_MINOR_BODY_COMPOSITION_ANALYZER 0x24 // BIT2 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_PEAK_FLOW_MONITOR 0x28 // BIT3 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_MEDICATION_MONITOR 0x2C // BIT2 | BIT3 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_KNEE_PROSTHESIS 0x30 // BIT4 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_ANKLE_PROSTHESIS 0x34 // BIT3 | BIT4 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_GENERIC_HEALTH_MANAGER 0x38 // BIT2 | BIT3 | BIT4 | BIT5
+#define COD_MAJOR_HEALTH_MINOR_PERSONAL_MOBILITY_DEVICE 0x3C // BIT4 | BIT5
+
/* 0x00 is used as unclassified for all minor device classes */
-#define BTM_COD_MINOR_UNCLASSIFIED 0x00
-#define BTM_COD_MINOR_WEARABLE_HEADSET 0x04
-#define BTM_COD_MINOR_CONFM_HANDSFREE 0x08
-#define BTM_COD_MINOR_CAR_AUDIO 0x20
-#define BTM_COD_MINOR_SET_TOP_BOX 0x24
+#define BTM_COD_MINOR_UNCLASSIFIED COD_MINOR_UNCATEGORIZED
+#define BTM_COD_MINOR_WEARABLE_HEADSET COD_MAJOR_AUDIO_MINOR_WEARABLE_HEADSET
+#define BTM_COD_MINOR_CONFM_HANDSFREE COD_MAJOR_AUDIO_MINOR_CONFM_HANDSFREE
+#define BTM_COD_MINOR_CAR_AUDIO COD_MAJOR_AUDIO_MINOR_CAR_AUDIO
+#define BTM_COD_MINOR_SET_TOP_BOX COD_MAJOR_AUDIO_MINOR_SET_TOP_BOX
/* minor device class field for Peripheral Major Class */
/* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */
-#define BTM_COD_MINOR_KEYBOARD 0x40
-#define BTM_COD_MINOR_POINTING 0x80
+#define BTM_COD_MINOR_KEYBOARD COD_MAJOR_PERIPH_MINOR_KEYBOARD
+#define BTM_COD_MINOR_POINTING COD_MAJOR_PERIPH_MINOR_POINTING
/* Bits 2-5 OR'd with selection from bits 6-7 */
/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */
-#define BTM_COD_MINOR_JOYSTICK 0x04
-#define BTM_COD_MINOR_GAMEPAD 0x08
-#define BTM_COD_MINOR_REMOTE_CONTROL 0x0C
-#define BTM_COD_MINOR_DIGITIZING_TABLET 0x14
-#define BTM_COD_MINOR_CARD_READER 0x18 /* e.g. SIM card reader */
-#define BTM_COD_MINOR_DIGITAL_PAN 0x1C
+#define BTM_COD_MINOR_JOYSTICK COD_MAJOR_PERIPH_MINOR_JOYSTICK
+#define BTM_COD_MINOR_GAMEPAD COD_MAJOR_PERIPH_MINOR_GAMEPAD
+#define BTM_COD_MINOR_REMOTE_CONTROL COD_MAJOR_PERIPH_MINOR_REMOTE_CONTROL
+#define BTM_COD_MINOR_DIGITIZING_TABLET COD_MAJOR_PERIPH_MINOR_DIGITIZING_TABLET
+#define BTM_COD_MINOR_CARD_READER COD_MAJOR_PERIPH_MINOR_CARD_READER
+#define BTM_COD_MINOR_DIGITAL_PAN COD_MAJOR_PERIPH_MINOR_DIGITAL_PEN
/* minor device class field for Imaging Major Class */
/* Bits 5-7 independently specify display, camera, scanner, or printer */
-#define BTM_COD_MINOR_DISPLAY 0x10
+#define BTM_COD_MINOR_DISPLAY COD_MAJOR_IMAGING_MINOR_DISPLAY
/* Bits 2-3 Reserved */
/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */
/* minor device class field for Wearable Major Class */
/* Bits 2-7 meaningful */
-#define BTM_COD_MINOR_WRIST_WATCH 0x04
-#define BTM_COD_MINOR_GLASSES 0x14
+#define BTM_COD_MINOR_WRIST_WATCH COD_MAJOR_WEARABLE_MINOR_WRIST_WATCH
+#define BTM_COD_MINOR_GLASSES COD_MAJOR_WEARABLE_MINOR_GLASSES
/* minor device class field for Health Major Class */
/* Bits 2-7 meaningful */
-#define BTM_COD_MINOR_BLOOD_MONITOR 0x04
-#define BTM_COD_MINOR_THERMOMETER 0x08
-#define BTM_COD_MINOR_WEIGHING_SCALE 0x0C
-#define BTM_COD_MINOR_GLUCOSE_METER 0x10
-#define BTM_COD_MINOR_PULSE_OXIMETER 0x14
-#define BTM_COD_MINOR_HEART_PULSE_MONITOR 0x18
-#define BTM_COD_MINOR_STEP_COUNTER 0x20
+#define BTM_COD_MINOR_BLOOD_MONITOR COD_MAJOR_HEALTH_MINOR_BLOOD_MONITOR
+#define BTM_COD_MINOR_THERMOMETER COD_MAJOR_HEALTH_MINOR_THERMOMETER
+#define BTM_COD_MINOR_WEIGHING_SCALE COD_MAJOR_HEALTH_MINOR_WEIGHING_SCALE
+#define BTM_COD_MINOR_GLUCOSE_METER COD_MAJOR_HEALTH_MINOR_GLUCOSE_METER
+#define BTM_COD_MINOR_PULSE_OXIMETER COD_MAJOR_HEALTH_MINOR_PULSE_OXIMETER
+#define BTM_COD_MINOR_HEART_PULSE_MONITOR COD_MAJOR_HEALTH_MINOR_HEART_PULSE_MONITOR
+#define BTM_COD_MINOR_STEP_COUNTER COD_MAJO_HEALTH_MINOR_STEP_COUNTER
/***************************
* major device class field
***************************/
-#define BTM_COD_MAJOR_COMPUTER 0x01
-#define BTM_COD_MAJOR_PHONE 0x02
-#define BTM_COD_MAJOR_AUDIO 0x04
-#define BTM_COD_MAJOR_PERIPHERAL 0x05
-#define BTM_COD_MAJOR_IMAGING 0x06
-#define BTM_COD_MAJOR_WEARABLE 0x07
-#define BTM_COD_MAJOR_HEALTH 0x09
-#define BTM_COD_MAJOR_UNCLASSIFIED 0x1F
+#define BTM_COD_MAJOR_COMPUTER COD_MAJOR_COMPUTER
+#define BTM_COD_MAJOR_PHONE COD_MAJOR_PHONE
+#define BTM_COD_MAJOR_AUDIO COD_MAJOR_AUDIO
+#define BTM_COD_MAJOR_PERIPHERAL COD_MAJOR_PERIPHERAL
+#define BTM_COD_MAJOR_IMAGING COD_MAJOR_IMAGING
+#define BTM_COD_MAJOR_WEARABLE COD_MAJOR_WEARABLE
+#define BTM_COD_MAJOR_HEALTH COD_MAJOR_HEALTH
+#define BTM_COD_MAJOR_UNCLASSIFIED COD_MAJOR_UNCLASSIFIED
/***************************
* service class fields
***************************/
-#define BTM_COD_SERVICE_LMTD_DISCOVER 0x0020
-#define BTM_COD_SERVICE_LE_AUDIO 0x0040
-#define BTM_COD_SERVICE_POSITIONING 0x0100
-#define BTM_COD_SERVICE_NETWORKING 0x0200
-#define BTM_COD_SERVICE_RENDERING 0x0400
-#define BTM_COD_SERVICE_CAPTURING 0x0800
-#define BTM_COD_SERVICE_OBJ_TRANSFER 0x1000
-#define BTM_COD_SERVICE_AUDIO 0x2000
-#define BTM_COD_SERVICE_TELEPHONY 0x4000
-#define BTM_COD_SERVICE_INFORMATION 0x8000
+#define BTM_COD_SERVICE_LMTD_DISCOVER COD_SERVICE_LMTD_DISCOVER
+#define BTM_COD_SERVICE_LE_AUDIO COD_SERVICE_LE_AUDIO
+#define BTM_COD_SERVICE_POSITIONING COD_SERVICE_POSITIONING
+#define BTM_COD_SERVICE_NETWORKING COD_SERVICE_NETWORKING
+#define BTM_COD_SERVICE_RENDERING COD_SERVICE_RENDERING
+#define BTM_COD_SERVICE_CAPTURING COD_SERVICE_CAPTURING
+#define BTM_COD_SERVICE_OBJ_TRANSFER COD_SERVICE_OBJ_TRANSFER
+#define BTM_COD_SERVICE_AUDIO COD_SERVICE_AUDIO
+#define BTM_COD_SERVICE_TELEPHONY COD_SERVICE_TELEPHONY
+#define BTM_COD_SERVICE_INFORMATION COD_SERVICE_INFORMATION
/* the COD masks */
#define BTM_COD_MINOR_CLASS_MASK 0xFC
diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h
index 9a1679768b..d89028d27f 100644
--- a/system/stack/include/btm_ble_api_types.h
+++ b/system/stack/include/btm_ble_api_types.h
@@ -29,6 +29,7 @@
#include "stack/include/bt_octets.h"
#include "stack/include/btm_status.h"
#include "stack/include/hci_error_code.h"
+#include "stack/include/ble_appearance.h"
#include "types/ble_address_with_type.h"
#include "types/raw_address.h"
@@ -216,60 +217,70 @@ typedef uint8_t BLE_SIGNATURE[BTM_BLE_AUTH_SIGN_LEN]; /* Device address */
#endif
/* Appearance Values Reported with BTM_BLE_AD_TYPE_APPEARANCE */
-#define BTM_BLE_APPEARANCE_UKNOWN 0x0000
-#define BTM_BLE_APPEARANCE_GENERIC_PHONE 0x0040
-#define BTM_BLE_APPEARANCE_GENERIC_COMPUTER 0x0080
-#define BTM_BLE_APPEARANCE_GENERIC_WATCH 0x00C0
-#define BTM_BLE_APPEARANCE_SPORTS_WATCH 0x00C1
-#define BTM_BLE_APPEARANCE_GENERIC_CLOCK 0x0100
-#define BTM_BLE_APPEARANCE_GENERIC_DISPLAY 0x0140
-#define BTM_BLE_APPEARANCE_GENERIC_REMOTE 0x0180
-#define BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0
-#define BTM_BLE_APPEARANCE_GENERIC_TAG 0x0200
-#define BTM_BLE_APPEARANCE_GENERIC_KEYRING 0x0240
-#define BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280
-#define BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0
-#define BTM_BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300
-#define BTM_BLE_APPEARANCE_THERMOMETER_EAR 0x0301
-#define BTM_BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340
-#define BTM_BLE_APPEARANCE_HEART_RATE_BELT 0x0341
-#define BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380
-#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381
-#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382
-#define BTM_BLE_APPEARANCE_GENERIC_HID 0x03C0
-#define BTM_BLE_APPEARANCE_HID_KEYBOARD 0x03C1
-#define BTM_BLE_APPEARANCE_HID_MOUSE 0x03C2
-#define BTM_BLE_APPEARANCE_HID_JOYSTICK 0x03C3
-#define BTM_BLE_APPEARANCE_HID_GAMEPAD 0x03C4
-#define BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5
-#define BTM_BLE_APPEARANCE_HID_CARD_READER 0x03C6
-#define BTM_BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7
-#define BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8
-#define BTM_BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400
-#define BTM_BLE_APPEARANCE_GENERIC_WALKING 0x0440
-#define BTM_BLE_APPEARANCE_WALKING_IN_SHOE 0x0441
-#define BTM_BLE_APPEARANCE_WALKING_ON_SHOE 0x0442
-#define BTM_BLE_APPEARANCE_WALKING_ON_HIP 0x0443
-#define BTM_BLE_APPEARANCE_GENERIC_CYCLING 0x0480
-#define BTM_BLE_APPEARANCE_CYCLING_COMPUTER 0x0481
-#define BTM_BLE_APPEARANCE_CYCLING_SPEED 0x0482
-#define BTM_BLE_APPEARANCE_CYCLING_CADENCE 0x0483
-#define BTM_BLE_APPEARANCE_CYCLING_POWER 0x0484
-#define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485
-#define BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE 0x0940
-#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD 0x0941
-#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET 0x0942
-#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES 0x0943
-#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND 0x0944
-#define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40
-#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41
-#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42
-#define BTM_BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80
-#define BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440
-#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441
-#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442
-#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443
-#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444
+#define BTM_BLE_APPEARANCE_UNKNOWN BLE_APPEARANCE_UNKNOWN
+#define BTM_BLE_APPEARANCE_GENERIC_PHONE BLE_APPEARANCE_GENERIC_PHONE
+#define BTM_BLE_APPEARANCE_GENERIC_COMPUTER BLE_APPEARANCE_GENERIC_COMPUTER
+#define BTM_BLE_APPEARANCE_GENERIC_WATCH BLE_APPEARANCE_GENERIC_WATCH
+#define BTM_BLE_APPEARANCE_SPORTS_WATCH BLE_APPEARANCE_SPORTS_WATCH
+#define BTM_BLE_APPEARANCE_GENERIC_CLOCK BLE_APPEARANCE_GENERIC_CLOCK
+#define BTM_BLE_APPEARANCE_GENERIC_DISPLAY BLE_APPEARANCE_GENERIC_DISPLAY
+#define BTM_BLE_APPEARANCE_GENERIC_REMOTE BLE_APPEARANCE_GENERIC_REMOTE
+#define BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES BLE_APPEARANCE_GENERIC_EYEGLASSES
+#define BTM_BLE_APPEARANCE_GENERIC_TAG BLE_APPEARANCE_GENERIC_TAG
+#define BTM_BLE_APPEARANCE_GENERIC_KEYRING BLE_APPEARANCE_GENERIC_KEYRING
+#define BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER BLE_APPEARANCE_GENERIC_MEDIA_PLAYER
+#define BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER BLE_APPEARANCE_GENERIC_BARCODE_SCANNER
+#define BTM_BLE_APPEARANCE_GENERIC_THERMOMETER BLE_APPEARANCE_GENERIC_THERMOMETER
+#define BTM_BLE_APPEARANCE_THERMOMETER_EAR BLE_APPEARANCE_THERMOMETER_EAR
+#define BTM_BLE_APPEARANCE_GENERIC_HEART_RATE BLE_APPEARANCE_GENERIC_HEART_RATE
+#define BTM_BLE_APPEARANCE_HEART_RATE_BELT BLE_APPEARANCE_HEART_RATE_BELT
+#define BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE
+#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM BLE_APPEARANCE_BLOOD_PRESSURE_ARM
+#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST BLE_APPEARANCE_BLOOD_PRESSURE_WRIST
+#define BTM_BLE_APPEARANCE_GENERIC_HID BLE_APPEARANCE_GENERIC_HID
+#define BTM_BLE_APPEARANCE_HID_KEYBOARD BLE_APPEARANCE_HID_KEYBOARD
+#define BTM_BLE_APPEARANCE_HID_MOUSE BLE_APPEARANCE_HID_MOUSE
+#define BTM_BLE_APPEARANCE_HID_JOYSTICK BLE_APPEARANCE_HID_JOYSTICK
+#define BTM_BLE_APPEARANCE_HID_GAMEPAD BLE_APPEARANCE_HID_GAMEPAD
+#define BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET BLE_APPEARANCE_HID_DIGITIZER_TABLET
+#define BTM_BLE_APPEARANCE_HID_CARD_READER BLE_APPEARANCE_HID_CARD_READER
+#define BTM_BLE_APPEARANCE_HID_DIGITAL_PEN BLE_APPEARANCE_HID_DIGITAL_PEN
+#define BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER BLE_APPEARANCE_HID_BARCODE_SCANNER
+#define BTM_BLE_APPEARANCE_GENERIC_GLUCOSE BLE_APPEARANCE_GENERIC_GLUCOSE
+#define BTM_BLE_APPEARANCE_GENERIC_WALKING BLE_APPEARANCE_GENERIC_WALKING
+#define BTM_BLE_APPEARANCE_WALKING_IN_SHOE BLE_APPEARANCE_WALKING_IN_SHOE
+#define BTM_BLE_APPEARANCE_WALKING_ON_SHOE BLE_APPEARANCE_WALKING_ON_SHOE
+#define BTM_BLE_APPEARANCE_WALKING_ON_HIP BLE_APPEARANCE_WALKING_ON_HIP
+#define BTM_BLE_APPEARANCE_GENERIC_CYCLING BLE_APPEARANCE_GENERIC_CYCLING
+#define BTM_BLE_APPEARANCE_CYCLING_COMPUTER BLE_APPEARANCE_CYCLING_COMPUTER
+#define BTM_BLE_APPEARANCE_CYCLING_SPEED BLE_APPEARANCE_CYCLING_SPEED
+#define BTM_BLE_APPEARANCE_CYCLING_CADENCE BLE_APPEARANCE_CYCLING_CADENCE
+#define BTM_BLE_APPEARANCE_CYCLING_POWER BLE_APPEARANCE_CYCLING_POWER
+#define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE BLE_APPEARANCE_CYCLING_SPEED_CADENCE
+#define BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE \
+ BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE
+#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD \
+ BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD
+#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET \
+ BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET
+#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES \
+ BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES
+#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND \
+ BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND
+#define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER BLE_APPEARANCE_GENERIC_PULSE_OXIMETER
+#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP
+#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST BLE_APPEARANCE_PULSE_OXIMETER_WRIST
+#define BTM_BLE_APPEARANCE_GENERIC_WEIGHT BLE_APPEARANCE_GENERIC_WEIGHT
+#define BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS \
+ BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS
+#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION \
+ BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION
+#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV \
+ BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV
+#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD \
+ BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD
+#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV \
+ BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV
/* Structure returned with Rand/Encrypt complete callback */
typedef struct {
diff --git a/system/stack/include/port_api.h b/system/stack/include/port_api.h
index 3a9fd7e4b3..d515813b81 100644
--- a/system/stack/include/port_api.h
+++ b/system/stack/include/port_api.h
@@ -360,6 +360,19 @@ typedef void(tPORT_MGMT_CALLBACK)(const tPORT_RESULT code, uint16_t port_handle)
/*******************************************************************************
*
+ * Function PORT_SetAppUid
+ *
+ * Description This function configures connection according to the
+ * specifications in the tPORT_STATE structure.
+ *
+ * Parameters: handle - Handle returned in the RFCOMM_CreateConnection
+ * app_uid - Uid of app that requested the socket
+ *
+ ******************************************************************************/
+[[nodiscard]] int PORT_SetAppUid(uint16_t handle, uint32_t app_uid);
+
+/*******************************************************************************
+ *
* Function PORT_SetState
*
* Description This function configures connection according to the
diff --git a/system/stack/include/security_client_callbacks.h b/system/stack/include/security_client_callbacks.h
index da1bd4e425..d9d19f33f6 100644
--- a/system/stack/include/security_client_callbacks.h
+++ b/system/stack/include/security_client_callbacks.h
@@ -110,7 +110,7 @@ typedef struct {
tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
tBTM_BLE_SEC_ACT sec_act);
bool (*BTM_IsEncrypted)(const RawAddress& bd_addr, tBT_TRANSPORT transport);
- bool (*BTM_SecIsSecurityPending)(const RawAddress& bd_addr);
+ bool (*BTM_SecIsLeSecurityPending)(const RawAddress& bd_addr);
bool (*BTM_IsLinkKeyKnown)(const RawAddress& bd_addr, tBT_TRANSPORT transport);
// Secure service management
diff --git a/system/stack/include/smp_status.h b/system/stack/include/smp_status.h
index 86670a5161..1d222ab419 100644
--- a/system/stack/include/smp_status.h
+++ b/system/stack/include/smp_status.h
@@ -41,25 +41,27 @@ typedef enum : uint8_t {
SMP_NUMERIC_COMPAR_FAIL = 0x0C,
SMP_BR_PARING_IN_PROGR = 0x0D,
SMP_XTRANS_DERIVE_NOT_ALLOW = 0x0E,
- SMP_MAX_FAIL_RSN_PER_SPEC = SMP_XTRANS_DERIVE_NOT_ALLOW,
+ SMP_KEY_REJECTED = 0x0F,
+ SMP_BUSY = 0x10, /*device is not ready to perform a pairing procedure*/
+ SMP_MAX_FAIL_RSN_PER_SPEC = SMP_BUSY,
/* self defined error code */
- SMP_PAIR_INTERNAL_ERR = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x01), /* 0x0F */
+ SMP_PAIR_INTERNAL_ERR = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x01), /* 0x11 */
/* Unknown IO capability, unable to decide association model */
- SMP_UNKNOWN_IO_CAP = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x02), /* 0x10 */
+ SMP_UNKNOWN_IO_CAP = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x02), /* 0x12 */
- SMP_BUSY = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x05), /* 0x13 */
- SMP_ENC_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x06), /* 0x14 */
- SMP_STARTED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x07), /* 0x15 */
- SMP_RSP_TIMEOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x08), /* 0x16 */
+ SMP_IMPL_BUSY = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x05), /* 0x15 */
+ SMP_ENC_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x06), /* 0x16 */
+ SMP_STARTED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x07), /* 0x17 */
+ SMP_RSP_TIMEOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x08), /* 0x18 */
/* Unspecified failure reason */
- SMP_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0A), /* 0x18 */
+ SMP_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0A), /* 0x1A */
- SMP_CONN_TOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0B), /* 0x19 */
- SMP_SIRK_DEVICE_INVALID = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0C), /* 0x1a */
- SMP_USER_CANCELLED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0D), /* 0x1b */
+ SMP_CONN_TOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0B), /* 0x1B */
+ SMP_SIRK_DEVICE_INVALID = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0C), /* 0x1C */
+ SMP_USER_CANCELLED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0D), /* 0x1D */
} tSMP_STATUS;
inline std::string smp_status_text(const tSMP_STATUS& status) {
@@ -79,9 +81,11 @@ inline std::string smp_status_text(const tSMP_STATUS& status) {
CASE_RETURN_TEXT(SMP_NUMERIC_COMPAR_FAIL);
CASE_RETURN_TEXT(SMP_BR_PARING_IN_PROGR);
CASE_RETURN_TEXT(SMP_XTRANS_DERIVE_NOT_ALLOW);
+ CASE_RETURN_TEXT(SMP_KEY_REJECTED);
+ CASE_RETURN_TEXT(SMP_BUSY);
CASE_RETURN_TEXT(SMP_PAIR_INTERNAL_ERR);
CASE_RETURN_TEXT(SMP_UNKNOWN_IO_CAP);
- CASE_RETURN_TEXT(SMP_BUSY);
+ CASE_RETURN_TEXT(SMP_IMPL_BUSY);
CASE_RETURN_TEXT(SMP_ENC_FAIL);
CASE_RETURN_TEXT(SMP_STARTED);
CASE_RETURN_TEXT(SMP_RSP_TIMEOUT);
diff --git a/system/stack/rfcomm/port_api.cc b/system/stack/rfcomm/port_api.cc
index d74d4d1835..fea9367fbc 100644
--- a/system/stack/rfcomm/port_api.cc
+++ b/system/stack/rfcomm/port_api.cc
@@ -142,7 +142,7 @@ int RFCOMM_CreateConnectionWithSecurity(uint16_t uuid, uint8_t scn, bool is_serv
log::error(
"already at opened state {}, RFC_state={}, MCB_state={}, "
"bd_addr={}, scn={}, is_server={}, mtu={}, uuid=0x{:x}, dlci={}, p_mcb={}, port={}",
- static_cast<int>(p_port->state), static_cast<int>(p_port->rfc.state),
+ static_cast<int>(p_port->state), static_cast<int>(p_port->rfc.sm_cb.state),
p_port->rfc.p_mcb ? p_port->rfc.p_mcb->state : 0, bd_addr, scn, is_server, mtu,
uuid, dlci, std::format_ptr(p_mcb), p_port->handle);
*p_handle = p_port->handle;
@@ -455,14 +455,14 @@ int PORT_CheckConnection(uint16_t handle, RawAddress* bd_addr, uint16_t* p_lcid)
}
log::verbose("handle={}, in_use={}, port_state={}, p_mcb={}, peer_ready={}, rfc_state={}", handle,
p_port->in_use, p_port->state, std::format_ptr(p_port->rfc.p_mcb),
- p_port->rfc.p_mcb ? p_port->rfc.p_mcb->peer_ready : -1, p_port->rfc.state);
+ p_port->rfc.p_mcb ? p_port->rfc.p_mcb->peer_ready : -1, p_port->rfc.sm_cb.state);
if (!p_port->in_use || (p_port->state == PORT_CONNECTION_STATE_CLOSED)) {
return PORT_NOT_OPENED;
}
if (!p_port->rfc.p_mcb || !p_port->rfc.p_mcb->peer_ready ||
- (p_port->rfc.state != RFC_STATE_OPENED)) {
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
return PORT_LINE_ERR;
}
@@ -509,8 +509,8 @@ bool PORT_IsOpening(RawAddress* bd_addr) {
if (multiplexer_cb.state == RFC_MX_STATE_CONNECTED) {
const tPORT* p_port = get_port_from_mcb(&multiplexer_cb);
log::info("RFC_MX_STATE_CONNECTED, found_port={}, tRFC_PORT_STATE={}",
- (p_port != nullptr) ? "T" : "F", (p_port != nullptr) ? p_port->rfc.state : 0);
- if ((p_port == nullptr) || (p_port->rfc.state < RFC_STATE_OPENED)) {
+ (p_port != nullptr) ? "T" : "F", (p_port != nullptr) ? p_port->rfc.sm_cb.state : 0);
+ if ((p_port == nullptr) || (p_port->rfc.sm_cb.state < RFC_STATE_OPENED)) {
/* Port is not established yet. */
*bd_addr = multiplexer_cb.bd_addr;
log::info("In RFC_MX_STATE_CONNECTED but port is not established yet, returning true");
@@ -554,8 +554,8 @@ bool PORT_IsCollisionDetected(RawAddress bd_addr) {
if (multiplexer_cb.state == RFC_MX_STATE_CONNECTED) {
const tPORT* p_port = get_port_from_mcb(&multiplexer_cb);
log::info("RFC_MX_STATE_CONNECTED, found_port={}, tRFC_PORT_STATE={}",
- (p_port != nullptr) ? "T" : "F", (p_port != nullptr) ? p_port->rfc.state : 0);
- if ((p_port == nullptr) || (p_port->rfc.state < RFC_STATE_OPENED)) {
+ (p_port != nullptr) ? "T" : "F", (p_port != nullptr) ? p_port->rfc.sm_cb.state : 0);
+ if ((p_port == nullptr) || (p_port->rfc.sm_cb.state < RFC_STATE_OPENED)) {
// Port is not established yet
log::info(
"In RFC_MX_STATE_CONNECTED but port is not established yet, "
@@ -570,6 +570,30 @@ bool PORT_IsCollisionDetected(RawAddress bd_addr) {
/*******************************************************************************
*
+ * Function PORT_SetAppUid
+ *
+ * Description This function configures connection according to the
+ * specifications in the tPORT_STATE structure.
+ *
+ * Parameters: handle - Handle returned in the RFCOMM_CreateConnection
+ * app_uid - Uid of app that requested the socket
+ *
+ ******************************************************************************/
+int PORT_SetAppUid(uint16_t handle, uint32_t app_uid) {
+ tPORT* p_port = get_port_from_handle(handle);
+
+ if (p_port == nullptr) {
+ log::error("Unable to get RFCOMM port control block bad handle:{}", handle);
+ return PORT_BAD_HANDLE;
+ }
+
+ p_port->app_uid = app_uid;
+
+ return PORT_SUCCESS;
+}
+
+/*******************************************************************************
+ *
* Function PORT_SetSettings
*
* Description This function configures connection according to the
@@ -828,7 +852,7 @@ int PORT_ReadData(uint16_t handle, char* p_data, uint16_t max_len, uint16_t* p_l
static int port_write(tPORT* p_port, BT_HDR* p_buf) {
/* We should not allow to write data in to server port when connection is not
* opened */
- if (p_port->is_server && (p_port->rfc.state != RFC_STATE_OPENED)) {
+ if (p_port->is_server && (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
osi_free(p_buf);
return PORT_CLOSED;
}
@@ -837,7 +861,7 @@ static int port_write(tPORT* p_port, BT_HDR* p_buf) {
/* Peer is not ready or Port is not yet opened or initial port control */
/* command has not been sent */
if (p_port->tx.peer_fc || !p_port->rfc.p_mcb || !p_port->rfc.p_mcb->peer_ready ||
- (p_port->rfc.state != RFC_STATE_OPENED) ||
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED) ||
((p_port->port_ctrl & (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED)) !=
(PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED))) {
if ((p_port->tx.queue_size > PORT_TX_CRITICAL_WM) ||
@@ -857,7 +881,7 @@ static int port_write(tPORT* p_port, BT_HDR* p_buf) {
"Data is enqueued. flow disabled {} peer_ready {} state {} ctrl_state "
"{:x}",
p_port->tx.peer_fc, p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready,
- p_port->rfc.state, p_port->port_ctrl);
+ p_port->rfc.sm_cb.state, p_port->port_ctrl);
fixed_queue_enqueue(p_port->tx.queue, p_buf);
p_port->tx.queue_size += p_buf->len;
diff --git a/system/stack/rfcomm/port_int.h b/system/stack/rfcomm/port_int.h
index 94d9f4cc8b..6854ba9524 100644
--- a/system/stack/rfcomm/port_int.h
+++ b/system/stack/rfcomm/port_int.h
@@ -34,6 +34,7 @@
#include "stack/include/l2cap_types.h"
#include "stack/include/port_api.h"
#include "stack/include/rfcdefs.h"
+#include "stack/rfcomm/rfc_event.h"
#include "stack/rfcomm/rfc_state.h"
#include "types/raw_address.h"
@@ -45,7 +46,7 @@
#define PORT_FC_CREDIT 2 /* use RFCOMM credit based flow control */
/*
- * Define Port Data Transfere control block
+ * Define Port Data Transfer control block
*/
typedef struct {
fixed_queue_t* queue; /* Queue of buffers waiting to be sent */
@@ -104,10 +105,22 @@ typedef struct {
} tRFC_MCB;
/*
+ * RFCOMM Port State Machine Control Block
+ */
+struct RfcommPortSm {
+ tRFC_PORT_STATE state;
+ tRFC_PORT_STATE state_prior;
+ tRFC_PORT_EVENT last_event;
+ tPORT_RESULT close_reason;
+ uint64_t open_timestamp;
+ uint64_t close_timestamp;
+};
+
+/*
* RFCOMM Port Connection Control Block
*/
typedef struct {
- tRFC_PORT_STATE state; /* Current state of the connection */
+ RfcommPortSm sm_cb; // State machine control block
#define RFC_RSP_PN 0x01
#define RFC_RSP_RPN_REPLY 0x02
@@ -155,8 +168,9 @@ typedef struct {
tPORT_CONNECTION_STATE state; /* State of the application */
- uint8_t scn; /* Service channel number */
- uint16_t uuid; /* Service UUID */
+ uint8_t scn; /* Service channel number */
+ uint16_t uuid; /* Service UUID */
+ uint32_t app_uid; /* UID of the app for which this socket was created */
RawAddress bd_addr; /* BD ADDR of the device for the multiplexer channel */
bool is_server; /* true if the server application */
diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc
index a4cf8d5adf..14a9cb5bf0 100644
--- a/system/stack/rfcomm/port_rfc.cc
+++ b/system/stack/rfcomm/port_rfc.cc
@@ -180,7 +180,7 @@ void port_start_close(tPORT* p_port) {
}
/* Check if RFCOMM side has been closed while the message was queued */
- if ((p_mcb == NULL) || (p_port->rfc.state == RFC_STATE_CLOSED)) {
+ if ((p_mcb == NULL) || (p_port->rfc.sm_cb.state == RFC_STATE_CLOSED)) {
/* Call management callback function before calling port_release_port() to
* clear tPort */
if (p_port->p_mgmt_callback) {
@@ -899,7 +899,7 @@ void PORT_FlowInd(tRFC_MCB* p_mcb, uint8_t dlci, bool enable_data) {
if (dlci == 0) {
p_port = &rfc_cb.port.port[i];
if (!p_port->in_use || (p_port->rfc.p_mcb != p_mcb) ||
- (p_port->rfc.state != RFC_STATE_OPENED)) {
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
continue;
}
}
@@ -991,7 +991,7 @@ void port_rfc_closed(tPORT* p_port, uint8_t res) {
log::warn("port_rfc_closed in OPENING state ignored");
rfc_port_timer_stop(p_port);
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
if (p_mcb) {
p_mcb->port_handles[p_port->dlci] = 0;
@@ -1049,10 +1049,10 @@ void port_rfc_closed(tPORT* p_port, uint8_t res) {
p_port->p_mgmt_callback(static_cast<tPORT_RESULT>(res2), p_port->handle);
}
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
log::info(
- "RFCOMM connection closed, index={}, state={}, reason={}[{}], "
+ "RFCOMM connection closed, port_handle={}, state={}, reason={}[{}], "
"UUID=0x{:x}, bd_addr={}, is_server={}",
p_port->handle, p_port->state, PORT_GetResultString(res), res, p_port->uuid,
p_port->bd_addr, p_port->is_server);
diff --git a/system/stack/rfcomm/port_utils.cc b/system/stack/rfcomm/port_utils.cc
index 25e1da9f4c..dc59e58987 100644
--- a/system/stack/rfcomm/port_utils.cc
+++ b/system/stack/rfcomm/port_utils.cc
@@ -199,8 +199,8 @@ void port_select_mtu(tPORT* p_port) {
*
******************************************************************************/
void port_release_port(tPORT* p_port) {
- log::verbose("p_port: {} state: {} keep_handle: {}", std::format_ptr(p_port), p_port->rfc.state,
- p_port->keep_port_handle);
+ log::verbose("p_port: {} state: {} keep_handle: {}", std::format_ptr(p_port),
+ p_port->rfc.sm_cb.state, p_port->keep_port_handle);
mutex_global_lock();
BT_HDR* p_buf;
@@ -219,7 +219,7 @@ void port_release_port(tPORT* p_port) {
p_port->state = PORT_CONNECTION_STATE_CLOSED;
- if (p_port->rfc.state == RFC_STATE_CLOSED) {
+ if (p_port->rfc.sm_cb.state == RFC_STATE_CLOSED) {
if (p_port->rfc.p_mcb) {
p_port->rfc.p_mcb->port_handles[p_port->dlci] = 0;
@@ -228,6 +228,7 @@ void port_release_port(tPORT* p_port) {
}
rfc_port_timer_stop(p_port);
+ p_port->rfc.sm_cb = {};
mutex_global_lock();
fixed_queue_free(p_port->tx.queue, nullptr);
diff --git a/system/stack/rfcomm/rfc_port_fsm.cc b/system/stack/rfcomm/rfc_port_fsm.cc
index 0f3ec064ff..be47e22617 100644
--- a/system/stack/rfcomm/rfc_port_fsm.cc
+++ b/system/stack/rfcomm/rfc_port_fsm.cc
@@ -77,11 +77,11 @@ void rfc_port_sm_execute(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
log::assert_that(p_port != nullptr, "NULL port event {}", event);
// logs for state RFC_STATE_OPENED handled in rfc_port_sm_opened()
- if (p_port->rfc.state != RFC_STATE_OPENED) {
+ if (p_port->rfc.sm_cb.state != RFC_STATE_OPENED) {
log::info("bd_addr:{}, handle:{}, state:{}, event:{}", p_port->bd_addr, p_port->handle,
- rfcomm_port_state_text(p_port->rfc.state), rfcomm_port_event_text(event));
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state), rfcomm_port_event_text(event));
}
- switch (p_port->rfc.state) {
+ switch (p_port->rfc.sm_cb.state) {
case RFC_STATE_CLOSED:
rfc_port_sm_state_closed(p_port, event, p_data);
break;
@@ -122,7 +122,7 @@ void rfc_port_sm_execute(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
void rfc_port_sm_state_closed(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
switch (event) {
case RFC_PORT_EVENT_OPEN:
- p_port->rfc.state = RFC_STATE_ORIG_WAIT_SEC_CHECK;
+ p_port->rfc.sm_cb.state = RFC_STATE_ORIG_WAIT_SEC_CHECK;
btm_sec_mx_access_request(p_port->rfc.p_mcb->bd_addr, true, p_port->sec_mask,
&rfc_sec_check_complete, p_port);
return;
@@ -143,7 +143,7 @@ void rfc_port_sm_state_closed(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data
rfc_timer_stop(p_port->rfc.p_mcb);
/* Open will be continued after security checks are passed */
- p_port->rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
+ p_port->rfc.sm_cb.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
btm_sec_mx_access_request(p_port->rfc.p_mcb->bd_addr, false, p_port->sec_mask,
&rfc_sec_check_complete, p_port);
return;
@@ -167,11 +167,11 @@ void rfc_port_sm_state_closed(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data
case RFC_PORT_EVENT_TIMEOUT:
PORT_TimeOutCloseMux(p_port->rfc.p_mcb);
- log::error("Port error state {} event {}", p_port->rfc.state, event);
+ log::error("Port error state {} event {}", p_port->rfc.sm_cb.state, event);
return;
default:
log::error("Received unexpected event:{} in state:{}", rfcomm_port_event_text(event),
- rfcomm_port_state_text(p_port->rfc.state));
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state));
}
log::warn("Event ignored {}", rfcomm_port_event_text(event));
@@ -199,7 +199,7 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat
rfc_port_timer_start(p_port, RFC_DISC_TIMEOUT);
rfc_send_disc(p_port->rfc.p_mcb, p_port->dlci);
p_port->rfc.expected_rsp = 0;
- p_port->rfc.state = RFC_STATE_DISC_WAIT_UA;
+ p_port->rfc.sm_cb.state = RFC_STATE_DISC_WAIT_UA;
return;
case RFC_PORT_EVENT_CLEAR:
@@ -213,7 +213,7 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat
case RFC_PORT_EVENT_UA:
rfc_port_timer_stop(p_port);
- p_port->rfc.state = RFC_STATE_OPENED;
+ p_port->rfc.sm_cb.state = RFC_STATE_OPENED;
if (uuid_logging_acceptlist.find(p_port->uuid) != uuid_logging_acceptlist.end()) {
// Find Channel Control Block by Channel ID
@@ -267,13 +267,13 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat
return;
case RFC_PORT_EVENT_TIMEOUT:
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu,
RFCOMM_ERROR);
return;
default:
log::error("Received unexpected event:{} in state:{}", rfcomm_port_event_text(event),
- rfcomm_port_state_text(static_cast<tRFC_PORT_STATE>(p_port->rfc.state)));
+ rfcomm_port_state_text(static_cast<tRFC_PORT_STATE>(p_port->rfc.sm_cb.state)));
}
log::warn("Event ignored {}", rfcomm_port_event_text(event));
}
@@ -296,7 +296,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
if (*((tBTM_STATUS*)p_data) != tBTM_STATUS::BTM_SUCCESS) {
log::error("Security check failed result:{} state:{} port_handle:{}",
btm_status_text(*((tBTM_STATUS*)p_data)),
- rfcomm_port_state_text(p_port->rfc.state), p_port->handle);
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state), p_port->handle);
/* Authentication/authorization failed. If link is still */
/* up send DM and check if we need to start inactive timer */
if (p_port->rfc.p_mcb) {
@@ -306,7 +306,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
}
} else {
log::debug("Security check succeeded state:{} port_handle:{}",
- rfcomm_port_state_text(static_cast<tRFC_PORT_STATE>(p_port->rfc.state)),
+ rfcomm_port_state_text(static_cast<tRFC_PORT_STATE>(p_port->rfc.sm_cb.state)),
p_port->handle);
PORT_DlcEstablishInd(p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu);
}
@@ -334,7 +334,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
case RFC_PORT_EVENT_DISC:
btm_sec_abort_access_req(p_port->rfc.p_mcb->bd_addr);
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci);
PORT_DlcReleaseInd(p_port->rfc.p_mcb, p_port->dlci);
@@ -351,7 +351,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
}
} else {
rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci);
- p_port->rfc.state = RFC_STATE_OPENED;
+ p_port->rfc.sm_cb.state = RFC_STATE_OPENED;
if (uuid_logging_acceptlist.find(p_port->uuid) != uuid_logging_acceptlist.end()) {
// Find Channel Control Block by Channel ID
@@ -378,7 +378,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
return;
default:
log::error("Received unexpected event:{} in state:{}", rfcomm_port_event_text(event),
- rfcomm_port_state_text(p_port->rfc.state));
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state));
}
log::warn("Event ignored {}", event);
}
@@ -400,16 +400,16 @@ void rfc_port_sm_orig_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
if (*((tBTM_STATUS*)p_data) != tBTM_STATUS::BTM_SUCCESS) {
log::error("Security check failed result:{} state:{} handle:{}",
btm_status_text(*((tBTM_STATUS*)p_data)),
- rfcomm_port_state_text(p_port->rfc.state), p_port->handle);
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state), p_port->handle);
p_port->rfc.p_mcb->is_disc_initiator = true;
PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci, 0, RFCOMM_SECURITY_ERR);
rfc_port_closed(p_port);
} else {
log::debug("Security check succeeded state:{} handle:{}",
- rfcomm_port_state_text(p_port->rfc.state), p_port->handle);
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state), p_port->handle);
rfc_send_sabme(p_port->rfc.p_mcb, p_port->dlci);
rfc_port_timer_start(p_port, RFC_PORT_T1_TIMEOUT);
- p_port->rfc.state = RFC_STATE_SABME_WAIT_UA;
+ p_port->rfc.sm_cb.state = RFC_STATE_SABME_WAIT_UA;
}
return;
@@ -434,7 +434,7 @@ void rfc_port_sm_orig_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
return;
default:
log::error("Received unexpected event:{} in state:{}", rfcomm_port_event_text(event),
- rfcomm_port_state_text(p_port->rfc.state));
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state));
}
log::warn("Event ignored {}", rfcomm_port_event_text(event));
}
@@ -452,21 +452,21 @@ void rfc_port_sm_orig_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void*
void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
switch (event) {
case RFC_PORT_EVENT_OPEN:
- log::error("RFC_PORT_EVENT_OPEN bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::error("RFC_PORT_EVENT_OPEN bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
return;
case RFC_PORT_EVENT_CLOSE:
- log::info("RFC_PORT_EVENT_CLOSE bd_addr:{}, handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::info("RFC_PORT_EVENT_CLOSE bd_addr:{}, port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
rfc_port_timer_start(p_port, RFC_DISC_TIMEOUT);
rfc_send_disc(p_port->rfc.p_mcb, p_port->dlci);
p_port->rfc.expected_rsp = 0;
- p_port->rfc.state = RFC_STATE_DISC_WAIT_UA;
+ p_port->rfc.sm_cb.state = RFC_STATE_DISC_WAIT_UA;
return;
case RFC_PORT_EVENT_CLEAR:
- log::warn("RFC_PORT_EVENT_CLEAR bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::warn("RFC_PORT_EVENT_CLEAR bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
rfc_port_closed(p_port);
return;
@@ -475,7 +475,7 @@ void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
// Send credits in the frame. Pass them in the layer specific member of the hdr.
// There might be an initial case when we reduced rx_max and credit_rx is still bigger.
// Make sure that we do not send 255
- log::verbose("RFC_PORT_EVENT_DATA bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::verbose("RFC_PORT_EVENT_DATA bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
if ((p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) &&
(((BT_HDR*)p_data)->len < p_port->peer_mtu) && (!p_port->rx.user_fc) &&
@@ -490,27 +490,27 @@ void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
return;
case RFC_PORT_EVENT_UA:
- log::verbose("RFC_PORT_EVENT_UA bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::verbose("RFC_PORT_EVENT_UA bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
return;
case RFC_PORT_EVENT_SABME:
- log::verbose("RFC_PORT_EVENT_SABME bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::verbose("RFC_PORT_EVENT_SABME bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci);
return;
case RFC_PORT_EVENT_DM:
- log::info("RFC_EVENT_DM bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr, p_port->handle,
- p_port->dlci, p_port->scn);
+ log::info("RFC_EVENT_DM bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ p_port->handle, p_port->dlci, p_port->scn);
PORT_DlcReleaseInd(p_port->rfc.p_mcb, p_port->dlci);
rfc_port_closed(p_port);
return;
case RFC_PORT_EVENT_DISC:
- log::info("RFC_PORT_EVENT_DISC bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::info("RFC_PORT_EVENT_DISC bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci);
if (!fixed_queue_is_empty(p_port->rx.queue)) {
/* give a chance to upper stack to close port properly */
@@ -522,19 +522,19 @@ void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) {
return;
case RFC_PORT_EVENT_UIH:
- log::verbose("RFC_PORT_EVENT_UIH bd_addr:{}, handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::verbose("RFC_PORT_EVENT_UIH bd_addr:{}, port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
rfc_port_uplink_data(p_port, (BT_HDR*)p_data);
return;
case RFC_PORT_EVENT_TIMEOUT:
PORT_TimeOutCloseMux(p_port->rfc.p_mcb);
- log::error("RFC_PORT_EVENT_TIMEOUT bd_addr:{} handle:{} dlci:{} scn:{}", p_port->bd_addr,
+ log::error("RFC_PORT_EVENT_TIMEOUT bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr,
p_port->handle, p_port->dlci, p_port->scn);
return;
default:
- log::error("Received unexpected event:{} bd_addr:{} handle:{} dlci:{} scn:{}",
+ log::error("Received unexpected event:{} bd_addr:{} port_handle:{} dlci:{} scn:{}",
rfcomm_port_event_text(event), p_port->bd_addr, p_port->handle, p_port->dlci,
p_port->scn);
break;
@@ -560,7 +560,7 @@ void rfc_port_sm_disc_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data
return;
case RFC_PORT_EVENT_CLEAR:
- log::warn("RFC_PORT_EVENT_CLEAR, handle:{}", p_port->handle);
+ log::warn("RFC_PORT_EVENT_CLEAR, port_handle:{}", p_port->handle);
rfc_port_closed(p_port);
return;
@@ -573,7 +573,7 @@ void rfc_port_sm_disc_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data
FALLTHROUGH_INTENDED; /* FALLTHROUGH */
case RFC_PORT_EVENT_DM:
- log::warn("RFC_EVENT_DM|RFC_EVENT_UA[{}], handle:{}", event, p_port->handle);
+ log::warn("RFC_EVENT_DM|RFC_EVENT_UA[{}], port_handle:{}", event, p_port->handle);
if (com::android::bluetooth::flags::rfcomm_always_disc_initiator_in_disc_wait_ua()) {
// If we got a DM in RFC_STATE_DISC_WAIT_UA, it's likely that both ends
// attempt to DISC at the same time and both get a DM.
@@ -602,12 +602,12 @@ void rfc_port_sm_disc_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data
return;
case RFC_PORT_EVENT_TIMEOUT:
- log::error("RFC_EVENT_TIMEOUT, handle:{}", p_port->handle);
+ log::error("RFC_EVENT_TIMEOUT, port_handle:{}", p_port->handle);
rfc_port_closed(p_port);
return;
default:
log::error("Received unexpected event:{} in state:{}", rfcomm_port_event_text(event),
- rfcomm_port_state_text(p_port->rfc.state));
+ rfcomm_port_state_text(p_port->rfc.sm_cb.state));
}
log::warn("Event ignored {}", rfcomm_port_event_text(event));
diff --git a/system/stack/rfcomm/rfc_port_if.cc b/system/stack/rfcomm/rfc_port_if.cc
index 0973f0370a..af6906fbb5 100644
--- a/system/stack/rfcomm/rfc_port_if.cc
+++ b/system/stack/rfcomm/rfc_port_if.cc
@@ -257,7 +257,8 @@ void RFCOMM_ControlReq(tRFC_MCB* p_mcb, uint8_t dlci, tPORT_CTRL* p_pars) {
return;
}
- if ((p_port->state != PORT_CONNECTION_STATE_OPENED) || (p_port->rfc.state != RFC_STATE_OPENED)) {
+ if ((p_port->state != PORT_CONNECTION_STATE_OPENED) ||
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
return;
}
@@ -285,7 +286,8 @@ void RFCOMM_FlowReq(tRFC_MCB* p_mcb, uint8_t dlci, bool enable) {
return;
}
- if ((p_port->state != PORT_CONNECTION_STATE_OPENED) || (p_port->rfc.state != RFC_STATE_OPENED)) {
+ if ((p_port->state != PORT_CONNECTION_STATE_OPENED) ||
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
return;
}
@@ -312,7 +314,8 @@ void RFCOMM_LineStatusReq(tRFC_MCB* p_mcb, uint8_t dlci, uint8_t status) {
return;
}
- if ((p_port->state != PORT_CONNECTION_STATE_OPENED) || (p_port->rfc.state != RFC_STATE_OPENED)) {
+ if ((p_port->state != PORT_CONNECTION_STATE_OPENED) ||
+ (p_port->rfc.sm_cb.state != RFC_STATE_OPENED)) {
return;
}
diff --git a/system/stack/rfcomm/rfc_utils.cc b/system/stack/rfcomm/rfc_utils.cc
index f57214644d..87beaaf566 100644
--- a/system/stack/rfcomm/rfc_utils.cc
+++ b/system/stack/rfcomm/rfc_utils.cc
@@ -319,8 +319,8 @@ void rfc_sec_check_complete(RawAddress /* bd_addr */, tBT_TRANSPORT /* transport
tPORT* p_port = (tPORT*)p_ref_data;
/* Verify that PORT is still waiting for Security to complete */
- if (!p_port->in_use || ((p_port->rfc.state != RFC_STATE_ORIG_WAIT_SEC_CHECK) &&
- (p_port->rfc.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) {
+ if (!p_port->in_use || ((p_port->rfc.sm_cb.state != RFC_STATE_ORIG_WAIT_SEC_CHECK) &&
+ (p_port->rfc.sm_cb.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) {
return;
}
@@ -341,7 +341,7 @@ void rfc_sec_check_complete(RawAddress /* bd_addr */, tBT_TRANSPORT /* transport
void rfc_port_closed(tPORT* p_port) {
tRFC_MCB* p_mcb = p_port->rfc.p_mcb;
rfc_port_timer_stop(p_port);
- p_port->rfc.state = RFC_STATE_CLOSED;
+ p_port->rfc.sm_cb.state = RFC_STATE_CLOSED;
/* If multiplexer channel was up mark it as down */
if (p_mcb) {
diff --git a/system/stack/smp/smp_api.cc b/system/stack/smp/smp_api.cc
index 0d66b0a064..a46f113aec 100644
--- a/system/stack/smp/smp_api.cc
+++ b/system/stack/smp/smp_api.cc
@@ -89,7 +89,7 @@ tSMP_STATUS SMP_Pair(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type) {
if (p_cb->state != SMP_STATE_IDLE || p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD ||
p_cb->smp_over_br) {
/* pending security on going, reject this one */
- return SMP_BUSY;
+ return SMP_IMPL_BUSY;
} else {
p_cb->flags = SMP_PAIR_FLAGS_WE_STARTED_DD;
p_cb->pairing_bda = bd_addr;
@@ -135,7 +135,7 @@ tSMP_STATUS SMP_BR_PairWith(const RawAddress& bd_addr) {
if (p_cb->state != SMP_STATE_IDLE || p_cb->smp_over_br ||
p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) {
/* pending security on going, reject this one */
- return SMP_BUSY;
+ return SMP_IMPL_BUSY;
}
p_cb->role = HCI_ROLE_CENTRAL;
diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc
index 81b005a78c..e5aefe3c6a 100644
--- a/system/stack/smp/smp_utils.cc
+++ b/system/stack/smp/smp_utils.cc
@@ -1231,7 +1231,7 @@ void smp_reject_unexpected_pairing_command(const RawAddress& bd_addr) {
p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
UINT8_TO_STREAM(p, SMP_OPCODE_PAIRING_FAILED);
- UINT8_TO_STREAM(p, SMP_PAIR_NOT_SUPPORT);
+ UINT8_TO_STREAM(p, SMP_BUSY);
p_buf->offset = L2CAP_MIN_OFFSET;
p_buf->len = SMP_PAIR_FAIL_SIZE;
diff --git a/system/stack/test/a2dp/AndroidTest.xml b/system/stack/test/a2dp/AndroidTest.xml
index 71fc46cdb9..ace94ad9b8 100644
--- a/system/stack/test/a2dp/AndroidTest.xml
+++ b/system/stack/test/a2dp/AndroidTest.xml
@@ -34,7 +34,7 @@
<!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/system/stack/test/a2dp/AndroidTestForce32.xml b/system/stack/test/a2dp/AndroidTestForce32.xml
index c2fa5e44dc..afb13d3266 100644
--- a/system/stack/test/a2dp/AndroidTestForce32.xml
+++ b/system/stack/test/a2dp/AndroidTestForce32.xml
@@ -33,7 +33,7 @@
<!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/system/stack/test/connection_manager_test.cc b/system/stack/test/connection_manager_test.cc
index 8c99963279..7e59e85f08 100644
--- a/system/stack/test/connection_manager_test.cc
+++ b/system/stack/test/connection_manager_test.cc
@@ -57,6 +57,8 @@ constexpr tAPP_ID CLIENT2 = 2;
constexpr tAPP_ID CLIENT3 = 3;
constexpr tAPP_ID CLIENT10 = 10;
+std::string get_client_name(uint8_t /* gatt_if */) { return ""; }
+
const tBLE_BD_ADDR BTM_Sec_GetAddressWithType(const RawAddress& bd_addr) {
return tBLE_BD_ADDR{.type = BLE_ADDR_PUBLIC, .bda = bd_addr};
}
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index 285aa23ea8..9c5eb868d0 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -53,16 +53,6 @@ struct TestMutables {
TestMutables test_state_;
} // namespace
-namespace connection_manager {
-bool background_connect_remove(uint8_t /*app_id*/, const RawAddress& /*address*/) { return false; }
-bool direct_connect_remove(uint8_t /*app_id*/, const RawAddress& /*address*/,
- bool /*connection_timeout*/) {
- return false;
-}
-bool is_background_connection(const RawAddress& /*address*/) { return false; }
-
-} // namespace connection_manager
-
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;
@@ -81,7 +71,6 @@ tGATT_STATUS attp_send_sr_msg(tGATT_TCB& /*tcb*/, uint16_t /*cid*/, BT_HDR* /*p_
void gatt_act_discovery(tGATT_CLCB* /*p_clcb*/) {}
bool gatt_disconnect(tGATT_TCB* /*p_tcb*/) { return false; }
-void gatt_cancel_connect(const RawAddress& /*bd_addr*/, tBT_TRANSPORT /*transport*/) {}
tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB* /*p_tcb*/) { return GATT_CH_CLOSE; }
tGATT_STATUS gatts_db_read_attr_value_by_type(tGATT_TCB& /*tcb*/, uint16_t /*cid*/,
tGATT_SVC_DB* /*p_db*/, uint8_t /*op_code*/,
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index a7b9082824..f4df22397b 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -46,7 +46,6 @@ void gatt_update_app_use_link_flag(tGATT_IF /*gatt_if*/, tGATT_TCB* /*p_tcb*/, b
bool /*check_acl_link*/) {}
void gatts_proc_srv_chg_ind_ack(tGATT_TCB) {}
bool gatt_disconnect(tGATT_TCB* /*p_tcb*/) { return false; }
-void gatt_cancel_connect(const RawAddress& /*bd_addr*/, tBT_TRANSPORT /*transport*/) {}
tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB* /*p_tcb*/) { return GATT_CH_CLOSE; }
void gatt_set_ch_state(tGATT_TCB* /*p_tcb*/, tGATT_CH_STATE /*ch_state*/) {}
diff --git a/system/stack/test/gatt/stack_gatt_test.cc b/system/stack/test/gatt/stack_gatt_test.cc
index 71928440a4..9f0cacf48a 100644
--- a/system/stack/test/gatt/stack_gatt_test.cc
+++ b/system/stack/test/gatt/stack_gatt_test.cc
@@ -124,7 +124,6 @@ TEST_F(StackGattTest, lifecycle_tGATT_REG) {
memset(reg0.get(), 0, sizeof(tGATT_REG));
// Restore the complex structure after memset
memset(&reg1.name, 0, sizeof(std::string));
- memset(&reg1.direct_connect_request, 0, sizeof(std::set<RawAddress>));
memset(&reg1.mtu_prefs, 0, sizeof(std::map<RawAddress, uint16_t>));
reg1 = {};
ASSERT_EQ(0, memcmp(reg0.get(), &reg1, actual_sizeof_tGATT_REG()));
diff --git a/system/stack/test/rfcomm/stack_rfcomm_port_test.cc b/system/stack/test/rfcomm/stack_rfcomm_port_test.cc
index 54ca9943b6..084e109c72 100644
--- a/system/stack/test/rfcomm/stack_rfcomm_port_test.cc
+++ b/system/stack/test/rfcomm/stack_rfcomm_port_test.cc
@@ -48,9 +48,9 @@ TEST_F(StackRfcommPortTest, PORT_IsOpening__basic) {
ASSERT_TRUE(PORT_IsOpening(&bd_addr));
rfc_cb.port.rfc_mcb[0].state = RFC_MX_STATE_CONNECTED;
rfc_cb.port.port[0].rfc.p_mcb = &rfc_cb.port.rfc_mcb[0];
- rfc_cb.port.port[0].rfc.state = RFC_STATE_OPENED;
+ rfc_cb.port.port[0].rfc.sm_cb.state = RFC_STATE_OPENED;
ASSERT_FALSE(PORT_IsOpening(&bd_addr));
- rfc_cb.port.port[0].rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
+ rfc_cb.port.port[0].rfc.sm_cb.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
ASSERT_TRUE(PORT_IsOpening(&bd_addr));
rfc_cb.port.rfc_mcb[0].state = RFC_MX_STATE_DISC_WAIT_UA;
ASSERT_FALSE(PORT_IsOpening(&bd_addr));
@@ -90,9 +90,9 @@ TEST_F(StackRfcommPortTest, PORT_IsCollisionDetected__basic) {
rfc_cb.port.rfc_mcb[0].state = RFC_MX_STATE_CONNECTED;
rfc_cb.port.port[0].rfc.p_mcb = &rfc_cb.port.rfc_mcb[0];
- rfc_cb.port.port[0].rfc.state = RFC_STATE_OPENED;
+ rfc_cb.port.port[0].rfc.sm_cb.state = RFC_STATE_OPENED;
ASSERT_FALSE(PORT_IsCollisionDetected(test_bd_addr));
- rfc_cb.port.port[0].rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
+ rfc_cb.port.port[0].rfc.sm_cb.state = RFC_STATE_TERM_WAIT_SEC_CHECK;
ASSERT_TRUE(PORT_IsCollisionDetected(test_bd_addr));
rfc_cb.port.rfc_mcb[0].state = RFC_MX_STATE_DISC_WAIT_UA;
ASSERT_FALSE(PORT_IsCollisionDetected(test_bd_addr));
diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc
index 41d61e738b..8f020ae1c5 100644
--- a/system/stack/test/stack_smp_test.cc
+++ b/system/stack/test/stack_smp_test.cc
@@ -375,11 +375,13 @@ TEST(SmpStatusText, smp_status_text) {
std::make_pair(SMP_NUMERIC_COMPAR_FAIL, "SMP_NUMERIC_COMPAR_FAIL"),
std::make_pair(SMP_BR_PARING_IN_PROGR, "SMP_BR_PARING_IN_PROGR"),
std::make_pair(SMP_XTRANS_DERIVE_NOT_ALLOW, "SMP_XTRANS_DERIVE_NOT_ALLOW"),
+ std::make_pair(SMP_KEY_REJECTED, "SMP_KEY_REJECTED"),
+ std::make_pair(SMP_BUSY, "SMP_BUSY"),
std::make_pair(SMP_MAX_FAIL_RSN_PER_SPEC,
- "SMP_XTRANS_DERIVE_NOT_ALLOW"), // NOTE: Dup
+ "SMP_BUSY"), // NOTE: Dup
std::make_pair(SMP_PAIR_INTERNAL_ERR, "SMP_PAIR_INTERNAL_ERR"),
std::make_pair(SMP_UNKNOWN_IO_CAP, "SMP_UNKNOWN_IO_CAP"),
- std::make_pair(SMP_BUSY, "SMP_BUSY"),
+ std::make_pair(SMP_IMPL_BUSY, "SMP_IMPL_BUSY"),
std::make_pair(SMP_ENC_FAIL, "SMP_ENC_FAIL"),
std::make_pair(SMP_STARTED, "SMP_STARTED"),
std::make_pair(SMP_RSP_TIMEOUT, "SMP_RSP_TIMEOUT"),
diff --git a/system/test/Android.bp b/system/test/Android.bp
index c5a3b0c2d6..595f148754 100644
--- a/system/test/Android.bp
+++ b/system/test/Android.bp
@@ -205,7 +205,6 @@ filegroup {
filegroup {
name: "TestMockMainShim",
srcs: [
- "mock/mock_main_shim.cc",
"mock/mock_main_shim_BtifConfigInterface.cc",
"mock/mock_main_shim_acl.cc",
"mock/mock_main_shim_acl_api.cc",
@@ -363,19 +362,11 @@ filegroup {
filegroup {
name: "TestMockMainShimLeScanning",
srcs: [
- "mock/mock_main_shim.cc",
"mock/mock_main_shim_le_scanning_manager.cc",
],
}
filegroup {
- name: "TestMockMainShimFlags",
- srcs: [
- "mock/mock_main_shim.cc",
- ],
-}
-
-filegroup {
name: "TestMockBtif",
srcs: [
":TestCommonCoreInterface",
diff --git a/system/test/headless/property.cc b/system/test/headless/property.cc
index fe6e21f1ed..2b5df50ff2 100644
--- a/system/test/headless/property.cc
+++ b/system/test/headless/property.cc
@@ -99,6 +99,10 @@ std::map<::bt_property_type_t,
return new headless::property::void_t(data, len,
BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP);
}},
+ {BT_PROPERTY_UUIDS_LE,
+ [](const uint8_t* data, const size_t len) -> headless::bt_property_t* {
+ return new headless::property::uuid_t(data, len);
+ }},
};
} // namespace
diff --git a/system/test/headless/property.h b/system/test/headless/property.h
index 6af17a244f..1113495f24 100644
--- a/system/test/headless/property.h
+++ b/system/test/headless/property.h
@@ -52,6 +52,7 @@ inline std::string bt_property_type_text(const ::bt_property_type_t type) {
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_MODEL_NUM);
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP);
CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_ADDR_TYPE);
+ CASE_RETURN_TEXT(BT_PROPERTY_UUIDS_LE);
CASE_RETURN_TEXT(BT_PROPERTY_RESERVED_0x14);
default:
RETURN_UNKNOWN_TYPE_STRING(::bt_property_type_t, type);
diff --git a/system/test/mock/mock_bta_dm_act.cc b/system/test/mock/mock_bta_dm_act.cc
index 9fbf408f13..9f2686d4fd 100644
--- a/system/test/mock/mock_bta_dm_act.cc
+++ b/system/test/mock/mock_bta_dm_act.cc
@@ -77,7 +77,6 @@ struct bta_dm_on_encryption_change bta_dm_on_encryption_change;
struct bta_dm_rm_cback bta_dm_rm_cback;
struct bta_dm_set_dev_name bta_dm_set_dev_name;
struct bta_dm_set_encryption bta_dm_set_encryption;
-struct handle_remote_features_complete handle_remote_features_complete;
} // namespace bta_dm_act
} // namespace mock
@@ -250,9 +249,5 @@ void bta_dm_set_encryption(const RawAddress& bd_addr, tBT_TRANSPORT transport,
inc_func_call_count(__func__);
test::mock::bta_dm_act::bta_dm_set_encryption(bd_addr, transport, p_callback, sec_act);
}
-void handle_remote_features_complete(const RawAddress& bd_addr) {
- inc_func_call_count(__func__);
- test::mock::bta_dm_act::handle_remote_features_complete(bd_addr);
-}
// END mockcify generation
diff --git a/system/test/mock/mock_bta_dm_act.h b/system/test/mock/mock_bta_dm_act.h
index 09fd1bbbc1..df329a53af 100644
--- a/system/test/mock/mock_bta_dm_act.h
+++ b/system/test/mock/mock_bta_dm_act.h
@@ -498,15 +498,6 @@ struct bta_dm_set_encryption {
};
extern struct bta_dm_set_encryption bta_dm_set_encryption;
-// Name: handle_remote_features_complete
-// Params: const RawAddress& bd_addr
-// Return: void
-struct handle_remote_features_complete {
- std::function<void(const RawAddress& bd_addr)> body{[](const RawAddress& /* bd_addr */) {}};
- void operator()(const RawAddress& bd_addr) { body(bd_addr); }
-};
-extern struct handle_remote_features_complete handle_remote_features_complete;
-
} // namespace bta_dm_act
} // namespace mock
} // namespace test
diff --git a/system/test/mock/mock_bta_gattc_api.cc b/system/test/mock/mock_bta_gattc_api.cc
index 7bea0245ce..6932102e84 100644
--- a/system/test/mock/mock_bta_gattc_api.cc
+++ b/system/test/mock/mock_bta_gattc_api.cc
@@ -69,8 +69,8 @@ tGATT_STATUS BTA_GATTC_RegisterForNotifications(tGATT_IF /* client_if */,
return GATT_SUCCESS;
}
void BTA_GATTC_AppDeregister(tGATT_IF /* client_if */) { inc_func_call_count(__func__); }
-void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* /* p_client_cb */, BtaAppRegisterCallback /* cb */,
- bool /* eatt_support */) {
+void BTA_GATTC_AppRegister(const std::string& /* name */, tBTA_GATTC_CBACK* /* p_client_cb */,
+ BtaAppRegisterCallback /* cb */, bool /* eatt_support */) {
inc_func_call_count(__func__);
}
void BTA_GATTC_CancelOpen(tGATT_IF /* client_if */, const RawAddress& /* remote_bda */,
diff --git a/system/test/mock/mock_bta_jv_api.cc b/system/test/mock/mock_bta_jv_api.cc
index ec913b5b01..52f8ffcabe 100644
--- a/system/test/mock/mock_bta_jv_api.cc
+++ b/system/test/mock/mock_bta_jv_api.cc
@@ -76,14 +76,16 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t /* handle */, uint32_t /* rfcomm_slot_
tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC /* sec_mask */, uint8_t /* remote_scn */,
const RawAddress& /* peer_bd_addr */,
tBTA_JV_RFCOMM_CBACK* /* p_cback */,
- uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */) {
+ uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */,
+ uint32_t /* app_uid */) {
inc_func_call_count(__func__);
return tBTA_JV_STATUS::SUCCESS;
}
tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC /* sec_mask */, uint8_t /* local_scn */,
uint8_t /* max_session */,
tBTA_JV_RFCOMM_CBACK* /* p_cback */,
- uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */) {
+ uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */,
+ uint32_t /* app_uid */) {
inc_func_call_count(__func__);
return tBTA_JV_STATUS::SUCCESS;
}
diff --git a/system/test/mock/mock_bta_leaudio.cc b/system/test/mock/mock_bta_leaudio.cc
index 2b5aeb8ff9..24824032a3 100644
--- a/system/test/mock/mock_bta_leaudio.cc
+++ b/system/test/mock/mock_bta_leaudio.cc
@@ -41,14 +41,12 @@ class HalVersionManager {
} // namespace audio
} // namespace bluetooth
-void LeAudioClient::AddFromStorage(const RawAddress& /* addr */, bool /* autoconnect */,
- int /* sink_audio_location */, int /* source_audio_location */,
- int /* sink_supported_context_types */,
- int /* source_supported_context_types */,
- const std::vector<uint8_t>& /* handles */,
- const std::vector<uint8_t>& /* sink_pacs */,
- const std::vector<uint8_t>& /* source_pacs */,
- const std::vector<uint8_t>& /* ases */) {
+void LeAudioClient::AddFromStorage(
+ const RawAddress& /* addr */, bool /* autoconnect */, int /* sink_audio_location */,
+ int /* source_audio_location */, int /* sink_supported_context_types */,
+ int /* source_supported_context_types */, const std::vector<uint8_t>& /* handles */,
+ const std::vector<uint8_t>& /* sink_pacs */, const std::vector<uint8_t>& /* source_pacs */,
+ const std::vector<uint8_t>& /* ases */, const std::vector<uint8_t>& /* gmap */) {
inc_func_call_count(__func__);
}
@@ -76,6 +74,12 @@ bool LeAudioClient::GetAsesForStorage(const RawAddress& /* addr */,
return false;
}
+bool LeAudioClient::GetGmapForStorage(const RawAddress& /* addr */,
+ std::vector<uint8_t>& /* out */) {
+ inc_func_call_count(__func__);
+ return false;
+}
+
void LeAudioClient::Cleanup(void) { inc_func_call_count(__func__); }
LeAudioClient* LeAudioClient::Get(void) {
diff --git a/system/test/mock/mock_btif_profile_storage.cc b/system/test/mock/mock_btif_profile_storage.cc
index a89422a7ee..a65103fa8e 100644
--- a/system/test/mock/mock_btif_profile_storage.cc
+++ b/system/test/mock/mock_btif_profile_storage.cc
@@ -55,6 +55,7 @@ struct btif_storage_leaudio_clear_service_data btif_storage_leaudio_clear_servic
struct btif_storage_leaudio_update_ase_bin btif_storage_leaudio_update_ase_bin;
struct btif_storage_leaudio_update_handles_bin btif_storage_leaudio_update_handles_bin;
struct btif_storage_leaudio_update_pacs_bin btif_storage_leaudio_update_pacs_bin;
+struct btif_storage_leaudio_update_gmap_bin btif_storage_leaudio_update_gmap_bin;
struct btif_storage_load_bonded_csis_devices btif_storage_load_bonded_csis_devices;
struct btif_storage_load_bonded_groups btif_storage_load_bonded_groups;
struct btif_storage_load_bonded_hearing_aids btif_storage_load_bonded_hearing_aids;
@@ -183,6 +184,10 @@ void btif_storage_leaudio_update_pacs_bin(const RawAddress& addr) {
inc_func_call_count(__func__);
test::mock::btif_profile_storage::btif_storage_leaudio_update_pacs_bin(addr);
}
+void btif_storage_leaudio_update_gmap_bin(const RawAddress& addr) {
+ inc_func_call_count(__func__);
+ test::mock::btif_profile_storage::btif_storage_leaudio_update_gmap_bin(addr);
+}
void btif_storage_load_bonded_csis_devices(void) {
inc_func_call_count(__func__);
test::mock::btif_profile_storage::btif_storage_load_bonded_csis_devices();
diff --git a/system/test/mock/mock_btif_profile_storage.h b/system/test/mock/mock_btif_profile_storage.h
index 7d322e543d..1d0bbd7c96 100644
--- a/system/test/mock/mock_btif_profile_storage.h
+++ b/system/test/mock/mock_btif_profile_storage.h
@@ -226,6 +226,15 @@ struct btif_storage_leaudio_update_pacs_bin {
};
extern struct btif_storage_leaudio_update_pacs_bin btif_storage_leaudio_update_pacs_bin;
+// Name: btif_storage_leaudio_update_gmap_bin
+// Params: void
+// Return: void
+struct btif_storage_leaudio_update_gmap_bin {
+ std::function<void(const RawAddress& addr)> body{[](const RawAddress& /* addr */) {}};
+ void operator()(const RawAddress& addr) { body(addr); }
+};
+extern struct btif_storage_leaudio_update_gmap_bin btif_storage_leaudio_update_gmap_bin;
+
// Name: btif_storage_load_bonded_csis_devices
// Params: void
// Return: void
diff --git a/system/test/mock/mock_main_shim.cc b/system/test/mock/mock_main_shim.cc
deleted file mode 100644
index 8e26aaeecd..0000000000
--- a/system/test/mock/mock_main_shim.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-/*
- * Generated mock file from original source file
- * Functions generated:14
- */
-
-#define LOG_TAG "bt_shim"
-
-#include "main/shim/shim.h"
-#include "test/common/mock_functions.h"
-
-namespace test {
-namespace mock {
-bool bluetooth_shim_is_gd_stack_started_up = false;
-}
-} // namespace test
-bool bluetooth::shim::is_gd_stack_started_up() {
- inc_func_call_count(__func__);
- return test::mock::bluetooth_shim_is_gd_stack_started_up;
-}
diff --git a/system/test/mock/mock_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc
index 291cb0c0fd..a78ff5bdf5 100644
--- a/system/test/mock/mock_main_shim_entry.cc
+++ b/system/test/mock/mock_main_shim_entry.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "test/mock/mock_main_shim_entry.h"
+
#include "hci/acl_manager_mock.h"
#include "hci/controller_interface_mock.h"
#include "hci/distance_measurement_manager_mock.h"
@@ -22,9 +24,16 @@
#include "hci/le_scanning_manager_mock.h"
#include "lpp/lpp_offload_interface_mock.h"
#include "main/shim/entry.h"
+#include "main/shim/shim.h"
#include "os/handler.h"
#include "storage/storage_module.h"
+namespace test {
+namespace mock {
+bool bluetooth_shim_is_gd_stack_started_up = false;
+} // namespace mock
+} // namespace test
+
namespace bluetooth {
namespace hci {
namespace testing {
@@ -65,6 +74,7 @@ hci::RemoteNameRequestModule* GetRemoteNameRequest() { return nullptr; }
lpp::LppOffloadInterface* GetLppOffloadManager() {
return lpp::testing::mock_lpp_offload_interface_;
}
+bool is_gd_stack_started_up() { return test::mock::bluetooth_shim_is_gd_stack_started_up; }
} // namespace shim
} // namespace bluetooth
diff --git a/system/test/mock/mock_stack_btm_interface.cc b/system/test/mock/mock_stack_btm_interface.cc
index 116d2008af..2361ce1307 100644
--- a/system/test/mock/mock_stack_btm_interface.cc
+++ b/system/test/mock/mock_stack_btm_interface.cc
@@ -137,7 +137,7 @@ struct btm_client_interface_t default_btm_client_interface = {
},
.BTM_IsEncrypted = [](const RawAddress& /* bd_addr */,
tBT_TRANSPORT /* transport */) -> bool { return false; },
- .BTM_SecIsSecurityPending = [](const RawAddress& /* bd_addr */) -> bool {
+ .BTM_SecIsLeSecurityPending = [](const RawAddress& /* bd_addr */) -> bool {
return false;
},
.BTM_IsLinkKeyKnown = [](const RawAddress& /* bd_addr */,
diff --git a/system/test/mock/mock_stack_btm_sec.cc b/system/test/mock/mock_stack_btm_sec.cc
index 2bfd288187..cdf4b97df6 100644
--- a/system/test/mock/mock_stack_btm_sec.cc
+++ b/system/test/mock/mock_stack_btm_sec.cc
@@ -58,7 +58,7 @@ struct BTM_SecBondCancel BTM_SecBondCancel;
struct BTM_SecClrService BTM_SecClrService;
struct BTM_SecClrServiceByPsm BTM_SecClrServiceByPsm;
struct BTM_SecGetDeviceLinkKeyType BTM_SecGetDeviceLinkKeyType;
-struct BTM_SecIsSecurityPending BTM_SecIsSecurityPending;
+struct BTM_SecIsLeSecurityPending BTM_SecIsLeSecurityPending;
struct BTM_SecRegister BTM_SecRegister;
struct BTM_SetEncryption BTM_SetEncryption;
struct BTM_SetPinType BTM_SetPinType;
@@ -121,7 +121,7 @@ tBTM_STATUS BTM_SecBondCancel::return_value = tBTM_STATUS::BTM_SUCCESS;
uint8_t BTM_SecClrService::return_value = 0;
uint8_t BTM_SecClrServiceByPsm::return_value = 0;
tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType::return_value = 0;
-bool BTM_SecIsSecurityPending::return_value = false;
+bool BTM_SecIsLeSecurityPending::return_value = false;
bool BTM_SecRegister::return_value = false;
tBTM_STATUS BTM_SetEncryption::return_value = tBTM_STATUS::BTM_SUCCESS;
bool BTM_SetSecurityLevel::return_value = false;
@@ -204,9 +204,9 @@ tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType(const RawAddress& bd_addr) {
inc_func_call_count(__func__);
return test::mock::stack_btm_sec::BTM_SecGetDeviceLinkKeyType(bd_addr);
}
-bool BTM_SecIsSecurityPending(const RawAddress& bd_addr) {
+bool BTM_SecIsLeSecurityPending(const RawAddress& bd_addr) {
inc_func_call_count(__func__);
- return test::mock::stack_btm_sec::BTM_SecIsSecurityPending(bd_addr);
+ return test::mock::stack_btm_sec::BTM_SecIsLeSecurityPending(bd_addr);
}
bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
inc_func_call_count(__func__);
diff --git a/system/test/mock/mock_stack_btm_sec.h b/system/test/mock/mock_stack_btm_sec.h
index 8f716f443b..c40e57c593 100644
--- a/system/test/mock/mock_stack_btm_sec.h
+++ b/system/test/mock/mock_stack_btm_sec.h
@@ -246,16 +246,16 @@ struct BTM_SecGetDeviceLinkKeyType {
};
extern struct BTM_SecGetDeviceLinkKeyType BTM_SecGetDeviceLinkKeyType;
-// Name: BTM_SecIsSecurityPending
+// Name: BTM_SecIsLeSecurityPending
// Params: const RawAddress& bd_addr
// Return: bool
-struct BTM_SecIsSecurityPending {
+struct BTM_SecIsLeSecurityPending {
static bool return_value;
std::function<bool(const RawAddress& bd_addr)> body{
[](const RawAddress& /* bd_addr */) { return return_value; }};
bool operator()(const RawAddress& bd_addr) { return body(bd_addr); }
};
-extern struct BTM_SecIsSecurityPending BTM_SecIsSecurityPending;
+extern struct BTM_SecIsLeSecurityPending BTM_SecIsLeSecurityPending;
// Name: BTM_SecRegister
// Params: const tBTM_APPL_INFO* p_cb_info
diff --git a/system/test/mock/mock_stack_gatt_main.cc b/system/test/mock/mock_stack_gatt_main.cc
index 2c09b4a768..ead90a71d1 100644
--- a/system/test/mock/mock_stack_gatt_main.cc
+++ b/system/test/mock/mock_stack_gatt_main.cc
@@ -33,9 +33,6 @@ bool gatt_act_connect(tGATT_REG* /* p_reg */, const RawAddress& /* bd_addr */,
inc_func_call_count(__func__);
return false;
}
-void gatt_cancel_connect(const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport*/) {
- inc_func_call_count(__func__);
-}
bool gatt_disconnect(tGATT_TCB* /* p_tcb */) {
inc_func_call_count(__func__);
return false;
diff --git a/system/test/mock/mock_stack_rfcomm_port_api.cc b/system/test/mock/mock_stack_rfcomm_port_api.cc
index b87a752689..388f0cbdfc 100644
--- a/system/test/mock/mock_stack_rfcomm_port_api.cc
+++ b/system/test/mock/mock_stack_rfcomm_port_api.cc
@@ -115,3 +115,7 @@ bool PORT_IsCollisionDetected(RawAddress /* bd_addr */) {
inc_func_call_count(__func__);
return false;
}
+int PORT_SetAppUid(uint16_t /* handle */, uint32_t /* app_uid */) {
+ inc_func_call_count(__func__);
+ return 0;
+}
diff --git a/system/test/mock/mock_stack_security_client_interface.h b/system/test/mock/mock_stack_security_client_interface.h
index 2e61c7b56b..224d880905 100644
--- a/system/test/mock/mock_stack_security_client_interface.h
+++ b/system/test/mock/mock_stack_security_client_interface.h
@@ -43,7 +43,7 @@ struct MockSecurityClientInterface : public SecurityClientInterface {
tBTM_BLE_SEC_ACT /* sec_act */));
MOCK_METHOD((bool), BTM_IsEncrypted,
(const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport */));
- MOCK_METHOD((bool), BTM_SecIsSecurityPending, (const RawAddress& /* bd_addr */));
+ MOCK_METHOD((bool), BTM_SecIsLeSecurityPending, (const RawAddress& /* bd_addr */));
MOCK_METHOD((bool), BTM_IsLinkKeyKnown,
(const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport */));
MOCK_METHOD((bool), BTM_SetSecurityLevel,
diff --git a/system/test/suite/gatt/gatt_unittest.cc b/system/test/suite/gatt/gatt_unittest.cc
index aed80c1f10..62631e8730 100644
--- a/system/test/suite/gatt/gatt_unittest.cc
+++ b/system/test/suite/gatt/gatt_unittest.cc
@@ -30,7 +30,7 @@ TEST_F(GattTest, GattClientRegister) {
// Registers gatt client.
bluetooth::Uuid gatt_client_uuid = bluetooth::Uuid::From128BitBE(
bluetooth::os::GenerateRandom<bluetooth::Uuid::kNumBytes128>());
- gatt_client_interface()->register_client(gatt_client_uuid, false);
+ gatt_client_interface()->register_client(gatt_client_uuid, "test", false);
semaphore_wait(register_client_callback_sem_);
EXPECT_TRUE(status() == BT_STATUS_SUCCESS) << "Error registering GATT client app callback.";
diff --git a/system/types/Android.bp b/system/types/Android.bp
index 00a0de546d..e00941d8ec 100644
--- a/system/types/Android.bp
+++ b/system/types/Android.bp
@@ -15,7 +15,7 @@ cc_library_headers {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
"com.android.media",
"com.android.media.swcodec",
],
@@ -45,7 +45,7 @@ cc_library_static {
export_header_lib_headers: ["libbluetooth-types-header"],
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "29",
}
diff --git a/system/udrv/Android.bp b/system/udrv/Android.bp
index 5a69d8e886..fb77f0f0f1 100644
--- a/system/udrv/Android.bp
+++ b/system/udrv/Android.bp
@@ -24,7 +24,7 @@ cc_library_static {
host_supported: true,
apex_available: [
"//apex_available:platform",
- "com.android.btservices",
+ "com.android.bt",
],
min_sdk_version: "Tiramisu",
header_libs: ["libbluetooth_headers"],
diff --git a/tools/rootcanal/hal/bluetooth_hci.cc b/tools/rootcanal/hal/bluetooth_hci.cc
index b08d59995b..b36c18687d 100644
--- a/tools/rootcanal/hal/bluetooth_hci.cc
+++ b/tools/rootcanal/hal/bluetooth_hci.cc
@@ -53,24 +53,6 @@ bool BtTestConsoleEnabled() {
} // namespace
-class BluetoothDeathRecipient : public hidl_death_recipient {
-public:
- BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
-
- void serviceDied(uint64_t /* cookie */,
- const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
- ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
- has_died_ = true;
- mHci->close();
- }
- sp<IBluetoothHci> mHci;
- bool getHasDied() const { return has_died_; }
- void setHasDied(bool has_died) { has_died_ = has_died; }
-
-private:
- bool has_died_{false};
-};
-
BluetoothHci::BluetoothHci() : death_recipient_(new BluetoothDeathRecipient(this)) {}
Return<void> BluetoothHci::initialize(const sp<V1_0::IBluetoothHciCallbacks>& cb) {
diff --git a/tools/rootcanal/hal/bluetooth_hci.h b/tools/rootcanal/hal/bluetooth_hci.h
index 95bda38957..95e3e45334 100644
--- a/tools/rootcanal/hal/bluetooth_hci.h
+++ b/tools/rootcanal/hal/bluetooth_hci.h
@@ -18,6 +18,7 @@
#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
#include <hidl/MQDescriptor.h>
+#include <log/log.h>
#include "hci_packetizer.h"
#include "model/controller/dual_mode_controller.h"
@@ -44,6 +45,25 @@ using android::net::ConnectCallback;
using rootcanal::Device;
using rootcanal::Phy;
+class BluetoothDeathRecipient : public hidl_death_recipient {
+public:
+ BluetoothDeathRecipient(const sp<IBluetoothHci> hci) : mHci(hci) {}
+
+ void serviceDied(uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+ ALOGE("BluetoothDeathRecipient::serviceDied - Bluetooth service died");
+ has_died_ = true;
+ mHci->close();
+ }
+
+ sp<IBluetoothHci> mHci;
+ bool getHasDied() const { return has_died_; }
+ void setHasDied(bool has_died) { has_died_ = has_died; }
+
+private:
+ bool has_died_{false};
+};
+
class BluetoothHci : public IBluetoothHci {
public:
BluetoothHci();
diff --git a/tools/rootcanal/rust/src/lmp/procedure/mod.rs b/tools/rootcanal/rust/src/lmp/procedure/mod.rs
index 5f93e62da9..d73c5d39e9 100644
--- a/tools/rootcanal/rust/src/lmp/procedure/mod.rs
+++ b/tools/rootcanal/rust/src/lmp/procedure/mod.rs
@@ -66,7 +66,7 @@ pub trait Context {
/// Future for Context::receive_hci_command and Context::receive_lmp_packet
pub struct ReceiveFuture<'a, C: ?Sized, P>(fn(&'a C) -> Poll<P>, &'a C);
-impl<'a, C, O> Future for ReceiveFuture<'a, C, O>
+impl<C, O> Future for ReceiveFuture<'_, C, O>
where
C: Context,
{
@@ -80,7 +80,7 @@ where
/// Future for Context::receive_hci_command and Context::receive_lmp_packet
pub struct SendAcceptedLmpPacketFuture<'a, C: ?Sized>(&'a C, lmp::Opcode);
-impl<'a, C> Future for SendAcceptedLmpPacketFuture<'a, C>
+impl<C> Future for SendAcceptedLmpPacketFuture<'_, C>
where
C: Context,
{