summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--android/app/Android.bp12
-rw-r--r--android/app/AndroidManifest.xml11
-rw-r--r--android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp131
-rw-r--r--android/app/jni/com_android_bluetooth_gatt.cpp8
-rw-r--r--android/app/jni/com_android_bluetooth_le_audio.cpp26
-rw-r--r--android/app/proto/keystore.proto (renamed from system/gd/proto/bluetooth/bluetoothKeystore/keystore.proto)3
-rw-r--r--android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java1
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java23
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java2
-rw-r--r--android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java34
-rw-r--r--android/app/src/com/android/bluetooth/bas/BatteryService.java2
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientService.java178
-rw-r--r--android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java42
-rw-r--r--android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java140
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterService.java112
-rw-r--r--android/app/src/com/android/bluetooth/btservice/AdapterState.java12
-rw-r--r--android/app/src/com/android/bluetooth/btservice/BondStateMachine.java10
-rw-r--r--android/app/src/com/android/bluetooth/btservice/Config.java51
-rw-r--r--android/app/src/com/android/bluetooth/btservice/ProfileService.java11
-rw-r--r--android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java42
-rw-r--r--android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java103
-rw-r--r--android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java74
-rw-r--r--android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java43
-rw-r--r--android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java4
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java88
-rw-r--r--android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java178
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattObjectsFactory.java17
-rw-r--r--android/app/src/com/android/bluetooth/gatt/GattService.java65
-rw-r--r--android/app/src/com/android/bluetooth/hap/HapClientService.java28
-rw-r--r--android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java1
-rw-r--r--android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java11
-rw-r--r--android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java8
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioService.java80
-rw-r--r--android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java14
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/TestUtils.java19
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java69
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java20
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java78
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java54
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java3
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java101
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/ConfigTest.java51
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java2
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java60
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java15
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreServiceTest.java29
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java7
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java25
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java24
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java7
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java61
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java24
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java4
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java24
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java3
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java25
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java3
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioStateMachineTest.java74
-rw-r--r--android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java10
-rw-r--r--android/pandora/server/configs/pts_bot_tests_config.json12
-rw-r--r--android/pandora/server/src/AndroidInternal.kt4
-rw-r--r--android/pandora/server/src/Gatt.kt15
-rw-r--r--android/pandora/server/src/GattServerManager.kt7
-rw-r--r--android/pandora/test/AndroidTest.xml4
-rw-r--r--android/pandora/test/asha_test.py11
-rw-r--r--android/pandora/test/config.yml4
-rw-r--r--android/pandora/test/gatt_test.py55
-rw-r--r--android/pandora/test/hfpclient_test.py123
-rw-r--r--android/pandora/test/main.py4
-rw-r--r--android/pandora/test/pyrightconfig.json9
-rw-r--r--android/pandora/test/sdp_test.py29
-rw-r--r--floss/hcidoc/src/groups/connections.rs37
-rw-r--r--floss/hcidoc/src/groups/informational.rs405
-rw-r--r--floss/pandora/floss/advertising_client.py24
-rw-r--r--floss/pandora/floss/floss_enums.py24
-rw-r--r--floss/pandora/floss/scanner_client.py24
-rw-r--r--floss/pandora/floss/utils.py115
-rw-r--r--floss/pandora/server/bluetooth.py249
-rw-r--r--floss/pandora/server/host.py489
-rw-r--r--floss/pandora/server/security.py102
-rw-r--r--floss/pandora/server/server.py60
-rw-r--r--framework/java/android/bluetooth/BluetoothGattServer.java6
-rw-r--r--pandora/server/bumble_experimental/asha.py2
-rw-r--r--service/Android.bp5
-rw-r--r--service/src/com/android/server/bluetooth/BluetoothManagerService.java23
-rw-r--r--system/audio_hal_interface/fuzzer/Android.bp1
-rw-r--r--system/binder/android/bluetooth/IBluetoothGatt.aidl7
-rw-r--r--system/bta/Android.bp22
-rw-r--r--system/bta/csis/csis_client.cc14
-rw-r--r--system/bta/csis/csis_client_test.cc34
-rw-r--r--system/bta/dm/bta_dm_act.cc57
-rw-r--r--system/bta/dm/bta_dm_main.cc6
-rw-r--r--system/bta/include/bta_le_audio_api.h3
-rw-r--r--system/bta/le_audio/audio_hal_client/audio_hal_client.h4
-rw-r--r--system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc10
-rw-r--r--system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc7
-rw-r--r--system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc7
-rw-r--r--system/bta/le_audio/broadcaster/broadcaster.cc4
-rw-r--r--system/bta/le_audio/client.cc126
-rw-r--r--system/bta/le_audio/le_audio_client_test.cc111
-rw-r--r--system/bta/le_audio/mock_state_machine.h3
-rw-r--r--system/bta/le_audio/state_machine.cc9
-rw-r--r--system/bta/le_audio/state_machine.h5
-rw-r--r--system/bta/le_audio/state_machine_test.cc13
-rw-r--r--system/bta/test/bta_dm_test.cc19
-rw-r--r--system/bta/test/bta_sdp_test.cc8
-rw-r--r--system/btcore/Android.bp4
-rw-r--r--system/btif/Android.bp26
-rw-r--r--system/btif/co/bta_av_co.cc21
-rw-r--r--system/btif/include/btif_av_co.h6
-rw-r--r--system/btif/src/btif_a2dp_sink.cc3
-rw-r--r--system/btif/src/btif_av.cc6
-rw-r--r--system/btif/src/btif_avrcp_audio_track.cc4
-rw-r--r--system/btif/src/btif_dm.cc5
-rw-r--r--system/btif/src/btif_rc.cc3
-rw-r--r--system/btif/test/btif_rc_test.cc39
-rw-r--r--system/build/Android.bp19
-rw-r--r--system/common/Android.bp16
-rw-r--r--system/device/Android.bp5
-rw-r--r--system/gd/Android.bp6
-rw-r--r--system/gd/btaa/activity_attribution.h12
-rw-r--r--system/gd/btaa/android/activity_attribution.cc9
-rw-r--r--system/gd/btaa/host/activity_attribution.cc2
-rw-r--r--system/gd/btaa/linux/activity_attribution.cc4
-rw-r--r--system/gd/dumpsys/bundler/Android.bp1
-rw-r--r--system/gd/hci/le_periodic_sync_manager.h4
-rw-r--r--system/gd/proto/Android.bp16
-rw-r--r--system/gd/rust/common/src/bridge.rs1
-rw-r--r--system/gd/rust/common/src/init_flags.rs1
-rw-r--r--system/gd/rust/common/src/parameter_provider.rs33
-rw-r--r--system/gd/rust/facade/src/main.rs12
-rw-r--r--system/gd/rust/linux/client/src/dbus_iface.rs2
-rw-r--r--system/gd/rust/linux/service/src/iface_bluetooth.rs2
-rw-r--r--system/gd/rust/linux/service/src/main.rs30
-rw-r--r--system/gd/rust/linux/stack/src/bluetooth.rs43
-rw-r--r--system/gd/rust/linux/stack/src/bluetooth_media.rs78
-rw-r--r--system/gd/rust/linux/stack/src/lib.rs4
-rw-r--r--system/gd/rust/linux/stack/src/socket_manager.rs2
-rw-r--r--system/gd/rust/shim/src/init_flags.rs2
-rw-r--r--system/gd/rust/stack/src/hal/hidl_hal.rs1
-rw-r--r--system/gd/rust/topshim/Android.bp3
-rw-r--r--system/gd/rust/topshim/bindings/wrapper.hpp2
-rw-r--r--system/gd/rust/topshim/facade/Android.bp1
-rw-r--r--system/gd/rust/topshim/facade/src/main.rs12
-rw-r--r--system/gd/rust/topshim/src/btif.rs20
-rw-r--r--system/hci/Android.bp7
-rw-r--r--system/include/hardware/bluetooth.h5
-rw-r--r--system/include/hardware/bt_activity_attribution.h41
-rw-r--r--system/main/Android.bp9
-rw-r--r--system/main/shim/activity_attribution.cc47
-rw-r--r--system/main/shim/activity_attribution.h2
-rw-r--r--system/main/shim/stack.cc3
-rw-r--r--system/main/test/main_shim_test.cc2
-rw-r--r--system/osi/Android.bp3
-rw-r--r--system/osi/test/fuzzers/alarm/Android.bp1
-rw-r--r--system/rust/src/connection/ffi.rs2
-rw-r--r--system/rust/src/core/ffi.rs8
-rw-r--r--system/rust/src/gatt/arbiter.rs12
-rw-r--r--system/rust/src/gatt/ffi.rs1
-rw-r--r--system/stack/Android.bp39
-rw-r--r--system/stack/btm/btm_int_types.h10
-rw-r--r--system/stack/btm/btm_scn.cc13
-rw-r--r--system/stack/btm/btm_scn.h2
-rw-r--r--system/stack/l2cap/l2c_api.cc8
-rw-r--r--system/stack/l2cap/l2c_ble.cc6
-rw-r--r--system/stack/l2cap/l2c_csm.cc10
-rw-r--r--system/stack/l2cap/l2c_int.h3
-rw-r--r--system/stack/l2cap/l2c_utils.cc9
-rw-r--r--system/stack/test/btm/btm_scn_test.cc91
-rw-r--r--system/stack/test/fuzzers/Android.bp1
-rw-r--r--system/test/headless/Android.bp1
-rw-r--r--system/test/mock/mock_bta_dm_act.cc5
-rw-r--r--system/test/mock/mock_bta_dm_act.h10
-rw-r--r--system/test/mock/mock_btif_co_bta_av_co.cc7
-rw-r--r--system/test/mock/mock_btif_co_bta_av_co.h11
-rw-r--r--system/test/mock/mock_main_shim_activity_attribution.cc3
-rw-r--r--system/test/mock/mock_stack_l2cap_utils.cc2
-rw-r--r--system/test/suite/Android.bp2
-rw-r--r--system/vendor_libs/linux/interface/Android.bp1
-rw-r--r--tools/rootcanal/Android.bp65
-rw-r--r--tools/rootcanal/CMakeLists.txt174
-rw-r--r--tools/rootcanal/hal/Android.bp (renamed from system/test/rootcanal/Android.bp)30
-rw-r--r--tools/rootcanal/hal/android.hardware.bluetooth@1.1-service.sim.rc (renamed from system/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc)0
-rw-r--r--tools/rootcanal/hal/bluetooth_hci.cc (renamed from system/test/rootcanal/bluetooth_hci.cc)2
-rw-r--r--tools/rootcanal/hal/bluetooth_hci.h (renamed from system/test/rootcanal/bluetooth_hci.h)0
-rw-r--r--tools/rootcanal/hal/service.cc (renamed from system/test/rootcanal/service.cc)0
-rw-r--r--tools/rootcanal/include/hci/address.h22
-rw-r--r--tools/rootcanal/include/hci/address_with_type.h2
-rw-r--r--tools/rootcanal/include/hci/class_of_device.h73
-rw-r--r--tools/rootcanal/include/hci/pcap_filter.h2
-rw-r--r--tools/rootcanal/lib/hci/address.cc14
-rw-r--r--tools/rootcanal/lib/hci/class_of_device.cc112
-rw-r--r--tools/rootcanal/lib/hci/pcap_filter.cc16
-rw-r--r--tools/rootcanal/model/controller/acl_connection_handler.cc2
-rw-r--r--tools/rootcanal/model/controller/controller_properties.h2
-rw-r--r--tools/rootcanal/model/controller/dual_mode_controller.cc128
-rw-r--r--tools/rootcanal/model/controller/dual_mode_controller.h2
-rw-r--r--tools/rootcanal/model/controller/ffi.cc4
-rw-r--r--tools/rootcanal/model/controller/le_advertiser.h2
-rw-r--r--tools/rootcanal/model/controller/link_layer_controller.cc119
-rw-r--r--tools/rootcanal/model/controller/link_layer_controller.h44
-rw-r--r--tools/rootcanal/model/controller/sco_connection.cc2
-rw-r--r--tools/rootcanal/model/devices/baseband_sniffer.cc15
-rw-r--r--tools/rootcanal/model/devices/baseband_sniffer.h2
-rw-r--r--tools/rootcanal/model/devices/beacon.h1
-rw-r--r--tools/rootcanal/model/devices/device.cc4
-rw-r--r--tools/rootcanal/model/devices/device.h3
-rw-r--r--tools/rootcanal/model/devices/hci_device.cc40
-rw-r--r--tools/rootcanal/model/devices/link_layer_socket_device.cc23
-rw-r--r--tools/rootcanal/model/hci/hci_sniffer.cc51
-rw-r--r--tools/rootcanal/model/hci/hci_sniffer.h15
-rw-r--r--tools/rootcanal/model/hci/hci_socket_transport.cc41
-rw-r--r--tools/rootcanal/model/hci/hci_socket_transport.h17
-rw-r--r--tools/rootcanal/model/hci/hci_transport.h24
-rw-r--r--tools/rootcanal/model/setup/phy_device.cc5
-rw-r--r--tools/rootcanal/model/setup/test_model.cc2
-rw-r--r--tools/rootcanal/packets/hci_packets.pdl (renamed from tools/rootcanal/packets/hci/hci_packets.pdl)69
-rw-r--r--tools/rootcanal/packets/link_layer_packets.pdl7
-rw-r--r--tools/rootcanal/py/controller.py18
-rw-r--r--tools/rootcanal/rust/CMakeLists.txt2
-rw-r--r--tools/rootcanal/rust/build.rs6
-rw-r--r--tools/rootcanal/rust/src/ffi.rs65
-rw-r--r--tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs203
-rw-r--r--tools/rootcanal/rust/src/packets.rs12
-rw-r--r--tools/rootcanal/rust/test/SP/BV-14bis-C.in106
-rw-r--r--tools/rootcanal/rust/test/SP/BV-15bis-C.in118
-rw-r--r--tools/rootcanal/test/LL/CON_/CEN/BV_41_C.py14
-rw-r--r--tools/rootcanal/test/LL/CON_/CEN/BV_43_C.py14
-rw-r--r--tools/rootcanal/test/LL/CON_/PER/BV_40_C.py4
-rw-r--r--tools/rootcanal/test/LL/CON_/PER/BV_42_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_07_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_09_C.py8
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_11_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_17_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/ADV/BV_19_C.py4
-rw-r--r--tools/rootcanal/test/LL/DDI/SCN/BV_19_C.py8
-rw-r--r--tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py8
-rw-r--r--tools/rootcanal/test/controller/le/le_add_device_to_resolving_list_test.cc12
-rw-r--r--tools/rootcanal/test/controller/le/le_scanning_filter_duplicates_test.cc32
-rw-r--r--tools/rootcanal/test/controller/le/le_set_extended_scan_enable_test.cc96
-rw-r--r--tools/rootcanal/test/controller/le/le_set_extended_scan_parameters_test.cc59
-rw-r--r--tools/rootcanal/test/controller/le/test_helpers.h10
-rw-r--r--tools/rootcanal/test/pcap_filter_unittest.cc2
245 files changed, 4582 insertions, 2883 deletions
diff --git a/Android.bp b/Android.bp
index 7be2e6ec9e..aef08f38e9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -76,6 +76,8 @@ cc_defaults {
// there are too many unused parameters in all the code.
"-Wno-unused-parameter",
],
+ c_std: "c99",
+ cpp_std: "c++17",
}
// Address Sanitizer is flaky on Android x86_64 binaries but it's not on x86
diff --git a/android/app/Android.bp b/android/app/Android.bp
index 2bf58974cb..ae6f315ddd 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -55,7 +55,7 @@ java_library {
cc_library_shared {
name: "libbluetooth_jni",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
srcs: ["jni/**/*.cpp"],
version_script: "libbluetooth_jni.map",
header_libs: [
@@ -77,7 +77,7 @@ cc_library_shared {
// is required to maintain FIPS compliance.
stl: "libc++_static",
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.audio.common@5.0",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.bluetooth.audio@2.0",
@@ -118,6 +118,7 @@ cc_library_shared {
"libchrome",
"libcutils",
"libevent",
+ "libflatbuffers-cpp",
"libfmq",
"libg722codec",
"libhidlbase",
@@ -125,6 +126,7 @@ cc_library_shared {
"libmodpb64",
"libopus",
"libosi",
+ "libprotobuf-cpp-lite",
"libudrv-uipc",
"libutils",
],
@@ -148,7 +150,7 @@ cc_library_shared {
cc_library {
name: "libbluetooth-core",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
header_libs: [
"jni_headers",
"libbluetooth_headers",
@@ -199,8 +201,12 @@ android_app {
srcs: [
":statslog-bluetooth-java-gen",
":statslog-bt-restricted-java-gen",
+ "proto/keystore.proto",
"src/**/*.java",
],
+ proto: {
+ type: "lite",
+ },
aaptflags: [
"--custom-package",
"com.android.bluetooth",
diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml
index 929cc0e42b..7a398344c0 100644
--- a/android/app/AndroidManifest.xml
+++ b/android/app/AndroidManifest.xml
@@ -221,17 +221,6 @@
</intent-filter>
</service>
- <!-- Generic Attribute (GATT) Profile Service -->
- <service android:process="@string/process"
- android:name="com.android.bluetooth.gatt.GattService"
- android:enabled="true"
- android:exported="true"
- android:permission="android.permission.ACCESS_BLUETOOTH_SHARE">
- <intent-filter>
- <action android:name="android.bluetooth.IBluetoothGatt"/>
- </intent-filter>
- </service>
-
<!-- Hearing Aid Profile (HAP) client Profile Service -->
<service android:process="@string/process"
android:name="com.android.bluetooth.hap.HapClientService"
diff --git a/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp b/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp
index 4e0247dccc..2790b00903 100644
--- a/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp
@@ -24,138 +24,10 @@
#include "com_android_bluetooth.h"
#include "hardware/bt_activity_attribution.h"
-using bluetooth::activity_attribution::ActivityAttributionCallbacks;
using bluetooth::activity_attribution::ActivityAttributionInterface;
namespace android {
-static jmethodID method_onWakeup;
-static jmethodID method_onActivityLogsReady;
-
static ActivityAttributionInterface* sActivityAttributionInterface = nullptr;
-static std::shared_timed_mutex interface_mutex;
-
-static jobject mCallbacksObj = nullptr;
-static std::shared_timed_mutex callbacks_mutex;
-
-class ActivityAttributionCallbacksImpl : public ActivityAttributionCallbacks {
- public:
- ~ActivityAttributionCallbacksImpl() = default;
-
- void OnWakeup(const Activity activity, const RawAddress& bd_addr) override {
- LOG(INFO) << __func__;
-
- std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
- CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
-
- ScopedLocalRef<jbyteArray> addr(
- sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
- if (!addr.get()) {
- LOG(ERROR)
- << "Failed to allocate jbyteArray for bd_addr of wakeup callback";
- return;
- }
-
- sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
- (jbyte*)&bd_addr);
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onWakeup, (jint)activity,
- addr.get());
- }
-
- void OnActivityLogsReady(
- const std::vector<BtaaAggregationEntry> logs) override {
- LOG(INFO) << __func__;
-
- std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
- CallbackEnv sCallbackEnv(__func__);
- if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
-
- jsize logs_size = logs.size() * sizeof(BtaaAggregationEntry);
- ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(),
- sCallbackEnv->NewByteArray(logs_size));
- if (!addr.get()) {
- LOG(ERROR) << "Failed to allocate jbyteArray for logs from activity "
- "logging callback";
- return;
- }
-
- sCallbackEnv->SetByteArrayRegion(addr.get(), 0, logs_size,
- (jbyte*)logs.data());
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onActivityLogsReady,
- addr.get());
- }
-};
-
-static ActivityAttributionCallbacksImpl sActivityAttributionCallbacks;
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
- method_onWakeup = env->GetMethodID(clazz, "onWakeup", "(I[B)V");
- method_onActivityLogsReady =
- env->GetMethodID(clazz, "onActivityLogsReady", "([B)V");
-
- LOG(INFO) << __func__ << ": succeeds";
-}
-
-static void initNative(JNIEnv* env, jobject object) {
- std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
- std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
- const bt_interface_t* btInf = getBluetoothInterface();
- if (btInf == nullptr) {
- LOG(ERROR) << "Bluetooth module is not loaded";
- return;
- }
-
- if (sActivityAttributionInterface != nullptr) {
- LOG(INFO)
- << "Cleaning up ActivityAttribution Interface before initializing...";
- sActivityAttributionInterface->Cleanup();
- sActivityAttributionInterface = nullptr;
- }
-
- if (mCallbacksObj != nullptr) {
- LOG(INFO) << "Cleaning up ActivityAttribution callback object";
- env->DeleteGlobalRef(mCallbacksObj);
- mCallbacksObj = nullptr;
- }
-
- if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
- LOG(ERROR)
- << "Failed to allocate Global Ref for ActivityAttribution Callbacks";
- return;
- }
-
- sActivityAttributionInterface =
- (ActivityAttributionInterface*)btInf->get_profile_interface(
- BT_ACTIVITY_ATTRIBUTION_ID);
- if (sActivityAttributionInterface == nullptr) {
- LOG(ERROR) << "Failed to get ActivityAttribution Interface";
- return;
- }
-
- sActivityAttributionInterface->RegisterCallbacks(
- &sActivityAttributionCallbacks);
-}
-
-static void cleanupNative(JNIEnv* env, jobject object) {
- std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
- std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
-
- const bt_interface_t* btInf = getBluetoothInterface();
- if (btInf == nullptr) {
- LOG(ERROR) << "Bluetooth module is not loaded";
- return;
- }
-
- if (sActivityAttributionInterface != nullptr) {
- sActivityAttributionInterface->Cleanup();
- sActivityAttributionInterface = nullptr;
- }
-
- if (mCallbacksObj != nullptr) {
- env->DeleteGlobalRef(mCallbacksObj);
- mCallbacksObj = nullptr;
- }
-}
static void notifyActivityAttributionInfoNative(JNIEnv* env, jobject object,
jint uid, jstring packageName,
@@ -186,9 +58,6 @@ static void notifyActivityAttributionInfoNative(JNIEnv* env, jobject object,
}
static JNINativeMethod sMethods[] = {
- {"classInitNative", "()V", (void*)classInitNative},
- {"initNative", "()V", (void*)initNative},
- {"cleanupNative", "()V", (void*)cleanupNative},
{"notifyActivityAttributionInfoNative",
"(ILjava/lang/String;Ljava/lang/String;)V",
(void*)notifyActivityAttributionInfoNative},
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index 1290aae868..1c329d68b2 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -2682,7 +2682,7 @@ static void stopDistanceMeasurementNative(JNIEnv* env, jobject object,
* JNI function definitinos
*/
-// JNI functions defined in AdvertiseManager class.
+// JNI functions defined in AdvertiseManagerNativeInterface class.
static JNINativeMethod sAdvertiseMethods[] = {
{"classInitNative", "()V", (void*)advertiseClassInitNative},
{"initializeNative", "()V", (void*)advertiseInitializeNative},
@@ -2691,8 +2691,8 @@ static JNINativeMethod sAdvertiseMethods[] = {
"(Landroid/bluetooth/le/AdvertisingSetParameters;[B[BLandroid/bluetooth/"
"le/PeriodicAdvertisingParameters;[BIIII)V",
(void*)startAdvertisingSetNative},
- {"getOwnAddressNative", "(I)V", (void*)getOwnAddressNative},
{"stopAdvertisingSetNative", "(I)V", (void*)stopAdvertisingSetNative},
+ {"getOwnAddressNative", "(I)V", (void*)getOwnAddressNative},
{"enableAdvertisingSetNative", "(IZII)V",
(void*)enableAdvertisingSetNative},
{"setAdvertisingDataNative", "(I[B)V", (void*)setAdvertisingDataNative},
@@ -2848,8 +2848,8 @@ int register_com_android_bluetooth_gatt(JNIEnv* env) {
env, "com/android/bluetooth/gatt/ScanNativeInterface", sScanMethods,
NELEM(sScanMethods));
register_success &= jniRegisterNativeMethods(
- env, "com/android/bluetooth/gatt/AdvertiseManager", sAdvertiseMethods,
- NELEM(sAdvertiseMethods));
+ env, "com/android/bluetooth/gatt/AdvertiseManagerNativeInterface",
+ sAdvertiseMethods, NELEM(sAdvertiseMethods));
register_success &= jniRegisterNativeMethods(
env, "com/android/bluetooth/gatt/PeriodicScanManager",
sPeriodicScanMethods, NELEM(sPeriodicScanMethods));
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index 5751231146..08f859c404 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -789,11 +789,15 @@ jobject prepareLeAudioContentMetadataObject(
JNIEnv* env, const std::map<uint8_t, std::vector<uint8_t>>& metadata) {
jstring program_info_str = nullptr;
if (metadata.count(bluetooth::le_audio::kLeAudioMetadataTypeProgramInfo)) {
- program_info_str = env->NewStringUTF(
- (const char*)(metadata
- .at(bluetooth::le_audio::
- kLeAudioMetadataTypeProgramInfo)
- .data()));
+ // Convert the metadata vector to string with null terminator
+ std::string p_str(
+ (const char*)metadata
+ .at(bluetooth::le_audio::kLeAudioMetadataTypeProgramInfo)
+ .data(),
+ metadata.at(bluetooth::le_audio::kLeAudioMetadataTypeProgramInfo)
+ .size());
+
+ program_info_str = env->NewStringUTF(p_str.c_str());
if (!program_info_str) {
LOG(ERROR) << "Failed to create new preset name String for preset name";
return nullptr;
@@ -802,10 +806,14 @@ jobject prepareLeAudioContentMetadataObject(
jstring language_str = nullptr;
if (metadata.count(bluetooth::le_audio::kLeAudioMetadataTypeLanguage)) {
- language_str = env->NewStringUTF(
- (const char*)(metadata
- .at(bluetooth::le_audio::kLeAudioMetadataTypeLanguage)
- .data()));
+ // Convert the metadata vector to string with null terminator
+ std::string l_str(
+ (const char*)metadata
+ .at(bluetooth::le_audio::kLeAudioMetadataTypeLanguage)
+ .data(),
+ metadata.at(bluetooth::le_audio::kLeAudioMetadataTypeLanguage).size());
+
+ language_str = env->NewStringUTF(l_str.c_str());
if (!language_str) {
LOG(ERROR) << "Failed to create new preset name String for language";
return nullptr;
diff --git a/system/gd/proto/bluetooth/bluetoothKeystore/keystore.proto b/android/app/proto/keystore.proto
index d0e418bd28..f0641ff6fa 100644
--- a/system/gd/proto/bluetooth/bluetoothKeystore/keystore.proto
+++ b/android/app/proto/keystore.proto
@@ -16,9 +16,6 @@
syntax = "proto2";
-// C++ namespace: bluetooth::metrics::BluetoothMetricsProto
-package bluetooth.keystore.BluetoothKeystoreProto;
-
option java_package = "com.android.bluetooth";
option java_outer_classname = "BluetoothKeystoreProto";
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index e7b0920476..02e8a5ace4 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -134,6 +134,7 @@ public class A2dpSinkService extends ProfileService {
* Set the device that should be allowed to actively stream
*/
public boolean setActiveDevice(BluetoothDevice device) {
+ Log.i(TAG, "setActiveDevice(device=" + device + ")");
synchronized (mActiveDeviceLock) {
if (mNativeInterface.setActiveDevice(device)) {
mActiveDevice = device;
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 25c7f9e7a1..a43594df76 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -208,8 +208,12 @@ public class AvrcpControllerService extends ProfileService {
*/
@VisibleForTesting
boolean setActiveDevice(BluetoothDevice device) {
+ if (DBG) {
+ Log.d(TAG, "setActiveDevice(device=" + device + ")");
+ }
A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
if (a2dpSinkService == null) {
+ Log.w(TAG, "setActiveDevice(device=" + device + "): A2DP Sink not available");
return false;
}
@@ -242,6 +246,8 @@ public class AvrcpControllerService extends ProfileService {
return true;
}
}
+
+ Log.w(TAG, "setActiveDevice(device=" + device + "): A2DP Sink request failed");
return false;
}
@@ -333,15 +339,21 @@ public class AvrcpControllerService extends ProfileService {
for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) {
requestedNode = stateMachine.findNode(parentMediaId);
if (requestedNode != null) {
- Log.d(TAG, "Found a node");
break;
}
}
}
+
+ if (DBG) {
+ Log.d(TAG, "getContents(" + parentMediaId + "): "
+ + (requestedNode == null
+ ? "Failed to find node"
+ : "node=" + requestedNode + ", device=" + requestedNode.getDevice()));
+ }
+
// If we don't find a node in the tree then do not have any way to browse for the contents.
// Return an empty list instead.
if (requestedNode == null) {
- if (DBG) Log.d(TAG, "Didn't find a node");
return new BrowseResult(new ArrayList(0), BrowseResult.ERROR_MEDIA_ID_INVALID);
}
if (parentMediaId.equals(BrowseTree.ROOT) && requestedNode.getChildrenCount() == 0) {
@@ -355,9 +367,8 @@ public class AvrcpControllerService extends ProfileService {
List<MediaItem> contents = requestedNode.getContents();
- if (DBG) Log.d(TAG, "Returning contents");
if (!requestedNode.isCached()) {
- if (DBG) Log.d(TAG, "node is not cached");
+ if (DBG) Log.d(TAG, "getContents(" + parentMediaId + "): node download pending");
refreshContents(requestedNode);
/* Ongoing downloads can have partial results and we want to make sure they get sent
* to the client. If a download gets kicked off as a result of this request, the
@@ -365,6 +376,10 @@ public class AvrcpControllerService extends ProfileService {
*/
return new BrowseResult(contents, BrowseResult.DOWNLOAD_PENDING);
}
+ if (DBG) {
+ Log.d(TAG, "getContents(" + parentMediaId + "): return node, contents="
+ + requestedNode.getContents());
+ }
return new BrowseResult(contents, BrowseResult.SUCCESS);
}
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 290dc35cb4..9f43d8199f 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -581,6 +581,8 @@ class AvrcpControllerStateMachine extends StateMachine {
mAddressedPlayer.updateCurrentTrack(track);
if (isActive()) {
BluetoothMediaBrowserService.trackChanged(track);
+ BluetoothMediaBrowserService.notifyChanged(
+ mAddressedPlayer.getPlaybackState());
}
if (previousTrack != null) {
removeUnusedArtwork(previousTrack.getCoverArtUuid());
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 25df4cd613..8d68409fb2 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -86,6 +86,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_LOCALE_CHANGED)) {
+ if (DBG) Log.d(TAG, "Locale has updated");
if (sBluetoothMediaBrowserService == null) return;
MediaSessionCompat session = sBluetoothMediaBrowserService.getSession();
MediaControllerCompat controller = session.getController();
@@ -106,7 +107,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
*/
@Override
public void onCreate() {
- if (DBG) Log.d(TAG, "onCreate");
+ if (DBG) Log.d(TAG, "Service Created");
super.onCreate();
// Create and configure the MediaSessionCompat
@@ -128,6 +129,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
@Override
public void onDestroy() {
+ if (DBG) Log.d(TAG, "Service Destroyed");
unregisterReceiver(mReceiver);
mReceiver = null;
}
@@ -191,6 +193,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
AvrcpControllerService avrcpControllerService =
AvrcpControllerService.getAvrcpControllerService();
if (avrcpControllerService == null) {
+ Log.w(TAG, "getContents(id=" + parentMediaId + "): AVRCP Controller Service not ready");
return new BrowseResult(new ArrayList(0), BrowseResult.ERROR_NO_AVRCP_SERVICE);
} else {
return avrcpControllerService.getContents(parentMediaId);
@@ -227,7 +230,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
@Override
public synchronized void onLoadChildren(final String parentMediaId,
final Result<List<MediaItem>> result) {
- if (DBG) Log.d(TAG, "onLoadChildren parentMediaId= " + parentMediaId);
+ if (DBG) Log.d(TAG, "Request for contents, id= " + parentMediaId);
BrowseResult contents = getContents(parentMediaId);
byte status = contents.getStatus();
List<MediaItem> results = contents.getResults();
@@ -236,8 +239,8 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
result.detach();
} else {
if (DBG) {
- Log.d(TAG, "id= " + parentMediaId + ", status= " + contents.getStatusString()
- + ", results=" + results);
+ Log.d(TAG, "Received Contents, id= " + parentMediaId + ", status= "
+ + contents.getStatusString() + ", results=" + results);
}
result.sendResult(results);
}
@@ -245,7 +248,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
- if (DBG) Log.d(TAG, "onGetRoot");
+ Log.i(TAG, "Browser Client Connection Request, client='" + clientPackageName + "')");
Bundle style = getDefaultStyle();
return new BrowserRoot(BrowseTree.ROOT, style);
}
@@ -263,6 +266,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
} else {
mSession.setQueue(null);
}
+ if (DBG) Log.d(TAG, "Now Playing List Changed, queue=" + mMediaQueue);
}
private void clearNowPlayingQueue() {
@@ -275,6 +279,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
if (node.getScope() == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) {
sBluetoothMediaBrowserService.updateNowPlayingQueue(node);
} else {
+ if (DBG) Log.d(TAG, "Browse Node contents changed, node=" + node);
sBluetoothMediaBrowserService.notifyChildrenChanged(node.getID());
}
}
@@ -293,7 +298,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
}
static synchronized void trackChanged(AvrcpItem track) {
- if (DBG) Log.d(TAG, "trackChanged setMetadata=" + track);
+ if (DBG) Log.d(TAG, "Track Changed, track=" + track);
if (sBluetoothMediaBrowserService != null) {
if (track != null) {
sBluetoothMediaBrowserService.mSession.setMetadata(track.toMediaMetadata());
@@ -307,7 +312,7 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
}
static synchronized void notifyChanged(PlaybackStateCompat playbackState) {
- Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
+ if (DBG) Log.d(TAG, "Playback State Changed, state=" + playbackState);
if (sBluetoothMediaBrowserService != null) {
sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
} else {
@@ -340,15 +345,16 @@ public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
/**
* Get playback state
*/
- public static synchronized int getPlaybackState() {
+ public static synchronized PlaybackStateCompat getPlaybackState() {
if (sBluetoothMediaBrowserService != null) {
- PlaybackStateCompat currentPlaybackState =
- sBluetoothMediaBrowserService.mSession.getController().getPlaybackState();
- if (currentPlaybackState != null) {
- return currentPlaybackState.getState();
- }
+ MediaSessionCompat session = sBluetoothMediaBrowserService.getSession();
+ if (session == null) return null;
+ MediaControllerCompat controller = session.getController();
+ PlaybackStateCompat playbackState =
+ controller == null ? null : controller.getPlaybackState();
+ return playbackState;
}
- return PlaybackStateCompat.STATE_ERROR;
+ return null;
}
/**
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java
index d9a70479f3..cc57d631c7 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryService.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java
@@ -53,8 +53,8 @@ import java.util.Objects;
* A profile service that connects to the Battery service (BAS) of BLE devices
*/
public class BatteryService extends ProfileService {
- private static final boolean DBG = false;
private static final String TAG = "BatteryService";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
// Timeout for state machine thread join, to prevent potential ANR.
private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1_000;
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 1d489f7974..f540051a96 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -50,6 +50,7 @@ import android.sysprop.BluetoothProperties;
import android.util.Log;
import android.util.Pair;
+import com.android.bluetooth.BluetoothEventLogger;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
@@ -60,6 +61,7 @@ import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -107,6 +109,11 @@ public class BassClientService extends ProfileService {
private Callbacks mCallbacks;
private BroadcastReceiver mIntentReceiver;
+ private static final int LOG_NB_EVENTS = 100;
+ private static final BluetoothEventLogger sEventLogger =
+ new BluetoothEventLogger(LOG_NB_EVENTS, TAG + " event log");
+ ;
+
@VisibleForTesting
ServiceFactory mServiceFactory = new ServiceFactory();
@@ -299,7 +306,7 @@ public class BassClientService extends ProfileService {
}
}
};
- registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
+ registerReceiver(mIntentReceiver, filter, Context.RECEIVER_EXPORTED);
setBassClientService(this);
// Saving PSync stuff for future addition
@@ -645,6 +652,14 @@ public class BassClientService extends ProfileService {
return;
}
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "connectionStateChanged: fromState= "
+ + BluetoothProfile.getConnectionStateName(fromState)
+ + ", toState= "
+ + BluetoothProfile.getConnectionStateName(toState));
+
// Check if the device is disconnected - if unbond, remove the state machine
if (toState == BluetoothProfile.STATE_DISCONNECTED) {
mPendingGroupOp.remove(device);
@@ -946,6 +961,10 @@ public class BassClientService extends ProfileService {
int broadcastId = (int)(((broadcastIdArray[2] & 0xff) << 16)
| ((broadcastIdArray[1] & 0xff) << 8)
| (broadcastIdArray[0] & 0xff));
+
+ sEventLogger.logd(DBG, TAG, "Broadcast Source Found: Broadcast ID: "
+ + broadcastId);
+
if (mScanBroadcasts.get(broadcastId) == null) {
log("selectBroadcastSource: broadcastId " + broadcastId);
mScanBroadcasts.put(broadcastId, result);
@@ -981,6 +1000,7 @@ public class BassClientService extends ProfileService {
serviceData, serviceDataMask).build());
}
scanner.startScan(filters, settings, mSearchScanCallback);
+ sEventLogger.logd(DBG, TAG, "startSearchingForSources");
mCallbacks.notifySearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
}
}
@@ -1008,6 +1028,7 @@ public class BassClientService extends ProfileService {
}
scanner.stopScan(mSearchScanCallback);
mSearchScanCallback = null;
+ sEventLogger.logd(DBG, TAG, "stopSearchingForSources");
mCallbacks.notifySearchStopped(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
mScanBroadcasts.clear();
}
@@ -1030,6 +1051,8 @@ public class BassClientService extends ProfileService {
}
synchronized (mStateMachines) {
+ sEventLogger.logd(DBG, TAG, "Select Broadcast Source");
+
BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
Message message = stateMachine.obtainMessage(
BassClientStateMachine.SELECT_BCAST_SOURCE);
@@ -1112,10 +1135,22 @@ public class BassClientService extends ProfileService {
sourceMetadata);
}
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "Add Broadcast Source: device: "
+ + sink
+ + ", sourceMetadata: "
+ + sourceMetadata
+ + ", isGroupOp: "
+ + isGroupOp);
+
Message message = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE);
message.obj = sourceMetadata;
stateMachine.sendMessage(message);
if (code != null && code.length != 0) {
+ sEventLogger.logd(DBG, TAG, "Set Broadcast Code (Add Source context)");
+
message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
message.obj = sourceMetadata;
message.arg1 = BassClientStateMachine.ARGTYPE_METADATA;
@@ -1183,6 +1218,16 @@ public class BassClientService extends ProfileService {
throw new IllegalStateException("modifySource: source operation already pending");
}
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "Modify Broadcast Source: device: "
+ + sink
+ + ", sourceId: "
+ + sourceId
+ + ", updatedMetadata: "
+ + updatedMetadata);
+
Message message =
stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
message.arg1 = deviceSourceId;
@@ -1190,6 +1235,7 @@ public class BassClientService extends ProfileService {
message.obj = updatedMetadata;
stateMachine.sendMessage(message);
if (code != null && code.length != 0) {
+ sEventLogger.logd(DBG, TAG, "Set Broadcast Code (Modify Source context)");
message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
message.obj = updatedMetadata;
message.arg1 = BassClientStateMachine.ARGTYPE_METADATA;
@@ -1238,6 +1284,14 @@ public class BassClientService extends ProfileService {
stateMachine.getCurrentBroadcastMetadata(sourceId);
if (metaData != null && recvState != null && recvState.getPaSyncState()
== BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "Remove Broadcast Source(Force lost PA sync): device: "
+ + sink
+ + ", sourceId: "
+ + sourceId);
+
log("Force source to lost PA sync");
Message message = stateMachine.obtainMessage(
BassClientStateMachine.UPDATE_BCAST_SOURCE);
@@ -1250,6 +1304,11 @@ public class BassClientService extends ProfileService {
continue;
}
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "Remove Broadcast Source: device: " + sink + ", sourceId: " + sourceId);
+
Message message =
stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE);
message.arg1 = deviceSourceId;
@@ -1479,60 +1538,177 @@ public class BassClientService extends ProfileService {
}
void notifySearchStarted(int reason) {
+ sEventLogger.logd(DBG, TAG, "notifySearchStarted: " + ", reason: " + reason);
obtainMessage(MSG_SEARCH_STARTED, reason, 0).sendToTarget();
}
void notifySearchStartFailed(int reason) {
+ sEventLogger.loge(TAG, "notifySearchStartFailed: " + ", reason: " + reason);
obtainMessage(MSG_SEARCH_STARTED_FAILED, reason, 0).sendToTarget();
}
void notifySearchStopped(int reason) {
+ sEventLogger.logd(DBG, TAG, "notifySearchStopped: " + ", reason: " + reason);
obtainMessage(MSG_SEARCH_STOPPED, reason, 0).sendToTarget();
}
void notifySearchStopFailed(int reason) {
+ sEventLogger.loge(TAG, "notifySearchStopFailed: " + ", reason: " + reason);
obtainMessage(MSG_SEARCH_STOPPED_FAILED, reason, 0).sendToTarget();
}
void notifySourceFound(BluetoothLeBroadcastMetadata source) {
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "invokeCallback: MSG_SOURCE_FOUND"
+ + ", source: "
+ + source.getSourceDevice()
+ + ", broadcastId: "
+ + source.getBroadcastId()
+ + ", broadcastName: "
+ + source.getBroadcastName()
+ + ", isPublic: "
+ + source.isPublicBroadcast()
+ + ", isEncrypted: "
+ + source.isEncrypted());
obtainMessage(MSG_SOURCE_FOUND, 0, 0, source).sendToTarget();
}
void notifySourceAdded(BluetoothDevice sink, BluetoothLeBroadcastReceiveState recvState,
int reason) {
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "notifySourceAdded: "
+ + ", source: "
+ + sink
+ + ", sourceId: "
+ + recvState.getSourceId()
+ + ", reason: "
+ + reason);
+
ObjParams param = new ObjParams(sink, recvState);
obtainMessage(MSG_SOURCE_ADDED, reason, recvState.getSourceId(), param).sendToTarget();
}
void notifySourceAddFailed(BluetoothDevice sink, BluetoothLeBroadcastMetadata source,
int reason) {
+ sEventLogger.loge(
+ TAG, "notifySourceAddFailed: " + ", source: " + sink + ", reason: " + reason);
ObjParams param = new ObjParams(sink, source);
obtainMessage(MSG_SOURCE_ADDED_FAILED, reason, 0, param).sendToTarget();
}
void notifySourceModified(BluetoothDevice sink, int sourceId, int reason) {
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "notifySourceModified: "
+ + ", source: "
+ + sink
+ + ", sourceId: "
+ + sourceId
+ + ", reason: "
+ + reason);
obtainMessage(MSG_SOURCE_MODIFIED, reason, sourceId, sink).sendToTarget();
}
void notifySourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) {
+ sEventLogger.loge(
+ TAG,
+ "notifySourceModifyFailed: " + ", source: " + sink + ", reason: " + reason);
obtainMessage(MSG_SOURCE_MODIFIED_FAILED, reason, sourceId, sink).sendToTarget();
}
void notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
+ sEventLogger.logd(
+ DBG,
+ TAG,
+ "notifySourceRemoved: "
+ + ", source: "
+ + sink
+ + ", sourceId: "
+ + sourceId
+ + ", reason: "
+ + reason);
obtainMessage(MSG_SOURCE_REMOVED, reason, sourceId, sink).sendToTarget();
}
void notifySourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
+ sEventLogger.loge(
+ TAG,
+ "notifySourceRemoveFailed: "
+ + ", source: "
+ + sink
+ + ", sourceId: "
+ + sourceId
+ + ", reason: "
+ + reason);
obtainMessage(MSG_SOURCE_REMOVED_FAILED, reason, sourceId, sink).sendToTarget();
}
void notifyReceiveStateChanged(BluetoothDevice sink, int sourceId,
BluetoothLeBroadcastReceiveState state) {
ObjParams param = new ObjParams(sink, state);
+ String subgroupState = " / SUB GROUPS: ";
+ for (int i = 0; i < state.getNumSubgroups(); i++) {
+ subgroupState += "IDX: " + i + ", SYNC: " + state.getBisSyncState().get(i);
+ }
+
+ sEventLogger.logd(
+ TAG,
+ "notifyReceiveStateChanged: "
+ + ", source: "
+ + sink
+ + ", state: SRC ID: "
+ + state.getSourceId()
+ + " / ADDR TYPE: "
+ + state.getSourceAddressType()
+ + " / SRC DEV: "
+ + state.getSourceDevice()
+ + " / ADV SID: "
+ + state.getSourceAdvertisingSid()
+ + " / BID: "
+ + state.getBroadcastId()
+ + " / PA STATE: "
+ + state.getPaSyncState()
+ + " / BENC STATE: "
+ + state.getBigEncryptionState()
+ + " / BAD CODE: "
+ + Arrays.toString(state.getBadCode())
+ + subgroupState);
obtainMessage(MSG_RECEIVESTATE_CHANGED, 0, sourceId, param).sendToTarget();
}
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+
+ sb.append("Broadcast Assistant Service instance:\n");
+
+ /* Dump first connected state machines */
+ for (Map.Entry<BluetoothDevice, BassClientStateMachine> entry : mStateMachines.entrySet()) {
+ BassClientStateMachine sm = entry.getValue();
+ if (sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
+ sm.dump(sb);
+ }
+ }
+
+ /* Dump at least all other than connected state machines */
+ for (Map.Entry<BluetoothDevice, BassClientStateMachine> entry : mStateMachines.entrySet()) {
+ BassClientStateMachine sm = entry.getValue();
+ if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+ sm.dump(sb);
+ }
+ }
+
+ sb.append("\n");
+ sEventLogger.dump(sb);
+ sb.append("\n");
+ }
+
/** Binder object: must be a static class or memory leak may occur */
@VisibleForTesting
static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub
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 2bb0923ac4..d625d92f8c 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -457,6 +457,14 @@ public class BassClientStateMachine extends StateMachine {
mNoStopScanOffload = false;
}
+ private void resetBluetoothGatt() {
+ // cleanup mBluetoothGatt
+ if (mBluetoothGatt != null) {
+ mBluetoothGatt.close();
+ mBluetoothGatt = null;
+ }
+ }
+
private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData(BaseData baseData,
BluetoothDevice device) {
return getBroadcastMetadataFromBaseData(baseData, device, false);
@@ -1261,6 +1269,7 @@ public class BassClientStateMachine extends StateMachine {
transitionTo(mConnected);
} else {
Log.w(TAG, "Connection failed to " + mDevice);
+ resetBluetoothGatt();
transitionTo(mDisconnected);
}
break;
@@ -1271,6 +1280,7 @@ public class BassClientStateMachine extends StateMachine {
Log.e(TAG, "Unknown device timeout " + device);
break;
}
+ resetBluetoothGatt();
transitionTo(mDisconnected);
break;
case PSYNC_ACTIVE_TIMEOUT:
@@ -1370,24 +1380,7 @@ public class BassClientStateMachine extends StateMachine {
log("no existing SI for update source op");
return null;
}
- BluetoothDevice broadcastSource = metaData.getSourceDevice();
- PeriodicAdvertisementResult paRes =
- mService.getPeriodicAdvertisementResult(broadcastSource);
- if (paRes == null) {
- Log.e(TAG, "No matching psync, scan res for update");
- mService.getCallbacks().notifySourceRemoveFailed(
- mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
- return null;
- }
- // populate metadata from BASE levelOne
- BaseData base = mService.getBase(paRes.getSyncHandle());
- if (base == null) {
- Log.e(TAG, "No valid base data populated for this device");
- mService.getCallbacks().notifySourceRemoveFailed(
- mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
- return null;
- }
- byte numSubGroups = base.getNumberOfSubgroupsofBIG();
+ byte numSubGroups = (byte) metaData.getSubgroups().size();
byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
int offset = 0;
// Opcode
@@ -1544,11 +1537,7 @@ public class BassClientStateMachine extends StateMachine {
Log.w(TAG, "device is already connected to Bass" + mDevice);
} else {
Log.w(TAG, "unexpected disconnected from " + mDevice);
- // cleanup mBluetoothGatt
- if (mBluetoothGatt != null) {
- mBluetoothGatt.close();
- mBluetoothGatt = null;
- }
+ resetBluetoothGatt();
cancelActiveSync(null);
transitionTo(mDisconnected);
}
@@ -1829,6 +1818,8 @@ public class BassClientStateMachine extends StateMachine {
Log.w(TAG, "should never happen from this state");
} else {
Log.w(TAG, "Unexpected disconnection " + mDevice);
+ resetBluetoothGatt();
+ cancelActiveSync(null);
transitionTo(mDisconnected);
}
break;
@@ -1980,6 +1971,11 @@ public class BassClientStateMachine extends StateMachine {
ProfileService.println(sb, " " + line);
}
scanner.close();
+ for (Map.Entry<Integer, BluetoothLeBroadcastReceiveState> entry :
+ mBluetoothLeBroadcastReceiveStates.entrySet()) {
+ BluetoothLeBroadcastReceiveState state = entry.getValue();
+ sb.append(state);
+ }
}
@Override
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index a871bdd229..73530ed150 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -23,9 +23,7 @@ import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.content.BroadcastReceiver;
@@ -69,13 +67,9 @@ import java.util.Set;
* devices is more than one, the rules below will apply.
* 2) The selected A2DP active device is the one used for AVRCP as well.
* 3) The HFP active device might be different from the A2DP active device.
- * 4) The Active Device Manager always listens for ACTION_ACTIVE_DEVICE_CHANGED
- * broadcasts for each profile:
- * - BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED for HearingAid
- * - BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED for LE audio
- * If such broadcast is received (e.g., triggered indirectly by user
- * action on the UI), the device in the received broadcast is marked
- * as the current active device for that profile.
+ * 4) The Active Device Manager always listens for the change of active devices.
+ * When it changed (e.g., triggered indirectly by user action on the UI),
+ * the new active device is marked as the current active device for that profile.
* 5) If there is a HearingAid active device, then A2DP, HFP and LE audio active devices
* must be set to null (i.e., A2DP, HFP and LE audio cannot have active devices).
* The reason is that A2DP, HFP or LE audio cannot be used together with HearingAid.
@@ -90,11 +84,8 @@ import java.util.Set;
* 7) If the currently active device (per profile) is disconnected, the
* Active Device Manager just marks that the profile has no active device,
* and the lastly activated BT device that is still connected would be selected.
- * 8) If there is already an active device, and the corresponding
- * ACTION_ACTIVE_DEVICE_CHANGED broadcast is received, the device
- * contained in the broadcast is marked as active. However, if
- * the contained device is null, the corresponding profile is marked
- * as having no active device.
+ * 8) If there is already an active device, however, if active device change notified
+ * with a null device, the corresponding profile is marked as having no active device.
* 9) If a wired audio device is connected, the audio output is switched
* by the Audio Framework itself to that device. We detect this here,
* and the active device for each profile (A2DP/HFP/HearingAid/LE audio) is set
@@ -153,7 +144,6 @@ public class ActiveDeviceManager {
private BluetoothDevice mClassicDeviceToBeActivated = null;
private BluetoothDevice mClassicDeviceNotToBeActivated = null;
- // Broadcast receiver for all changes
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -170,55 +160,12 @@ public class ActiveDeviceManager {
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int currentState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
mHandler.post(() -> handleAdapterStateChanged(currentState));
- return;
- }
-
- final BluetoothDevice device = intent.getParcelableExtra(
- BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
- final int previousState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
- final int currentState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-
- if (currentState != -1 && previousState == currentState) {
- return;
- }
-
- switch (action) {
- case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
- if (currentState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleHearingAidConnected(device));
- } else if (previousState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleHearingAidDisconnected(device));
- }
- break;
- case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
- if (currentState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleLeAudioConnected(device));
- } else if (previousState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleLeAudioDisconnected(device));
- }
- break;
- case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED:
- if (currentState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleHapConnected(device));
- } else if (previousState == BluetoothProfile.STATE_CONNECTED) {
- mHandler.post(() -> handleHapDisconnected(device));
- }
- break;
- case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
- mHandler.post(() -> handleHearingAidActiveDeviceChanged(device));
- break;
- case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
- mHandler.post(() -> handleLeAudioActiveDeviceChanged(device));
- break;
- default:
- Log.e(TAG, "Received unexpected intent, action=" + action);
- break;
}
}
};
/**
- * Called when A2DP connection state changed by A2dpStateMachine
+ * Called when A2DP connection state changed by A2dpService
*
* @param device The device of which connection state was changed
* @param fromState The previous connection state of the device
@@ -242,7 +189,7 @@ public class ActiveDeviceManager {
}
/**
- * Called when HFP connection state changed by HeadsetStateMachine
+ * Called when HFP connection state changed by HeadsetService
*
* @param device The device of which connection state was changed
* @param prevState The previous connection state of the device
@@ -257,14 +204,78 @@ public class ActiveDeviceManager {
}
/**
- * Called when HFP active state changed by HeadsetStateMachine
+ * Called when HFP active state changed by HeadsetService
*
- * @param device The device currently activated. {@code null} if no A2DP device activated
+ * @param device The device currently activated. {@code null} if no HFP device activated
*/
public void hfpActiveStateChanged(BluetoothDevice device) {
mHandler.post(() -> handleHfpActiveDeviceChanged(device));
}
+ /**
+ * Called when LE audio connection state changed by LeAudioService
+ *
+ * @param device The device of which connection state was changed
+ * @param prevState The previous connection state of the device
+ * @param newState The new connection state of the device
+ */
+ public void leAudioConnectionStateChanged(BluetoothDevice device, int prevState, int newState) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleLeAudioConnected(device));
+ } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleLeAudioDisconnected(device));
+ }
+ }
+
+ /**
+ * Called when LE audio active state changed by LeAudioService
+ *
+ * @param device The device currently activated. {@code null} if no LE audio device activated
+ */
+ public void leAudioActiveStateChanged(BluetoothDevice device) {
+ mHandler.post(() -> handleLeAudioActiveDeviceChanged(device));
+ }
+
+ /**
+ * Called when HearingAid connection state changed by HearingAidService
+ *
+ * @param device The device of which connection state was changed
+ * @param prevState The previous connection state of the device
+ * @param newState The new connection state of the device
+ */
+ public void hearingAidConnectionStateChanged(
+ BluetoothDevice device, int prevState, int newState) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleHearingAidConnected(device));
+ } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleHearingAidDisconnected(device));
+ }
+ }
+
+ /**
+ * Called when HearingAid active state changed by HearingAidService
+ *
+ * @param device The device currently activated. {@code null} if no HearingAid device activated
+ */
+ public void hearingAidActiveStateChanged(BluetoothDevice device) {
+ mHandler.post(() -> handleHearingAidActiveDeviceChanged(device));
+ }
+
+ /**
+ * Called when HAP connection state changed by HapClientService
+ *
+ * @param device The device of which connection state was changed
+ * @param prevState The previous connection state of the device
+ * @param newState The new connection state of the device
+ */
+ public void hapConnectionStateChanged(BluetoothDevice device, int prevState, int newState) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleHapConnected(device));
+ } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
+ mHandler.post(() -> handleHapDisconnected(device));
+ }
+ }
+
private void handleAdapterStateChanged(int currentState) {
if (DBG) {
Log.d(TAG, "handleAdapterStateChanged: currentState=" + currentState);
@@ -827,11 +838,6 @@ public class ActiveDeviceManager {
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
- filter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
- filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
- filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
mAdapterService.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 53382e3ff6..f84cc69ff4 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -79,11 +79,9 @@ import android.bluetooth.UidTraffic;
import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
@@ -122,6 +120,7 @@ import com.android.bluetooth.bass_client.BassClientService;
import com.android.bluetooth.btservice.InteropUtil.InteropFeature;
import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService;
+import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreNativeInterface;
import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.btservice.storage.MetadataDatabase;
@@ -382,7 +381,7 @@ public class AdapterService extends Service {
private BassClientService mBassClientService;
private BatteryService mBatteryService;
private BluetoothQualityReportNativeInterface mBluetoothQualityReportNativeInterface;
- private IBluetoothGatt mBluetoothGatt;
+ private GattService mGattService;
private volatile boolean mTestModeEnabled = false;
@@ -436,8 +435,6 @@ public class AdapterService extends Service {
private static final int MESSAGE_PROFILE_SERVICE_REGISTERED = 2;
private static final int MESSAGE_PROFILE_SERVICE_UNREGISTERED = 3;
private static final int MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT = 4;
- private static final int MESSAGE_ON_PROFILE_SERVICE_BIND = 5;
- private static final int MESSAGE_ON_PROFILE_SERVICE_UNBIND = 6;
class AdapterServiceHandler extends Handler {
AdapterServiceHandler(Looper looper) {
@@ -461,14 +458,6 @@ public class AdapterService extends Service {
verboseLog("handleMessage() - MESSAGE_PROFILE_SERVICE_UNREGISTERED");
unregisterProfileService((ProfileService) msg.obj);
break;
- case MESSAGE_ON_PROFILE_SERVICE_BIND:
- verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_BIND");
- onGattBind((IBinder) msg.obj);
- break;
- case MESSAGE_ON_PROFILE_SERVICE_UNBIND:
- verboseLog("handleMessage() - MESSAGE_ON_PROFILE_SERVICE_UNBIND");
- onGattUnbind();
- break;
case MESSAGE_PREFERRED_AUDIO_PROFILES_AUDIO_FRAMEWORK_TIMEOUT:
errorLog(
"handleMessage() - "
@@ -508,22 +497,6 @@ public class AdapterService extends Service {
mRegisteredProfiles.remove(profile);
}
- private void onGattBind(IBinder service) {
- mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
- try {
- mBluetoothGatt.startService();
- } catch (RemoteException e) {
- Log.e(TAG, "onGattBind: RemoteException", e);
- }
- }
-
- private void onGattUnbind() {
- mBluetoothGatt = null;
- Log.e(
- TAG,
- "onGattUnbind: Gatt service has disconnected from AdapterService unexpectedly");
- }
-
private void processProfileServiceStateChanged(ProfileService profile, int state) {
switch (state) {
case BluetoothAdapter.STATE_ON:
@@ -651,7 +624,9 @@ public class AdapterService extends Service {
mAdapterProperties = new AdapterProperties(this);
mAdapterStateMachine = new AdapterState(this, mLooper);
mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
- mBluetoothKeystoreService = new BluetoothKeystoreService(isCommonCriteriaMode());
+ mBluetoothKeystoreService =
+ new BluetoothKeystoreService(
+ new BluetoothKeystoreNativeInterface(), isCommonCriteriaMode());
mBluetoothKeystoreService.start();
int configCompareResult = mBluetoothKeystoreService.getCompareResult();
@@ -728,7 +703,6 @@ public class AdapterService extends Service {
mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this);
mActivityAttributionService = new ActivityAttributionService();
- mActivityAttributionService.start();
setAdapterService(this);
@@ -1015,47 +989,11 @@ public class AdapterService extends Service {
}
}
- class GattServiceConnection implements ServiceConnection {
- public void onServiceConnected(ComponentName componentName, IBinder service) {
- String name = componentName.getClassName();
- if (DBG) {
- Log.d(TAG, "GattServiceConnection.onServiceConnected: " + name);
- }
- if (!name.equals(GattService.class.getName())) {
- Log.e(TAG, "Unknown service connected: " + name);
- return;
- }
- mHandler.obtainMessage(MESSAGE_ON_PROFILE_SERVICE_BIND, service).sendToTarget();
- }
-
- public void onServiceDisconnected(ComponentName componentName) {
- // Called if we unexpectedly disconnect. This should never happen.
- String name = componentName.getClassName();
- Log.e(TAG, "GattServiceConnection.onServiceDisconnected: " + name);
- if (!name.equals(GattService.class.getName())) {
- Log.e(TAG, "Unknown service disconnected: " + name);
- return;
- }
- mHandler.sendEmptyMessage(MESSAGE_ON_PROFILE_SERVICE_UNBIND);
- }
- }
-
- private GattServiceConnection mGattConnection = new GattServiceConnection();
-
private void startGattProfileService() {
mStartedProfiles.add(GattService.class.getSimpleName());
- Intent intent = new Intent(this, GattService.class);
- if (!bindServiceAsUser(
- intent,
- mGattConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT)) {
- // This should never happen
- // unbindService will be called during stopGattProfileService triggered by AdapterState
- Log.e(TAG, "Error while binding to gatt. This Bluetooth session will timeout");
- unbindService(mGattConnection);
- }
+ mGattService = new GattService(this);
+ ((ProfileService) mGattService).doStart();
}
private void stopGattProfileService() {
@@ -1066,15 +1004,10 @@ public class AdapterService extends Service {
}
mStartedProfiles.remove(GattService.class.getSimpleName());
-
- try {
- if (mBluetoothGatt != null) {
- mBluetoothGatt.stopService();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "stopGattProfileService: RemoteException", e);
+ if (mGattService != null) {
+ ((ProfileService) mGattService).doStop();
+ mGattService = null;
}
- unbindService(mGattConnection);
}
private void invalidateBluetoothGetStateCache() {
@@ -1372,10 +1305,6 @@ public class AdapterService extends Service {
mSdpManager = null;
}
- if (mActivityAttributionService != null) {
- mActivityAttributionService.cleanup();
- }
-
if (mNativeAvailable) {
debugLog("cleanup() - Cleaning up adapter native");
cleanupNative();
@@ -5026,7 +4955,7 @@ public class AdapterService extends Service {
if (service == null) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
- if (!callerIsSystem(TAG, "setPreferredAudioProfiles")) {
+ if (!callerIsSystem(TAG, "notifyActiveDeviceChangeApplied")) {
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
}
requireNonNull(device);
@@ -5258,6 +5187,7 @@ public class AdapterService extends Service {
try {
AdapterService service = getService();
if (service != null) {
+ enforceBluetoothPrivilegedPermission(service);
service.unregAllGattClient(source);
}
receiver.send(null);
@@ -6089,7 +6019,10 @@ public class AdapterService extends Service {
if (leAudioSupported) {
Log.i(TAG, "setActiveDevice: Setting active Le Audio device " + device);
if (device == null) {
- mLeAudioService.removeActiveDevice(false);
+ /* If called by BluetoothAdapter it means Audio should not be stopped.
+ * For this reason let's say that fallback device exists
+ */
+ mLeAudioService.removeActiveDevice(true /* hasFallbackDevice */);
} else {
mLeAudioService.setActiveDevice(device);
}
@@ -6894,16 +6827,15 @@ public class AdapterService extends Service {
}
IBluetoothGatt getBluetoothGatt() {
- return mBluetoothGatt;
+ if (mGattService == null) {
+ return null;
+ }
+ return IBluetoothGatt.Stub.asInterface(((ProfileService) mGattService).getBinder());
}
void unregAllGattClient(AttributionSource source) {
- if (mBluetoothGatt != null) {
- try {
- mBluetoothGatt.unregAll(source);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to disconnect all apps.", e);
- }
+ if (mGattService != null) {
+ mGattService.unregAll(source);
}
}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterState.java b/android/app/src/com/android/bluetooth/btservice/AdapterState.java
index 87134e4673..ae395bf173 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterState.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterState.java
@@ -77,10 +77,14 @@ final class AdapterState extends StateMachine {
static final String BLE_STOP_TIMEOUT_DELAY_PROPERTY =
"ro.bluetooth.ble_stop_timeout_delay";
- static final int BLE_START_TIMEOUT_DELAY = 4000;
- static final int BLE_STOP_TIMEOUT_DELAY = 4000;
- static final int BREDR_START_TIMEOUT_DELAY = 4000;
- static final int BREDR_STOP_TIMEOUT_DELAY = 4000;
+ static final int BLE_START_TIMEOUT_DELAY =
+ 4000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+ static final int BLE_STOP_TIMEOUT_DELAY =
+ 4000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+ static final int BREDR_START_TIMEOUT_DELAY =
+ 4000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+ static final int BREDR_STOP_TIMEOUT_DELAY =
+ 4000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
private AdapterService mAdapterService;
private TurningOnState mTurningOnState = new TurningOnState();
diff --git a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
index a1af22f3c3..2bd4abb39b 100644
--- a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -250,6 +250,11 @@ final class BondStateMachine extends StateMachine {
}
break;
case SSP_REQUEST:
+ if (devProp == null) {
+ errorLog("devProp is null, maybe the device is disconnected");
+ break;
+ }
+
int passkey = msg.arg1;
int variant = msg.arg2;
boolean displayPasskey =
@@ -262,6 +267,11 @@ final class BondStateMachine extends StateMachine {
variant);
break;
case PIN_REQUEST:
+ if (devProp == null) {
+ errorLog("devProp is null, maybe the device is disconnected");
+ break;
+ }
+
BluetoothClass btClass = dev.getBluetoothClass();
int btDeviceClass = btClass.getDeviceClass();
if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index bc0ed8408c..caa1a0004a 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -56,6 +56,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
public class Config {
@@ -171,9 +172,14 @@ public class Config {
profile.mSupported = enabled;
}
}
+ if (enabled) {
+ sSupportedProfiles.add(profileClass);
+ } else {
+ sSupportedProfiles.remove(profileClass);
+ }
}
- private static Class[] sSupportedProfiles = new Class[0];
+ private static List<Class> sSupportedProfiles = new ArrayList<>();
private static boolean sIsGdEnabledUptoScanningLayer = false;
@@ -211,15 +217,20 @@ public class Config {
setProfileEnabled(HearingAidService.class, false);
}
- ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
- for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
- Log.i(TAG, "init: profile=" + config.mClass.getSimpleName() + ", enabled="
- + config.mSupported);
- if (config.mSupported) {
- profiles.add(config.mClass);
+ synchronized (sSupportedProfiles) {
+ sSupportedProfiles.clear();
+ for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
+ Log.i(
+ TAG,
+ "init: profile="
+ + config.mClass.getSimpleName()
+ + ", enabled="
+ + config.mSupported);
+ if (config.mSupported) {
+ sSupportedProfiles.add(config.mClass);
+ }
}
}
- sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);
if (ctx == null) {
return;
@@ -253,19 +264,17 @@ public class Config {
* Remove the input profiles from the supported list.
*/
static void removeProfileFromSupportedList(HashSet<Class> nonSupportedProfiles) {
- ArrayList<Class> profilesList = new ArrayList<Class>(Arrays.asList(sSupportedProfiles));
- Iterator<Class> iter = profilesList.iterator();
-
- while (iter.hasNext()) {
- Class profileClass = iter.next();
-
- if (nonSupportedProfiles.contains(profileClass)) {
- iter.remove();
- Log.v(TAG, "Remove " + profileClass.getSimpleName() + " from supported list.");
+ synchronized (sSupportedProfiles) {
+ Iterator<Class> iter = sSupportedProfiles.iterator();
+ while (iter.hasNext()) {
+ Class profileClass = iter.next();
+
+ if (nonSupportedProfiles.contains(profileClass)) {
+ iter.remove();
+ Log.v(TAG, "Remove " + profileClass.getSimpleName() + " from supported list.");
+ }
}
}
-
- sSupportedProfiles = profilesList.toArray(new Class[profilesList.size()]);
}
static void updateSupportedProfileMask(Boolean enable, Class profile, int supportedProfile) {
@@ -286,7 +295,9 @@ public class Config {
}
static Class[] getSupportedProfiles() {
- return sSupportedProfiles;
+ synchronized (sSupportedProfiles) {
+ return sSupportedProfiles.toArray(new Class[0]);
+ }
}
static boolean isGdEnabledUpToScanningLayer() {
diff --git a/android/app/src/com/android/bluetooth/btservice/ProfileService.java b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
index 5795847d1b..acd72c7c69 100644
--- a/android/app/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
@@ -18,6 +18,8 @@ package com.android.bluetooth.btservice;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.app.Service;
@@ -180,6 +182,11 @@ public abstract class ProfileService extends Service {
return mBinder;
}
+ IBinder getBinder() {
+ requireNonNull(mBinder, "Binder is null. onCreate need to be called first");
+ return mBinder;
+ }
+
@Override
// Suppressed since this is called from framework
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -295,7 +302,7 @@ public abstract class ProfileService extends Service {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS
})
- protected void doStart() {
+ void doStart() {
Log.v(mName, "doStart");
if (mAdapter == null) {
Log.w(mName, "Can't start profile service: device does not have BT");
@@ -321,7 +328,7 @@ public abstract class ProfileService extends Service {
mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);
}
- protected void doStop() {
+ void doStop() {
Log.v(mName, "doStop");
if (mAdapterService == null || mAdapterService.isStartedProfile(mName)) {
Log.w(mName, "Unexpectedly do Stop, don't stop.");
diff --git a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java
index b9cb9d778d..7755ce2d2c 100644
--- a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java
@@ -21,26 +21,15 @@
*/
package com.android.bluetooth.btservice.activityattribution;
-import android.util.Log;
-
import com.android.internal.annotations.GuardedBy;
-import java.util.Arrays;
-
/** ActivityAttribution Native Interface to/from JNI. */
public class ActivityAttributionNativeInterface {
- private static final boolean DBG = false;
- private static final String TAG = "ActivityAttributionNativeInterface";
-
@GuardedBy("INSTANCE_LOCK")
private static ActivityAttributionNativeInterface sInstance;
private static final Object INSTANCE_LOCK = new Object();
- static {
- classInitNative();
- }
-
/** Get singleton instance. */
public static ActivityAttributionNativeInterface getInstance() {
synchronized (INSTANCE_LOCK) {
@@ -51,40 +40,11 @@ public class ActivityAttributionNativeInterface {
}
}
- /** Initializes the native interface. */
- public void init() {
- initNative();
- }
-
- /** Cleanup the native interface. */
- public void cleanup() {
- cleanupNative();
- }
-
/** Notify the UID and package name of the app, and the address of associated active device */
public void notifyActivityAttributionInfo(int uid, String packageName, String deviceAddress) {
notifyActivityAttributionInfoNative(uid, packageName, deviceAddress);
}
- // Callbacks from the native stack back into the Java framework.
- // All callbacks are routed via the Service which will disambiguate which
- // state machine the message should be routed to.
-
- private void onWakeup(int activity, byte[] address) {
- Log.i(TAG, "onWakeup() BTAA: " + activity);
- }
-
- private void onActivityLogsReady(byte[] logs) {
- Log.i(TAG, "onActivityLogsReady() BTAA: " + Arrays.toString(logs));
- }
-
- // Native methods that call into the JNI interface
- private static native void classInitNative();
-
- private native void initNative();
-
- private native void cleanupNative();
-
private native void notifyActivityAttributionInfoNative(
- int uid, String packageName, String deviceAddress);
+ int uid, String packageName, String deviceAddress);
}
diff --git a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java
index 033459812a..5194a25313 100644
--- a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java
+++ b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java
@@ -16,108 +16,31 @@
package com.android.bluetooth.btservice.activityattribution;
-import android.util.Log;
+import static java.util.Objects.requireNonNull;
-import java.util.Objects;
+import android.util.Log;
/**
* Service used for attributes wakeup, wakelock and Bluetooth traffic into per-app and per-device
* based activities.
*/
public class ActivityAttributionService {
- private boolean mCleaningUp;
- private static ActivityAttributionService sActivityAttributionService;
- private static final boolean DBG = false;
- private static final String TAG = "ActivityAttributionService";
-
- ActivityAttributionNativeInterface mActivityAttributionNativeInterface;
-
- /** Start and initialize the Activity Attribution service. */
- public void start() {
- debugLog("start()");
-
- if (sActivityAttributionService != null) {
- Log.e(TAG, "start() called twice");
- return;
- }
-
- mActivityAttributionNativeInterface =
- Objects.requireNonNull(
- ActivityAttributionNativeInterface.getInstance(),
- "ActivityAttributionNativeInterface "
- + "cannot be null when ActivityAttributionService starts");
-
- // Mark service as started
- setActivityAttributionService(this);
- }
-
- /** Cleans up the Activity Attribution service. */
- public void cleanup() {
- debugLog("cleanup");
- if (mCleaningUp) {
- debugLog("already doing cleanup");
- return;
- }
-
- mCleaningUp = true;
-
- if (sActivityAttributionService == null) {
- debugLog("cleanup() called before start()");
- return;
- }
-
- // Mark service as stopped
- setActivityAttributionService(null);
+ private static final String TAG = ActivityAttributionService.class.getSimpleName();
- // Cleanup native interface
- mActivityAttributionNativeInterface.cleanup();
- mActivityAttributionNativeInterface = null;
- }
-
- /** Get the ActivityAttributionService instance */
- public static synchronized ActivityAttributionService getActivityAttributionService() {
- if (sActivityAttributionService == null) {
- Log.w(TAG, "getActivityAttributionService(): service is NULL");
- return null;
- }
-
- if (!sActivityAttributionService.isAvailable()) {
- Log.w(TAG, "getActivityAttributionService(): service is not available");
- return null;
- }
- return sActivityAttributionService;
- }
-
- /** Init JNI */
- public void initJni() {
- debugLog("initJni()");
- // Initialize native interface
- mActivityAttributionNativeInterface.init();
- }
+ private final ActivityAttributionNativeInterface mActivityAttributionNativeInterface =
+ requireNonNull(
+ ActivityAttributionNativeInterface.getInstance(),
+ "ActivityAttributionNativeInterface cannot be null");
/** Notify the UID and package name of the app, and the address of associated active device */
public void notifyActivityAttributionInfo(int uid, String packageName, String deviceAddress) {
- Log.d(TAG, "notifyActivityAttributionInfo"
- + " UID=" + uid
- + " packageName=" + packageName
- + " deviceAddress=" + deviceAddress);
+ Log.d(
+ TAG,
+ "notifyActivityAttributionInfo()"
+ + (" UID=" + uid)
+ + (" packageName=" + packageName)
+ + (" deviceAddress=" + deviceAddress));
mActivityAttributionNativeInterface.notifyActivityAttributionInfo(
uid, packageName, deviceAddress);
}
-
- private boolean isAvailable() {
- return !mCleaningUp;
- }
-
- private static synchronized void setActivityAttributionService(
- ActivityAttributionService instance) {
- debugLog("setActivityAttributionService(): set to: " + instance);
- sActivityAttributionService = instance;
- }
-
- private static void debugLog(String msg) {
- if (DBG) {
- Log.d(TAG, msg);
- }
- }
}
diff --git a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
index ebee407821..0b616accaf 100644
--- a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
@@ -18,44 +18,27 @@ package com.android.bluetooth.btservice.bluetoothkeystore;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
-final class BluetoothKeystoreNativeInterface {
+/** Native interface to be used by BluetoothKeystoreService */
+public class BluetoothKeystoreNativeInterface {
+ private static final String TAG = BluetoothKeystoreNativeInterface.class.getSimpleName();
- private static final String TAG = "BluetoothKeystoreNativeInterface";
-
- @GuardedBy("INSTANCE_LOCK")
- private static BluetoothKeystoreNativeInterface sInstance;
- private static final Object INSTANCE_LOCK = new Object();
+ private BluetoothKeystoreService mBluetoothKeystoreService = null;
static {
classInitNative();
}
- private BluetoothKeystoreNativeInterface() {
- }
-
- /**
- * Get singleton instance.
- */
- public static BluetoothKeystoreNativeInterface getInstance() {
- synchronized (INSTANCE_LOCK) {
- if (sInstance == null) {
- sInstance = new BluetoothKeystoreNativeInterface();
- }
- return sInstance;
- }
- }
-
/**
* Initializes the native interface.
*
- * priorities to configure.
+ * <p>priorities to configure.
*/
- public void init() {
+ public void init(BluetoothKeystoreService service) {
+ mBluetoothKeystoreService = service;
initNative();
}
@@ -64,6 +47,7 @@ final class BluetoothKeystoreNativeInterface {
*/
public void cleanup() {
cleanupNative();
+ mBluetoothKeystoreService = null;
}
// Callbacks from the native stack back into the Java framework.
@@ -71,30 +55,36 @@ final class BluetoothKeystoreNativeInterface {
// state machine the message should be routed to.
private void setEncryptKeyOrRemoveKeyCallback(String prefixString, String decryptedString) {
- BluetoothKeystoreService service = BluetoothKeystoreService.getBluetoothKeystoreService();
- if (service != null) {
- try {
- service.setEncryptKeyOrRemoveKey(prefixString, decryptedString);
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while operating.");
- } catch (IOException e) {
- Log.e(TAG, "IO error while file operating.");
- } catch (NoSuchAlgorithmException e) {
- Log.e(TAG, "encrypt could not find the algorithm: SHA256");
- }
- } else {
- Log.e(TAG, "Event ignored, service not available: " + prefixString);
+ final BluetoothKeystoreService service = mBluetoothKeystoreService;
+
+ if (service == null) {
+ Log.e(
+ TAG,
+ "setEncryptKeyOrRemoveKeyCallback: Event ignored, service not available: "
+ + prefixString);
+ return;
+ }
+
+ try {
+ service.setEncryptKeyOrRemoveKey(prefixString, decryptedString);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while operating.");
+ } catch (IOException e) {
+ Log.e(TAG, "IO error while file operating.");
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(TAG, "encrypt could not find the algorithm: SHA256");
}
}
private String getKeyCallback(String prefixString) {
- BluetoothKeystoreService service = BluetoothKeystoreService.getBluetoothKeystoreService();
- if (service != null) {
- return service.getKey(prefixString);
- } else {
- Log.e(TAG, "Event ignored, service not available: " + prefixString);
+ final BluetoothKeystoreService service = mBluetoothKeystoreService;
+
+ if (service == null) {
+ Log.e(TAG, "getKeyCallback: Event ignored, service not available: " + prefixString);
return null;
}
+
+ return service.getKey(prefixString);
}
// Native methods that call into the JNI interface
diff --git a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java
index 25b06c3775..743534420b 100644
--- a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java
+++ b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreService.java
@@ -46,7 +46,6 @@ import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -99,7 +98,7 @@ public class BluetoothKeystoreService {
private static final int CONFIG_BACKUP_COMPARE_PASS = 0b10;
private int mCompareResult;
- BluetoothKeystoreNativeInterface mBluetoothKeystoreNativeInterface;
+ private final BluetoothKeystoreNativeInterface mBluetoothKeystoreNativeInterface;
private ComputeDataThread mEncryptDataThread;
private ComputeDataThread mDecryptDataThread;
@@ -113,8 +112,10 @@ public class BluetoothKeystoreService {
private Base64.Decoder mDecoder = Base64.getDecoder();
private Base64.Encoder mEncoder = Base64.getEncoder();
- public BluetoothKeystoreService(boolean isCommonCriteriaMode) {
+ public BluetoothKeystoreService(
+ BluetoothKeystoreNativeInterface nativeInterface, boolean isCommonCriteriaMode) {
debugLog("new BluetoothKeystoreService isCommonCriteriaMode: " + isCommonCriteriaMode);
+ mBluetoothKeystoreNativeInterface = nativeInterface;
mIsCommonCriteriaMode = isCommonCriteriaMode;
mCompareResult = CONFIG_COMPARE_INIT;
startThread();
@@ -140,13 +141,6 @@ public class BluetoothKeystoreService {
return;
}
- mBluetoothKeystoreNativeInterface = Objects.requireNonNull(
- BluetoothKeystoreNativeInterface.getInstance(),
- "BluetoothKeystoreNativeInterface cannot be null when BluetoothKeystore starts");
-
- // Mark service as started
- setBluetoothKeystoreService(this);
-
try {
if (!keyStore.containsAlias(KEYALIAS) && mIsCommonCriteriaMode) {
infoLog("Enable Common Criteria mode for the first time, pass hash check.");
@@ -187,12 +181,9 @@ public class BluetoothKeystoreService {
debugLog("cleanup() called before start()");
return;
}
- // Mark service as stopped
- setBluetoothKeystoreService(null);
// Cleanup native interface
mBluetoothKeystoreNativeInterface.cleanup();
- mBluetoothKeystoreNativeInterface = null;
if (mIsCommonCriteriaMode) {
cleanupForCommonCriteriaModeEnable();
@@ -296,9 +287,7 @@ public class BluetoothKeystoreService {
stopThread();
startThread();
// Initialize native interface
- if (mBluetoothKeystoreNativeInterface != null) {
- mBluetoothKeystoreNativeInterface.init();
- }
+ mBluetoothKeystoreNativeInterface.init(this);
}
private boolean isAvailable() {
@@ -306,28 +295,6 @@ public class BluetoothKeystoreService {
}
/**
- * Get the BluetoothKeystoreService instance
- */
- public static synchronized BluetoothKeystoreService getBluetoothKeystoreService() {
- if (sBluetoothKeystoreService == null) {
- debugLog("getBluetoothKeystoreService(): service is NULL");
- return null;
- }
-
- if (!sBluetoothKeystoreService.isAvailable()) {
- debugLog("getBluetoothKeystoreService(): service is not available");
- return null;
- }
- return sBluetoothKeystoreService;
- }
-
- private static synchronized void setBluetoothKeystoreService(
- BluetoothKeystoreService instance) {
- debugLog("setBluetoothKeystoreService(): set to: " + instance);
- sBluetoothKeystoreService = instance;
- }
-
- /**
* Gets result of the checksum comparison
*/
public int getCompareResult() {
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index c2dbaaffce..2809b08d77 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -847,7 +847,7 @@ public class CsipSetCoordinatorService extends ProfileService {
intent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- sendBroadcast(intent, BLUETOOTH_PRIVILEGED);
+ sendOrderedBroadcast(intent, BLUETOOTH_PRIVILEGED);
/* Notify registered parties */
handleSetMemberAvailable(device, groupId);
@@ -890,7 +890,7 @@ public class CsipSetCoordinatorService extends ProfileService {
if (intent != null) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- sendBroadcast(intent, BLUETOOTH_PRIVILEGED);
+ sendOrderedBroadcast(intent, BLUETOOTH_PRIVILEGED);
}
synchronized (mStateMachines) {
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index e105abff5b..a632ef9210 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -51,20 +51,23 @@ public class AdvertiseManager {
private final GattService mService;
private final AdapterService mAdapterService;
+ private final AdvertiseManagerNativeInterface mNativeInterface;
private final AdvertiserMap mAdvertiserMap;
private Handler mHandler;
Map<IBinder, AdvertiserInfo> mAdvertisers = Collections.synchronizedMap(new HashMap<>());
static int sTempRegistrationId = -1;
- /**
- * Constructor of {@link AdvertiseManager}.
- */
- AdvertiseManager(GattService service, AdapterService adapterService,
+ /** Constructor of {@link AdvertiseManager}. */
+ AdvertiseManager(
+ GattService service,
+ AdvertiseManagerNativeInterface nativeInterface,
+ AdapterService adapterService,
AdvertiserMap advertiserMap) {
if (DBG) {
Log.d(TAG, "advertise manager created");
}
mService = service;
+ mNativeInterface = nativeInterface;
mAdapterService = adapterService;
mAdvertiserMap = advertiserMap;
}
@@ -73,7 +76,7 @@ public class AdvertiseManager {
* Start a {@link HandlerThread} that handles advertising operations.
*/
void start() {
- initializeNative();
+ mNativeInterface.init(this);
HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
thread.start();
mHandler = new Handler(thread.getLooper());
@@ -83,7 +86,7 @@ public class AdvertiseManager {
if (DBG) {
Log.d(TAG, "cleanup()");
}
- cleanupNative();
+ mNativeInterface.cleanup();
mAdvertisers.clear();
sTempRegistrationId = -1;
@@ -157,7 +160,7 @@ public class AdvertiseManager {
if (entry == null) {
Log.i(TAG, "onAdvertisingSetStarted() - no callback found for regId " + regId);
// Advertising set was stopped before it was properly registered.
- stopAdvertisingSetNative(advertiserId);
+ mNativeInterface.stopAdvertisingSet(advertiserId);
return;
}
@@ -252,8 +255,15 @@ public class AdvertiseManager {
mAdvertiserMap.recordAdvertiseStart(cbId, parameters, advertiseData,
scanResponse, periodicParameters, periodicData, duration, maxExtAdvEvents);
- startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes,
- periodicParameters, periodicDataBytes, duration, maxExtAdvEvents, cbId,
+ mNativeInterface.startAdvertisingSet(
+ parameters,
+ advDataBytes,
+ scanResponseBytes,
+ periodicParameters,
+ periodicDataBytes,
+ duration,
+ maxExtAdvEvents,
+ cbId,
serverIf);
} catch (IllegalArgumentException e) {
@@ -289,7 +299,7 @@ public class AdvertiseManager {
Log.w(TAG, "getOwnAddress() - bad advertiserId " + advertiserId);
return;
}
- getOwnAddressNative(advertiserId);
+ mNativeInterface.getOwnAddress(advertiserId);
}
void stopAdvertisingSet(IAdvertisingSetCallback callback) {
@@ -313,7 +323,7 @@ public class AdvertiseManager {
return;
}
- stopAdvertisingSetNative(advertiserId);
+ mNativeInterface.stopAdvertisingSet(advertiserId);
try {
callback.onAdvertisingSetStopped(advertiserId);
@@ -330,7 +340,7 @@ public class AdvertiseManager {
Log.w(TAG, "enableAdvertisingSet() - bad advertiserId " + advertiserId);
return;
}
- enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);
+ mNativeInterface.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents);
mAdvertiserMap.enableAdvertisingSet(advertiserId,
enable, duration, maxExtAdvEvents);
@@ -344,8 +354,8 @@ public class AdvertiseManager {
}
String deviceName = AdapterService.getAdapterService().getName();
try {
- setAdvertisingDataNative(advertiserId,
- AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+ mNativeInterface.setAdvertisingData(
+ advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
mAdvertiserMap.setAdvertisingData(advertiserId, data);
} catch (IllegalArgumentException e) {
@@ -366,8 +376,8 @@ public class AdvertiseManager {
}
String deviceName = AdapterService.getAdapterService().getName();
try {
- setScanResponseDataNative(advertiserId,
- AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+ mNativeInterface.setScanResponseData(
+ advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
mAdvertiserMap.setScanResponseData(advertiserId, data);
} catch (IllegalArgumentException e) {
@@ -386,7 +396,7 @@ public class AdvertiseManager {
Log.w(TAG, "setAdvertisingParameters() - bad advertiserId " + advertiserId);
return;
}
- setAdvertisingParametersNative(advertiserId, parameters);
+ mNativeInterface.setAdvertisingParameters(advertiserId, parameters);
mAdvertiserMap.setAdvertisingParameters(advertiserId, parameters);
}
@@ -398,7 +408,7 @@ public class AdvertiseManager {
Log.w(TAG, "setPeriodicAdvertisingParameters() - bad advertiserId " + advertiserId);
return;
}
- setPeriodicAdvertisingParametersNative(advertiserId, parameters);
+ mNativeInterface.setPeriodicAdvertisingParameters(advertiserId, parameters);
mAdvertiserMap.setPeriodicAdvertisingParameters(advertiserId, parameters);
}
@@ -411,8 +421,8 @@ public class AdvertiseManager {
}
String deviceName = AdapterService.getAdapterService().getName();
try {
- setPeriodicAdvertisingDataNative(advertiserId,
- AdvertiseHelper.advertiseDataToBytes(data, deviceName));
+ mNativeInterface.setPeriodicAdvertisingData(
+ advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName));
mAdvertiserMap.setPeriodicAdvertisingData(advertiserId, data);
} catch (IllegalArgumentException e) {
@@ -431,7 +441,7 @@ public class AdvertiseManager {
Log.w(TAG, "setPeriodicAdvertisingEnable() - bad advertiserId " + advertiserId);
return;
}
- setPeriodicAdvertisingEnableNative(advertiserId, enable);
+ mNativeInterface.setPeriodicAdvertisingEnable(advertiserId, enable);
}
void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
@@ -538,40 +548,4 @@ public class AdvertiseManager {
stats.onPeriodicAdvertiseEnabled(enable);
}
}
-
- static {
- classInitNative();
- }
-
- private static native void classInitNative();
-
- private native void initializeNative();
-
- private native void cleanupNative();
-
- private native void startAdvertisingSetNative(AdvertisingSetParameters parameters,
- byte[] advertiseData, byte[] scanResponse,
- PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration,
- int maxExtAdvEvents, int regId, int serverIf);
-
- private native void getOwnAddressNative(int advertiserId);
-
- private native void stopAdvertisingSetNative(int advertiserId);
-
- private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration,
- int maxExtAdvEvents);
-
- private native void setAdvertisingDataNative(int advertiserId, byte[] data);
-
- private native void setScanResponseDataNative(int advertiserId, byte[] data);
-
- private native void setAdvertisingParametersNative(int advertiserId,
- AdvertisingSetParameters parameters);
-
- private native void setPeriodicAdvertisingParametersNative(int advertiserId,
- PeriodicAdvertisingParameters parameters);
-
- private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data);
-
- private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable);
}
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
new file mode 100644
index 0000000000..7529bdb86d
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt;
+
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+
+import androidx.annotation.VisibleForTesting;
+
+/** Native interface for AdvertiseManager */
+@VisibleForTesting
+public class AdvertiseManagerNativeInterface {
+ AdvertiseManager mManager;
+
+ void init(AdvertiseManager manager) {
+ mManager = manager;
+ initializeNative();
+ }
+
+ void cleanup() {
+ cleanupNative();
+ mManager = null;
+ }
+
+ void startAdvertisingSet(
+ AdvertisingSetParameters parameters,
+ byte[] advertiseDataBytes,
+ byte[] scanResponseBytes,
+ PeriodicAdvertisingParameters periodicParameters,
+ byte[] periodicDataBytes,
+ int duration,
+ int maxExtAdvEvents,
+ int cbId,
+ int serverIf) {
+ startAdvertisingSetNative(
+ parameters,
+ advertiseDataBytes,
+ scanResponseBytes,
+ periodicParameters,
+ periodicDataBytes,
+ duration,
+ maxExtAdvEvents,
+ cbId,
+ serverIf);
+ }
+
+ void stopAdvertisingSet(int advertiserId) {
+ stopAdvertisingSetNative(advertiserId);
+ }
+
+ void getOwnAddress(int advertiserId) {
+ getOwnAddressNative(advertiserId);
+ }
+
+ void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
+ enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);
+ }
+
+ void setAdvertisingData(int advertiserId, byte[] advertiseDataBytes) {
+ setAdvertisingDataNative(advertiserId, advertiseDataBytes);
+ }
+
+ void setScanResponseData(int advertiserId, byte[] advertiseDataBytes) {
+ setScanResponseDataNative(advertiserId, advertiseDataBytes);
+ }
+
+ void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
+ setAdvertisingParametersNative(advertiserId, parameters);
+ }
+
+ void setPeriodicAdvertisingParameters(
+ int advertiserId, PeriodicAdvertisingParameters parameters) {
+ setPeriodicAdvertisingParametersNative(advertiserId, parameters);
+ }
+
+ void setPeriodicAdvertisingData(int advertiserId, byte[] advertiseDataBytes) {
+ setPeriodicAdvertisingDataNative(advertiserId, advertiseDataBytes);
+ }
+
+ void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
+ setPeriodicAdvertisingEnableNative(advertiserId, enable);
+ }
+
+ static {
+ classInitNative();
+ }
+
+ void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
+ throws Exception {
+ mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status);
+ }
+
+ void onOwnAddressRead(int advertiserId, int addressType, String address) throws Exception {
+ mManager.onOwnAddressRead(advertiserId, addressType, address);
+ }
+
+ void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
+ mManager.onAdvertisingEnabled(advertiserId, enable, status);
+ }
+
+ void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ mManager.onAdvertisingDataSet(advertiserId, status);
+ }
+
+ void onScanResponseDataSet(int advertiserId, int status) throws Exception {
+ mManager.onScanResponseDataSet(advertiserId, status);
+ }
+
+ void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
+ throws Exception {
+ mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status);
+ }
+
+ void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
+ mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
+ }
+
+ void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
+ mManager.onPeriodicAdvertisingDataSet(advertiserId, status);
+ }
+
+ void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
+ throws Exception {
+ mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
+ }
+
+ private static native void classInitNative();
+
+ private native void initializeNative();
+
+ private native void cleanupNative();
+
+ private native void startAdvertisingSetNative(
+ AdvertisingSetParameters parameters,
+ byte[] advertiseData,
+ byte[] scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ byte[] periodicData,
+ int duration,
+ int maxExtAdvEvents,
+ int regId,
+ int serverIf);
+
+ private native void stopAdvertisingSetNative(int advertiserId);
+
+ private native void getOwnAddressNative(int advertiserId);
+
+ private native void enableAdvertisingSetNative(
+ int advertiserId, boolean enable, int duration, int maxExtAdvEvents);
+
+ private native void setAdvertisingDataNative(int advertiserId, byte[] data);
+
+ private native void setScanResponseDataNative(int advertiserId, byte[] data);
+
+ private native void setAdvertisingParametersNative(
+ int advertiserId, AdvertisingSetParameters parameters);
+
+ private native void setPeriodicAdvertisingParametersNative(
+ int advertiserId, PeriodicAdvertisingParameters parameters);
+
+ private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data);
+
+ private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable);
+}
diff --git a/android/app/src/com/android/bluetooth/gatt/GattObjectsFactory.java b/android/app/src/com/android/bluetooth/gatt/GattObjectsFactory.java
index 14eb6c5f89..13a5dca54f 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattObjectsFactory.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattObjectsFactory.java
@@ -19,6 +19,9 @@ package com.android.bluetooth.gatt;
import android.util.Log;
import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.BluetoothAdapterProxy;
+
/**
* Factory class for object initialization to help with unit testing
*/
@@ -64,4 +67,18 @@ public class GattObjectsFactory {
public ScanNativeInterface getScanNativeInterface() {
return ScanNativeInterface.getInstance();
}
+
+ public ScanManager createScanManager(GattService service, AdapterService adapterService,
+ BluetoothAdapterProxy bluetoothAdapterProxy) {
+ return new ScanManager(service, adapterService, bluetoothAdapterProxy);
+ }
+
+ public PeriodicScanManager createPeriodicScanManager(AdapterService adapterService) {
+ return new PeriodicScanManager(adapterService);
+ }
+
+ public DistanceMeasurementManager createDistanceMeasurementManager(
+ AdapterService adapterService) {
+ return new DistanceMeasurementManager(adapterService);
+ }
}
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index a8cad1f127..0156835787 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -56,6 +56,7 @@ import android.bluetooth.le.ScanSettings;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.content.AttributionSource;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.PackageInfoFlags;
@@ -280,13 +281,9 @@ public class GattService extends ProfileService {
private AdapterService mAdapterService;
private BluetoothAdapterProxy mBluetoothAdapterProxy;
- @VisibleForTesting
AdvertiseManager mAdvertiseManager;
- @VisibleForTesting
PeriodicScanManager mPeriodicScanManager;
- @VisibleForTesting
DistanceMeasurementManager mDistanceMeasurementManager;
- @VisibleForTesting
ScanManager mScanManager;
private AppOpsManager mAppOps;
private CompanionDeviceManager mCompanionManager;
@@ -294,6 +291,11 @@ public class GattService extends ProfileService {
private Handler mTestModeHandler;
private final Object mTestModeLock = new Object();
+ public GattService(Context ctx) {
+ attachBaseContext(ctx);
+ onCreate();
+ }
+
public static boolean isEnabled() {
return BluetoothProperties.isProfileGattEnabled().orElse(true);
}
@@ -345,16 +347,24 @@ public class GattService extends ProfileService {
mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance();
mCompanionManager = getSystemService(CompanionDeviceManager.class);
mAppOps = getSystemService(AppOpsManager.class);
- mAdvertiseManager = new AdvertiseManager(this, mAdapterService, mAdvertiserMap);
+ mAdvertiseManager =
+ new AdvertiseManager(
+ this,
+ new AdvertiseManagerNativeInterface(),
+ mAdapterService,
+ mAdvertiserMap);
mAdvertiseManager.start();
- mScanManager = new ScanManager(this, mAdapterService, mBluetoothAdapterProxy);
+ mScanManager = GattObjectsFactory.getInstance()
+ .createScanManager(this, mAdapterService, mBluetoothAdapterProxy);
mScanManager.start();
- mPeriodicScanManager = new PeriodicScanManager(mAdapterService);
+ mPeriodicScanManager = GattObjectsFactory.getInstance()
+ .createPeriodicScanManager(mAdapterService);
mPeriodicScanManager.start();
- mDistanceMeasurementManager = new DistanceMeasurementManager(mAdapterService);
+ mDistanceMeasurementManager = GattObjectsFactory.getInstance()
+ .createDistanceMeasurementManager(mAdapterService);
mDistanceMeasurementManager.start();
setGattService(this);
@@ -608,34 +618,6 @@ public class GattService extends ProfileService {
}
@Override
- public void startService() {
- GattService service = mService;
- if (service == null) {
- Log.e(TAG, "startService: Service is null");
- return;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, null, "GattService startService")) {
- return;
- }
- service.doStart();
- }
-
- @Override
- public void stopService() {
- GattService service = mService;
- if (service == null) {
- Log.e(TAG, "stopService: Service is null");
- return;
- }
- if (!Utils.checkConnectPermissionForDataDelivery(
- service, null, "GattService stopService")) {
- return;
- }
- service.doStop();
- }
-
- @Override
public void getDevicesMatchingConnectionStates(int[] states,
AttributionSource attributionSource, SynchronousResultReceiver receiver) {
try {
@@ -1774,15 +1756,6 @@ public class GattService extends ProfileService {
}
@Override
- public void unregAll(AttributionSource attributionSource) {
- GattService service = getService();
- if (service == null) {
- return;
- }
- service.unregAll(attributionSource);
- }
-
- @Override
public void numHwTrackFiltersAvailable(AttributionSource attributionSource,
SynchronousResultReceiver receiver) {
try {
@@ -3431,7 +3404,7 @@ public class GattService extends ProfileService {
}
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- void unregAll(AttributionSource attributionSource) {
+ public void unregAll(AttributionSource attributionSource) {
for (Integer appId : mClientMap.getAllAppsIds()) {
if (DBG) {
Log.d(TAG, "unreg:" + appId);
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 104751275c..54d79205e5 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -18,7 +18,6 @@
package com.android.bluetooth.hap;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
@@ -46,6 +45,7 @@ import android.sysprop.BluetoothProperties;
import android.util.Log;
import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -83,7 +83,6 @@ public class HapClientService extends ProfileService {
private DatabaseManager mDatabaseManager;
private HandlerThread mStateMachinesThread;
private BroadcastReceiver mBondStateChangedReceiver;
- private BroadcastReceiver mConnectionStateChangedReceiver;
private final Map<BluetoothDevice, Integer> mDeviceCurrentPresetMap = new HashMap<>();
private final Map<BluetoothDevice, Integer> mDeviceFeaturesMap = new HashMap<>();
@@ -175,11 +174,6 @@ public class HapClientService extends ProfileService {
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mBondStateChangedReceiver = new BondStateChangedReceiver();
registerReceiver(mBondStateChangedReceiver, filter);
- filter = new IntentFilter();
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
- mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
- registerReceiver(mConnectionStateChangedReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
mCallbacks = new RemoteCallbackList<IBluetoothHapClientCallback>();
@@ -208,8 +202,6 @@ public class HapClientService extends ProfileService {
// Unregister broadcast receivers
unregisterReceiver(mBondStateChangedReceiver);
mBondStateChangedReceiver = null;
- unregisterReceiver(mConnectionStateChangedReceiver);
- mConnectionStateChangedReceiver = null;
// Destroy state machines and stop handler thread
synchronized (mStateMachines) {
@@ -453,6 +445,10 @@ public class HapClientService extends ProfileService {
removeStateMachine(device);
}
}
+ ActiveDeviceManager adManager = mAdapterService.getActiveDeviceManager();
+ if (adManager != null) {
+ adManager.hapConnectionStateChanged(device, fromState, toState);
+ }
}
/**
@@ -1659,18 +1655,4 @@ public class HapClientService extends ProfileService {
bondStateChanged(device, state);
}
}
-
- private class ConnectionStateChangedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED.equals(
- intent.getAction())) {
- return;
- }
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
- connectionStateChanged(device, fromState, toState);
- }
- }
}
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java b/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
index dc85f0aea3..1dfd45dcd9 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
@@ -179,6 +179,7 @@ final class HapClientStateMachine extends StateMachine {
log("Connection state " + mDevice + ": " + profileStateToString(prevState)
+ "->" + profileStateToString(newState));
+ mService.connectionStateChanged(mDevice, prevState, newState);
Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
index a0ab01e8ee..7618baf145 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -155,9 +155,6 @@ public class HearingAidService extends ProfileService {
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mBondStateChangedReceiver = new BondStateChangedReceiver();
registerReceiver(mBondStateChangedReceiver, filter);
- filter = new IntentFilter();
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
// Mark service as started
setHearingAidService(this);
@@ -733,6 +730,7 @@ public class HearingAidService extends ProfileService {
}
private void notifyActiveDeviceChanged() {
+ mAdapterService.getActiveDeviceManager().hearingAidActiveStateChanged(mActiveDevice);
Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveDevice);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -746,11 +744,11 @@ public class HearingAidService extends ProfileService {
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
for (AudioDeviceInfo deviceInfo : removedDevices) {
if (deviceInfo.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
- notifyActiveDeviceChanged();
if (DBG) {
Log.d(TAG, " onAudioDevicesRemoved: device type: " + deviceInfo.getType());
}
if (mAudioManager != null) {
+ notifyActiveDeviceChanged();
mAudioManager.unregisterAudioDeviceCallback(this);
} else {
Log.w(TAG, "onAudioDevicesRemoved: mAudioManager is null");
@@ -766,11 +764,11 @@ public class HearingAidService extends ProfileService {
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
for (AudioDeviceInfo deviceInfo : addedDevices) {
if (deviceInfo.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
- notifyActiveDeviceChanged();
if (DBG) {
Log.d(TAG, " onAudioDevicesAdded: device type: " + deviceInfo.getType());
}
if (mAudioManager != null) {
+ notifyActiveDeviceChanged();
mAudioManager.unregisterAudioDeviceCallback(this);
} else {
Log.w(TAG, "onAudioDevicesAdded: mAudioManager is null");
@@ -964,6 +962,9 @@ public class HearingAidService extends ProfileService {
removeStateMachine(device);
}
}
+ mAdapterService
+ .getActiveDeviceManager()
+ .hearingAidConnectionStateChanged(device, fromState, toState);
}
/**
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
index 977f6e966c..d3eb601dbe 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
@@ -79,6 +79,10 @@ public class HeadsetNativeInterface {
}
private byte[] getByteAddress(BluetoothDevice device) {
+ if (device == null) {
+ // Set bt_stack's active device to default if java layer set active device to null
+ return Utils.getBytesFromAddress("00:00:00:00:00:00");
+ }
return mAdapterService.getByteIdentityAddress(device);
}
@@ -491,10 +495,6 @@ public class HeadsetNativeInterface {
*/
@VisibleForTesting
public boolean setActiveDevice(BluetoothDevice device) {
- // Set bt_stack's active device to default if java layer set active device to null
- if (device == null) {
- return setActiveDeviceNative(Utils.getBytesFromAddress("00:00:00:00:00:00"));
- }
return setActiveDeviceNative(getByteAddress(device));
}
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 6e4b626266..9c2c7fee48 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -170,6 +170,7 @@ public class LeAudioService extends ProfileService {
mDirection = AUDIO_DIRECTION_NONE;
mCodecStatus = null;
mLostLeadDeviceWhileStreaming = null;
+ mCurrentLeadDevice = null;
mInbandRingtoneEnabled = isInbandRingtonEnabled;
mAvailableContexts = 0;
}
@@ -180,6 +181,7 @@ public class LeAudioService extends ProfileService {
public BluetoothLeAudioCodecStatus mCodecStatus;
/* This can be non empty only for the streaming time */
BluetoothDevice mLostLeadDeviceWhileStreaming;
+ BluetoothDevice mCurrentLeadDevice;
Boolean mInbandRingtoneEnabled;
Integer mAvailableContexts;
}
@@ -604,31 +606,11 @@ public class LeAudioService extends ProfileService {
}
BluetoothDevice getConnectedGroupLeadDevice(int groupId) {
- BluetoothDevice device = null;
-
- if (mActiveAudioOutDevice != null
- && getGroupId(mActiveAudioOutDevice) == groupId) {
- device = mActiveAudioOutDevice;
- } else {
- device = getFirstDeviceFromGroup(groupId);
- }
-
- if (device == null) {
- return device;
- }
-
- LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device);
- if (descriptor == null) {
- Log.e(TAG, "getConnectedGroupLeadDevice: No valid descriptor for device: " + device);
- return null;
- }
-
- LeAudioStateMachine sm = descriptor.mStateMachine;
- if (sm != null && sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
- return device;
+ if (getGroupId(mActiveAudioOutDevice) == groupId) {
+ return mActiveAudioOutDevice;
}
- return null;
+ return getLeadDeviceForTheGroup(groupId);
}
List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -986,11 +968,23 @@ public class LeAudioService extends ProfileService {
return 1;
}
- private BluetoothDevice getFirstDeviceFromGroup(Integer groupId) {
+ private BluetoothDevice getLeadDeviceForTheGroup(Integer groupId) {
if (groupId == LE_AUDIO_GROUP_ID_INVALID) {
return null;
}
synchronized (mGroupLock) {
+ LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
+ if (groupDescriptor == null) {
+ Log.e(TAG, "Group " + groupId + " does not exist");
+ return null;
+ }
+
+ if (groupDescriptor.mCurrentLeadDevice != null
+ && getConnectionState(groupDescriptor.mCurrentLeadDevice)
+ == BluetoothProfile.STATE_CONNECTED) {
+ return groupDescriptor.mCurrentLeadDevice;
+ }
+
for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) {
if (!descriptor.mGroupId.equals(groupId)) {
continue;
@@ -1000,7 +994,8 @@ public class LeAudioService extends ProfileService {
if (sm == null || sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
continue;
}
- return sm.getDevice();
+ groupDescriptor.mCurrentLeadDevice = sm.getDevice();
+ return groupDescriptor.mCurrentLeadDevice;
}
}
return null;
@@ -1132,6 +1127,37 @@ public class LeAudioService extends ProfileService {
}
/**
+ * Send broadcast intent about LeAudio connection state changed. This is called by
+ * LeAudioStateMachine.
+ */
+ void notifyConnectionStateChanged(BluetoothDevice device, int newState, int prevState) {
+ if (DBG) {
+ Log.d(
+ TAG,
+ "Notify connection state changed."
+ + device
+ + "("
+ + prevState
+ + " -> "
+ + newState
+ + ")");
+ }
+
+ mAdapterService
+ .getActiveDeviceManager()
+ .leAudioConnectionStateChanged(device, prevState, newState);
+ Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ Utils.sendBroadcast(
+ this, intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
+ }
+
+ /**
* Send broadcast intent about LeAudio active device.
* This is called when AudioManager confirms, LeAudio device
* is added or removed.
@@ -1143,6 +1169,7 @@ public class LeAudioService extends ProfileService {
+ ". Currently active device is " + mActiveAudioOutDevice);
}
+ mAdapterService.getActiveDeviceManager().leAudioActiveStateChanged(device);
Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -1302,7 +1329,7 @@ public class LeAudioService extends ProfileService {
BluetoothDevice previousActiveInDevice = mActiveAudioInDevice;
if (isActive) {
- device = getFirstDeviceFromGroup(groupId);
+ device = getLeadDeviceForTheGroup(groupId);
}
boolean isNewActiveOutDevice = updateActiveOutDevice(device, groupId,
@@ -3833,7 +3860,6 @@ public class LeAudioService extends ProfileService {
ProfileService.println(sb, " isConnected: " + groupDescriptor.mIsConnected);
ProfileService.println(sb, " mDirection: " + groupDescriptor.mDirection);
ProfileService.println(sb, " group lead: " + leadDevice);
- ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId));
ProfileService.println(sb, " lost lead device: "
+ groupDescriptor.mLostLeadDeviceWhileStreaming);
ProfileService.println(sb, " mInbandRingtoneEnabled: "
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 a1411f6934..0eb7577d72 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioStateMachine.java
@@ -47,15 +47,11 @@
package com.android.bluetooth.le_audio;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
-import android.content.Intent;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
@@ -514,15 +510,7 @@ final class LeAudioStateMachine extends StateMachine {
private void broadcastConnectionState(int newState, int prevState) {
log("Connection state " + mDevice + ": " + profileStateToString(prevState)
+ "->" + profileStateToString(newState));
-
- Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- Utils.sendBroadcast(mService, intent, BLUETOOTH_CONNECT,
- Utils.getTempAllowlistBroadcastOptions());
+ mService.notifyConnectionStateChanged(mDevice, newState, prevState);
}
private static String messageWhatToString(int what) {
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 b911669d84..d605ef2f8e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
@@ -39,6 +39,7 @@ import androidx.test.uiautomator.UiDevice;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.gatt.GattService;
import org.junit.Assert;
import org.junit.rules.TestRule;
@@ -117,6 +118,21 @@ public class TestUtils {
AdapterService.clearAdapterService(adapterService);
}
+ /** Helper function to mock getSystemService calls */
+ public static <T> void mockGetSystemService(
+ Context ctx, String serviceName, Class<T> serviceClass, T mockService) {
+ when(ctx.getSystemService(eq(serviceName))).thenReturn(mockService);
+ when(ctx.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName);
+ }
+
+ /** Helper function to mock getSystemService calls */
+ public static <T> T mockGetSystemService(
+ Context ctx, String serviceName, Class<T> serviceClass) {
+ T mockedService = mock(serviceClass);
+ mockGetSystemService(ctx, serviceName, serviceClass, mockedService);
+ return mockedService;
+ }
+
/**
* Start a profile service using the given {@link ServiceTestRule} and verify through
* {@link AdapterService#getAdapterService()} that the service is actually started within
@@ -135,6 +151,9 @@ public class TestUtils {
*/
public static <T extends ProfileService> void startService(ServiceTestRule serviceTestRule,
Class<T> profileServiceClass) throws TimeoutException {
+ if (profileServiceClass == GattService.class) {
+ Assert.assertFalse("GattService cannot be started as a service", true);
+ }
AdapterService adapterService = AdapterService.getAdapterService();
Assert.assertNotNull("Adapter service should not be null", adapterService);
Assert.assertTrue("AdapterService.getAdapterService() must return a mocked or spied object"
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 80c62f0edc..2fa6849c3e 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
@@ -405,7 +405,7 @@ public class AvrcpControllerStateMachineTest {
BluetoothMediaBrowserService.getTransportControls();
Assert.assertNotNull(transportControls);
Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
- BluetoothMediaBrowserService.getPlaybackState());
+ BluetoothMediaBrowserService.getPlaybackState().getState());
mAvrcpStateMachine.disconnect();
numBroadcastsSent += 2;
verify(mAvrcpControllerService,
@@ -434,7 +434,7 @@ public class AvrcpControllerStateMachineTest {
int numBroadcastsSent = setUpConnectedState(false, true);
Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
- BluetoothMediaBrowserService.getPlaybackState());
+ BluetoothMediaBrowserService.getPlaybackState().getState());
mAvrcpStateMachine.disconnect();
numBroadcastsSent += 2;
verify(mAvrcpControllerService,
@@ -1268,6 +1268,69 @@ public class AvrcpControllerStateMachineTest {
Assert.assertFalse(mAvrcpStateMachine.isActive());
}
+ @Test
+ public void testTrackChangedWhileActive_currentTrackAndQueueNumberUpdated() {
+ setUpConnectedState(true, true);
+
+ // Set track
+ AvrcpItem track = makeTrack("Song 1", "artist", "album", 1, 2, "none", 10, null);
+ setCurrentTrack(track);
+
+ // Set current Now Playing list
+ List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
+ nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
+ nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
+ setNowPlayingList(nowPlayingList);
+
+ // Set playing
+ setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+
+ // Wait
+ TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+ // Verify track and playback state
+ MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
+ Assert.assertNotNull(session);
+ MediaControllerCompat controller = session.getController();
+ Assert.assertNotNull(controller);
+
+ MediaMetadataCompat metadata = controller.getMetadata();
+ Assert.assertNotNull(metadata);
+ 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));
+
+ PlaybackStateCompat playbackState = controller.getPlaybackState();
+ Assert.assertNotNull(playbackState);
+ Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
+ Assert.assertEquals(0, playbackState.getActiveQueueItemId());
+
+ // Track changes, with new metadata and new track number
+ track = makeTrack("Song 2", "artist", "album", 2, 2, "none", 10, null);
+ setCurrentTrack(track);
+ TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+ // Assert new track metadata and active queue item
+ metadata = controller.getMetadata();
+ Assert.assertNotNull(metadata);
+ 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));
+
+ playbackState = controller.getPlaybackState();
+ Assert.assertNotNull(playbackState);
+ Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
+ Assert.assertEquals(1, playbackState.getActiveQueueItemId());
+ }
+
/**
* Test receiving a track change update when we're not the active device
*/
@@ -1318,7 +1381,7 @@ public class AvrcpControllerStateMachineTest {
eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
- BluetoothMediaBrowserService.getPlaybackState());
+ BluetoothMediaBrowserService.getPlaybackState().getState());
}
/**
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 999d74607e..7e0aca04fb 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
@@ -919,7 +919,12 @@ public class BassClientStateMachineTest {
Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
msg.obj = BluetoothProfile.STATE_CONNECTING;
+ BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
+ Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
+ mBassClientStateMachine.mBluetoothGatt = btGatt;
sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class);
+ verify(btGatt).close();
+ assertNull(mBassClientStateMachine.mBluetoothGatt);
}
@Test
@@ -941,8 +946,13 @@ public class BassClientStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
+ BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
+ Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
+ mBassClientStateMachine.mBluetoothGatt = btGatt;
Message msg = mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, mTestDevice);
sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class);
+ verify(btGatt).close();
+ assertNull(mBassClientStateMachine.mBluetoothGatt);
}
@Test
@@ -991,9 +1001,14 @@ public class BassClientStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
+ BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
+ Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
+ mBassClientStateMachine.mBluetoothGatt = btGatt;
Message noneConnectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
noneConnectedMsg.obj = BluetoothProfile.STATE_DISCONNECTING;
sendMessageAndVerifyTransition(noneConnectedMsg, BassClientStateMachine.Disconnected.class);
+ verify(btGatt).close();
+ assertNull(mBassClientStateMachine.mBluetoothGatt);
}
@Test
@@ -1379,11 +1394,16 @@ public class BassClientStateMachineTest {
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
+ BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
+ Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class);
+ mBassClientStateMachine.mBluetoothGatt = btGatt;
Message msgToNoneConnectedState =
mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
msgToNoneConnectedState.obj = BluetoothProfile.STATE_DISCONNECTING;
sendMessageAndVerifyTransition(
msgToNoneConnectedState, BassClientStateMachine.Disconnected.class);
+ verify(btGatt).close();
+ assertNull(mBassClientStateMachine.mBluetoothGatt);
}
/**
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 4e37c265a9..1b444ced73 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
@@ -32,13 +32,9 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHapClient;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.content.Context;
-import android.content.Intent;
import android.media.AudioManager;
import android.util.ArrayMap;
import android.util.SparseIntArray;
@@ -51,6 +47,7 @@ import com.android.bluetooth.TestUtils;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.bluetooth.hap.HapClientService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -101,6 +98,7 @@ public class ActiveDeviceManagerTest {
@Mock private HearingAidService mHearingAidService;
@Mock private LeAudioService mLeAudioService;
@Mock private AudioManager mAudioManager;
+ @Mock private HapClientService mHapClientService;
@Before
public void setUp() throws Exception {
@@ -1213,13 +1211,11 @@ public class ActiveDeviceManagerTest {
* Helper to indicate Hearing Aid connected for a device.
*/
private void hearingAidConnected(BluetoothDevice device) {
+ mDeviceConnectionStack.add(device);
mMostRecentDevice = device;
- Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.hearingAidConnectionStateChanged(
+ device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
}
/**
@@ -1230,24 +1226,19 @@ public class ActiveDeviceManagerTest {
mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
- Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.hearingAidConnectionStateChanged(
+ device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
}
/**
* Helper to indicate Hearing Aid active device changed for a device.
*/
private void hearingAidActiveDeviceChanged(BluetoothDevice device) {
- mMostRecentDevice = device;
-
- Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
mDeviceConnectionStack.remove(device);
mDeviceConnectionStack.add(device);
+ mMostRecentDevice = device;
+
+ mActiveDeviceManager.hearingAidActiveStateChanged(device);
}
/**
@@ -1256,11 +1247,8 @@ public class ActiveDeviceManagerTest {
private void leAudioConnected(BluetoothDevice device) {
mMostRecentDevice = device;
- Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.leAudioConnectionStateChanged(
+ device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
}
/**
@@ -1271,11 +1259,8 @@ public class ActiveDeviceManagerTest {
mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
- Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.leAudioConnectionStateChanged(
+ device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
}
/**
@@ -1286,9 +1271,7 @@ public class ActiveDeviceManagerTest {
mDeviceConnectionStack.add(device);
mMostRecentDevice = device;
- Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.leAudioActiveStateChanged(device);
}
/**
@@ -1298,39 +1281,18 @@ public class ActiveDeviceManagerTest {
mDeviceConnectionStack.add(device);
mMostRecentDevice = device;
- Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.hapConnectionStateChanged(
+ device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
}
- /**
- * Helper to indicate LE Hearing Aid disconnected for a device.
- */
+ /** Helper to indicate LE Hearing Aid disconnected for a device. */
private void leHearingAidDisconnected(BluetoothDevice device) {
mDeviceConnectionStack.remove(device);
mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
- Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
- intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
- }
-
- /**
- * Helper to indicate LE Audio Hearing Aid device changed for a device.
- */
- private void leHearingAidActiveDeviceChanged(BluetoothDevice device) {
- mDeviceConnectionStack.remove(device);
- mDeviceConnectionStack.add(device);
- mMostRecentDevice = device;
-
- Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+ mActiveDeviceManager.hapConnectionStateChanged(
+ device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
}
private class TestDatabaseManager extends DatabaseManager {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
index 9d81e88e5f..beb4a4554c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
@@ -36,6 +36,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.os.BatteryStatsManager;
import android.os.Binder;
@@ -91,16 +92,10 @@ public class AdapterServiceFactoryResetTest {
private @Mock android.app.Application mApplication;
private @Mock MetricsLogger mMockMetricsLogger;
- // Mocked SystemService
- private @Mock AlarmManager mMockAlarmManager;
- private @Mock AppOpsManager mMockAppOpsManager;
- private @Mock AudioManager mMockAudioManager;
- private @Mock DevicePolicyManager mMockDevicePolicyManager;
- private @Mock UserManager mMockUserManager;
-
// SystemService that are not mocked
private BluetoothManager mBluetoothManager;
private CompanionDeviceManager mCompanionDeviceManager;
+ private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
private PermissionCheckerManager mPermissionCheckerManager;
private PermissionManager mPermissionManager;
@@ -131,6 +126,12 @@ public class AdapterServiceFactoryResetTest {
when(mMockContext.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName);
}
+ <T> T mockGetSystemService(String serviceName, Class<T> serviceClass) {
+ T mockedService = mock(serviceClass);
+ mockGetSystemService(serviceName, serviceClass, mockedService);
+ return mockedService;
+ }
+
@Before
public void setUp() throws PackageManager.NameNotFoundException {
Log.e(TAG, "setUp()");
@@ -156,23 +157,20 @@ public class AdapterServiceFactoryResetTest {
}
});
- mPowerManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PowerManager.class);
- mPermissionCheckerManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PermissionCheckerManager.class);
-
- mPermissionManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PermissionManager.class);
+ Context targetContext = InstrumentationRegistry.getTargetContext();
- mBluetoothManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(BluetoothManager.class);
-
- mCompanionDeviceManager =
- InstrumentationRegistry.getTargetContext()
- .getSystemService(CompanionDeviceManager.class);
+ mBluetoothManager = targetContext.getSystemService(BluetoothManager.class);
+ mCompanionDeviceManager = targetContext.getSystemService(CompanionDeviceManager.class);
+ mDisplayManager = targetContext.getSystemService(DisplayManager.class);
+ mPermissionCheckerManager = targetContext.getSystemService(PermissionCheckerManager.class);
+ mPermissionManager = targetContext.getSystemService(PermissionManager.class);
+ mPowerManager = targetContext.getSystemService(PowerManager.class);
when(mMockContext.getCacheDir())
.thenReturn(InstrumentationRegistry.getTargetContext().getCacheDir());
+ when(mMockContext.getUser())
+ .thenReturn(InstrumentationRegistry.getTargetContext().getUser());
+ when(mMockContext.getPackageName()).thenReturn("com.android.bluetooth");
when(mMockContext.getApplicationInfo()).thenReturn(mMockApplicationInfo);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
@@ -182,12 +180,13 @@ public class AdapterServiceFactoryResetTest {
when(mMockContext.getUserId()).thenReturn(Process.BLUETOOTH_UID);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
- mockGetSystemService(Context.ALARM_SERVICE, AlarmManager.class, mMockAlarmManager);
- mockGetSystemService(Context.APP_OPS_SERVICE, AppOpsManager.class, mMockAppOpsManager);
- mockGetSystemService(Context.AUDIO_SERVICE, AudioManager.class, mMockAudioManager);
- mockGetSystemService(
- Context.DEVICE_POLICY_SERVICE, DevicePolicyManager.class, mMockDevicePolicyManager);
- mockGetSystemService(Context.USER_SERVICE, UserManager.class, mMockUserManager);
+ mockGetSystemService(Context.ALARM_SERVICE, AlarmManager.class);
+ mockGetSystemService(Context.APP_OPS_SERVICE, AppOpsManager.class);
+ mockGetSystemService(Context.AUDIO_SERVICE, AudioManager.class);
+ DevicePolicyManager dpm =
+ mockGetSystemService(Context.DEVICE_POLICY_SERVICE, DevicePolicyManager.class);
+ doReturn(false).when(dpm).isCommonCriteriaModeEnabled(any());
+ mockGetSystemService(Context.USER_SERVICE, UserManager.class);
mockGetSystemService(
Context.BATTERY_STATS_SERVICE, BatteryStatsManager.class, mBatteryStatsManager);
@@ -196,6 +195,7 @@ public class AdapterServiceFactoryResetTest {
Context.COMPANION_DEVICE_SERVICE,
CompanionDeviceManager.class,
mCompanionDeviceManager);
+ mockGetSystemService(Context.DISPLAY_SERVICE, DisplayManager.class, mDisplayManager);
mockGetSystemService(
Context.PERMISSION_CHECKER_SERVICE,
PermissionCheckerManager.class,
@@ -221,8 +221,6 @@ public class AdapterServiceFactoryResetTest {
UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
Utils.setForegroundUserId(callingUser.getIdentifier());
- when(mMockDevicePolicyManager.isCommonCriteriaModeEnabled(any())).thenReturn(false);
-
when(mIBluetoothCallback.asBinder()).thenReturn(mBinder);
doReturn(Process.BLUETOOTH_UID).when(mMockPackageManager)
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
index 96ccb11add..e02a34d8d4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
@@ -162,6 +162,9 @@ public class AdapterServiceRestartTest {
when(mMockContext.getCacheDir()).thenReturn(InstrumentationRegistry.getTargetContext()
.getCacheDir());
+ when(mMockContext.getUser())
+ .thenReturn(InstrumentationRegistry.getTargetContext().getUser());
+ when(mMockContext.getPackageName()).thenReturn("com.android.bluetooth");
when(mMockContext.getApplicationInfo()).thenReturn(mMockApplicationInfo);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
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 61aefd7b9f..db52e36d63 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
@@ -26,6 +26,7 @@ import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
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.Mockito.*;
@@ -43,6 +44,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.os.BatteryStatsManager;
import android.os.Binder;
@@ -142,16 +144,10 @@ public class AdapterServiceTest {
private @Mock android.app.Application mApplication;
private @Mock MetricsLogger mMockMetricsLogger;
- // Mocked SystemService
- private @Mock AlarmManager mMockAlarmManager;
- private @Mock AppOpsManager mMockAppOpsManager;
- private @Mock AudioManager mMockAudioManager;
- private @Mock DevicePolicyManager mMockDevicePolicyManager;
- private @Mock UserManager mMockUserManager;
-
// SystemService that are not mocked
private BluetoothManager mBluetoothManager;
private CompanionDeviceManager mCompanionDeviceManager;
+ private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
private PermissionCheckerManager mPermissionCheckerManager;
private PermissionManager mPermissionManager;
@@ -222,8 +218,11 @@ public class AdapterServiceTest {
}
<T> void mockGetSystemService(String serviceName, Class<T> serviceClass, T mockService) {
- when(mMockContext.getSystemService(eq(serviceName))).thenReturn(mockService);
- when(mMockContext.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName);
+ TestUtils.mockGetSystemService(mMockContext, serviceName, serviceClass, mockService);
+ }
+
+ <T> T mockGetSystemService(String serviceName, Class<T> serviceClass) {
+ return TestUtils.mockGetSystemService(mMockContext, serviceName, serviceClass);
}
@Before
@@ -251,23 +250,20 @@ public class AdapterServiceTest {
}
});
- mPowerManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PowerManager.class);
- mPermissionCheckerManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PermissionCheckerManager.class);
-
- mPermissionManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(PermissionManager.class);
+ Context targetContext = InstrumentationRegistry.getTargetContext();
- mBluetoothManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(BluetoothManager.class);
-
- mCompanionDeviceManager =
- InstrumentationRegistry.getTargetContext()
- .getSystemService(CompanionDeviceManager.class);
+ mBluetoothManager = targetContext.getSystemService(BluetoothManager.class);
+ mCompanionDeviceManager = targetContext.getSystemService(CompanionDeviceManager.class);
+ mDisplayManager = targetContext.getSystemService(DisplayManager.class);
+ mPermissionCheckerManager = targetContext.getSystemService(PermissionCheckerManager.class);
+ mPermissionManager = targetContext.getSystemService(PermissionManager.class);
+ mPowerManager = targetContext.getSystemService(PowerManager.class);
when(mMockContext.getCacheDir()).thenReturn(InstrumentationRegistry.getTargetContext()
.getCacheDir());
+ when(mMockContext.getUser())
+ .thenReturn(InstrumentationRegistry.getTargetContext().getUser());
+ when(mMockContext.getPackageName()).thenReturn("com.android.bluetooth");
when(mMockContext.getApplicationInfo()).thenReturn(mMockApplicationInfo);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
@@ -277,12 +273,13 @@ public class AdapterServiceTest {
when(mMockContext.getUserId()).thenReturn(Process.BLUETOOTH_UID);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
- mockGetSystemService(Context.ALARM_SERVICE, AlarmManager.class, mMockAlarmManager);
- mockGetSystemService(Context.APP_OPS_SERVICE, AppOpsManager.class, mMockAppOpsManager);
- mockGetSystemService(Context.AUDIO_SERVICE, AudioManager.class, mMockAudioManager);
- mockGetSystemService(
- Context.DEVICE_POLICY_SERVICE, DevicePolicyManager.class, mMockDevicePolicyManager);
- mockGetSystemService(Context.USER_SERVICE, UserManager.class, mMockUserManager);
+ mockGetSystemService(Context.ALARM_SERVICE, AlarmManager.class);
+ mockGetSystemService(Context.APP_OPS_SERVICE, AppOpsManager.class);
+ mockGetSystemService(Context.AUDIO_SERVICE, AudioManager.class);
+ DevicePolicyManager dpm =
+ mockGetSystemService(Context.DEVICE_POLICY_SERVICE, DevicePolicyManager.class);
+ doReturn(false).when(dpm).isCommonCriteriaModeEnabled(any());
+ mockGetSystemService(Context.USER_SERVICE, UserManager.class);
mockGetSystemService(
Context.BATTERY_STATS_SERVICE, BatteryStatsManager.class, mBatteryStatsManager);
@@ -291,6 +288,7 @@ public class AdapterServiceTest {
Context.COMPANION_DEVICE_SERVICE,
CompanionDeviceManager.class,
mCompanionDeviceManager);
+ mockGetSystemService(Context.DISPLAY_SERVICE, DisplayManager.class, mDisplayManager);
mockGetSystemService(
Context.PERMISSION_CHECKER_SERVICE,
PermissionCheckerManager.class,
@@ -303,8 +301,6 @@ public class AdapterServiceTest {
.thenReturn(InstrumentationRegistry.getTargetContext()
.getSharedPreferences("AdapterServiceTestPrefs", Context.MODE_PRIVATE));
- doReturn(true).when(mMockContext).bindServiceAsUser(any(), any(), anyInt(), any());
-
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
return InstrumentationRegistry.getTargetContext().getDatabasePath((String) args[0]);
@@ -316,8 +312,6 @@ public class AdapterServiceTest {
UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
Utils.setForegroundUserId(callingUser.getIdentifier());
- when(mMockDevicePolicyManager.isCommonCriteriaModeEnabled(any())).thenReturn(false);
-
when(mIBluetoothCallback.asBinder()).thenReturn(mBinder);
doReturn(Process.BLUETOOTH_UID).when(mMockPackageManager)
@@ -367,14 +361,23 @@ public class AdapterServiceTest {
syncHandler(mLooper, what);
}
+ private void dropNextMessage(int what) {
+ Message msg = mLooper.nextMessage();
+ assertThat(msg).isNotNull();
+ assertWithMessage("Not the expected Message:\n" + msg).that(msg.what).isEqualTo(what);
+ Log.d(TAG, "Message dropped on purpose: " + msg);
+ }
+
private static void syncHandler(TestLooper looper, int... what) {
IntStream.of(what)
.forEach(
w -> {
Message msg = looper.nextMessage();
assertThat(msg).isNotNull();
- assertThat(msg.what).isEqualTo(w);
- Log.d(TAG, "Processing message: " + msg.what);
+ assertWithMessage("Not the expected Message:\n" + msg)
+ .that(msg.what)
+ .isEqualTo(w);
+ Log.d(TAG, "Processing message: " + msg);
msg.getTarget().dispatchMessage(msg);
});
}
@@ -422,12 +425,9 @@ public class AdapterServiceTest {
adapter.enable(false);
syncHandler(looper, AdapterState.BLE_TURN_ON);
verifyStateChange(callback, STATE_OFF, STATE_BLE_TURNING_ON);
- verify(ctx).bindServiceAsUser(any(), any(), anyInt(), any());
- adapter.addProfile(gattService);
syncHandler(looper, MESSAGE_PROFILE_SERVICE_REGISTERED);
- adapter.onProfileServiceStateChanged(gattService, STATE_ON);
syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
// Native loop is not in TestLooper and it will send a event later
@@ -550,9 +550,7 @@ public class AdapterServiceTest {
adapter.stopBle();
syncHandler(looper, AdapterState.BLE_TURN_OFF);
verifyStateChange(callback, STATE_BLE_ON, STATE_BLE_TURNING_OFF);
- verify(ctx).unbindService(any());
- adapter.onProfileServiceStateChanged(gattService, STATE_OFF);
syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
// Native loop is not in TestLooper and it will send a event later
@@ -630,14 +628,15 @@ public class AdapterServiceTest {
mAdapterService.enable(false);
syncHandler(AdapterState.BLE_TURN_ON);
verifyStateChange(STATE_OFF, STATE_BLE_TURNING_ON);
- verify(mMockContext).bindServiceAsUser(any(), any(), anyInt(), any());
-
- mAdapterService.addProfile(mMockGattService);
+ assertThat(mAdapterService.getBluetoothGatt()).isNotNull();
syncHandler(MESSAGE_PROFILE_SERVICE_REGISTERED);
+ // Fetch next message and never process it to simulate a timeout.
+ dropNextMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+
mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
syncHandler(AdapterState.BLE_START_TIMEOUT);
- verify(mMockContext).unbindService(any());
+ assertThat(mAdapterService.getBluetoothGatt()).isNull();
// Native loop is not in TestLooper and it will send a event later
mLooper.startAutoDispatch();
@@ -666,7 +665,11 @@ public class AdapterServiceTest {
mAdapterService.stopBle();
syncHandler(AdapterState.BLE_TURN_OFF);
verifyStateChange(STATE_BLE_ON, STATE_BLE_TURNING_OFF, CONTEXT_SWITCH_MS);
- verify(mMockContext).unbindService(any()); // Stop GATT
+ assertThat(mAdapterService.getBluetoothGatt()).isNull();
+
+ // Fetch Gatt message and never process it to simulate a timeout.
+ dropNextMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+ dropNextMessage(MESSAGE_PROFILE_SERVICE_UNREGISTERED);
mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
syncHandler(AdapterState.BLE_STOP_TIMEOUT);
@@ -711,7 +714,7 @@ public class AdapterServiceTest {
verifyStateChange(STATE_TURNING_OFF, STATE_BLE_ON);
// Ensure GATT is still running
- verify(mMockContext, times(0)).unbindService(any());
+ assertThat(mAdapterService.getBluetoothGatt()).isNotNull();
}
/**
@@ -735,10 +738,9 @@ public class AdapterServiceTest {
mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
syncHandler(AdapterState.BREDR_STOP_TIMEOUT);
verifyStateChange(STATE_TURNING_OFF, STATE_BLE_TURNING_OFF);
- verify(mMockContext).unbindService(any());
- mAdapterService.onProfileServiceStateChanged(mMockGattService, STATE_OFF);
syncHandler(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+ syncHandler(MESSAGE_PROFILE_SERVICE_UNREGISTERED);
// TODO(b/280518177): The only timeout to fire here should be the BREDR
mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
@@ -778,10 +780,7 @@ public class AdapterServiceTest {
// Do not call stopBle(). The Adapter should turn itself off.
syncHandler(AdapterState.BLE_TURN_OFF);
verifyStateChange(STATE_BLE_ON, STATE_BLE_TURNING_OFF, CONTEXT_SWITCH_MS);
- verify(mMockContext).unbindService(any()); // stop Gatt
-
- mAdapterService.onProfileServiceStateChanged(mMockGattService, STATE_OFF);
- syncHandler(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+ syncHandler(MESSAGE_PROFILE_SERVICE_STATE_CHANGED); // stop GATT
// Native loop is not in TestLooper and it will send a event later
mLooper.startAutoDispatch();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ConfigTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ConfigTest.java
new file mode 100644
index 0000000000..7b97035ed5
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ConfigTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.btservice;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.bluetooth.csip.CsipSetCoordinatorService;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+@RunWith(JUnit4.class)
+public final class ConfigTest {
+ @Test
+ public void setProfileEnabled() {
+ boolean enabled =
+ Arrays.stream(Config.getSupportedProfiles())
+ .anyMatch(cls -> cls == CsipSetCoordinatorService.class);
+
+ Config.setProfileEnabled(CsipSetCoordinatorService.class, false);
+ assertThat(
+ Arrays.stream(Config.getSupportedProfiles())
+ .anyMatch(cls -> cls == CsipSetCoordinatorService.class))
+ .isFalse();
+
+ Config.setProfileEnabled(CsipSetCoordinatorService.class, true);
+ assertThat(
+ Arrays.stream(Config.getSupportedProfiles())
+ .anyMatch(cls -> cls == CsipSetCoordinatorService.class))
+ .isTrue();
+
+ Config.setProfileEnabled(CsipSetCoordinatorService.class, enabled);
+ }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
index 98a5a199de..ce04cd6215 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
@@ -59,7 +59,7 @@ public class PhonePolicyTest {
private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
private static final int CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS = 1000;
private static final int CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS =
- CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS * 3 / 2;
+ CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS * 3;
private HandlerThread mHandlerThread;
private BluetoothAdapter mAdapter;
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 4cc055e35d..15721bb004 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
@@ -16,6 +16,10 @@
package com.android.bluetooth.btservice;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@@ -32,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.bluetooth.gatt.GattService;
import org.junit.After;
import org.junit.Assert;
@@ -78,7 +83,13 @@ public class ProfileServiceTest {
}
private void setAllProfilesState(int state, int invocationNumber) throws TimeoutException {
+ int profileCount = mProfiles.length;
for (Class profile : mProfiles) {
+ if (profile == GattService.class) {
+ // GattService is no longer a service to be start independently
+ profileCount--;
+ continue;
+ }
setProfileState(profile, state);
}
if (invocationNumber == 0) {
@@ -87,11 +98,15 @@ public class ProfileServiceTest {
return;
}
ArgumentCaptor<ProfileService> argument = ArgumentCaptor.forClass(ProfileService.class);
- verify(mMockAdapterService, timeout(PROFILE_START_MILLIS).times(
- mProfiles.length * invocationNumber)).onProfileServiceStateChanged(
- argument.capture(), eq(state));
+ verify(
+ mMockAdapterService,
+ timeout(PROFILE_START_MILLIS).times(profileCount * invocationNumber))
+ .onProfileServiceStateChanged(argument.capture(), eq(state));
List<ProfileService> argumentProfiles = argument.getAllValues();
for (Class profile : mProfiles) {
+ if (profile == GattService.class) {
+ continue;
+ }
int matches = 0;
for (ProfileService arg : argumentProfiles) {
if (arg.getClass().getName().equals(profile.getName())) {
@@ -118,27 +133,41 @@ public class ProfileServiceTest {
return mStartedProfileMap.get((String) args[0]);
}
});
+ doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase();
+
when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mLocationManager);
when(mMockAdapterService.getSystemServiceName(LocationManager.class))
.thenReturn(Context.LOCATION_SERVICE);
+ // Despite calling on the Mock of adapterService, mockito cannot handle native method and
+ // will call the real method instead, allowing to initialize the native library
+ // when(mMockAdapterService.initNative(anyBoolean(), anyBoolean(), anyInt(), any(),
+ // anyBoolean(), anyString())).thenCallRealMethod();
+ doCallRealMethod()
+ .when(mMockAdapterService)
+ .initNative(anyBoolean(), anyBoolean(), anyInt(), any(), anyBoolean(), anyString());
+ doCallRealMethod().when(mMockAdapterService).enableNative();
+ doCallRealMethod().when(mMockAdapterService).disableNative();
+ doCallRealMethod().when(mMockAdapterService).cleanupNative();
+
mProfiles = Config.getSupportedProfiles();
+ TestUtils.setAdapterService(mMockAdapterService);
+
+ Assert.assertNotNull(AdapterService.getAdapterService());
mMockAdapterService.initNative(false /* is_restricted */,
false /* is_common_criteria_mode */, 0 /* config_compare_result */,
new String[0], false, "");
-
- TestUtils.setAdapterService(mMockAdapterService);
- doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase();
-
- Assert.assertNotNull(AdapterService.getAdapterService());
+ mMockAdapterService.enableNative();
}
@After
public void tearDown()
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ mMockAdapterService.disableNative();
mMockAdapterService.cleanupNative();
+
TestUtils.clearAdapterService(mMockAdapterService);
mMockAdapterService = null;
mProfiles = null;
@@ -172,13 +201,18 @@ public class ProfileServiceTest {
*/
@Test
public void testEnableDisableInterleaved() throws TimeoutException {
+ int invocationNumber = mProfiles.length;
for (Class profile : mProfiles) {
+ if (profile == GattService.class) {
+ // GattService is no longer a service to be start independently
+ invocationNumber--;
+ continue;
+ }
setProfileState(profile, BluetoothAdapter.STATE_ON);
setProfileState(profile, BluetoothAdapter.STATE_OFF);
}
ArgumentCaptor<ProfileService> starts = ArgumentCaptor.forClass(ProfileService.class);
ArgumentCaptor<ProfileService> stops = ArgumentCaptor.forClass(ProfileService.class);
- int invocationNumber = mProfiles.length;
verify(mMockAdapterService,
timeout(PROFILE_START_MILLIS).times(invocationNumber)).onProfileServiceStateChanged(
starts.capture(), eq(BluetoothAdapter.STATE_ON));
@@ -204,6 +238,10 @@ public class ProfileServiceTest {
public void testRepeatedEnableDisableSingly() throws TimeoutException {
int profileNumber = 0;
for (Class profile : mProfiles) {
+ if (profile == GattService.class) {
+ // GattService is no longer a service to be start independently
+ continue;
+ }
for (int i = 0; i < NUM_REPEATS; i++) {
setProfileState(profile, BluetoothAdapter.STATE_ON);
ArgumentCaptor<ProfileService> start =
@@ -230,6 +268,10 @@ public class ProfileServiceTest {
public void testProfileServiceRegisterUnregister() throws TimeoutException {
int profileNumber = 0;
for (Class profile : mProfiles) {
+ if (profile == GattService.class) {
+ // GattService is no longer a service to be start independently
+ continue;
+ }
for (int i = 0; i < NUM_REPEATS; i++) {
setProfileState(profile, BluetoothAdapter.STATE_ON);
ArgumentCaptor<ProfileService> start =
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java
index 7f845b5dc8..8a4db9a7a4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java
@@ -16,12 +16,9 @@
package com.android.bluetooth.btservice.activityattribution;
-import static com.google.common.truth.Truth.assertThat;
-
import android.os.Binder;
import android.os.Process;
-import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -30,24 +27,12 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class ActivityAttributionServiceTest {
- private static final String TAG = "ActivityAttributionServiceTest";
private ActivityAttributionService mActivityAttributionService;
@Before
public void setUp() {
Assume.assumeTrue("Ignore test when the user is not primary.", isPrimaryUser());
mActivityAttributionService = new ActivityAttributionService();
- mActivityAttributionService.start();
- assertThat(mActivityAttributionService).isNotNull();
- }
-
- @After
- public void tearDown() {
- if (!isPrimaryUser()) {
- return;
- }
- mActivityAttributionService.cleanup();
- mActivityAttributionService = null;
}
private boolean isPrimaryUser() {
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 f65ab82331..1e78e0c86b 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
@@ -20,15 +20,6 @@ import android.os.Binder;
import android.os.Process;
import android.util.Log;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
@@ -36,12 +27,25 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
@RunWith(JUnit4.class)
public final class BluetoothKeystoreServiceTest {
private static final String TAG = "BluetoothKeystoreServiceTest";
private BluetoothKeystoreService mBluetoothKeystoreService;
+ @Mock private BluetoothKeystoreNativeInterface mMockNativeInterface;
+
// Please also check bt_stack string configuration if you want to change the content.
private static final String CONFIG_FILE_PREFIX = "bt_config-origin";
private static final String CONFIG_BACKUP_PREFIX = "bt_config-backup";
@@ -119,8 +123,9 @@ public final class BluetoothKeystoreServiceTest {
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
Assume.assumeTrue("Ignore test when the user is not primary.", isPrimaryUser());
- mBluetoothKeystoreService = new BluetoothKeystoreService(true);
+ mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, true);
Assert.assertNotNull(mBluetoothKeystoreService);
// backup origin config data.
try {
@@ -280,7 +285,7 @@ public final class BluetoothKeystoreServiceTest {
mBluetoothKeystoreService.cleanupForCommonCriteriaModeEnable();
// new mBluetoothKeystoreService and the Common Criteria mode is false.
- mBluetoothKeystoreService = new BluetoothKeystoreService(false);
+ mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, false);
Assert.assertNotNull(mBluetoothKeystoreService);
mBluetoothKeystoreService.loadConfigData();
@@ -304,7 +309,7 @@ public final class BluetoothKeystoreServiceTest {
mBluetoothKeystoreService.cleanupForCommonCriteriaModeDisable();
// new mBluetoothKeystoreService and the Common Criteria mode is true.
- mBluetoothKeystoreService = new BluetoothKeystoreService(true);
+ mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, true);
mBluetoothKeystoreService.loadConfigData();
Assert.assertTrue(mBluetoothKeystoreService.getCompareResult() == 0);
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 74d0e64365..c7bf403325 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
@@ -16,7 +16,6 @@
package com.android.bluetooth.gatt;
-import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -59,6 +58,8 @@ public class AdvertiseManagerTest {
@Mock
private GattService.AdvertiserMap mAdvertiserMap;
+ @Mock private AdvertiseManagerNativeInterface mNativeInterface;
+
@Mock
private IAdvertisingSetCallback mCallback;
@@ -74,7 +75,9 @@ public class AdvertiseManagerTest {
TestUtils.setAdapterService(mAdapterService);
- mAdvertiseManager = new AdvertiseManager(mService, mAdapterService, mAdvertiserMap);
+ mAdvertiseManager =
+ new AdvertiseManager(mService, mNativeInterface, mAdapterService, mAdvertiserMap);
+
AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
AdvertiseData advertiseData = new AdvertiseData.Builder().build();
AdvertiseData scanResponse = new AdvertiseData.Builder().build();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
index 359ad3104e..b1bed3da63 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
@@ -18,9 +18,6 @@ package com.android.bluetooth.gatt;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
@@ -28,6 +25,7 @@ import android.content.Context;
import android.location.LocationManager;
import android.os.WorkSource;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
@@ -64,28 +62,23 @@ public class AppScanStatsTest {
@Mock
private AdapterService mAdapterService;
- @Mock private LocationManager mLocationManager;
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
TestUtils.setAdapterService(mAdapterService);
- doReturn(true).when(mAdapterService).isStartedProfile(anyString());
- when(mAdapterService.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mLocationManager);
- when(mAdapterService.getSystemServiceName(LocationManager.class))
- .thenReturn(Context.LOCATION_SERVICE);
-
- TestUtils.startService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
+
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.LOCATION_SERVICE, LocationManager.class);
+
+ mService = new GattService(InstrumentationRegistry.getTargetContext());
+ mService.start();
}
@After
public void tearDown() throws Exception {
- doReturn(false).when(mAdapterService).isStartedProfile(anyString());
- TestUtils.stopService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
+ mService.stop();
+ mService = null;
TestUtils.clearAdapterService(mAdapterService);
}
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 3ff84eea8d..e391a38c95 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
@@ -18,10 +18,8 @@ package com.android.bluetooth.gatt;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSetParameters;
@@ -30,6 +28,7 @@ import android.content.Context;
import android.location.LocationManager;
import android.os.Binder;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
@@ -64,8 +63,6 @@ public class ContextMapTest {
@Mock
private AdapterService mAdapterService;
- @Mock private LocationManager mLocationManager;
-
@Mock
private AppAdvertiseStats appAdvertiseStats;
@@ -78,23 +75,20 @@ public class ContextMapTest {
BluetoothMethodProxy.setInstanceForTesting(mMapMethodProxy);
TestUtils.setAdapterService(mAdapterService);
- doReturn(true).when(mAdapterService).isStartedProfile(anyString());
- when(mAdapterService.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mLocationManager);
- when(mAdapterService.getSystemServiceName(LocationManager.class))
- .thenReturn(Context.LOCATION_SERVICE);
-
- TestUtils.startService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
+
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.LOCATION_SERVICE, LocationManager.class);
+
+ mService = new GattService(InstrumentationRegistry.getTargetContext());
+ mService.start();
}
@After
public void tearDown() throws Exception {
BluetoothMethodProxy.setInstanceForTesting(null);
- doReturn(false).when(mAdapterService).isStartedProfile(anyString());
- TestUtils.stopService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
+ mService.stop();
+ mService = null;
TestUtils.clearAdapterService(mAdapterService);
}
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 714e911a41..1c1fda0007 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
@@ -756,13 +756,6 @@ public class GattServiceBinderTest {
}
@Test
- public void unregAll() throws Exception {
- mBinder.unregAll(mAttributionSource);
-
- verify(mService).unregAll(mAttributionSource);
- }
-
- @Test
public void numHwTrackFiltersAvailable() throws Exception {
mBinder.numHwTrackFiltersAvailable(mAttributionSource, SynchronousResultReceiver.get());
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 af9fd7dc99..6d092c0787 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
@@ -25,21 +25,17 @@ import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattService;
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.IAdvertisingSetCallback;
import android.bluetooth.le.IDistanceMeasurementCallback;
import android.bluetooth.le.IPeriodicAdvertisingCallback;
import android.bluetooth.le.IScannerCallback;
import android.bluetooth.le.PeriodicAdvertisingParameters;
-import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.AttributionSource;
@@ -47,7 +43,6 @@ import android.content.Context;
import android.content.res.Resources;
import android.location.LocationManager;
import android.os.Binder;
-import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.WorkSource;
@@ -56,14 +51,12 @@ import androidx.test.filters.SmallTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.CompanionManager;
import org.junit.After;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -97,11 +90,11 @@ public class GattServiceTest {
@Mock private GattService.ScannerMap mScannerMap;
@Mock private GattService.ScannerMap.App mApp;
@Mock private GattService.PendingIntentInfo mPiInfo;
+ @Mock private PeriodicScanManager mPeriodicScanManager;
@Mock private ScanManager mScanManager;
@Mock private Set<String> mReliableQueue;
@Mock private GattService.ServerMap mServerMap;
@Mock private DistanceMeasurementManager mDistanceMeasurementManager;
- @Mock private LocationManager mLocationManager;
@Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
@@ -122,10 +115,13 @@ public class GattServiceTest {
MockitoAnnotations.initMocks(this);
TestUtils.setAdapterService(mAdapterService);
- doReturn(true).when(mAdapterService).isStartedProfile(anyString());
GattObjectsFactory.setInstanceForTesting(mFactory);
doReturn(mNativeInterface).when(mFactory).getNativeInterface();
+ doReturn(mScanManager).when(mFactory).createScanManager(any(), any(), any());
+ doReturn(mPeriodicScanManager).when(mFactory).createPeriodicScanManager(any());
+ doReturn(mDistanceMeasurementManager).when(mFactory)
+ .createDistanceMeasurementManager(any());
mAdapter = BluetoothAdapter.getDefaultAdapter();
mAttributionSource = mAdapter.getAttributionSource();
@@ -136,62 +132,43 @@ public class GattServiceTest {
when(mAdapterService.getSharedPreferences(anyString(), anyInt()))
.thenReturn(InstrumentationRegistry.getTargetContext()
.getSharedPreferences("GattServiceTestPrefs", Context.MODE_PRIVATE));
- when(mAdapterService.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mLocationManager);
- when(mAdapterService.getSystemServiceName(LocationManager.class))
- .thenReturn(Context.LOCATION_SERVICE);
+
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.LOCATION_SERVICE, LocationManager.class);
mBtCompanionManager = new CompanionManager(mAdapterService, null);
doReturn(mBtCompanionManager).when(mAdapterService).getCompanionManager();
- TestUtils.startService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- Assert.assertNotNull(mService);
+ mService = new GattService(InstrumentationRegistry.getTargetContext());
+ mService.start();
mService.mClientMap = mClientMap;
mService.mScannerMap = mScannerMap;
- mService.mScanManager = mScanManager;
mService.mReliableQueue = mReliableQueue;
mService.mServerMap = mServerMap;
- mService.mDistanceMeasurementManager = mDistanceMeasurementManager;
}
@After
public void tearDown() throws Exception {
- doReturn(false).when(mAdapterService).isStartedProfile(anyString());
- TestUtils.stopService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- Assert.assertNull(mService);
+ mService.stop();
+ mService = null;
+
TestUtils.clearAdapterService(mAdapterService);
GattObjectsFactory.setInstanceForTesting(null);
}
@Test
- public void testInitialize() {
- Assert.assertEquals(mService, GattService.getGattService());
- verify(mNativeInterface).init(eq(mService));
- }
-
- @Test
public void testServiceUpAndDown() throws Exception {
for (int i = 0; i < TIMES_UP_AND_DOWN; i++) {
- GattService gattService = GattService.getGattService();
- doReturn(false).when(mAdapterService).isStartedProfile(anyString());
- TestUtils.stopService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- Assert.assertNull(mService);
- gattService.cleanup();
+ mService.stop();
+ mService = null;
+
TestUtils.clearAdapterService(mAdapterService);
reset(mAdapterService);
TestUtils.setAdapterService(mAdapterService);
- doReturn(true).when(mAdapterService).isStartedProfile(anyString());
- when(mAdapterService.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mLocationManager);
- when(mAdapterService.getSystemServiceName(LocationManager.class))
- .thenReturn(Context.LOCATION_SERVICE);
- TestUtils.startService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- Assert.assertNotNull(mService);
+
+ mService = new GattService(InstrumentationRegistry.getTargetContext());
+ mService.start();
}
}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
index cb9a42d2d6..42d26f77b0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
@@ -29,7 +29,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doReturn;
@@ -76,6 +75,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
/**
* Test cases for {@link ScanManager}.
@@ -104,7 +104,7 @@ public class ScanManagerTest {
@Mock private AdapterService mAdapterService;
@Mock private BluetoothAdapterProxy mBluetoothAdapterProxy;
@Mock private LocationManager mLocationManager;
- @Mock private GattObjectsFactory mFactory;
+ @Spy private GattObjectsFactory mFactory = GattObjectsFactory.getInstance();
@Mock private GattNativeInterface mNativeInterface;
@Mock private ScanNativeInterface mScanNativeInterface;
@Mock private MetricsLogger mMetricsLogger;
@@ -117,17 +117,15 @@ public class ScanManagerTest {
MockitoAnnotations.initMocks(this);
TestUtils.setAdapterService(mAdapterService);
- doReturn(true).when(mAdapterService).isStartedProfile(anyString());
when(mAdapterService.getScanTimeoutMillis()).
thenReturn((long) DELAY_DEFAULT_SCAN_TIMEOUT_MS);
when(mAdapterService.getNumOfOffloadedScanFilterSupported())
.thenReturn(DEFAULT_NUM_OFFLOAD_SCAN_FILTER);
when(mAdapterService.getOffloadedScanResultStorage())
.thenReturn(DEFAULT_BYTES_OFFLOAD_SCAN_RESULT_STORAGE);
- when(mAdapterService.getSystemService(Context.LOCATION_SERVICE))
- .thenReturn(mLocationManager);
- when(mAdapterService.getSystemServiceName(LocationManager.class))
- .thenReturn(Context.LOCATION_SERVICE);
+
+ TestUtils.mockGetSystemService(
+ mAdapterService, Context.LOCATION_SERVICE, LocationManager.class, mLocationManager);
doReturn(true).when(mLocationManager).isLocationEnabled();
BluetoothAdapterProxy.setInstanceForTesting(mBluetoothAdapterProxy);
@@ -142,9 +140,8 @@ public class ScanManagerTest {
MetricsLogger.setInstanceForTesting(mMetricsLogger);
- TestUtils.startService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- assertThat(mService).isNotNull();
+ mService = new GattService(InstrumentationRegistry.getTargetContext());
+ mService.start();
mScanManager = mService.getScanManager();
assertThat(mScanManager).isNotNull();
@@ -160,10 +157,9 @@ public class ScanManagerTest {
@After
public void tearDown() throws Exception {
- doReturn(false).when(mAdapterService).isStartedProfile(anyString());
- TestUtils.stopService(mServiceRule, GattService.class);
- mService = GattService.getGattService();
- assertThat(mService).isNull();
+ mService.stop();
+ mService = null;
+
TestUtils.clearAdapterService(mAdapterService);
BluetoothAdapterProxy.setInstanceForTesting(null);
GattObjectsFactory.setInstanceForTesting(null);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
index 3fb878c7a6..d7c8e61b4b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
@@ -28,13 +28,11 @@ import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.content.Context;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.Message;
import android.test.suitebuilder.annotation.MediumTest;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
@@ -55,7 +53,6 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class HapClientStateMachineTest {
private BluetoothAdapter mAdapter;
- private Context mTargetContext;
private HandlerThread mHandlerThread;
private HapClientStateMachine mHapClientStateMachine;
private BluetoothDevice mTestDevice;
@@ -70,7 +67,6 @@ public class HapClientStateMachineTest {
@Before
public void setUp() throws Exception {
- mTargetContext = InstrumentationRegistry.getTargetContext();
// Set up mocks and test assets
MockitoAnnotations.initMocks(this);
TestUtils.setAdapterService(mAdapterService);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
index 0405a79edc..32e643541b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
@@ -86,8 +86,7 @@ public class HapClientTest {
private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false");
private static final int TIMEOUT_MS = 1000;
- @Rule
- public final ServiceTestRule mServiceRule = new ServiceTestRule();
+ @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
private BluetoothAdapter mAdapter;
private BluetoothDevice mDevice;
private BluetoothDevice mDevice2;
@@ -99,20 +98,13 @@ public class HapClientTest {
private HasIntentReceiver mHasIntentReceiver;
private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue;
- @Mock
- private AdapterService mAdapterService;
- @Mock
- private DatabaseManager mDatabaseManager;
- @Mock
- private HapClientNativeInterface mNativeInterface;
- @Mock
- private ServiceFactory mServiceFactory;
- @Mock
- private CsipSetCoordinatorService mCsipService;
- @Mock
- private IBluetoothHapClientCallback mCallback;
- @Mock
- private Binder mBinder;
+ @Mock private AdapterService mAdapterService;
+ @Mock private DatabaseManager mDatabaseManager;
+ @Mock private HapClientNativeInterface mNativeInterface;
+ @Mock private ServiceFactory mServiceFactory;
+ @Mock private CsipSetCoordinatorService mCsipService;
+ @Mock private IBluetoothHapClientCallback mCallback;
+ @Mock private Binder mBinder;
@Before
public void setUp() throws Exception {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index 645528b0d1..44b6bfa362 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -44,6 +44,7 @@ import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
@@ -80,6 +81,7 @@ public class HearingAidServiceTest {
private BroadcastReceiver mHearingAidIntentReceiver;
@Mock private AdapterService mAdapterService;
+ @Mock private ActiveDeviceManager mActiveDeviceManager;
@Mock private DatabaseManager mDatabaseManager;
@Mock private HearingAidNativeInterface mNativeInterface;
@Mock private AudioManager mAudioManager;
@@ -97,6 +99,7 @@ public class HearingAidServiceTest {
}
TestUtils.setAdapterService(mAdapterService);
+ doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
index d75199350a..185a7aa772 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
@@ -41,6 +41,7 @@ import android.os.ParcelUuid;
import android.os.PowerManager;
import android.os.RemoteException;
import android.telecom.PhoneAccount;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.intent.Intents;
@@ -82,9 +83,11 @@ import java.util.concurrent.LinkedBlockingQueue;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class HeadsetServiceAndStateMachineTest {
+ private static final String TAG = "HeadsetServiceAndStateMachineTest";
private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
private static final int START_VR_TIMEOUT_MILLIS = 1000;
private static final int START_VR_TIMEOUT_WAIT_MILLIS = START_VR_TIMEOUT_MILLIS * 3 / 2;
+ private static final int STATE_CHANGE_TIMEOUT_MILLIS = 1000;
private static final int MAX_HEADSET_CONNECTIONS = 5;
private static final ParcelUuid[] FAKE_HEADSET_UUID = {BluetoothUuid.HFP};
private static final String TEST_PHONE_NUMBER = "1234567890";
@@ -470,7 +473,7 @@ public class HeadsetServiceAndStateMachineTest {
BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
Assert.assertTrue(mHeadsetService.setActiveDevice(activeDevice));
verify(mNativeInterface).setActiveDevice(activeDevice);
- waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
+ verify(mActiveDeviceManager).hfpActiveStateChanged(activeDevice);
Assert.assertEquals(activeDevice, mHeadsetService.getActiveDevice());
// Start virtual call
Assert.assertTrue(mHeadsetService.startScoUsingVirtualVoiceCall());
@@ -1176,11 +1179,11 @@ public class HeadsetServiceAndStateMachineTest {
verify(mObjectsFactory).makeStateMachine(device,
mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
mNativeInterface, mSystemInterface);
- // 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
- waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
- BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
+ verify(mActiveDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
+ .hfpConnectionStateChanged(
+ device,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
mHeadsetService.getConnectionState(device));
Assert.assertEquals(Collections.singletonList(device),
@@ -1191,11 +1194,11 @@ public class HeadsetServiceAndStateMachineTest {
new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
mHeadsetService.messageFromNative(slcConnectedEvent);
- // 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
- waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
+ verify(mActiveDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
+ .hfpConnectionStateChanged(
+ device,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_CONNECTED);
Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
mHeadsetService.getConnectionState(device));
}
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 008fc0d349..b689546d9b 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
@@ -56,6 +56,7 @@ import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -109,6 +110,7 @@ public class LeAudioServiceTest {
private BroadcastReceiver mLeAudioIntentReceiver;
@Mock private AdapterService mAdapterService;
+ @Mock private ActiveDeviceManager mActiveDeviceManager;
@Mock private AudioManager mAudioManager;
@Mock private DatabaseManager mDatabaseManager;
@Mock private LeAudioNativeInterface mNativeInterface;
@@ -169,6 +171,7 @@ public class LeAudioServiceTest {
doReturn(MAX_LE_AUDIO_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices();
doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService)
.getRemoteUuids(any(BluetoothDevice.class));
+ doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
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 e75f2fda45..ac69b84dfa 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,15 +17,17 @@
package com.android.bluetooth.le_audio;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
-import static com.google.common.truth.Truth.assertThat;
-
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -45,7 +47,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -146,12 +147,10 @@ public class LeAudioStateMachineTest {
connStCh.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTING;
mLeAudioStateMachine.sendMessage(LeAudioStateMachine.STACK_EVENT, connStCh);
- // Verify that one connection state broadcast is executed
- ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(TIMEOUT_MS).times(1)).sendBroadcast(
- intentArgument1.capture(), anyString(), any(Bundle.class));
- assertThat(BluetoothProfile.STATE_CONNECTING).isEqualTo(
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ // Verify that one connection state change is notifyed
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_CONNECTING), anyInt());
// Check that we are in Connecting state
assertThat(mLeAudioStateMachine.getCurrentState())
@@ -164,11 +163,14 @@ public class LeAudioStateMachineTest {
connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
mLeAudioStateMachine.sendMessage(LeAudioStateMachine.STACK_EVENT, connCompletedEvent);
- // Verify that the expected number of broadcasts are executed:
- // - two calls to broadcastConnectionState(): Disconnected -> Conecting -> Connected
- ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(TIMEOUT_MS).times(2)).sendBroadcast(
- intentArgument2.capture(), anyString(), any(Bundle.class));
+ // Verify that the expected number of notification are called:
+ // - two calls to notifyConnectionStateChanged(): Disconnected -> Connecting -> Connected
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_CONNECTING), anyInt());
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_CONNECTED), anyInt());
// Check that we are in Connected state
assertThat(mLeAudioStateMachine.getCurrentState())
.isInstanceOf(LeAudioStateMachine.Connected.class);
@@ -188,25 +190,19 @@ public class LeAudioStateMachineTest {
// Send a connect request
mLeAudioStateMachine.sendMessage(LeAudioStateMachine.CONNECT, mTestDevice);
- // Verify that one connection state broadcast is executed
- ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(TIMEOUT_MS).times(1)).sendBroadcast(
- intentArgument1.capture(),
- anyString(), any(Bundle.class));
- assertThat(BluetoothProfile.STATE_CONNECTING).isEqualTo(
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_CONNECTING), anyInt());
// Check that we are in Connecting state
assertThat(mLeAudioStateMachine.getCurrentState())
.isInstanceOf(LeAudioStateMachine.Connecting.class);
- // Verify that one connection state broadcast is executed
- ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(LeAudioStateMachine.sConnectTimeoutMs * 2).times(
- 2)).sendBroadcast(intentArgument2.capture(), anyString(),
- any(Bundle.class));
- assertThat(BluetoothProfile.STATE_DISCONNECTED).isEqualTo(
- intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(LeAudioStateMachine.sConnectTimeoutMs * 2))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_DISCONNECTED), anyInt());
// Check that we are in Disconnected state
assertThat(mLeAudioStateMachine.getCurrentState())
@@ -231,25 +227,19 @@ public class LeAudioStateMachineTest {
connStCh.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTING;
mLeAudioStateMachine.sendMessage(LeAudioStateMachine.STACK_EVENT, connStCh);
- // Verify that one connection state broadcast is executed
- ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(TIMEOUT_MS).times(1)).sendBroadcast(
- intentArgument1.capture(),
- anyString(), any(Bundle.class));
- assertThat(BluetoothProfile.STATE_CONNECTING).isEqualTo(
- intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(TIMEOUT_MS))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_CONNECTING), anyInt());
// Check that we are in Connecting state
assertThat(mLeAudioStateMachine.getCurrentState())
.isInstanceOf(LeAudioStateMachine.Connecting.class);
- // Verify that one connection state broadcast is executed
- ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
- verify(mLeAudioService, timeout(LeAudioStateMachine.sConnectTimeoutMs * 2).times(
- 2)).sendBroadcast(intentArgument2.capture(), anyString(),
- any(Bundle.class));
- assertThat(intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1))
- .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+ // Verify that one connection state change is notified
+ verify(mLeAudioService, timeout(LeAudioStateMachine.sConnectTimeoutMs * 2))
+ .notifyConnectionStateChanged(
+ any(), eq(BluetoothProfile.STATE_DISCONNECTED), anyInt());
// Check that we are in Disconnected state
assertThat(mLeAudioStateMachine.getCurrentState())
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 953d93249a..d61d3de936 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
@@ -49,7 +49,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
@MediumTest
@RunWith(AndroidJUnit4.class)
public class BluetoothOppServiceTest {
@@ -90,7 +89,11 @@ public class BluetoothOppServiceTest {
// Since the update thread is not run (we mocked it), it will not clean itself on interrupt
// (normally, the service will wait for the update thread to clean itself after
// being interrupted). We clean it manually here
- mService.mUpdateThread = null;
+ BluetoothOppService service = mService;
+ if (service != null) {
+ service.mUpdateThread = null;
+ }
+
BluetoothMethodProxy.setInstanceForTesting(null);
TestUtils.stopService(mServiceRule, BluetoothOppService.class);
TestUtils.clearAdapterService(mAdapterService);
@@ -187,5 +190,4 @@ public class BluetoothOppServiceTest {
eq(BluetoothShare._ID + " < " + 20),
any());
}
-}
-
+} \ No newline at end of file
diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json
index cadb57dfd0..51f8f39f33 100644
--- a/android/pandora/server/configs/pts_bot_tests_config.json
+++ b/android/pandora/server/configs/pts_bot_tests_config.json
@@ -127,7 +127,6 @@
"AVRCP/TG/MCN/CB/BV-02-I",
"AVRCP/TG/MCN/CB/BV-03-I",
"AVRCP/TG/MCN/CB/BV-05-C",
- "AVRCP/TG/MCN/CB/BV-06-C",
"AVRCP/TG/MCN/CB/BV-06-I",
"AVRCP/TG/MCN/CB/BV-08-C",
"AVRCP/TG/MCN/NP/BI-01-C",
@@ -535,10 +534,6 @@
"MAP/MSE/MSM/BV-06-I",
"MAP/MSE/MSM/BV-07-I",
"MAP/MSE/MSM/BV-08-I",
- "OPP/CL/GOEP/BC/BV-02-I",
- "OPP/CL/GOEP/CON/BV-01-C",
- "OPP/CL/OPH/BV-01-I",
- "OPP/CL/OPH/BV-34-I",
"OPP/SR/GOEP/BC/BV-01-I",
"OPP/SR/GOEP/CON/BV-02-C",
"OPP/SR/GOEP/ROB/BV-01-C",
@@ -560,7 +555,6 @@
"PBAP/PSE/GOEP/SRM/BI-03-C",
"PBAP/PSE/GOEP/SRM/BI-05-C",
"PBAP/PSE/GOEP/SRM/BV-08-C",
- "PBAP/PSE/GOEP/SRMP/BI-02-C",
"PBAP/PSE/GOEP/SRMP/BV-02-C",
"PBAP/PSE/PBB/BI-01-C",
"PBAP/PSE/PBB/BI-07-C",
@@ -734,6 +728,7 @@
"AVRCP/CT/PTT/BV-01-I",
"AVRCP/TG/MCN/CB/BI-02-C",
"AVRCP/TG/MCN/CB/BV-04-I",
+ "AVRCP/TG/MCN/CB/BV-06-C",
"AVRCP/TG/MCN/CB/BV-09-C",
"AVRCP/TG/NFY/BV-04-C",
"BNEP/TX-TYPE-0/BV-20-C",
@@ -912,6 +907,10 @@
"MAP/MSE/MMN/BV-07-I",
"MAP/MSE/MMN/BV-14-I",
"MAP/MSE/MMU/BV-02-I",
+ "OPP/CL/GOEP/BC/BV-02-I",
+ "OPP/CL/GOEP/CON/BV-01-C",
+ "OPP/CL/OPH/BV-01-I",
+ "OPP/CL/OPH/BV-34-I",
"OPP/SR/BCP/BV-02-I",
"OPP/SR/GOEP/ROB/BV-02-C",
"OPP/SR/OPH/BV-10-I",
@@ -924,6 +923,7 @@
"PAN/NAP/MISC/UUID/BV-02-C",
"PAN/PANU/IP/APP/BV-04-I",
"PAN/PANU/IPv4/AUTONET/BV-01-I",
+ "PBAP/PSE/GOEP/SRMP/BI-02-C",
"PBAP/PSE/SSM/BV-07-C",
"RFCOMM/DEVA-DEVB/RFC/BV-04-C",
"RFCOMM/DEVA-DEVB/RFC/BV-07-C",
diff --git a/android/pandora/server/src/AndroidInternal.kt b/android/pandora/server/src/AndroidInternal.kt
index 0418e4e09c..c0392d4308 100644
--- a/android/pandora/server/src/AndroidInternal.kt
+++ b/android/pandora/server/src/AndroidInternal.kt
@@ -60,8 +60,8 @@ class AndroidInternal(val context: Context) : AndroidImplBase(), Closeable {
// PTS does not configure the Extended Inquiry Response with the
// device name; the device will be found after the Inquiry Timeout
- // (12.8sec) has elapsed.
- private val BT_DEVICE_SELECT_WAIT_TIMEOUT = 20000L
+ // (40 secondes) has elapsed.
+ private val BT_DEVICE_SELECT_WAIT_TIMEOUT = 40000L
private val IMAGE_FILE_NAME = "OPP_TEST_IMAGE.bmp"
private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!!
diff --git a/android/pandora/server/src/Gatt.kt b/android/pandora/server/src/Gatt.kt
index c683dd705a..6ca2fe7370 100644
--- a/android/pandora/server/src/Gatt.kt
+++ b/android/pandora/server/src/Gatt.kt
@@ -31,9 +31,7 @@ import java.io.Closeable
import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -276,18 +274,15 @@ class Gatt(private val context: Context) : GATTImplBase(), Closeable {
service.addCharacteristic(characteristic)
}
- val fullService = coroutineScope {
- val firstService = mScope.async { serverManager.newServiceFlow.first() }
- serverManager.server.addService(service)
- firstService.await()
- }
+ serverManager.server.addService(service)
+ val addedService = serverManager.serviceFlow.first()
RegisterServiceResponse.newBuilder()
.setService(
GattService.newBuilder()
- .setHandle(fullService.instanceId)
- .setType(fullService.type)
- .setUuid(fullService.uuid.toString().uppercase())
+ .setHandle(addedService.instanceId)
+ .setType(addedService.type)
+ .setUuid(addedService.uuid.toString().uppercase())
.addAllIncludedServices(generateServicesList(service.includedServices, 1))
.addAllCharacteristics(generateCharacteristicsList(service.characteristics))
.build()
diff --git a/android/pandora/server/src/GattServerManager.kt b/android/pandora/server/src/GattServerManager.kt
index 89f3f110f5..b8085e0f3a 100644
--- a/android/pandora/server/src/GattServerManager.kt
+++ b/android/pandora/server/src/GattServerManager.kt
@@ -28,16 +28,17 @@ import android.util.Log
import java.util.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
class GattServerManager(
bluetoothManager: BluetoothManager,
context: Context,
- globalScope: CoroutineScope
+ scope: CoroutineScope
) {
val TAG = "PandoraGattServerManager"
val services = mutableMapOf<UUID, BluetoothGattService>()
- val newServiceFlow = MutableSharedFlow<BluetoothGattService>(extraBufferCapacity = 8)
+ val serviceFlow = MutableSharedFlow<BluetoothGattService>()
var negociatedMtu = -1
val callback =
@@ -45,7 +46,7 @@ class GattServerManager(
override fun onServiceAdded(status: Int, service: BluetoothGattService) {
Log.i(TAG, "onServiceAdded status=$status")
check(status == BluetoothGatt.GATT_SUCCESS)
- check(newServiceFlow.tryEmit(service))
+ scope.launch { serviceFlow.emit(service) }
}
override fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
Log.i(TAG, "onMtuChanged mtu=$mtu")
diff --git a/android/pandora/test/AndroidTest.xml b/android/pandora/test/AndroidTest.xml
index 9906aeac75..29353f27ae 100644
--- a/android/pandora/test/AndroidTest.xml
+++ b/android/pandora/test/AndroidTest.xml
@@ -23,9 +23,7 @@
<option name="install-arg" value="-r" />
<option name="install-arg" value="-g" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunHostCommandTargetPreparer">
- <option name="host-setup-command" value="adb -s $SERIAL forward tcp:6211 vsock:2:7300" />
- <option name="host-teardown-command" value="adb -s $SERIAL forward --remove tcp:6211" />
+ <target_preparer class="com.android.tradefed.targetprep.RootcanalForwarderPreparer">
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="cmd bluetooth_manager enable" />
diff --git a/android/pandora/test/asha_test.py b/android/pandora/test/asha_test.py
index 73f75d82bb..83b8d456d8 100644
--- a/android/pandora/test/asha_test.py
+++ b/android/pandora/test/asha_test.py
@@ -16,10 +16,7 @@ import asyncio
import avatar
import enum
import grpc
-import inspect
-import itertools
import logging
-import math
import numpy as np
from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices, asynchronous
@@ -34,7 +31,7 @@ from mobly.asserts import assert_in # type: ignore
from mobly.asserts import assert_is_not_none # type: ignore
from mobly.asserts import assert_not_equal # type: ignore
from mobly.asserts import assert_true # type: ignore
-from pandora._utils import AioStream, Stream
+from pandora._utils import AioStream
from pandora.host_pb2 import PUBLIC, RANDOM, AdvertiseResponse, Connection, DataTypes, OwnAddressType, ScanningResponse
from pandora.security_pb2 import LE_LEVEL3
from pandora_experimental.asha_grpc_aio import Asha as AioAsha, add_AshaServicer_to_server
@@ -868,6 +865,9 @@ class AshaTest(base_test.BaseTestClass): # type: ignore[misc]
Verify that DUT sends a correct AudioControlPoint `Stop` command.
"""
+ # TODO(b/290204194) Re-activate this test ASAP
+ raise signals.TestSkip('TODO(b/290204194) Re-activate this test ASAP')
+
async def ref_device_connect(ref_device: BumblePandoraDevice, ear: Ear) -> Tuple[Connection, Connection]:
advertisement = await self.ref_advertise_asha(ref_device=ref_device, ref_address_type=RANDOM, ear=ear)
ref = await self.dut_scan_for_asha(dut_address_type=RANDOM, ear=ear)
@@ -1064,6 +1064,9 @@ class AshaTest(base_test.BaseTestClass): # type: ignore[misc]
Verify Refs cannot recevice audio data after DUT stops media streaming.
"""
+ # TODO(b/290204194) Re-activate this test ASAP
+ raise signals.TestSkip('TODO(b/290204194) Re-activate this test ASAP')
+
async def ref_device_connect(ref_device: BumblePandoraDevice, ear: Ear) -> Tuple[Connection, Connection]:
advertisement = await self.ref_advertise_asha(ref_device=ref_device, ref_address_type=RANDOM, ear=ear)
ref = await self.dut_scan_for_asha(dut_address_type=RANDOM, ear=ear)
diff --git a/android/pandora/test/config.yml b/android/pandora/test/config.yml
index 3682981ff2..3dd993b15d 100644
--- a/android/pandora/test/config.yml
+++ b/android/pandora/test/config.yml
@@ -5,8 +5,8 @@ TestBeds:
Controllers:
AndroidDevice: '*'
BumbleDevice:
- - transport: 'tcp-client:127.0.0.1:6211'
- - transport: 'tcp-client:127.0.0.1:6211'
+ - tcp: ${ROOTCANAL_HCI_ADDRESS}
+ - tcp: ${ROOTCANAL_HCI_ADDRESS}
- Name: bumble.bumbles
Controllers:
BumbleDevice:
diff --git a/android/pandora/test/gatt_test.py b/android/pandora/test/gatt_test.py
index df8b912cc4..09c4dcf0ef 100644
--- a/android/pandora/test/gatt_test.py
+++ b/android/pandora/test/gatt_test.py
@@ -14,6 +14,7 @@
import asyncio
import avatar
+import grpc
import logging
from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices
@@ -26,6 +27,7 @@ from mobly.asserts import assert_equal # type: ignore
from mobly.asserts import assert_in # type: ignore
from mobly.asserts import assert_is_not_none # type: ignore
from mobly.asserts import assert_not_in # type: ignore
+from mobly.asserts import assert_true # type: ignore
from pandora.host_pb2 import RANDOM, Connection, DataTypes
from pandora.security_pb2 import LE_LEVEL3, PairingEventAnswer, SecureResponse
from pandora_experimental.gatt_grpc import GATT
@@ -238,6 +240,59 @@ class GattTest(base_test.BaseTestClass): # type: ignore[misc]
assert_in(SERVICE_UUID_1, (service.uuid for service in second_discovery.services))
assert_in(SERVICE_UUID_2, (service.uuid for service in second_discovery.services))
+ @avatar.asynchronous
+ async def test_eatt_when_not_encrypted_no_timeout(self) -> None:
+ if not isinstance(self.ref, BumblePandoraDevice):
+ raise signals.TestSkip('Test require Bumble as reference device(s)')
+ advertise = self.dut.aio.host.Advertise(
+ legacy=True,
+ connectable=True,
+ own_address_type=RANDOM,
+ data=DataTypes(manufacturer_specific_data=b'pause cafe'),
+ )
+
+ scan = self.ref.aio.host.Scan()
+ dut = await anext((x async for x in scan if b'pause cafe' in x.data.manufacturer_specific_data))
+ scan.cancel()
+
+ ref_dut = (await self.ref.aio.host.ConnectLE(own_address_type=RANDOM, **dut.address_asdict())).connection
+ assert_is_not_none(ref_dut)
+ assert ref_dut
+ advertise.cancel()
+
+ connection = self.ref.device.lookup_connection(int.from_bytes(ref_dut.cookie.value, 'big'))
+ assert connection
+
+ connection_request = (
+ b"\x17" # code of L2CAP_CREDIT_BASED_CONNECTION_REQ
+ b"\x01" # identifier
+ b"\x0a\x00" # data length
+ b"\x27\x00" # psm(EATT)
+ b"\x64\x00" # MTU
+ b"\x64\x00" # MPS
+ b"\x64\x00" # initial credit
+ b"\x40\x00" # source cid[0]
+ )
+
+ fut = asyncio.get_running_loop().create_future()
+ setattr(self.ref.device.l2cap_channel_manager, "on_[0x18]", lambda _, _1, frame: fut.set_result(frame))
+ self.ref.device.l2cap_channel_manager.send_control_frame( # type:ignore
+ connection, 0x05, connection_request
+ )
+ control_frame = await fut
+
+ assert_equal(bytes(control_frame)[10], 0x05) # All connections refused – insufficient authentication
+ assert_true(await is_connected(self.ref, ref_dut), "Device is no longer connected")
+
+
+async def is_connected(device: PandoraDevice, connection: Connection) -> bool:
+ try:
+ await device.aio.host.WaitDisconnection(connection=connection, timeout=5)
+ return False
+ except grpc.RpcError as e:
+ assert_equal(e.code(), grpc.StatusCode.DEADLINE_EXCEEDED) # type: ignore
+ return True
+
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
diff --git a/android/pandora/test/hfpclient_test.py b/android/pandora/test/hfpclient_test.py
index e32e539bd7..d655590529 100644
--- a/android/pandora/test/hfpclient_test.py
+++ b/android/pandora/test/hfpclient_test.py
@@ -24,32 +24,32 @@ from bumble.core import (
BT_L2CAP_PROTOCOL_ID,
BT_RFCOMM_PROTOCOL_ID,
)
-from bumble.rfcomm import Server as RfcommServer, DLC
+from bumble.hfp import HfpProtocol
+from bumble.rfcomm import DLC, Server as RfcommServer
from bumble.sdp import (
- DataElement,
- ServiceAttribute,
+ SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
- SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+ DataElement,
+ ServiceAttribute,
)
-from bumble.hfp import HfpProtocol
from mobly import base_test, test_runner
from mobly.asserts import assert_equal # type: ignore
-from mobly.asserts import assert_not_equal # type: ignore
from mobly.asserts import assert_in # type: ignore
+from mobly.asserts import assert_not_equal # type: ignore
from mobly.asserts import assert_not_in # type: ignore
from pandora.host_pb2 import Connection as PandoraConnection
from pandora.security_pb2 import LEVEL2
-from typing import Optional, Tuple, List, Dict
+from typing import Dict, List, Optional, Tuple
SDP_PROFILE_SUPPORTED_FEATURES_ID = 0x0311
-HFP_AG_FEATURE_HF_INDICATORS = (1 << 10)
+HFP_AG_FEATURE_HF_INDICATORS = 1 << 10
HFP_AG_FEATURE_DEFAULT = HFP_AG_FEATURE_HF_INDICATORS
-HFP_HF_FEATURE_HF_INDICATORS = (1 << 8)
-HFP_HF_FEATURE_DEFAULT = hex(0x01b5)
+HFP_HF_FEATURE_HF_INDICATORS = 1 << 8
+HFP_HF_FEATURE_DEFAULT = hex(0x01B5)
PROPERTY_HF_ENABLED = 'bluetooth.profile.hfp.hf.enabled'
PROPERTY_HF_FEATURES = 'bluetooth.hfp.hf_client_features.config'
@@ -128,8 +128,8 @@ class HfpClientTest(base_test.BaseTestClass): # type: ignore[misc]
def on_dlc(dlc: DLC) -> None:
dlc_connected.set_result(dlc)
- rfcomm_server = RfcommServer(self.ref.device)
- channel_number = rfcomm_server.listen(on_dlc)
+ rfcomm_server = RfcommServer(self.ref.device) # type: ignore
+ channel_number = rfcomm_server.listen(on_dlc) # type: ignore
# Setup SDP records
self.ref.device.sdp_service_records = make_bumble_ag_sdp_records(HFP_VERSION_1_7, channel_number, 0)
@@ -142,7 +142,7 @@ class HfpClientTest(base_test.BaseTestClass): # type: ignore[misc]
dlc = await dlc_connected
assert isinstance(dlc, DLC)
- return HfpProtocol(dlc)
+ return HfpProtocol(dlc) # type: ignore
@avatar.parameterized((True,), (False,)) # type: ignore[misc]
@avatar.asynchronous
@@ -155,7 +155,6 @@ class HfpClientTest(base_test.BaseTestClass): # type: ignore[misc]
ref_dut_hfp_protocol = await self.make_hfp_connection()
class TestAgServer(HfpAgServer):
-
def on_brsf(self, hf_features: int) -> None:
# HF indicators should be enabled
assert_not_equal(hf_features & HFP_HF_FEATURE_HF_INDICATORS, 0)
@@ -172,8 +171,9 @@ class HfpClientTest(base_test.BaseTestClass): # type: ignore[misc]
await server.serve()
-def make_bumble_ag_sdp_records(hfp_version: int, rfcomm_channel: int,
- ag_sdp_features: int) -> Dict[int, List[ServiceAttribute]]:
+def make_bumble_ag_sdp_records(
+ hfp_version: int, rfcomm_channel: int, ag_sdp_features: int
+) -> Dict[int, List[ServiceAttribute]]:
return {
0x00010001: [
ServiceAttribute(
@@ -182,29 +182,39 @@ def make_bumble_ag_sdp_records(hfp_version: int, rfcomm_channel: int,
),
ServiceAttribute(
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
- DataElement.sequence([
- DataElement.uuid(BT_HANDSFREE_AUDIO_GATEWAY_SERVICE),
- DataElement.uuid(BT_GENERIC_AUDIO_SERVICE),
- ]),
+ DataElement.sequence(
+ [
+ DataElement.uuid(BT_HANDSFREE_AUDIO_GATEWAY_SERVICE),
+ DataElement.uuid(BT_GENERIC_AUDIO_SERVICE),
+ ]
+ ),
),
ServiceAttribute(
SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
- DataElement.sequence([
- DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]),
- DataElement.sequence([
- DataElement.uuid(BT_RFCOMM_PROTOCOL_ID),
- DataElement.unsigned_integer_8(rfcomm_channel),
- ]),
- ]),
+ DataElement.sequence(
+ [
+ DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]),
+ DataElement.sequence(
+ [
+ DataElement.uuid(BT_RFCOMM_PROTOCOL_ID),
+ DataElement.unsigned_integer_8(rfcomm_channel),
+ ]
+ ),
+ ]
+ ),
),
ServiceAttribute(
SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
- DataElement.sequence([
- DataElement.sequence([
- DataElement.uuid(BT_HANDSFREE_AUDIO_GATEWAY_SERVICE),
- DataElement.unsigned_integer_16(hfp_version),
- ])
- ]),
+ DataElement.sequence(
+ [
+ DataElement.sequence(
+ [
+ DataElement.uuid(BT_HANDSFREE_AUDIO_GATEWAY_SERVICE),
+ DataElement.unsigned_integer_16(hfp_version),
+ ]
+ )
+ ]
+ ),
),
ServiceAttribute(
SDP_PROFILE_SUPPORTED_FEATURES_ID,
@@ -224,17 +234,20 @@ class HfpAgServer:
self.terminated = False
self.hf_features = 0 # Unknown
+ def send_response_line(self, response: str) -> None:
+ self.protocol.send_response_line(response) # type: ignore
+
async def serve(self) -> None:
while not self.terminated:
- line = await self.protocol.next_line()
+ line = await self.protocol.next_line() # type: ignore
if line.startswith('AT+BRSF='):
- hf_features = int(line[len('AT+BRSF='):])
+ hf_features = int(line[len('AT+BRSF=') :])
self.on_brsf(hf_features)
elif line.startswith('AT+BIND=?'):
self.on_bind_read_capabilities()
elif line.startswith('AT+BIND='):
- indicators = [int(i) for i in line[len('AT+BIND='):].split(',')]
+ indicators = [int(i) for i in line[len('AT+BIND=') :].split(',')]
self.on_bind_list(indicators)
elif line.startswith('AT+BIND?'):
self.on_bind_read_configuration()
@@ -243,50 +256,54 @@ class HfpAgServer:
elif line.startswith('AT+CIND?'):
self.on_cind_test()
# TODO(b/286226902): Implement handlers for these commands
- elif line.startswith((
+ elif line.startswith(
+ (
'AT+CLIP=',
'AT+VGS=',
'AT+BIA=',
'AT+CMER=',
'AT+XEVENT=',
'AT+XAPL=',
- )):
- self.protocol.send_response_line('OK')
+ )
+ ):
+ self.send_response_line('OK')
else:
- self.protocol.send_response_line('ERROR')
+ self.send_response_line('ERROR')
def on_brsf(self, hf_features: int) -> None:
self.hf_features = hf_features
- self.protocol.send_response_line(f'+BRSF: {self.ag_features}')
- self.protocol.send_response_line('OK')
+ self.send_response_line(f'+BRSF: {self.ag_features}')
+ self.send_response_line('OK')
# AT+CIND?
def on_cind_read(self) -> None:
- self.protocol.send_response_line('+CIND: 0,0,1,4,1,5,0')
- self.protocol.send_response_line('OK')
+ self.send_response_line('+CIND: 0,0,1,4,1,5,0')
+ self.send_response_line('OK')
# AT+CIND=?
def on_cind_test(self) -> None:
- self.protocol.send_response_line('+CIND: ("call",(0,1)),("callsetup",(0-3)),("service",(0-1)),'
- '("signal",(0-5)),("roam",(0,1)),("battchg",(0-5)),'
- '("callheld",(0-2))')
- self.protocol.send_response_line('OK')
+ self.send_response_line(
+ '+CIND: ("call",(0,1)),("callsetup",(0-3)),("service",(0-1)),'
+ '("signal",(0-5)),("roam",(0,1)),("battchg",(0-5)),'
+ '("callheld",(0-2))'
+ )
+ self.send_response_line('OK')
# AT+BIND=
def on_bind_list(self, indicators: list[int]) -> None:
self.enabled_hf_indicators = indicators[:]
- self.protocol.send_response_line('OK')
+ self.send_response_line('OK')
# AT+BIND=?
def on_bind_read_capabilities(self) -> None:
- self.protocol.send_response_line('+BIND: ' + ','.join(map(str, self.enabled_hf_indicators)))
- self.protocol.send_response_line('OK')
+ self.send_response_line('+BIND: ' + ','.join(map(str, self.enabled_hf_indicators)))
+ self.send_response_line('OK')
# AT+BIND?
def on_bind_read_configuration(self) -> None:
for i in self.enabled_hf_indicators:
- self.protocol.send_response_line(f'+BIND: {i},1')
- self.protocol.send_response_line('OK')
+ self.send_response_line(f'+BIND: {i},1')
+ self.send_response_line('OK')
if __name__ == '__main__':
diff --git a/android/pandora/test/main.py b/android/pandora/test/main.py
index b62f64d106..a544aac85a 100644
--- a/android/pandora/test/main.py
+++ b/android/pandora/test/main.py
@@ -20,9 +20,9 @@ import cases.le_host_test
import cases.le_security_test
import cases.security_test
import gatt_test
+import hfpclient_test
import sdp_test
import smp_test
-import hfpclient_test
_TEST_CLASSES_LIST = [
cases.host_test.HostTest,
@@ -49,7 +49,7 @@ if __name__ == "__main__":
# This is a hack for `tradefed` because of `b/166468397`.
if '--' in sys.argv:
index = sys.argv.index('--')
- sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+ sys.argv = sys.argv[:1] + sys.argv[index + 1 :]
# Enable bumble snoop logger.
ns, argv = _parse_cli_args()
diff --git a/android/pandora/test/pyrightconfig.json b/android/pandora/test/pyrightconfig.json
index fdee8e8031..af18d50edc 100644
--- a/android/pandora/test/pyrightconfig.json
+++ b/android/pandora/test/pyrightconfig.json
@@ -5,4 +5,13 @@
"reportMissingTypeStubs": false,
"reportUnknownLambdaType": false,
"reportImportCycles": false,
+ "extraPaths": [
+ "../../../pandora/server",
+ "../../../../../../external/pandora/avatar",
+ "../../../../../../external/python/bumble",
+ "../../../../../../external/python/mobly",
+ "../../../../../../external/python/pyee",
+ "../../../../../../out/soong/.intermediates/external/pandora/bt-test-interfaces/python/pandora-python-gen-src/gen/",
+ "../../../../../../out/soong/.intermediates/packages/modules/Bluetooth/pandora/interfaces/python/pandora_experimental-python-gen-src/gen/"
+ ]
} \ No newline at end of file
diff --git a/android/pandora/test/sdp_test.py b/android/pandora/test/sdp_test.py
index be78f1384c..d4d9db309d 100644
--- a/android/pandora/test/sdp_test.py
+++ b/android/pandora/test/sdp_test.py
@@ -14,30 +14,25 @@
import asyncio
import avatar
-import itertools
import logging
-import sys
-import os
from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices
from bumble.colors import color
from bumble.core import BT_AUDIO_SOURCE_SERVICE
from bumble.sdp import (
- Client as SDP_Client,
- SDP_PUBLIC_BROWSE_ROOT,
SDP_ALL_ATTRIBUTES_RANGE,
+ SDP_PUBLIC_BROWSE_ROOT,
SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
- DataElement,
+ Client as SDP_Client,
ServiceAttribute,
)
-from mobly import base_test, signals, test_runner
+from mobly import base_test, test_runner
from mobly.asserts import assert_equal # type: ignore
from mobly.asserts import assert_in # type: ignore
-from mobly.asserts import assert_is_not_none # type: ignore
from mobly.asserts import assert_is_none # type: ignore
+from mobly.asserts import assert_is_not_none # type: ignore
from mobly.asserts import fail # type: ignore
-from pandora.host_pb2 import Connection
-from typing import Any, Literal, Optional, Tuple, Union
+from typing import Optional
class SdpTest(base_test.BaseTestClass): # type: ignore[misc]
@@ -136,8 +131,9 @@ class SdpTest(base_test.BaseTestClass): # type: ignore[misc]
# For each service in the root browse group, get all its attributes
for service_record_handle in service_record_handles:
- attributes = await sdp_client.get_attributes(service_record_handle,
- [SDP_ALL_ATTRIBUTES_RANGE]) # type: ignore
+ attributes = await sdp_client.get_attributes(
+ service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE]
+ ) # type: ignore
print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow'))
for attribute in attributes:
print(' ', attribute.to_string(with_colors=True))
@@ -179,11 +175,12 @@ class SdpTest(base_test.BaseTestClass): # type: ignore[misc]
# Verify Audio Source service is present
service_found = False
for service_record_handle in service_record_handles:
- attributes = await sdp_client.get_attributes(service_record_handle,
- [SDP_ALL_ATTRIBUTES_RANGE]) # type: ignore
+ attributes = await sdp_client.get_attributes(
+ service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE]
+ ) # type: ignore
for attribute in attributes:
- if (attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID):
- if (ServiceAttribute.is_uuid_in_value(BT_AUDIO_SOURCE_SERVICE, attribute.value)): # type: ignore
+ if attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID:
+ if ServiceAttribute.is_uuid_in_value(BT_AUDIO_SOURCE_SERVICE, attribute.value): # type: ignore
service_found = True
self.ref.log.info(f'Service found')
assert service_found
diff --git a/floss/hcidoc/src/groups/connections.rs b/floss/hcidoc/src/groups/connections.rs
index d1216be279..7d600b3191 100644
--- a/floss/hcidoc/src/groups/connections.rs
+++ b/floss/hcidoc/src/groups/connections.rs
@@ -292,29 +292,24 @@ impl OddDisconnectionsRule {
EventChild::DisconnectionComplete(dsc) => {
let handle = dsc.get_connection_handle();
- match self.active_handles.remove(&handle) {
- Some(_) => {
- // Check if this is a NOCP type disconnection and flag it.
- match self.nocp_by_handle.get_mut(&handle) {
- Some(nocp_data) => {
- if let Some(acl_front_ts) = nocp_data.inflight_acl_ts.pop_front() {
- self.signals.push(Signal {
- index: packet.index,
- ts: packet.ts.clone(),
- tag: ConnectionSignal::NocpDisconnect.into(),
- });
-
- self.reportable.push((
- packet.ts,
- format!("DisconnectionComplete for handle({}) showed incomplete in-flight ACL at {}",
- handle, acl_front_ts)));
- }
- }
- None => (),
+ self.active_handles.remove(&handle);
+
+ // Check if this is a NOCP type disconnection and flag it.
+ match self.nocp_by_handle.get_mut(&handle) {
+ Some(nocp_data) => {
+ if let Some(acl_front_ts) = nocp_data.inflight_acl_ts.pop_front() {
+ self.signals.push(Signal {
+ index: packet.index,
+ ts: packet.ts.clone(),
+ tag: ConnectionSignal::NocpDisconnect.into(),
+ });
+
+ self.reportable.push((
+ packet.ts,
+ format!("DisconnectionComplete for handle({}) showed incomplete in-flight ACL at {}",
+ handle, acl_front_ts)));
}
}
-
- // No issue if none, probably device is connected before snoop started.
None => (),
}
diff --git a/floss/hcidoc/src/groups/informational.rs b/floss/hcidoc/src/groups/informational.rs
index 803c3c38bc..f3f38afb2d 100644
--- a/floss/hcidoc/src/groups/informational.rs
+++ b/floss/hcidoc/src/groups/informational.rs
@@ -1,8 +1,10 @@
///! Rule group for general information.
use chrono::NaiveDateTime;
+use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::convert::Into;
use std::fmt;
+use std::hash::Hash;
use std::io::Write;
use crate::engine::{Rule, RuleGroup, Signal};
@@ -17,7 +19,21 @@ type ConnectionHandle = u16;
const INVALID_TS: NaiveDateTime = NaiveDateTime::MAX;
-#[derive(Copy, Clone)]
+fn print_start_end_timestamps(start: NaiveDateTime, end: NaiveDateTime) -> String {
+ fn print_time(ts: NaiveDateTime) -> String {
+ if ts == INVALID_TS {
+ return "N/A".to_owned();
+ }
+ return format!("{}", ts.time());
+ }
+
+ if start == end && start != INVALID_TS {
+ return format!("{} - Failed", start.time());
+ }
+ return format!("{} to {}", print_time(start), print_time(end));
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
enum AddressType {
None,
BREDR,
@@ -108,24 +124,51 @@ impl DeviceInformation {
acl_state: AclState::None,
}
}
+
+ fn is_connection_active(&self) -> bool {
+ // not empty and last connection's end time is not set.
+ return !self.acls.is_empty() && self.acls.last().unwrap().end_time == INVALID_TS;
+ }
+
+ fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation {
+ if !self.is_connection_active() {
+ let acl = AclInformation::new(*handle);
+ self.acls.push(acl);
+ }
+ return self.acls.last_mut().unwrap();
+ }
+
+ fn report_connection_start(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) {
+ let mut acl = AclInformation::new(handle);
+ let initiator = self.acl_state.into();
+ acl.report_start(initiator, ts);
+ self.acls.push(acl);
+ self.acl_state = AclState::Connected;
+ }
+
+ fn report_connection_end(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) {
+ let acl = self.get_or_allocate_connection(&handle);
+ acl.report_end(ts);
+ self.acl_state = AclState::None;
+ }
+
+ fn print_names(names: &HashSet<String>) -> String {
+ if names.len() > 1 {
+ format!("{:?}", names)
+ } else {
+ names.iter().next().unwrap_or(&String::from("<Unknown name>")).to_owned()
+ }
+ }
}
impl fmt::Display for DeviceInformation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fn print_names(names: &HashSet<String>) -> String {
- if names.len() > 1 {
- format!("{:?}", names)
- } else {
- names.iter().next().unwrap_or(&String::from("<Unknown name>")).to_owned()
- }
- }
-
let _ = writeln!(
f,
"{address} ({address_type}, {device_names}), {num_connections} connections",
address = self.address,
address_type = self.address_type,
- device_names = print_names(&self.names),
+ device_names = DeviceInformation::print_names(&self.names),
num_connections = self.acls.len()
);
for acl in &self.acls {
@@ -142,29 +185,138 @@ struct AclInformation {
end_time: NaiveDateTime,
handle: ConnectionHandle,
initiator: InitiatorType,
+ profiles: HashMap<ProfileType, Vec<ProfileInformation>>,
+}
+
+impl AclInformation {
+ pub fn new(handle: ConnectionHandle) -> Self {
+ AclInformation {
+ start_time: INVALID_TS,
+ end_time: INVALID_TS,
+ handle: handle,
+ initiator: InitiatorType::Unknown,
+ profiles: HashMap::new(),
+ }
+ }
+
+ fn get_or_allocate_profile(&mut self, profile_type: &ProfileType) -> &mut ProfileInformation {
+ if !self.profiles.contains_key(profile_type)
+ || self.profiles.get(profile_type).unwrap().last().unwrap().end_time != INVALID_TS
+ {
+ self.profiles.insert(*profile_type, vec![ProfileInformation::new(*profile_type)]);
+ }
+
+ return self.profiles.get_mut(profile_type).unwrap().last_mut().unwrap();
+ }
+
+ fn report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
+ self.initiator = initiator;
+ self.start_time = ts;
+ }
+
+ fn report_end(&mut self, ts: NaiveDateTime) {
+ // disconnect the active profiles
+ let profile_types: Vec<ProfileType> = self.profiles.keys().cloned().collect();
+ for profile_type in profile_types {
+ if let Some(profile) = self.profiles.get(&profile_type).unwrap().last() {
+ if profile.end_time != INVALID_TS {
+ self.report_profile_end(profile_type, ts);
+ }
+ }
+ }
+ self.end_time = ts;
+ }
+
+ fn report_profile_start(
+ &mut self,
+ profile_type: ProfileType,
+ initiator: InitiatorType,
+ ts: NaiveDateTime,
+ ) {
+ let mut profile = ProfileInformation::new(profile_type);
+ profile.report_start(initiator, ts);
+ if !self.profiles.contains_key(&profile_type) {
+ self.profiles.insert(profile_type, vec![]);
+ }
+ self.profiles.get_mut(&profile_type).unwrap().push(profile);
+ }
+
+ fn report_profile_end(&mut self, profile_type: ProfileType, ts: NaiveDateTime) {
+ let profile = self.get_or_allocate_profile(&profile_type);
+ profile.report_end(ts);
+ }
}
impl fmt::Display for AclInformation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fn print_time(ts: NaiveDateTime) -> String {
- if ts == INVALID_TS {
- return "N/A".to_owned();
+ let _ = writeln!(
+ f,
+ " Handle: {handle}, {initiator}, {timestamp_info}",
+ handle = self.handle,
+ initiator = self.initiator,
+ timestamp_info = print_start_end_timestamps(self.start_time, self.end_time)
+ );
+
+ for (_profile_type, profiles) in self.profiles.iter() {
+ for profile in profiles {
+ let _ = write!(f, "{}", profile);
}
- return format!("{}", ts.time());
}
- fn print_timestamps(start: NaiveDateTime, end: NaiveDateTime) -> String {
- if start == end {
- return format!("{} - Failed", start.time());
- }
- return format!("{} to {}", print_time(start), print_time(end));
+
+ Ok(())
+ }
+}
+
+// Currently only HFP is possible to be detected. Other profiles needs us to parse L2CAP packets.
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+enum ProfileType {
+ HFP,
+}
+
+impl fmt::Display for ProfileType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let str = match self {
+ ProfileType::HFP => "HFP",
+ };
+ write!(f, "{}", str)
+ }
+}
+
+struct ProfileInformation {
+ start_time: NaiveDateTime,
+ end_time: NaiveDateTime,
+ profile_type: ProfileType,
+ initiator: InitiatorType,
+}
+
+impl ProfileInformation {
+ pub fn new(profile_type: ProfileType) -> Self {
+ ProfileInformation {
+ start_time: INVALID_TS,
+ end_time: INVALID_TS,
+ profile_type: profile_type,
+ initiator: InitiatorType::Unknown,
}
+ }
+
+ fn report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
+ self.initiator = initiator;
+ self.start_time = ts;
+ }
+
+ fn report_end(&mut self, ts: NaiveDateTime) {
+ self.end_time = ts;
+ }
+}
+impl fmt::Display for ProfileInformation {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
- "> Handle: {handle}, {initiator}, {timestamp_info}",
- handle = self.handle,
+ " {profile}, {initiator}, {timestamp_info}",
+ profile = self.profile_type,
initiator = self.initiator,
- timestamp_info = print_timestamps(self.start_time, self.end_time)
+ timestamp_info = print_start_end_timestamps(self.start_time, self.end_time)
)
}
}
@@ -173,11 +325,19 @@ impl fmt::Display for AclInformation {
struct InformationalRule {
devices: HashMap<Address, DeviceInformation>,
handles: HashMap<ConnectionHandle, Address>,
+ sco_handles: HashMap<ConnectionHandle, ConnectionHandle>,
+ // unknownConnections store connections which is initiated before btsnoop starts.
+ unknown_connections: HashMap<ConnectionHandle, AclInformation>,
}
impl InformationalRule {
pub fn new() -> Self {
- InformationalRule { devices: HashMap::new(), handles: HashMap::new() }
+ InformationalRule {
+ devices: HashMap::new(),
+ handles: HashMap::new(),
+ sco_handles: HashMap::new(),
+ unknown_connections: HashMap::new(),
+ }
}
fn get_or_allocate_device(&mut self, address: &Address) -> &mut DeviceInformation {
@@ -187,19 +347,40 @@ impl InformationalRule {
return self.devices.get_mut(address).unwrap();
}
+ fn get_or_allocate_unknown_connection(
+ &mut self,
+ handle: &ConnectionHandle,
+ ) -> &mut AclInformation {
+ if !self.unknown_connections.contains_key(handle) {
+ self.unknown_connections.insert(*handle, AclInformation::new(*handle));
+ }
+ return self.unknown_connections.get_mut(handle).unwrap();
+ }
+
+ fn get_or_allocate_connection(&mut self, handle: &ConnectionHandle) -> &mut AclInformation {
+ if !self.handles.contains_key(&handle) {
+ let conn = self.get_or_allocate_unknown_connection(&handle);
+ return conn;
+ }
+
+ let address = &self.handles.get(handle).unwrap().clone();
+ let device = self.get_or_allocate_device(address);
+ return device.get_or_allocate_connection(handle);
+ }
+
fn report_address_type(&mut self, address: &Address, address_type: AddressType) {
- let info = self.get_or_allocate_device(address);
- info.address_type.update(address_type);
+ let device = self.get_or_allocate_device(address);
+ device.address_type.update(address_type);
}
fn report_name(&mut self, address: &Address, name: &String) {
- let info = self.get_or_allocate_device(address);
- info.names.insert(name.into());
+ let device = self.get_or_allocate_device(address);
+ device.names.insert(name.into());
}
fn report_acl_state(&mut self, address: &Address, state: AclState) {
- let info = self.get_or_allocate_device(address);
- info.acl_state = state;
+ let device = self.get_or_allocate_device(address);
+ device.acl_state = state;
}
fn report_connection_start(
@@ -208,37 +389,62 @@ impl InformationalRule {
handle: ConnectionHandle,
ts: NaiveDateTime,
) {
- let info = self.get_or_allocate_device(address);
- info.acls.push(AclInformation {
- start_time: ts,
- end_time: INVALID_TS,
- handle: handle,
- initiator: info.acl_state.into(),
- });
- info.acl_state = AclState::Connected;
+ let device = self.get_or_allocate_device(address);
+ device.report_connection_start(handle, ts);
self.handles.insert(handle, *address);
}
+ fn report_sco_connection_start(
+ &mut self,
+ address: &Address,
+ handle: ConnectionHandle,
+ ts: NaiveDateTime,
+ ) {
+ if !self.devices.contains_key(address) {
+ // To simplify things, let's not process unknown devices
+ return;
+ }
+
+ let device = self.devices.get_mut(address).unwrap();
+ if !device.is_connection_active() {
+ // SCO is connected, but ACL is not. This is weird, but let's ignore for simplicity.
+ eprintln!("[{}] SCO is connected, but ACL is not.", address);
+ return;
+ }
+
+ // Whatever handle value works here - we aren't allocating a new one.
+ let acl = device.get_or_allocate_connection(&0);
+ let acl_handle = acl.handle;
+ // We need to listen the HCI commands to determine the correct initiator.
+ // Here we just assume host for simplicity.
+ acl.report_profile_start(ProfileType::HFP, InitiatorType::Host, ts);
+
+ self.sco_handles.insert(handle, acl_handle);
+ }
+
fn report_connection_end(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) {
- if !self.handles.contains_key(&handle) {
- // For simplicity we can't process unknown handle. This probably can be improved.
+ // This might be a SCO disconnection event, so check that first
+ if self.sco_handles.contains_key(&handle) {
+ let acl_handle = self.sco_handles[&handle];
+ let conn = self.get_or_allocate_connection(&acl_handle);
+ conn.report_profile_end(ProfileType::HFP, ts);
return;
}
- let info = self.get_or_allocate_device(&self.handles.get(&handle).unwrap().clone());
-
- // If we can't find the matching acl connection, create one.
- if info.acls.is_empty() || info.acls.last().unwrap().end_time != INVALID_TS {
- info.acls.push(AclInformation {
- start_time: INVALID_TS,
- end_time: ts,
- handle: handle,
- initiator: InitiatorType::Unknown,
- });
+
+ // Not recognized as SCO, assume it's an ACL handle.
+ if let Some(address) = self.handles.get(&handle) {
+ // This device is known
+ let device = self.devices.get_mut(address).unwrap();
+ device.report_connection_end(handle, ts);
+ self.handles.remove(&handle);
+
+ // remove the associated SCO handle, if any
+ self.sco_handles.retain(|_sco_handle, acl_handle| *acl_handle != handle);
} else {
- info.acls.last_mut().unwrap().end_time = ts;
+ // Unknown device.
+ let conn = self.get_or_allocate_unknown_connection(&handle);
+ conn.report_end(ts);
}
- info.acl_state = AclState::None;
- self.handles.remove(&handle);
}
fn report_reset(&mut self, ts: NaiveDateTime) {
@@ -247,6 +453,28 @@ impl InformationalRule {
for handle in handles {
self.report_connection_end(handle, ts);
}
+ self.sco_handles.clear();
+ }
+
+ fn _report_profile_start(
+ &mut self,
+ handle: ConnectionHandle,
+ profile_type: ProfileType,
+ initiator: InitiatorType,
+ ts: NaiveDateTime,
+ ) {
+ let conn = self.get_or_allocate_connection(&handle);
+ conn.report_profile_start(profile_type, initiator, ts);
+ }
+
+ fn _report_profile_end(
+ &mut self,
+ handle: ConnectionHandle,
+ profile_type: ProfileType,
+ ts: NaiveDateTime,
+ ) {
+ let conn = self.get_or_allocate_connection(&handle);
+ conn.report_profile_end(profile_type, ts);
}
fn process_gap_data(&mut self, address: &Address, data: &GapData) {
@@ -296,6 +524,18 @@ impl Rule for InformationalRule {
}
}
+ EventChild::SynchronousConnectionComplete(ev) => {
+ self.report_sco_connection_start(
+ &ev.get_bd_addr(),
+ ev.get_connection_handle(),
+ packet.ts,
+ );
+ // If failed, assume it's the end of connection.
+ if ev.get_status() != ErrorCode::Success {
+ self.report_connection_end(ev.get_connection_handle(), packet.ts);
+ }
+ }
+
EventChild::DisconnectionComplete(ev) => {
self.report_connection_end(ev.get_connection_handle(), packet.ts);
}
@@ -416,13 +656,68 @@ impl Rule for InformationalRule {
}
fn report(&self, writer: &mut dyn Write) {
- if self.devices.is_empty() {
+ /* Sort when displaying the addresses, from the most to the least important:
+ * (1) Device with connections > Device without connections
+ * (2) Device with known name > Device with unknown name
+ * (3) BREDR > LE > Dual
+ * (4) Name, lexicographically (case sensitive)
+ * (5) Address, alphabetically
+ */
+ fn sort_addresses(a: &DeviceInformation, b: &DeviceInformation) -> Ordering {
+ let connection_order = a.acls.is_empty().cmp(&b.acls.is_empty());
+ if connection_order != Ordering::Equal {
+ return connection_order;
+ }
+
+ let known_name_order = a.names.is_empty().cmp(&b.names.is_empty());
+ if known_name_order != Ordering::Equal {
+ return known_name_order;
+ }
+
+ let address_type_order = a.address_type.cmp(&b.address_type);
+ if address_type_order != Ordering::Equal {
+ return address_type_order;
+ }
+
+ let a_name = format!("{}", DeviceInformation::print_names(&a.names));
+ let b_name = format!("{}", DeviceInformation::print_names(&b.names));
+ let name_order = a_name.cmp(&b_name);
+ if name_order != Ordering::Equal {
+ return name_order;
+ }
+
+ let a_address = <[u8; 6]>::from(a.address);
+ let b_address = <[u8; 6]>::from(b.address);
+ for i in (0..6).rev() {
+ let address_order = a_address[i].cmp(&b_address[i]);
+ if address_order != Ordering::Equal {
+ return address_order;
+ }
+ }
+ // This shouldn't be executed
+ return Ordering::Equal;
+ }
+
+ if self.devices.is_empty() && self.unknown_connections.is_empty() {
return;
}
+ let mut addresses: Vec<Address> = self.devices.keys().cloned().collect();
+ addresses.sort_unstable_by(|a, b| sort_addresses(&self.devices[a], &self.devices[b]));
+
let _ = writeln!(writer, "InformationalRule report:");
- for (_, info) in &self.devices {
- let _ = write!(writer, "{}", info);
+ if !self.unknown_connections.is_empty() {
+ let _ = writeln!(
+ writer,
+ "Connections initiated before snoop start, {} connections",
+ self.unknown_connections.len()
+ );
+ for (_, acl) in &self.unknown_connections {
+ let _ = write!(writer, "{}", acl);
+ }
+ }
+ for address in addresses {
+ let _ = write!(writer, "{}", self.devices[&address]);
}
}
diff --git a/floss/pandora/floss/advertising_client.py b/floss/pandora/floss/advertising_client.py
index df02a91951..c9d8090501 100644
--- a/floss/pandora/floss/advertising_client.py
+++ b/floss/pandora/floss/advertising_client.py
@@ -501,6 +501,30 @@ class FlossAdvertisingClient(BluetoothAdvertisingCallbacks):
self.proxy().UnregisterAdvertiserCallback(self.callback_id)
return True
+ def register_callback_observer(self, name, observer):
+ """Add an observer for all callbacks.
+
+ Args:
+ name:
+ Name of the observer.
+ observer:
+ Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothAdvertisingCallbacks):
+ self.callbacks.add_observer(name, observer)
+
+ def unregister_callback_observer(self, name, observer):
+ """Remove an observer for all callbacks.
+
+ Args:
+ name:
+ Name of the observer.
+ observer:
+ Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothAdvertisingCallbacks):
+ self.callbacks.remove_observer(name, observer)
+
@utils.glib_call(None)
def start_advertising_set(self, parameters, advertise_data, scan_response, periodic_parameters, periodic_data,
duration, max_ext_adv_events):
diff --git a/floss/pandora/floss/floss_enums.py b/floss/pandora/floss/floss_enums.py
index afb3411ffe..86169b1d2f 100644
--- a/floss/pandora/floss/floss_enums.py
+++ b/floss/pandora/floss/floss_enums.py
@@ -154,4 +154,26 @@ class SspVariant(enum.IntEnum):
PASSKEY_CONFIRMATION = 0
PASSKEY_ENTRY = 1
CONSENT = 2
- PASSKEY_NOTIFICATION = 3 \ No newline at end of file
+ PASSKEY_NOTIFICATION = 3
+
+
+class BleAddressType(enum.IntEnum):
+ BLE_ADDR_PUBLIC = 0x00
+ BLE_ADDR_RANDOM = 0x01
+ BLE_ADDR_PUBLIC_ID = 0x02
+ BLE_ADDR_RANDOM_ID = 0x03
+ BLE_ADDR_ANONYMOUS = 0xFF
+
+
+class OwnAddressType(enum.IntEnum):
+ DEFAULT = -1
+ PUBLIC = 0
+ RANDOM = 1
+
+
+class CompanyIdentifiers(enum.IntEnum):
+ """Bluetooth SIG Company ID values.
+
+ Bluetooth SIG official document: https://www.bluetooth.com/specifications/assigned-numbers/
+ """
+ GOOGLE = 0x00E0
diff --git a/floss/pandora/floss/scanner_client.py b/floss/pandora/floss/scanner_client.py
index 6331c5de8e..293526fa11 100644
--- a/floss/pandora/floss/scanner_client.py
+++ b/floss/pandora/floss/scanner_client.py
@@ -432,6 +432,30 @@ class FlossScannerClient(BluetoothScannerCallbacks):
self.callback_id = self.proxy().RegisterScannerCallback(objpath)
return True
+ def register_callback_observer(self, name, observer):
+ """Add an observer for all callbacks.
+
+ Args:
+ name:
+ Name of the observer.
+ observer:
+ Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothScannerCallbacks):
+ self.callbacks.add_observer(name, observer)
+
+ def unregister_callback_observer(self, name, observer):
+ """Remove an observer for all callbacks.
+
+ Args:
+ name:
+ Name of the observer.
+ observer:
+ Observer that implements all callback classes.
+ """
+ if isinstance(observer, BluetoothScannerCallbacks):
+ self.callbacks.remove_observer(name, observer)
+
def wait_for_on_scanner_registered(self, uuid):
"""Waits for register scanner.
diff --git a/floss/pandora/floss/utils.py b/floss/pandora/floss/utils.py
index 0fbab48c79..47b4d5fef3 100644
--- a/floss/pandora/floss/utils.py
+++ b/floss/pandora/floss/utils.py
@@ -18,7 +18,9 @@ import logging
import threading
import time
+from floss.pandora.floss import floss_enums
from gi.repository import GLib
+from pandora import host_pb2
# All GLIB method calls should wait this many seconds by default
GLIB_METHOD_CALL_TIMEOUT = 2
@@ -337,3 +339,116 @@ class PropertySet:
raise self.PropertySetterMissing('{} has no getter.'.format(prop_name))
return setter(*args)
+
+
+def address_from(request_address: bytes):
+ """Converts address from grpc server format to floss format."""
+ address = request_address.hex()
+ address = f'{address[:2]}:{address[2:4]}:{address[4:6]}:{address[6:8]}:{address[8:10]}:{address[10:12]}'
+ return address.upper()
+
+
+def address_to(address: str):
+ """Converts address from floss format to grpc server format."""
+ request_address = bytes.fromhex(address.replace(':', ''))
+ return request_address
+
+
+def uuid16_to_uuid128(uuid16: str):
+ return f'0000{uuid16}-0000-1000-8000-00805f9b34fb'
+
+
+def uuid32_to_uuid128(uuid32: str):
+ return f'{uuid32}-0000-1000-8000-00805f9b34fb'
+
+
+def advertise_data_from(request_data: host_pb2.DataTypes):
+ """Mapping DataTypes to a dict.
+
+ The dict content follows the format of floss AdvertiseData.
+
+ Args:
+ request_data : advertising data.
+
+ Raises:
+ NotImplementedError: if request data is not implemented.
+
+ Returns:
+ dict: advertising data.
+ """
+ advertise_data = {
+ 'service_uuids': [],
+ 'solicit_uuids': [],
+ 'transport_discovery_data': [],
+ 'manufacturer_data': {},
+ 'service_data': {},
+ 'include_tx_power_level': False,
+ 'include_device_name': False,
+ }
+
+ # incomplete_service_class_uuids
+ if (request_data.incomplete_service_class_uuids16 or request_data.incomplete_service_class_uuids32 or
+ request_data.incomplete_service_class_uuids128):
+ raise NotImplementedError('Incomplete service class uuid not supported')
+
+ # service_uuids
+ for uuid16 in request_data.complete_service_class_uuids16:
+ advertise_data['service_uuids'].append(uuid16_to_uuid128(uuid16))
+
+ for uuid32 in request_data.complete_service_class_uuids32:
+ advertise_data['service_uuids'].append(uuid32_to_uuid128(uuid32))
+
+ for uuid128 in request_data.complete_service_class_uuids128:
+ advertise_data['service_uuids'].append(uuid128)
+
+ # solicit_uuids
+ for uuid16 in request_data.service_solicitation_uuids16:
+ advertise_data['solicit_uuids'].append(uuid16_to_uuid128(uuid16))
+
+ for uuid32 in request_data.service_solicitation_uuids32:
+ advertise_data['solicit_uuids'].append(uuid32_to_uuid128(uuid32))
+
+ for uuid128 in request_data.service_solicitation_uuids128:
+ advertise_data['solicit_uuids'].append(uuid128)
+
+ # service_data
+ for (uuid16, data) in request_data.service_data_uuid16:
+ advertise_data['service_data'][uuid16_to_uuid128(uuid16)] = data
+
+ for (uuid32, data) in request_data.service_data_uuid32:
+ advertise_data['service_data'][uuid32_to_uuid128(uuid32)] = data
+
+ for (uuid128, data) in request_data.service_data_uuid128:
+ advertise_data['service_data'][uuid128] = data
+
+ advertise_data['manufacturer_data'][hex(
+ floss_enums.CompanyIdentifiers.GOOGLE)] = request_data.manufacturer_specific_data
+
+ # The name is derived from adapter directly in floss.
+ if request_data.WhichOneof('shortened_local_name_oneof') in ('include_shortened_local_name',
+ 'include_complete_local_name'):
+ advertise_data['include_device_name'] = getattr(request_data,
+ request_data.WhichOneof('shortened_local_name_oneof')).value
+
+ # The tx power level is decided by the lower layers.
+ if request_data.WhichOneof('tx_power_level_oneof') == 'include_tx_power_level':
+ advertise_data['include_tx_power_level'] = request_data.include_tx_power_level
+ return advertise_data
+
+
+def create_observer_name(observer):
+ """Generates an unique name for an observer.
+
+ Args:
+ observer: an observer class to observer the bluetooth callbacks.
+
+ Returns:
+ str: an unique name.
+ """
+ return observer.__class__.__name__ + str(id(observer))
+
+
+# anext build-in is new in python3.10. Deprecate this function
+# when we are able to use it.
+async def anext(ait):
+ return await ait.__anext__()
diff --git a/floss/pandora/server/bluetooth.py b/floss/pandora/server/bluetooth.py
new file mode 100644
index 0000000000..1e1301a816
--- /dev/null
+++ b/floss/pandora/server/bluetooth.py
@@ -0,0 +1,249 @@
+# Copyright 2023 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.
+"""All functions relative to the bluetooth procedure."""
+
+import asyncio
+import logging
+import threading
+import traceback
+
+from floss.pandora.floss import adapter_client
+from floss.pandora.floss import advertising_client
+from floss.pandora.floss import manager_client
+from floss.pandora.floss import scanner_client
+from floss.pandora.floss import utils
+from gi.repository import GLib
+import pydbus
+
+
+class Bluetooth(object):
+ """A bluetooth facade exposes all bluetooth functions."""
+
+ # Default to this adapter during init. We will initialize to the correct
+ # default adapter after the manager client is initialized.
+ DEFAULT_ADAPTER = 0
+
+ # Time to sleep between polls
+ ADAPTER_CLIENT_POLL_INTERVAL = 0.1
+
+ # How long we wait for the adapter to come up after we start it.
+ ADAPTER_DAEMON_TIMEOUT_SEC = 20
+
+ # How long we wait for the manager daemon to come up after we start it.
+ DAEMON_TIMEOUT_SEC = 5
+
+ # Default scanner settings
+ SCANNER_INTERVAL = 0
+ SCANNER_WINDOW = 0
+ SCANNER_SCAN_TYPE = 0
+
+ def __init__(self):
+ self.setup_mainloop()
+
+ # self state
+ self.is_clean = False
+
+ # GRPC server state
+ self.pairing_events: asyncio.Queue = None
+ self.pairing_answers = None
+
+ # DBUS clients
+ self.manager_client = manager_client.FlossManagerClient(self.bus)
+ self.adapter_client = adapter_client.FlossAdapterClient(self.bus, self.DEFAULT_ADAPTER)
+ self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, self.DEFAULT_ADAPTER)
+ self.scanner_client = scanner_client.FlossScannerClient(self.bus, self.DEFAULT_ADAPTER)
+
+ def __del__(self):
+ if not self.is_clean:
+ self.cleanup()
+
+ def cleanup(self):
+ self.mainloop_quit.set()
+ self.mainloop.quit()
+ self.is_clean = True
+
+ def setup_mainloop(self):
+ """Start mainloop thread in background.
+
+ This will also initialize a few
+ other variables (self.bus, self.mainloop, self.event_context) that may
+ be necessary for proper operation.
+
+ Raises:
+ RuntimeError: if we timeout to wait for the mainloop ready.
+ """
+
+ self.mainloop_quit = threading.Event()
+ self.mainloop_ready = threading.Event()
+ self.thread = threading.Thread(name=utils.GLIB_THREAD_NAME, target=Bluetooth.mainloop_thread, args=(self,))
+ self.thread.start()
+
+ # Wait for mainloop to be ready
+ if not self.mainloop_ready.wait(timeout=5):
+ raise RuntimeError('Unable to initialize GLib mainloop')
+
+ def mainloop_thread(self):
+ # Set up mainloop. All subsequent buses and connections will use this
+ # mainloop. We also use a separate main context to avoid multithreading
+ # issues.
+ GLib.threads_init()
+ self.mainloop = GLib.MainLoop()
+
+ # Set up bus connection
+ self.bus = pydbus.SystemBus()
+
+ # Set thread ready
+ self.mainloop_ready.set()
+
+ while not self.mainloop_quit.is_set():
+ self.mainloop.run()
+
+ def set_powered(self, powered: bool):
+ """Set the power of bluetooth adapter and bluetooth clients.
+
+ Args:
+ powered: Power on or power off.
+
+ Returns:
+ bool: True if success, False otherwise.
+ """
+ default_adapter = self.manager_client.get_default_adapter()
+
+ def _is_adapter_down(client):
+ return lambda: not client.has_proxy()
+
+ def _is_adapter_ready(client):
+ return lambda: client.has_proxy() and client.get_address()
+
+ if powered:
+ # FIXME: Close rootcanal will cause manager_client failed call has_default_adapter.
+ # if not self.manager_client.has_default_adapter():
+ # logging.warning('set_powered: Default adapter not available.')
+ # return False
+ self.manager_client.start(default_adapter)
+
+ self.adapter_client = adapter_client.FlossAdapterClient(self.bus, default_adapter)
+ self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, default_adapter)
+ self.scanner_client = scanner_client.FlossScannerClient(self.bus, default_adapter)
+
+ try:
+ utils.poll_for_condition(condition=_is_adapter_ready(self.adapter_client),
+ desc='Wait for adapter start',
+ sleep_interval=self.ADAPTER_CLIENT_POLL_INTERVAL,
+ timeout=self.ADAPTER_DAEMON_TIMEOUT_SEC)
+ except TimeoutError as e:
+ logging.error('timeout: error starting adapter daemon: %s', e)
+ logging.error(traceback.format_exc())
+ return False
+
+ # We need to observe callbacks for proper operation.
+ if not self.adapter_client.register_callbacks():
+ logging.error('adapter_client: Failed to register callbacks')
+ return False
+ if not self.advertising_client.register_advertiser_callback():
+ logging.error('advertising_client: Failed to register advertiser callbacks')
+ return False
+ if not self.scanner_client.register_scanner_callback():
+ logging.error('scanner_client: Failed to register callbacks')
+ return False
+ else:
+ self.manager_client.stop(default_adapter)
+ try:
+ utils.poll_for_condition(condition=_is_adapter_down(self.adapter_client),
+ desc='Wait for adapter stop',
+ sleep_interval=self.ADAPTER_CLIENT_POLL_INTERVAL,
+ timeout=self.ADAPTER_DAEMON_TIMEOUT_SEC)
+ except TimeoutError as e:
+ logging.error('timeout: error stopping adapter daemon: %s', e)
+ logging.error(traceback.format_exc())
+ return False
+ return True
+
+ def reset(self):
+ if not self.set_powered(False):
+ return False
+
+ if not self.set_powered(True):
+ return False
+ return True
+
+ def get_address(self):
+ return self.adapter_client.get_address()
+
+ def is_connected(self, address):
+ return self.adapter_client.is_connected(address)
+
+ def is_bonded(self, address):
+ return self.adapter_client.is_bonded(address)
+
+ def is_discovering(self):
+ return self.adapter_client.is_discovering()
+
+ def set_discoverable(self, mode, duration=60):
+ return self.adapter_client.set_property('Discoverable', mode, duration)
+
+ def create_bond(self, address, transport):
+ return self.adapter_client.create_bond(address, transport)
+
+ def set_pairing_confirmation(self, address, accept):
+ return self.adapter_client.set_pairing_confirmation(address, accept)
+
+ def connect_device(self, address):
+ return self.adapter_client.connect_all_enabled_profiles(address)
+
+ def disconnect_device(self, address):
+ return self.adapter_client.disconnect_all_enabled_profiles(address)
+
+ def start_discovery(self):
+ if self.adapter_client.is_discovering():
+ logging.warning('Adapter is already discovering.')
+ return True
+ return self.adapter_client.start_discovery()
+
+ def stop_discovery(self):
+ if not self.adapter_client.is_discovering():
+ logging.warning('Discovery is already stopped.')
+ return True
+ return self.adapter_client.stop_discovery()
+
+ def start_advertising_set(self, parameters, advertise_data, scan_response, periodic_parameters, periodic_data,
+ duration, max_ext_adv_events):
+ parameters = self.advertising_client.make_dbus_advertising_set_parameters(parameters)
+ advertise_data = self.advertising_client.make_dbus_advertise_data(advertise_data)
+ scan_response = utils.make_kv_optional_value(self.advertising_client.make_dbus_advertise_data(scan_response))
+ periodic_parameters = utils.make_kv_optional_value(
+ self.advertising_client.make_dbus_periodic_advertising_parameters(periodic_parameters))
+ periodic_data = utils.make_kv_optional_value(self.advertising_client.make_dbus_advertise_data(periodic_data))
+
+ return self.advertising_client.start_advertising_set(parameters, advertise_data, scan_response,
+ periodic_parameters, periodic_data, duration,
+ max_ext_adv_events)
+
+ def stop_advertising_set(self, advertiser_id):
+ return self.advertising_client.stop_advertising_set(advertiser_id)
+
+ def register_scanner(self):
+ return self.scanner_client.register_scanner()
+
+ def start_scan(self, scanner_id, settings=None, scan_filter=None):
+ if settings is None:
+ settings = self.scanner_client.make_dbus_scan_settings(self.SCANNER_INTERVAL, self.SCANNER_WINDOW,
+ self.SCANNER_SCAN_TYPE)
+ return self.scanner_client.start_scan(scanner_id, settings, scan_filter)
+
+ def stop_scan(self, scanner_id):
+ if not self.scanner_client.remove_monitor(scanner_id):
+ logging.error('Failed to stop scanning.')
+ return False
+ return True
diff --git a/floss/pandora/server/host.py b/floss/pandora/server/host.py
index 624500ed0a..15955fdca6 100644
--- a/floss/pandora/server/host.py
+++ b/floss/pandora/server/host.py
@@ -1,20 +1,30 @@
# Copyright 2023 Google LLC
#
-# Licensed under the Apache License, Version 2.0 (the "License");
+# 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,
+# 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.
"""Host grpc interface."""
+import asyncio
+import logging
from typing import AsyncGenerator
+import uuid as uuid_module
+from floss.pandora.floss import adapter_client
+from floss.pandora.floss import advertising_client
+from floss.pandora.floss import floss_enums
+from floss.pandora.floss import scanner_client
+from floss.pandora.floss import utils
+from floss.pandora.server import bluetooth as bluetooth_module
+from google.protobuf import any_pb2
from google.protobuf import empty_pb2
import grpc
from pandora import host_grpc_aio
@@ -31,86 +41,457 @@ class HostService(host_grpc_aio.HostServicer):
/pandora/bt-test-interfaces/pandora/host.proto
"""
+ def __init__(self, server: grpc.aio.Server, bluetooth: bluetooth_module.Bluetooth):
+ self.server = server
+ self.bluetooth = bluetooth
+ self.waited_connections = set()
+
async def FactoryReset(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ self.waited_connections = set()
+ asyncio.create_task(self.server.stop(None))
+ return empty_pb2.Empty()
async def Reset(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ self.waited_connections = set()
+ self.bluetooth.reset()
+ return empty_pb2.Empty()
async def ReadLocalAddress(self, request: empty_pb2.Empty,
context: grpc.ServicerContext) -> host_pb2.ReadLocalAddressResponse:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ address = self.bluetooth.get_address()
+ return host_pb2.ReadLocalAddressResponse(address=utils.address_to(address))
async def Connect(self, request: host_pb2.ConnectRequest,
context: grpc.ServicerContext) -> host_pb2.ConnectResponse:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+
+ class PairingObserver(adapter_client.BluetoothCallbacks, adapter_client.BluetoothConnectionCallbacks):
+ """Observer to observe the bond state and the connection state."""
+
+ def __init__(self, client: adapter_client, task):
+ self.client = client
+ self.task = task
+
+ @utils.glib_callback()
+ def on_bond_state_changed(self, status, address, state):
+ if address != self.task['address']:
+ return
+
+ if status != 0:
+ self.task['connect_device'].set_result(
+ (False, f'{address} failed to bond. Status: {status}, State: {state}'))
+ return
+
+ if state == floss_enums.BondState.BONDED:
+ if not self.client.is_connected(self.task['address']):
+ logging.info('{address} calling connect_all_enabled_profiles')
+ if not self.client.connect_all_enabled_profiles(self.task['address']):
+ self.task['connect_device'].set_result(
+ (False, f'{self.task["address"]} failed on connect_all_enabled_profiles'))
+ else:
+ self.task['connect_device'].set_result((True, None))
+
+ @utils.glib_callback()
+ def on_ssp_request(self, remote_device, class_of_device, variant, passkey):
+ address, _ = remote_device
+ if address != self.task['address']:
+ return
+
+ if variant == floss_enums.SspVariant.CONSENT:
+ self.client.set_pairing_confirmation(address,
+ True,
+ method_callback=self.on_set_pairing_confirmation)
+
+ @utils.glib_callback()
+ def on_set_pairing_confirmation(self, err, result):
+ if err or not result:
+ self.task['connect_device'].set_result(
+ (False, f'Pairing confirmation failed: err: {err}, result: {result}'))
+
+ @utils.glib_callback()
+ def on_device_connected(self, remote_device):
+ address, _ = remote_device
+ if address != self.task['address']:
+ return
+
+ if self.client.is_bonded(address):
+ self.task['connect_device'].set_result((True, None))
+
+ address = utils.address_from(request.address)
+
+ if not self.bluetooth.is_connected(address):
+ try:
+ connect_device = asyncio.Future()
+ observer = PairingObserver(self.bluetooth.adapter_client, {
+ 'connect_device': connect_device,
+ 'address': address
+ })
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+
+ if self.bluetooth.is_bonded(address):
+ self.bluetooth.connect_device(address)
+ else:
+ if not self.bluetooth.create_bond(address, floss_enums.BtTransport.BR_EDR):
+ raise RuntimeError('Failed to call create_bond.')
+
+ success, reason = await connect_device
+ if not success:
+ raise RuntimeError(f'Failed to connect to the {address}. Reason: {reason}')
+ finally:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+
+ cookie = any_pb2.Any(value=utils.address_to(request.address))
+ return host_pb2.ConnectResponse(connection=host_pb2.Connection(cookie=cookie))
async def WaitConnection(self, request: host_pb2.WaitConnectionRequest,
context: grpc.ServicerContext) -> host_pb2.WaitConnectionResponse:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+
+ class ConnectionObserver(adapter_client.BluetoothConnectionCallbacks):
+ """Observer to observe the connection state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_device_connected(self, remote_device):
+ address, _ = remote_device
+ if address != self.task['address']:
+ return
+
+ self.task['wait_connection'].set_result(address)
+
+ if request.address is None:
+ raise ValueError('Request address field must be set.')
+ address = utils.address_from(request.address)
+
+ if not self.bluetooth.is_connected(address) or address not in self.waited_connections:
+ try:
+ wait_connection = asyncio.Future()
+ observer = ConnectionObserver({'wait_connection': wait_connection, 'address': address})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+
+ await wait_connection
+ finally:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+ self.waited_connection.add(address)
+
+ cookie = any_pb2.Any(value=utils.address_to(address))
+ return host_pb2.WaitConnectionResponse(connection=host_pb2.Connection(cookie=cookie))
async def ConnectLE(self, request: host_pb2.ConnectLERequest,
context: grpc.ServicerContext) -> host_pb2.ConnectLEResponse:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
async def Disconnect(self, request: host_pb2.DisconnectRequest, context: grpc.ServicerContext) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ address = utils.address_from(request.connection.cookie.value)
+ if self.bluetooth.is_connected(address):
+ self.bluetooth.disconnect_device(address)
+ return empty_pb2.Empty()
async def WaitDisconnection(self, request: host_pb2.WaitDisconnectionRequest,
context: grpc.ServicerContext) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+
+ class ConnectionObserver(adapter_client.BluetoothConnectionCallbacks):
+ """Observer to observe the connection state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_device_disconnected(self, remote_device):
+ address, _ = remote_device
+ if address != self.task['address']:
+ return
+ self.task['wait_disconnection'].set_result(address)
+
+ if request.address is None:
+ raise ValueError('Request address field must be set')
+ address = utils.address_from(request.address)
+
+ if self.bluetooth.is_connected(address):
+ try:
+ wait_disconnection = asyncio.Future()
+ observer = ConnectionObserver({'wait_disconnection': wait_disconnection, 'address': address})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+ await wait_disconnection
+ finally:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+
+ return empty_pb2.Empty()
async def Advertise(self, request: host_pb2.AdvertiseRequest,
context: grpc.ServicerContext) -> AsyncGenerator[host_pb2.AdvertiseResponse, None]:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
- yield host_pb2.AdvertiseResponse() # no-op: to make the linter happy
+ parameters = {
+ 'connectable': request.connectable,
+ 'scannable': True,
+ 'is_legacy': True, # ROOTCANAL: Extended advertising ignored because the scanner is legacy.
+ 'is_anonymous': False,
+ 'include_tx_power': True,
+ 'primary_phy': 1,
+ 'secondary_phy': 1,
+ 'interval': request.interval,
+ 'tx_power_level': 127, # 0x7f
+ 'own_address_type': -1, # default
+ }
+
+ primary_phy = request.primary_phy
+ if primary_phy == host_pb2.PRIMARY_1M:
+ parameters['primary_phy'] = floss_enums.LePhy.PHY1M
+ elif primary_phy == host_pb2.PRIMARY_CODED:
+ parameters['primary_phy'] = floss_enums.LePhy.PHY_CODED
+
+ secondary_phy = request.secondary_phy
+ if secondary_phy == host_pb2.SECONDARY_NONE:
+ parameters['secondary_phy'] = floss_enums.LePhy.INVALID
+ elif secondary_phy == host_pb2.SECONDARY_1M:
+ parameters['secondary_phy'] = floss_enums.LePhy.PHY1M
+ elif secondary_phy == host_pb2.SECONDARY_2M:
+ parameters['secondary_phy'] = floss_enums.LePhy.PHY2M
+ elif secondary_phy == host_pb2.SECONDARY_CODED:
+ parameters['secondary_phy'] = floss_enums.LePhy.PHY_CODED
+
+ own_address_type = request.own_address_type
+ if own_address_type in (host_pb2.PUBLIC, host_pb2.RESOLVABLE_OR_PUBLIC):
+ parameters['own_address_type'] = floss_enums.OwnAddressType.PUBLIC
+ elif own_address_type in (host_pb2.RANDOM, host_pb2.RESOLVABLE_OR_RANDOM):
+ parameters['own_address_type'] = floss_enums.OwnAddressType.RANDOM
+
+ # TODO: b/289480188 - Support more data and scan response data if needed.
+ advertise_data = utils.advertise_data_from(request.data)
+
+ class AdvertisingObserver(advertising_client.BluetoothAdvertisingCallbacks):
+ """Observer to observe the advertising state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_advertising_set_started(self, reg_id, advertiser_id, tx_power, status):
+ if reg_id != self.task['reg_id']:
+ return
+
+ if status is None or floss_enums.GattStatus(status) != floss_enums.GattStatus.SUCCESS:
+ logging.error('Failed to start advertising.')
+ advertiser_id = None
+ self.task['start_advertising'].set_result(advertiser_id)
+
+ class ConnectionObserver(adapter_client.BluetoothConnectionCallbacks):
+ """Observer to observe all connections."""
+
+ def __init__(self, loop: asyncio.AbstractEventLoop, task):
+ self.loop = loop
+ self.task = task
+
+ @utils.glib_callback()
+ def on_device_connected(self, remote_device):
+ address, _ = remote_device
+ asyncio.run_coroutine_threadsafe(self.task['connections'].put(address), self.loop)
+
+ started_ids = []
+ observers = []
+ try:
+ if request.connectable:
+ connections = asyncio.Queue()
+ observer = ConnectionObserver(asyncio.get_running_loop(), {'connections': connections})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+ observers.append((name, observer))
+
+ while True:
+ if not self.bluetooth.advertising_client.active_advs:
+ reg_id = self.bluetooth.start_advertising_set(parameters, advertise_data, None, None, None, 0, 0)
+
+ advertising_request = {
+ 'start_advertising': asyncio.get_running_loop().create_future(),
+ 'reg_id': reg_id
+ }
+ observer = AdvertisingObserver(advertising_request)
+ name = utils.create_observer_name(observer)
+ self.bluetooth.advertising_client.register_callback_observer(name, observer)
+ observers.append((name, observer))
+
+ advertiser_id = await asyncio.wait_for(advertising_request['start_advertising'], timeout=5)
+ started_ids.append(advertiser_id)
+
+ if not request.connectable:
+ await asyncio.sleep(1)
+ continue
+
+ logging.info('Advertise: Wait for LE connection...')
+ address = await connections.get()
+ logging.info(f'Advertise: Connected to {address}')
+
+ cookie = any_pb2.Any(value=utils.address_to(address))
+ yield host_pb2.AdvertiseResponse(connection=host_pb2.Connection(cookie=cookie))
+
+ # Wait a small delay before restarting the advertisement.
+ await asyncio.sleep(1)
+ finally:
+ for name, observer in observers:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+ self.bluetooth.advertising_client.unregister_callback_observer(name, observer)
+
+ for started in started_ids:
+ self.bluetooth.stop_advertising_set(started)
async def Scan(self, request: host_pb2.ScanRequest,
context: grpc.ServicerContext) -> AsyncGenerator[host_pb2.ScanningResponse, None]:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
- yield host_pb2.ScanningResponse() # no-op: to make the linter happy
+
+ class ScanObserver(scanner_client.BluetoothScannerCallbacks):
+ """Observer to observer the scan state and scan results."""
+
+ def __init__(self, loop: asyncio.AbstractEventLoop, task):
+ self.loop = loop
+ self.task = task
+
+ @utils.glib_callback()
+ def on_scanner_registered(self, uuid, scanner_id, status):
+ uuid = uuid_module.UUID(bytes=bytes(uuid))
+ if uuid != self.task['uuid']:
+ return
+
+ if floss_enums.GattStatus(status) != floss_enums.GattStatus.SUCCESS:
+ logging.error('Failed to register scanner! uuid: {uuid}')
+ scanner_id = None
+ self.task['register_scanner'].set_result(scanner_id)
+
+ @utils.glib_callback()
+ def on_scan_result(self, scan_result):
+ asyncio.run_coroutine_threadsafe(self.task['scan_results'].put(scan_result), self.loop)
+
+ scanner_id = None
+ name = None
+ observer = None
+ try:
+ uuid = self.bluetooth.register_scanner()
+ scan = {
+ 'register_scanner': asyncio.get_running_loop().create_future(),
+ 'uuid': uuid,
+ 'scan_results': asyncio.Queue()
+ }
+ observer = ScanObserver(asyncio.get_running_loop(), scan)
+ name = utils.create_observer_name(observer)
+ self.bluetooth.scanner_client.register_callback_observer(name, observer)
+
+ scanner_id = await asyncio.wait_for(scan['register_scanner'], timeout=10)
+
+ self.bluetooth.start_scan(scanner_id)
+ while True:
+ scan_result = await scan['scan_results'].get()
+
+ response = host_pb2.ScanningResponse()
+ response.tx_power = scan_result['tx_power']
+ response.rssi = scan_result['rssi']
+ response.sid = scan_result['advertising_sid']
+ response.periodic_advertising_interval = scan_result['periodic_adv_int']
+
+ if scan_result['primary_phy'] == floss_enums.LePhy.PHY1M:
+ response.primary_phy = host_pb2.PRIMARY_1M
+ elif scan_result['primary_phy'] == floss_enums.LePhy.PHY_CODED:
+ response.primary_phy = host_pb2.PRIMARY_CODED
+ else:
+ pass
+
+ if scan_result['secondary_phy'] == floss_enums.LePhy.INVALID:
+ response.secondary_phy = host_pb2.SECONDARY_NONE
+ elif scan_result['secondary_phy'] == floss_enums.LePhy.PHY1M:
+ response.secondary_phy = host_pb2.SECONDARY_1M
+ elif scan_result['secondary_phy'] == floss_enums.LePhy.PHY2M:
+ response.secondary_phy = host_pb2.SECONDARY_2M
+ elif scan_result['secondary_phy'] == floss_enums.LePhy.PHY_CODED:
+ response.secondary_phy = host_pb2.SECONDARY_CODED
+
+ address = bytes.fromhex(scan_result['address'].replace(':', ''))
+ if scan_result['addr_type'] == floss_enums.BleAddressType.BLE_ADDR_PUBLIC:
+ response.public = address
+ elif scan_result['addr_type'] == floss_enums.BleAddressType.BLE_ADDR_RANDOM:
+ response.random = address
+ elif scan_result['addr_type'] == floss_enums.BleAddressType.BLE_ADDR_PUBLIC_ID:
+ response.public_identity = address
+ elif scan_result['addr_type'] == floss_enums.BleAddressType.BLE_ADDR_RANDOM_ID:
+ response.random_static_identity = address
+
+ # TODO: b/289480188 - Support more data if needed.
+ mode = host_pb2.NOT_DISCOVERABLE
+ if scan_result['flags'] & (1 << 0):
+ mode = host_pb2.DISCOVERABLE_LIMITED
+ elif scan_result['flags'] & (1 << 1):
+ mode = host_pb2.DISCOVERABLE_GENERAL
+ else:
+ mode = host_pb2.NOT_DISCOVERABLE
+ response.data.le_discoverability_mode = mode
+ yield response
+ finally:
+ if scanner_id is not None:
+ self.bluetooth.stop_scan(scanner_id)
+ if name is not None and observer is not None:
+ self.bluetooth.scanner_client.unregister_callback_observer(name, observer)
async def Inquiry(self, request: empty_pb2.Empty,
context: grpc.ServicerContext) -> AsyncGenerator[host_pb2.InquiryResponse, None]:
+
+ class InquiryResultObserver(adapter_client.BluetoothCallbacks):
+ """Observer to observe all inquiry results."""
+
+ def __init__(self, loop: asyncio.AbstractEventLoop, task):
+ self.loop = loop
+ self.task = task
+
+ @utils.glib_callback()
+ def on_device_found(self, remote_device):
+ address, _ = remote_device
+ asyncio.run_coroutine_threadsafe(self.task['inquiry_results'].put(address), self.loop)
+
+ class DiscoveryObserver(adapter_client.BluetoothCallbacks):
+ """Observer to observe discovery state."""
+
+ def __init__(self, task):
+ self.task = task
+
+ @utils.glib_callback()
+ def on_discovering_changed(self, discovering):
+ if discovering == self.task['discovering']:
+ self.task['start_inquiry'].set_result(discovering)
+
+ observers = []
+ try:
+ if not self.bluetooth.is_discovering():
+ inquriy = {'start_inquiry': asyncio.Future(), 'discovering': True}
+ observer = DiscoveryObserver(inquriy)
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+ observers.append((name, observer))
+
+ self.bluetooth.start_discovery()
+ await asyncio.wait_for(inquriy['start_inquiry'], timeout=10)
+
+ inquiry_results = asyncio.Queue()
+ observer = InquiryResultObserver(asyncio.get_running_loop(), {'inquiry_results': inquiry_results})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+ observers.append((name, observer))
+
+ while True:
+ address = await inquiry_results.get()
+ yield host_pb2.InquiryResponse(address=utils.address_to(address))
+ finally:
+ self.bluetooth.stop_discovery()
+ for name, observer in observers:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+
+ async def SetDiscoverabilityMode(self, request: host_pb2.SetDiscoverabilityModeRequest,
+ context: grpc.ServicerContext) -> empty_pb2.Empty:
+ mode = request.mode
+ duration = 600 # Sets general, limited default to 60s. This is unused by the non-discoverable mode.
+ self.bluetooth.set_discoverable(mode, duration)
+ return empty_pb2.Empty()
+
+ async def SetConnectabilityMode(self, request: host_pb2.SetConnectabilityModeRequest,
+ context: grpc.ServicerContext) -> empty_pb2.Empty:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
- yield host_pb2.InquiryResponse() # no-op: to make the linter happy
-
- async def SetDiscoverabilityMode(
- self,
- request: host_pb2.SetDiscoverabilityModeRequest,
- context: grpc.ServicerContext,
- ) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
-
- async def SetConnectabilityMode(
- self,
- request: host_pb2.SetConnectabilityModeRequest,
- context: grpc.ServicerContext,
- ) -> empty_pb2.Empty:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
diff --git a/floss/pandora/server/security.py b/floss/pandora/server/security.py
index 123080ae02..6311415a47 100644
--- a/floss/pandora/server/security.py
+++ b/floss/pandora/server/security.py
@@ -13,14 +13,22 @@
# limitations under the License.
"""Security grpc interface."""
+import asyncio
+import logging
from typing import AsyncGenerator
from typing import AsyncIterator
+from floss.pandora.floss import adapter_client
+from floss.pandora.floss import floss_enums
+from floss.pandora.floss import utils
+from floss.pandora.server import bluetooth as bluetooth_module
+from google.protobuf import any_pb2
from google.protobuf import empty_pb2
from google.protobuf import wrappers_pb2
import grpc
-from pandora import security_pb2
+from pandora import host_pb2
from pandora import security_grpc_aio
+from pandora import security_pb2
class SecurityService(security_grpc_aio.SecurityServicer):
@@ -33,24 +41,88 @@ class SecurityService(security_grpc_aio.SecurityServicer):
/pandora/bt-test-interfaces/pandora/security.proto
"""
+ def __init__(self, server: grpc.aio.Server, bluetooth: bluetooth_module.Bluetooth):
+ self.server = server
+ self.bluetooth = bluetooth
+
async def OnPairing(self, request: AsyncIterator[security_pb2.PairingEventAnswer],
context: grpc.ServicerContext) -> AsyncGenerator[security_pb2.PairingEvent, None]:
- context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
- yield security_pb2.PairingEvent() # no-op: to make the linter happy
+
+ class PairingObserver(adapter_client.BluetoothCallbacks):
+ """Observer to observe all pairing events."""
+
+ def __init__(self, loop: asyncio.AbstractEventLoop, task):
+ self.loop = loop
+ self.task = task
+
+ @utils.glib_callback()
+ def on_ssp_request(self, remote_device, class_of_device, variant, passkey):
+ address, name = remote_device
+
+ result = (address, name, class_of_device, variant, passkey)
+ asyncio.run_coroutine_threadsafe(self.task['pairing_events'].put(result), self.loop)
+
+ async def streaming_answers(self):
+ while True:
+ pairing_answer = await utils.anext(self.bluetooth.pairing_answers)
+ answer = pairing_answer.WhichOneof('answer')
+ address = utils.address_from(pairing_answer.event.connection.cookie.value)
+
+ logging.info(f'pairing_answer: {pairing_answer} address: {address}')
+
+ if answer == 'confirm':
+ self.bluetooth.set_pairing_confirmation(address, True)
+ elif answer == 'passkey':
+ pass # TODO: b/289480188 - Supports this method.
+ elif answer == 'pin':
+ pass # TODO: b/289480188 - Supports this method.
+
+ observers = []
+ try:
+ self.bluetooth.pairing_events = asyncio.Queue()
+ observer = PairingObserver(asyncio.get_running_loop(), {'pairing_events': self.bluetooth.pairing_events})
+ name = utils.create_observer_name(observer)
+ self.bluetooth.adapter_client.register_callback_observer(name, observer)
+ observers.append((name, observer))
+
+ self.bluetooth.pairing_answers = request
+ streaming_answers_task = asyncio.create_task(streaming_answers(self))
+ await streaming_answers_task
+
+ while True:
+ address, name, _, variant, passkey = await self.bluetooth.pairing_events.get()
+
+ event = security_pb2.PairingEvent()
+ event.connection.CopyFrom(host_pb2.Connection(cookie=any_pb2.Any(value=utils.address_to(address))))
+
+ if variant == floss_enums.SspVariant.PASSKEY_CONFIRMATION:
+ event.numeric_comparison = passkey
+ elif variant == floss_enums.SspVariant.PASSKEY_ENTRY:
+ event.passkey_entry_request.CopyFrom(empty_pb2.Empty())
+ elif variant == floss_enums.SspVariant.CONSENT:
+ event.just_works.CopyFrom(empty_pb2.Empty())
+ elif variant == floss_enums.SspVariant.PASSKEY_NOTIFICATION:
+ event.passkey_entry_notification.CopyFrom(passkey)
+ yield event
+ finally:
+ streaming_answers_task.cancel()
+ for name, observer in observers:
+ self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
+
+ self.bluetooth.pairing_events = None
+ self.bluetooth.pairing_answers = None
async def Secure(self, request: security_pb2.SecureRequest,
context: grpc.ServicerContext) -> security_pb2.SecureResponse:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
async def WaitSecurity(self, request: security_pb2.WaitSecurityRequest,
context: grpc.ServicerContext) -> security_pb2.WaitSecurityResponse:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
class SecurityStorageService(security_grpc_aio.SecurityStorageServicer):
@@ -63,14 +135,18 @@ class SecurityStorageService(security_grpc_aio.SecurityStorageServicer):
/pandora/bt-test-interfaces/pandora/security.proto
"""
+ def __init__(self, server: grpc.aio.Server, bluetooth: bluetooth_module.Bluetooth):
+ self.server = server
+ self.bluetooth = bluetooth
+
async def IsBonded(self, request: security_pb2.IsBondedRequest,
context: grpc.ServicerContext) -> wrappers_pb2.BoolValue:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
async def DeleteBond(self, request: security_pb2.DeleteBondRequest,
context: grpc.ServicerContext) -> empty_pb2.Empty:
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore
- context.set_details("Method not implemented!") # type: ignore
- raise NotImplementedError("Method not implemented!")
+ context.set_details('Method not implemented!') # type: ignore
+ raise NotImplementedError('Method not implemented!')
diff --git a/floss/pandora/server/server.py b/floss/pandora/server/server.py
new file mode 100644
index 0000000000..0e0d8ae7b8
--- /dev/null
+++ b/floss/pandora/server/server.py
@@ -0,0 +1,60 @@
+# Copyright 2023 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.
+"""Floss bluetooth test server."""
+
+import asyncio
+import logging
+
+from floss.pandora.server import bluetooth as bluetoot_module
+from floss.pandora.server import host
+from floss.pandora.server import security
+import grpc
+from pandora import host_grpc_aio
+from pandora import security_grpc_aio
+
+
+async def serve(port):
+ """Start serving the Floss bluetooth test server."""
+
+ logging.basicConfig(filename='/var/log/grpc_server_log', filemode='a', level=logging.DEBUG)
+
+ try:
+ while True:
+ server = grpc.aio.server()
+ bluetooth = bluetoot_module.Bluetooth()
+ bluetooth.reset()
+
+ host_service = host.HostService(server, bluetooth)
+ host_grpc_aio.add_HostServicer_to_server(host_service, server)
+
+ security_service = security.SecurityService(server, bluetooth)
+ security_grpc_aio.add_SecurityServicer_to_server(security_service, server)
+
+ security_storage_service = security.SecurityStorageService(server, bluetooth)
+ security_grpc_aio.add_SecurityStorageServicer_to_server(security_storage_service, server)
+
+ server.add_insecure_port(f'localhost:{port}')
+ await server.start()
+ await server.wait_for_termination()
+
+ bluetooth.cleanup()
+ del bluetooth
+ finally:
+ await server.stop(None)
+ bluetooth.cleanup()
+ del bluetooth
+
+
+if __name__ == '__main__':
+ asyncio.run(serve(8999))
diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java
index 05eaf1ac6a..03910657c1 100644
--- a/framework/java/android/bluetooth/BluetoothGattServer.java
+++ b/framework/java/android/bluetooth/BluetoothGattServer.java
@@ -668,7 +668,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Set the preferred connection PHY for this app. Please note that this is just a
- * recommendation, whether the PHY change will happen depends on other applications peferences,
+ * recommendation, whether the PHY change will happen depends on other applications preferences,
* local and remote controller capabilities. Controller can override these settings. <p> {@link
* BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if
* no PHY change happens. It is also triggered when remote device updates the PHY.
@@ -856,7 +856,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Add a service to the list of services to be hosted.
*
- * <p>Once a service has been addded to the list, the service and its
+ * <p>Once a service has been added to the list, the service and its
* included characteristics will be provided by the local device.
*
* <p>If the local device has already exposed services when this function
@@ -943,7 +943,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Returns a list of GATT services offered by this device.
*
- * <p>An application must call {@link #addService} to add a serice to the
+ * <p>An application must call {@link #addService} to add a service to the
* list of services offered by this device.
*
* @return List of services. Returns an empty list if no services have been added yet.
diff --git a/pandora/server/bumble_experimental/asha.py b/pandora/server/bumble_experimental/asha.py
index ce09a6b686..f2c85a7db9 100644
--- a/pandora/server/bumble_experimental/asha.py
+++ b/pandora/server/bumble_experimental/asha.py
@@ -229,7 +229,7 @@ class AshaService(AshaServicer):
if not (connection := self.device.lookup_connection(connection_handle)):
raise RuntimeError(f"Unknown connection for connection_handle:{connection_handle}")
- decoder = G722Decoder()
+ decoder = G722Decoder() # type: ignore
queue: asyncio.Queue[bytes] = asyncio.Queue()
def on_data(asha_connection: BumbleConnection, data: bytes) -> None:
diff --git a/service/Android.bp b/service/Android.bp
index 97715d825d..7211e68801 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -45,11 +45,6 @@ java_defaults {
"-Xep:ReferenceEquality:ERROR",
],
},
- product_variables: {
- pdk: {
- enabled: false,
- },
- },
sdk_version: "system_server_current",
diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
index a9c16deeb2..f95ff2ac3f 100644
--- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
@@ -67,6 +67,7 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -113,10 +114,13 @@ class BluetoothManagerService {
private static final int CRASH_LOG_MAX_SIZE = 100;
private static final int DEFAULT_REBIND_COUNT = 3;
- private static final int TIMEOUT_BIND_MS = 3000; // Maximum msec to wait for a bind
+ // Maximum msec to wait for a bind
+ private static final int TIMEOUT_BIND_MS =
+ 3000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
// Timeout value for synchronous binder call
- private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(3);
+ private static final Duration SYNC_CALLS_TIMEOUT =
+ Duration.ofSeconds(3 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1));
/**
* @return timeout value for synchronous binder call
@@ -126,15 +130,20 @@ class BluetoothManagerService {
}
// Maximum msec to wait for service restart
- private static final int SERVICE_RESTART_TIME_MS = 400;
+ private static final int SERVICE_RESTART_TIME_MS
+ = 400 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
// Maximum msec to wait for restart due to error
- private static final int ERROR_RESTART_TIME_MS = 3000;
+ private static final int ERROR_RESTART_TIME_MS
+ = 3000 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
// Maximum msec to delay MESSAGE_USER_SWITCHED
- private static final int USER_SWITCHED_TIME_MS = 200;
+ private static final int USER_SWITCHED_TIME_MS
+ = 200 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
// Delay for the addProxy function in msec
- private static final int ADD_PROXY_DELAY_MS = 100;
+ private static final int ADD_PROXY_DELAY_MS
+ = 100 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
// Delay for retrying enable and disable in msec
- private static final int ENABLE_DISABLE_DELAY_MS = 300;
+ private static final int ENABLE_DISABLE_DELAY_MS
+ = 300 * SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
@VisibleForTesting static final int MESSAGE_ENABLE = 1;
@VisibleForTesting static final int MESSAGE_DISABLE = 2;
diff --git a/system/audio_hal_interface/fuzzer/Android.bp b/system/audio_hal_interface/fuzzer/Android.bp
index eed40552e9..0fc5e26792 100644
--- a/system/audio_hal_interface/fuzzer/Android.bp
+++ b/system/audio_hal_interface/fuzzer/Android.bp
@@ -77,7 +77,6 @@ cc_defaults {
"libchrome",
"libcutils",
"libevent",
- "libflatbuffers-cpp",
"libg722codec",
"libhidlbase",
"libjsoncpp",
diff --git a/system/binder/android/bluetooth/IBluetoothGatt.aidl b/system/binder/android/bluetooth/IBluetoothGatt.aidl
index 221deaa7b1..8c5305e02a 100644
--- a/system/binder/android/bluetooth/IBluetoothGatt.aidl
+++ b/system/binder/android/bluetooth/IBluetoothGatt.aidl
@@ -47,11 +47,6 @@ import com.android.modules.utils.SynchronousResultReceiver;
*/
oneway interface IBluetoothGatt {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
- void startService();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
- void stopService();
-
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void getDevicesMatchingConnectionStates(in int[] states, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)")
@@ -181,8 +176,6 @@ oneway interface IBluetoothGatt {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void disconnectAll(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
- void unregAll(in AttributionSource attributionSource);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void numHwTrackFiltersAvailable(in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
void leSubrateRequest(in int clientIf, in String address, in int subrateMin, in int subrateMax, in int maxLatency,
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 8a3cd65a37..518161945b 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -9,7 +9,7 @@ package {
cc_defaults {
name: "fluoride_bta_defaults",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
local_include_dirs: [
"dm",
"hd",
@@ -147,6 +147,9 @@ cc_library_static {
],
},
},
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -189,6 +192,9 @@ cc_library_static {
"lib-bt-packets",
"libcom.android.sysprop.bluetooth",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -237,7 +243,6 @@ cc_test {
"libbt-bta",
"libbt-bta-core",
"libbt-common",
- "libbt-protos-lite",
"libbtcore",
"libchrome",
"libcom.android.sysprop.bluetooth",
@@ -283,7 +288,6 @@ cc_test {
"libbt-bta",
"libbt-bta-core",
"libbt-common",
- "libbt-protos-lite",
"libbtcore",
"libchrome",
"libosi",
@@ -398,13 +402,11 @@ cc_test {
static_libs: [
"libbluetooth-types",
"libbt-common",
- "libbt-protos-lite",
"libbtcore",
"libbtdevice",
"libchrome",
"libcom.android.sysprop.bluetooth",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
],
sanitize: {
@@ -460,7 +462,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"libosi",
@@ -504,7 +505,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"libosi",
@@ -559,7 +559,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"libosi",
@@ -723,7 +722,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libflatbuffers-cpp",
@@ -795,7 +793,6 @@ cc_test {
"crypto_toolbox_for_tests",
"libbt-audio-hal-interface",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libflatbuffers-cpp",
@@ -858,7 +855,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"libosi",
@@ -911,7 +907,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"liblc3",
@@ -975,7 +970,6 @@ cc_test {
static_libs: [
"libbt-audio-hal-interface",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libgmock",
@@ -1036,7 +1030,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
],
@@ -1098,7 +1091,6 @@ cc_test {
static_libs: [
"crypto_toolbox_for_tests",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libcom.android.sysprop.bluetooth",
"libevent",
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 1a2533ef97..8a0feab816 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -1309,16 +1309,23 @@ class CsisClientImpl : public CsisClient {
}
}
- void CsisActiveObserverSet(bool enable) {
+ static void csis_ad_type_filter_set(bool enable) {
bool is_ad_type_filter_supported =
bluetooth::shim::is_ad_type_filter_supported();
- LOG_INFO("Group_id %d: enable: %d, is_ad_type_filter_supported: %d",
- discovering_group_, enable, is_ad_type_filter_supported);
+
+ LOG_INFO("enable: %d, is_ad_type_filter_supported: %d", enable,
+ is_ad_type_filter_supported);
+
if (is_ad_type_filter_supported) {
bluetooth::shim::set_ad_type_rsi_filter(enable);
} else {
bluetooth::shim::set_empty_filter(enable);
}
+ }
+
+ void CsisActiveObserverSet(bool enable) {
+ LOG_INFO("Group_id %d: enable: %d", discovering_group_, enable);
+ csis_ad_type_filter_set(enable);
BTA_DmBleCsisObserve(
enable, [](tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) {
@@ -1330,6 +1337,7 @@ class CsisClientImpl : public CsisClient {
if (event == BTA_DM_INQ_CMPL_EVT) {
LOG(INFO) << "BLE observe complete. Num Resp: "
<< static_cast<int>(p_data->inq_cmpl.num_resps);
+ csis_ad_type_filter_set(false);
instance->OnCsisObserveCompleted();
instance->CsisObserverSetBackground(true);
return;
diff --git a/system/bta/csis/csis_client_test.cc b/system/bta/csis/csis_client_test.cc
index 544fb41fa2..64fcf5ee0e 100644
--- a/system/bta/csis/csis_client_test.cc
+++ b/system/bta/csis/csis_client_test.cc
@@ -885,6 +885,40 @@ TEST_F(CsisClientTest, test_get_set_sirk) {
ASSERT_EQ(g_1->GetSirk(), sirk);
}
+TEST_F(CsisClientTest, test_csis_member_not_found) {
+ EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)).Times(1);
+ SetSampleDatabaseDoubleCsis(0x001, 1, 2);
+ TestAppRegister();
+
+ /* Here we handle Background Scan request */
+ Mock::VerifyAndClearExpectations(&dm_interface);
+
+ tBTA_DM_SEARCH_CBACK* p_results_cb = nullptr;
+ /* Here is actual Active Scan request */
+ EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _))
+ .WillOnce(DoAll(SaveArg<1>(&p_results_cb)));
+
+ TestConnect(test_address);
+ InjectConnectedEvent(test_address, 1);
+ GetSearchCompleteEvent(1);
+
+ Mock::VerifyAndClearExpectations(&dm_interface);
+ /* Verify that scanner has been called to start filtering */
+ ASSERT_EQ(1, get_func_call_count("set_empty_filter"));
+
+ /* Check callback is not null and simulate no member found and scan
+ * completed*/
+ ASSERT_NE(p_results_cb, nullptr);
+
+ tBTA_DM_SEARCH result;
+ result.inq_cmpl.num_resps = 80;
+
+ p_results_cb(BTA_DM_INQ_CMPL_EVT, &result);
+
+ /* Verify that scanner has been called to stop filtering */
+ ASSERT_EQ(2, get_func_call_count("set_empty_filter"));
+}
+
class CsisMultiClientTest : public CsisClientTest {
protected:
const RawAddress test_address_1 = GetTestAddress(1);
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 48a2bd6a46..0782e049c6 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -846,8 +846,11 @@ void bta_dm_close_acl(const RawAddress& bd_addr, bool remove_dev,
break;
}
if (index != bta_dm_cb.device_list.count) {
- if (remove_dev)
+ if (remove_dev) {
+ LOG_INFO("Setting remove_dev_pending for %s",
+ ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
bta_dm_cb.device_list.peer_device[index].remove_dev_pending = true;
+ }
} else {
APPL_TRACE_ERROR("unknown device, remove ACL failed");
}
@@ -1080,8 +1083,8 @@ static bool bta_dm_read_remote_device_name(const RawAddress& bd_addr,
bta_dm_search_cb.peer_bdaddr = bd_addr;
bta_dm_search_cb.peer_name[0] = 0;
- btm_status = BTM_ReadRemoteDeviceName(bta_dm_search_cb.peer_bdaddr,
- bta_dm_remname_cback, transport);
+ btm_status = get_btm_client_interface().peer.BTM_ReadRemoteDeviceName(
+ bta_dm_search_cb.peer_bdaddr, bta_dm_remname_cback, transport);
if (btm_status == BTM_CMD_STARTED) {
APPL_TRACE_DEBUG("%s: BTM_ReadRemoteDeviceName is started", __func__);
@@ -1094,7 +1097,8 @@ static bool bta_dm_read_remote_device_name(const RawAddress& bd_addr,
* "bta_dm_remname_cback" */
/* adding callback to get notified that current reading remote name done */
- BTM_SecAddRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
+ get_btm_client_interface().security.BTM_SecAddRmtNameNotifyCallback(
+ &bta_dm_service_search_remname_cback);
return (true);
} else {
@@ -1420,7 +1424,8 @@ void bta_dm_sdp_result(tBTA_DM_MSG* p_data) {
/* callbacks */
/* start next bd_addr if necessary */
- BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
+ get_btm_client_interface().security.BTM_SecDeleteRmtNameNotifyCallback(
+ &bta_dm_service_search_remname_cback);
BTM_LogHistory(
kBtmLogTag, bta_dm_search_cb.peer_bdaddr, "Discovery completed",
@@ -1490,7 +1495,8 @@ void bta_dm_sdp_result(tBTA_DM_MSG* p_data) {
if (bta_dm_search_cb.p_sdp_db)
osi_free_and_reset((void**)&bta_dm_search_cb.p_sdp_db);
- BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
+ get_btm_client_interface().security.BTM_SecDeleteRmtNameNotifyCallback(
+ &bta_dm_service_search_remname_cback);
p_msg = (tBTA_DM_MSG*)osi_calloc(sizeof(tBTA_DM_MSG));
p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT;
@@ -2053,8 +2059,8 @@ static void bta_dm_discover_device(const RawAddress& remote_bd_addr) {
if connection exists, we don't have to wait for ACL
link to go down to start search on next device */
if (transport == BT_TRANSPORT_BR_EDR) {
- if (BTM_IsAclConnectionUp(bta_dm_search_cb.peer_bdaddr,
- BT_TRANSPORT_BR_EDR))
+ if (get_btm_client_interface().peer.BTM_IsAclConnectionUp(
+ bta_dm_search_cb.peer_bdaddr, BT_TRANSPORT_BR_EDR))
bta_dm_search_cb.wait_disc = false;
else
bta_dm_search_cb.wait_disc = true;
@@ -2269,13 +2275,15 @@ static void bta_dm_remname_cback(const tBTM_REMOTE_DEV_NAME* p_remote_name) {
p_remote_name->remote_bd_name[0], p_remote_name->length);
if (bta_dm_search_cb.peer_bdaddr == p_remote_name->bd_addr) {
- BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
+ get_btm_client_interface().security.BTM_SecDeleteRmtNameNotifyCallback(
+ &bta_dm_service_search_remname_cback);
} else {
// if we got a different response, maybe ignore it
// we will have made a request directly from BTM_ReadRemoteDeviceName so we
// expect a dedicated response for us
if (p_remote_name->hci_status == HCI_ERR_CONNECTION_EXISTS) {
- BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
+ get_btm_client_interface().security.BTM_SecDeleteRmtNameNotifyCallback(
+ &bta_dm_service_search_remname_cback);
LOG_INFO(
"Assume command failed due to disconnection hci_status:%s peer:%s",
hci_error_code_text(p_remote_name->hci_status).c_str(),
@@ -2903,6 +2911,8 @@ static void bta_dm_acl_down(const RawAddress& bd_addr,
}
}
if (remove_device) {
+ LOG_INFO("remove_dev_pending actually removing %s",
+ ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
bta_dm_process_remove_device_no_callback(bd_addr);
}
@@ -3068,8 +3078,13 @@ static void bta_dm_delay_role_switch_cback(UNUSED_ATTR void* data) {
******************************************************************************/
static void bta_dm_reset_sec_dev_pending(const RawAddress& remote_bd_addr) {
for (size_t i = 0; i < bta_dm_cb.device_list.count; i++) {
- if (bta_dm_cb.device_list.peer_device[i].peer_bdaddr == remote_bd_addr) {
- bta_dm_cb.device_list.peer_device[i].remove_dev_pending = false;
+ auto& dev = bta_dm_cb.device_list.peer_device[i];
+ if (dev.peer_bdaddr == remote_bd_addr) {
+ if (dev.remove_dev_pending) {
+ LOG_INFO("Clearing remove_dev_pending for %s",
+ ADDRESS_TO_LOGGABLE_CSTR(dev.peer_bdaddr));
+ dev.remove_dev_pending = false;
+ }
return;
}
}
@@ -3096,8 +3111,11 @@ static void bta_dm_remove_sec_dev_entry(const RawAddress& remote_bd_addr) {
__func__);
BTM_SecClearSecurityFlags(remote_bd_addr);
for (int i = 0; i < bta_dm_cb.device_list.count; i++) {
- if (bta_dm_cb.device_list.peer_device[i].peer_bdaddr == remote_bd_addr) {
- bta_dm_cb.device_list.peer_device[i].remove_dev_pending = TRUE;
+ auto& dev = bta_dm_cb.device_list.peer_device[i];
+ if (dev.peer_bdaddr == remote_bd_addr) {
+ LOG_INFO("Setting remove_dev_pending for %s",
+ ADDRESS_TO_LOGGABLE_CSTR(dev.peer_bdaddr));
+ dev.remove_dev_pending = TRUE;
break;
}
}
@@ -3169,7 +3187,8 @@ static const char* bta_dm_get_remname(void) {
/* If the name isn't already stored, try retrieving from BTM */
if (*p_name == '\0') {
- const char* p_temp = BTM_SecReadDevName(bta_dm_search_cb.peer_bdaddr);
+ const char* p_temp = get_btm_client_interface().security.BTM_SecReadDevName(
+ bta_dm_search_cb.peer_bdaddr);
if (p_temp != NULL) p_name = (const char*)p_temp;
}
@@ -3970,6 +3989,12 @@ static uint8_t bta_dm_ble_smp_cback(tBTM_LE_EVT event, const RawAddress& bda,
sec_event.ble_key.key_type = p_data->key.key_type;
sec_event.ble_key.p_key_value = p_data->key.p_key_value;
bta_dm_cb.p_sec_cback(BTA_DM_BLE_KEY_EVT, &sec_event);
+
+ // Setting remove_dev_pending flag to false, where it will avoid deleting
+ // the security device record when the ACL connection link goes down in
+ // case of reconnection.
+ if (bta_dm_cb.device_list.count) bta_dm_reset_sec_dev_pending(bda);
+
break;
case BTM_LE_COMPLT_EVT:
@@ -4450,7 +4475,7 @@ void btm_dm_start_gatt_discovery(const RawAddress& bd_addr) {
* Parameters:
*
******************************************************************************/
-void bta_dm_proc_open_evt(tBTA_GATTC_OPEN* p_data) {
+static void bta_dm_proc_open_evt(tBTA_GATTC_OPEN* p_data) {
VLOG(1) << "DM Search state= " << bta_dm_search_get_state()
<< " search_cb.peer_dbaddr:" << bta_dm_search_cb.peer_bdaddr
<< " connected_bda=" << p_data->remote_bda.address;
diff --git a/system/bta/dm/bta_dm_main.cc b/system/bta/dm/bta_dm_main.cc
index 827439f007..af60d17186 100644
--- a/system/bta/dm/bta_dm_main.cc
+++ b/system/bta/dm/bta_dm_main.cc
@@ -211,7 +211,11 @@ bool bta_dm_search_sm_execute(const BT_HDR_RIGID* p_msg) {
break;
case BTA_DM_API_SEARCH_CANCEL_EVT:
bta_dm_search_clear_queue();
- bta_dm_search_set_state(BTA_DM_SEARCH_CANCELLING);
+
+ if (bluetooth::common::init_flags::
+ bta_dm_stop_discovery_on_search_cancel_is_enabled()) {
+ bta_dm_search_set_state(BTA_DM_SEARCH_CANCELLING);
+ }
bta_dm_search_cancel_notify();
break;
case BTA_DM_DISC_CLOSE_TOUT_EVT:
diff --git a/system/bta/include/bta_le_audio_api.h b/system/bta/include/bta_le_audio_api.h
index 38b6c2e883..1d61bc3906 100644
--- a/system/bta/include/bta_le_audio_api.h
+++ b/system/bta/include/bta_le_audio_api.h
@@ -63,6 +63,9 @@ class LeAudioClient {
bluetooth::le_audio::btle_audio_codec_config_t output_codec_config) = 0;
virtual void SetCcidInformation(int ccid, int context_type) = 0;
virtual void SetInCall(bool in_call) = 0;
+ virtual bool IsInCall() = 0;
+ virtual void SetInVoipCall(bool in_call) = 0;
+ virtual bool IsInVoipCall() = 0;
virtual void SendAudioProfilePreferences(
const int group_id, bool is_output_preference_le_audio,
bool is_duplex_preference_le_audio) = 0;
diff --git a/system/bta/le_audio/audio_hal_client/audio_hal_client.h b/system/bta/le_audio/audio_hal_client/audio_hal_client.h
index d24ab7e67d..e470b3f614 100644
--- a/system/bta/le_audio/audio_hal_client/audio_hal_client.h
+++ b/system/bta/le_audio/audio_hal_client/audio_hal_client.h
@@ -102,7 +102,7 @@ class LeAudioSinkAudioHalClient {
class Callbacks {
public:
virtual ~Callbacks() = default;
- virtual void OnAudioSuspend(std::promise<void> do_suspend_promise) = 0;
+ virtual void OnAudioSuspend(void) = 0;
virtual void OnAudioResume(void) = 0;
virtual void OnAudioMetadataUpdate(
std::vector<struct record_track_metadata> sink_metadata) = 0;
@@ -139,7 +139,7 @@ class LeAudioSourceAudioHalClient {
public:
virtual ~Callbacks() = default;
virtual void OnAudioDataReady(const std::vector<uint8_t>& data) = 0;
- virtual void OnAudioSuspend(std::promise<void> do_suspend_promise) = 0;
+ virtual void OnAudioSuspend(void) = 0;
virtual void OnAudioResume(void) = 0;
virtual void OnAudioMetadataUpdate(
std::vector<struct playback_track_metadata> source_metadata) = 0;
diff --git a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
index 5a6729299c..75fbcec646 100644
--- a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
@@ -208,8 +208,7 @@ class MockLeAudioClientAudioSinkEventReceiver
public:
MOCK_METHOD((void), OnAudioDataReady, (const std::vector<uint8_t>& data),
(override));
- MOCK_METHOD((void), OnAudioSuspend, (std::promise<void> do_suspend_promise),
- (override));
+ MOCK_METHOD((void), OnAudioSuspend, (), (override));
MOCK_METHOD((void), OnAudioResume, (), (override));
MOCK_METHOD((void), OnAudioMetadataUpdate,
(std::vector<struct playback_track_metadata> source_metadata),
@@ -219,8 +218,7 @@ class MockLeAudioClientAudioSinkEventReceiver
class MockAudioHalClientEventReceiver
: public LeAudioSinkAudioHalClient::Callbacks {
public:
- MOCK_METHOD((void), OnAudioSuspend, (std::promise<void> do_suspend_promise),
- (override));
+ MOCK_METHOD((void), OnAudioSuspend, (), (override));
MOCK_METHOD((void), OnAudioResume, (), (override));
MOCK_METHOD((void), OnAudioMetadataUpdate,
(std::vector<struct record_track_metadata> sink_metadata),
@@ -413,7 +411,7 @@ TEST_F(LeAudioClientAudioTest, testLeAudioClientAudioSinkSuspend) {
/* Expect LeAudio registered event listener to get called when HAL calls the
* audio_hal_client's internal suspend callback.
*/
- EXPECT_CALL(mock_hal_source_event_receiver_, OnAudioSuspend(_)).Times(1);
+ EXPECT_CALL(mock_hal_source_event_receiver_, OnAudioSuspend()).Times(1);
ASSERT_TRUE(source_audio_hal_stream_cb.on_suspend_());
}
@@ -427,7 +425,7 @@ TEST_F(LeAudioClientAudioTest, testAudioHalClientSuspend) {
/* Expect LeAudio registered event listener to get called when HAL calls the
* audio_hal_client's internal suspend callback.
*/
- EXPECT_CALL(mock_hal_sink_event_receiver_, OnAudioSuspend(_)).Times(1);
+ EXPECT_CALL(mock_hal_sink_event_receiver_, OnAudioSuspend()).Times(1);
ASSERT_TRUE(sink_audio_hal_stream_cb.on_suspend_());
}
diff --git a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
index 8d435901dc..feb570fbe8 100644
--- a/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_sink_hal_client.cc
@@ -143,16 +143,11 @@ bool SinkImpl::OnSuspendReq() {
return false;
}
- std::promise<void> do_suspend_promise;
- std::future<void> do_suspend_future = do_suspend_promise.get_future();
-
bt_status_t status = do_in_main_thread(
FROM_HERE,
base::BindOnce(&LeAudioSinkAudioHalClient::Callbacks::OnAudioSuspend,
- base::Unretained(audioSinkCallbacks_),
- std::move(do_suspend_promise)));
+ base::Unretained(audioSinkCallbacks_)));
if (status == BT_STATUS_SUCCESS) {
- do_suspend_future.wait();
return true;
}
diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
index 38156929f2..130b652807 100644
--- a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc
@@ -252,16 +252,11 @@ bool SourceImpl::OnSuspendReq() {
return false;
}
- // Call OnAudioSuspend and block till it returns.
- std::promise<void> do_suspend_promise;
- std::future<void> do_suspend_future = do_suspend_promise.get_future();
bt_status_t status = do_in_main_thread(
FROM_HERE,
base::BindOnce(&LeAudioSourceAudioHalClient::Callbacks::OnAudioSuspend,
- base::Unretained(audioSourceCallbacks_),
- std::move(do_suspend_promise)));
+ base::Unretained(audioSourceCallbacks_)));
if (status == BT_STATUS_SUCCESS) {
- do_suspend_future.wait();
return true;
}
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index a066d80b49..50269141ee 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -1037,11 +1037,9 @@ class LeAudioBroadcasterImpl : public LeAudioBroadcaster, public BigCallbacks {
LOG_VERBOSE("All data sent.");
}
- virtual void OnAudioSuspend(
- std::promise<void> do_suspend_promise) override {
+ virtual void OnAudioSuspend(void) override {
LOG_INFO();
/* TODO: Should we suspend all broadcasts - remove BIGs? */
- do_suspend_promise.set_value();
if (instance)
instance->audio_data_path_state_ = AudioDataPathState::SUSPENDED;
}
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 63b304dcf2..e565eb54ce 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -251,6 +251,7 @@ class LeAudioClientImpl : public LeAudioClient {
audio_receiver_state_(AudioState::IDLE),
audio_sender_state_(AudioState::IDLE),
in_call_(false),
+ in_voip_call_(false),
current_source_codec_config({0, 0, 0, 0}),
current_sink_codec_config({0, 0, 0, 0}),
lc3_encoder_left_mem(nullptr),
@@ -304,6 +305,10 @@ class LeAudioClientImpl : public LeAudioClient {
void ReconfigureAfterVbcClose() {
LOG_DEBUG("VBC close timeout");
+ if (IsInVoipCall()) {
+ SetInVoipCall(false);
+ }
+
auto group = aseGroups_.FindById(active_group_id_);
if (!group) {
LOG_ERROR("Invalid group: %d", active_group_id_);
@@ -917,11 +922,18 @@ class LeAudioClientImpl : public LeAudioClient {
remote_contexts.source.clear();
}
+ /* Do not put the TBS CCID when not using Telecom for the VoIP calls. */
+ auto ccid_contexts = remote_contexts;
+ if (IsInVoipCall() && !IsInCall()) {
+ ccid_contexts.sink.unset(LeAudioContextType::CONVERSATIONAL);
+ ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL);
+ }
+
BidirectionalPair<std::vector<uint8_t>> ccids = {
.sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
- remote_contexts.sink),
+ ccid_contexts.sink),
.source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
- remote_contexts.source)};
+ ccid_contexts.source)};
if (group->IsPendingConfiguration()) {
return groupStateMachine_->ConfigureStream(
group, configuration_context_type_, remote_contexts, ccids);
@@ -1037,6 +1049,15 @@ class LeAudioClientImpl : public LeAudioClient {
in_call_ = in_call;
}
+ bool IsInCall() override { return in_call_; }
+
+ void SetInVoipCall(bool in_call) override {
+ LOG_DEBUG("in_voip_call: %d", in_call);
+ in_voip_call_ = in_call;
+ }
+
+ bool IsInVoipCall() override { return in_voip_call_; }
+
void SendAudioProfilePreferences(
const int group_id, bool is_output_preference_le_audio,
bool is_duplex_preference_le_audio) override {
@@ -1544,8 +1565,7 @@ class LeAudioClientImpl : public LeAudioClient {
dev->SetConnectionState(DeviceConnectState::DISCONNECTED);
}
}
- if (group->GetState() ==
- le_audio::types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+ if (group->IsStreaming() || !group->IsReleasingOrIdle()) {
leAudioDevice->closing_stream_for_disconnection_ = true;
groupStateMachine_->StopStream(group);
return;
@@ -1783,35 +1803,35 @@ class LeAudioClientImpl : public LeAudioClient {
contexts, len, value)) {
return;
}
- leAudioDevice->SetAvailableContexts(contexts);
- leAudioDevice->SetAvailableContexts(contexts);
- if (!group) {
+ leAudioDevice->SetAvailableContexts(contexts);
+
+ if (!group) {
return;
- }
+ }
- /* Check if we should attach to stream this device */
- if (group->IsInTransition() || !group->IsStreaming()) {
+ /* Check if we should attach to stream this device */
+ if (group->IsInTransition() || !group->IsStreaming()) {
return;
- }
+ }
- if (leAudioDevice->HaveActiveAse()) {
+ if (leAudioDevice->HaveActiveAse()) {
/* Do nothing, device is streaming */
return;
- }
+ }
- if (leAudioDevice->GetConnectionState() !=
- DeviceConnectState::CONNECTED) {
+ if (leAudioDevice->GetConnectionState() !=
+ DeviceConnectState::CONNECTED) {
/* Do nothing, wait until device is connected */
return;
- }
+ }
- auto group_metadata_contexts =
- get_bidirectional(group->GetMetadataContexts());
- auto device_available_contexts = leAudioDevice->GetAvailableContexts();
- if (group_metadata_contexts.test_any(device_available_contexts)) {
+ auto group_metadata_contexts =
+ get_bidirectional(group->GetMetadataContexts());
+ auto device_available_contexts = leAudioDevice->GetAvailableContexts();
+ if (group_metadata_contexts.test_any(device_available_contexts)) {
AttachToStreamingGroupIfNeeded(leAudioDevice);
- }
+ }
} else if (hdl == leAudioDevice->audio_supp_cont_hdls_.val_hdl) {
BidirectionalPair<AudioContexts> supp_audio_contexts;
@@ -2943,7 +2963,20 @@ class LeAudioClientImpl : public LeAudioClient {
return;
}
- if (!groupStateMachine_->AttachToStream(group, leAudioDevice)) {
+ /* Do not put the TBS CCID when not using Telecom for the VoIP calls. */
+ auto ccid_contexts = group->GetMetadataContexts();
+ if (IsInVoipCall() && !IsInCall()) {
+ ccid_contexts.sink.unset(LeAudioContextType::CONVERSATIONAL);
+ ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL);
+ }
+ BidirectionalPair<std::vector<uint8_t>> ccids = {
+ .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
+ ccid_contexts.sink),
+ .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
+ ccid_contexts.source)};
+
+ if (!groupStateMachine_->AttachToStream(group, leAudioDevice,
+ std::move(ccids))) {
LOG_WARN("Could not add device %s to the group %d streaming. ",
ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
group->group_id_);
@@ -3898,7 +3931,7 @@ class LeAudioClientImpl : public LeAudioClient {
}
/* Check if the device resume is expected */
- if (!group->GetCachedCodecConfigurationByDirection(
+ if (!group->GetCodecConfigurationByDirection(
configuration_context_type_,
le_audio::types::kLeAudioDirectionSink)) {
LOG(ERROR) << __func__ << ", invalid resume request for context type: "
@@ -4324,7 +4357,7 @@ class LeAudioClientImpl : public LeAudioClient {
bluetooth::common::ToString(available_remote_contexts).c_str(),
bluetooth::common::ToString(configuration_context_type_).c_str());
- if (in_call_) {
+ if (IsInCall()) {
LOG_DEBUG(" In Call preference used.");
return LeAudioContextType::CONVERSATIONAL;
}
@@ -4336,10 +4369,10 @@ class LeAudioClientImpl : public LeAudioClient {
LeAudioContextType context_priority_list[] = {
/* Highest priority first */
LeAudioContextType::CONVERSATIONAL,
- /* Skip the RINGTONE to avoid reconfigurations when adjusting
- * call volume slider while not in a call.
- * LeAudioContextType::RINGTONE,
+ /* Handling RINGTONE will cause the ringtone volume slider to trigger
+ * reconfiguration. This will be fixed in b/283349711.
*/
+ LeAudioContextType::RINGTONE,
LeAudioContextType::LIVE,
LeAudioContextType::VOICEASSISTANTS,
LeAudioContextType::GAME,
@@ -4600,11 +4633,29 @@ class LeAudioClientImpl : public LeAudioClient {
BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts(
LeAudioDeviceGroup* group, int remote_direction) {
+ // Inject conversational when ringtone is played - this is required for all
+ // the VoIP applications which are not using the telecom API.
+ if ((remote_direction == le_audio::types::kLeAudioDirectionSink) &&
+ local_metadata_context_types_.source.test(
+ LeAudioContextType::RINGTONE)) {
+ /* Simulate, we are already in the call. Sending RINGTONE when there is
+ * no incoming call to accept or reject on TBS could confuse the remote
+ * device and interrupt the stream establish procedure.
+ */
+ if (!IsInCall()) {
+ SetInVoipCall(true);
+ }
+ } else if (IsInVoipCall()) {
+ SetInVoipCall(false);
+ }
+
/* Make sure we have CONVERSATIONAL when in a call and it is not mixed
* with any other bidirectional context
*/
- if (in_call_) {
- LOG_DEBUG(" In Call preference used.");
+ if (IsInCall() || IsInVoipCall()) {
+ LOG_DEBUG(" In Call preference used: %s, voip call: %s",
+ (IsInCall() ? "true" : "false"),
+ (IsInVoipCall() ? "true" : "false"));
local_metadata_context_types_.sink.unset_all(kLeAudioContextAllBidir);
local_metadata_context_types_.source.unset_all(kLeAudioContextAllBidir);
local_metadata_context_types_.sink.set(
@@ -4634,6 +4685,12 @@ class LeAudioClientImpl : public LeAudioClient {
BidirectionalPair<AudioContexts> remote_metadata = {
.sink = local_metadata_context_types_.source,
.source = local_metadata_context_types_.sink};
+
+ if (IsInVoipCall()) {
+ LOG_DEBUG("Unsetting RINGTONE from remote sink ");
+ remote_metadata.sink.unset(LeAudioContextType::RINGTONE);
+ }
+
LOG_DEBUG("local_metadata_context_types_.source= %s",
ToString(local_metadata_context_types_.source).c_str());
LOG_DEBUG("local_metadata_context_types_.sink= %s",
@@ -5135,11 +5192,13 @@ class LeAudioClientImpl : public LeAudioClient {
false)) {
return;
}
+
/* If group is inactive, phone is in call and Group is not having CIS
* connected, notify upper layer about it, so it can decide to create SCO if
* it is in the handover case
*/
- if (in_call_ && active_group_id_ == bluetooth::groups::kGroupUnknown) {
+ if ((IsInCall() || IsInVoipCall()) &&
+ active_group_id_ == bluetooth::groups::kGroupUnknown) {
callbacks_->OnGroupStatus(group_id, GroupStatus::TURNED_IDLE_DURING_CALL);
}
}
@@ -5313,6 +5372,7 @@ class LeAudioClientImpl : public LeAudioClient {
AudioState audio_sender_state_;
/* Keep in call state. */
bool in_call_;
+ bool in_voip_call_;
/* Reconnection mode */
tBTM_BLE_CONN_TYPE reconnection_mode_;
@@ -5524,9 +5584,8 @@ class SourceCallbacksImpl : public LeAudioSourceAudioHalClient::Callbacks {
void OnAudioDataReady(const std::vector<uint8_t>& data) override {
if (instance) instance->OnAudioDataReady(data);
}
- void OnAudioSuspend(std::promise<void> do_suspend_promise) override {
+ void OnAudioSuspend(void) override {
if (instance) instance->OnLocalAudioSourceSuspend();
- do_suspend_promise.set_value();
}
void OnAudioResume(void) override {
@@ -5542,9 +5601,8 @@ class SourceCallbacksImpl : public LeAudioSourceAudioHalClient::Callbacks {
class SinkCallbacksImpl : public LeAudioSinkAudioHalClient::Callbacks {
public:
- void OnAudioSuspend(std::promise<void> do_suspend_promise) override {
+ void OnAudioSuspend(void) override {
if (instance) instance->OnLocalAudioSinkSuspend();
- do_suspend_promise.set_value();
}
void OnAudioResume(void) override {
if (instance) instance->OnLocalAudioSinkResume();
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index 0b4a43fc3d..cf11893d70 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -739,16 +739,18 @@ class UnicastTestNoInit : public Test {
return true;
});
- ON_CALL(mock_state_machine_, AttachToStream(_, _))
+ ON_CALL(mock_state_machine_, AttachToStream(_, _, _))
.WillByDefault([](LeAudioDeviceGroup* group,
- LeAudioDevice* leAudioDevice) {
+ LeAudioDevice* leAudioDevice,
+ types::BidirectionalPair<std::vector<uint8_t>>
+ ccids) {
if (group->GetState() !=
types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
return false;
}
group->Configure(group->GetConfigurationContextType(),
- group->GetMetadataContexts());
+ group->GetMetadataContexts(), ccids);
if (!group->CigAssignCisIds(leAudioDevice)) return false;
group->CigAssignCisConnHandlesToAses(leAudioDevice);
@@ -1694,10 +1696,7 @@ class UnicastTestNoInit : public Test {
void LocalAudioSourceSuspend(void) {
ASSERT_NE(unicast_source_hal_cb_, nullptr);
- std::promise<void> do_suspend_sink_promise;
- auto do_suspend_sink_future = do_suspend_sink_promise.get_future();
- unicast_source_hal_cb_->OnAudioSuspend(std::move(do_suspend_sink_promise));
- do_suspend_sink_future.wait();
+ unicast_source_hal_cb_->OnAudioSuspend();
}
void LocalAudioSourceResume(bool expected_confirmation = true) {
@@ -1719,10 +1718,7 @@ class UnicastTestNoInit : public Test {
void LocalAudioSinkSuspend(void) {
ASSERT_NE(unicast_sink_hal_cb_, nullptr);
- std::promise<void> do_suspend_source_promise;
- auto do_suspend_source_future = do_suspend_source_promise.get_future();
- unicast_sink_hal_cb_->OnAudioSuspend(std::move(do_suspend_source_promise));
- do_suspend_source_future.wait();
+ unicast_sink_hal_cb_->OnAudioSuspend();
}
void LocalAudioSinkResume(void) {
@@ -1775,8 +1771,6 @@ class UnicastTestNoInit : public Test {
* might have different state that it is in the le_audio code - as tearing
* down CISes might take some time
*/
- std::promise<void> do_suspend_sink_promise;
- auto do_suspend_sink_future = do_suspend_sink_promise.get_future();
/* It's enough to call only one resume even if it'll be bi-directional
* streaming. First suspend will trigger GroupStop.
*
@@ -1784,16 +1778,11 @@ class UnicastTestNoInit : public Test {
* If there will be such test oriented scenario, such resume choose logic
* should be applied.
*/
- unicast_source_hal_cb_->OnAudioSuspend(std::move(do_suspend_sink_promise));
- do_suspend_sink_future.wait();
+ unicast_source_hal_cb_->OnAudioSuspend();
if (suspend_source) {
ASSERT_NE(unicast_sink_hal_cb_, nullptr);
- std::promise<void> do_suspend_source_promise;
- auto do_suspend_source_future = do_suspend_source_promise.get_future();
- unicast_sink_hal_cb_->OnAudioSuspend(
- std::move(do_suspend_source_promise));
- do_suspend_source_future.wait();
+ unicast_sink_hal_cb_->OnAudioSuspend();
}
}
@@ -4164,6 +4153,88 @@ TEST_F(UnicastTest, RemoveWhileStreaming) {
ASSERT_EQ(group, nullptr);
}
+TEST_F(UnicastTest, DisconnecteWhileAlmostStreaming) {
+ const RawAddress test_address0 = GetTestAddress(0);
+ int group_id = bluetooth::groups::kGroupUnknown;
+
+ SetSampleDatabaseEarbudsValid(
+ 1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
+ codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
+ default_channel_cnt, 0x0004,
+ /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/,
+ true /*add_pacs*/, default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/,
+ 0 /*rank*/);
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnConnectionState(ConnectionState::CONNECTED, test_address0))
+ .Times(1);
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
+ .WillOnce(DoAll(SaveArg<1>(&group_id)));
+
+ ConnectLeAudio(test_address0);
+ ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
+
+ constexpr int gmcs_ccid = 1;
+ constexpr int gtbs_ccid = 2;
+
+ // Audio sessions are started only when device gets active
+ EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
+ EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
+ LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
+ LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
+ LeAudioClient::Get()->GroupSetActive(group_id);
+
+ types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
+ .source = {}};
+ EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, ccids)).Times(1);
+
+ /* We want here to CIS be established but device not being yet in streaming
+ * state
+ */
+ StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+ Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+ SyncOnMainLoop();
+
+ /* This is test code, which will change the group state to the one which
+ * is required by test
+ */
+ auto group_inject = streaming_groups.at(group_id);
+ group_inject->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING);
+
+ EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
+
+ LeAudioDeviceGroup* group = nullptr;
+ EXPECT_CALL(mock_state_machine_, ProcessHciNotifAclDisconnected(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&group)));
+
+ EXPECT_CALL(mock_audio_hal_client_callbacks_,
+ OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
+ .Times(1);
+
+ /*
+ * StopStream will put calls on main_loop so to keep the correct order
+ * of operations and to avoid races we put the test command on main_loop as
+ * well.
+ */
+ do_in_main_thread(FROM_HERE, base::BindOnce(
+ [](LeAudioClient* client,
+ const RawAddress& test_address0) {
+ client->Disconnect(test_address0);
+ },
+ LeAudioClient::Get(), test_address0));
+
+ SyncOnMainLoop();
+ Mock::VerifyAndClearExpectations(&mock_groups_module_);
+ Mock::VerifyAndClearExpectations(&mock_state_machine_);
+ Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+
+ ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
+}
+
TEST_F(UnicastTest, EarbudsTwsStyleStreaming) {
const RawAddress test_address0 = GetTestAddress(0);
int group_id = bluetooth::groups::kGroupUnknown;
diff --git a/system/bta/le_audio/mock_state_machine.h b/system/bta/le_audio/mock_state_machine.h
index 02bad5f30a..5a2be11b9e 100644
--- a/system/bta/le_audio/mock_state_machine.h
+++ b/system/bta/le_audio/mock_state_machine.h
@@ -33,7 +33,8 @@ class MockLeAudioGroupStateMachine : public le_audio::LeAudioGroupStateMachine {
(override));
MOCK_METHOD((bool), AttachToStream,
(le_audio::LeAudioDeviceGroup * group,
- le_audio::LeAudioDevice* leAudioDevice),
+ le_audio::LeAudioDevice* leAudioDevice,
+ le_audio::types::BidirectionalPair<std::vector<uint8_t>> ccids),
(override));
MOCK_METHOD((void), SuspendStream, (le_audio::LeAudioDeviceGroup * group),
(override));
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 3ead35ff49..8a20bc427d 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -137,8 +137,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
log_history_ = nullptr;
}
- bool AttachToStream(LeAudioDeviceGroup* group,
- LeAudioDevice* leAudioDevice) override {
+ bool AttachToStream(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
+ BidirectionalPair<std::vector<uint8_t>> ccids) override {
LOG(INFO) << __func__ << " group id: " << group->group_id_
<< " device: " << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_);
@@ -155,11 +155,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
return false;
}
- BidirectionalPair<std::vector<uint8_t>> ccids = {
- .sink = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
- group->GetMetadataContexts().sink),
- .source = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
- group->GetMetadataContexts().source)};
if (!group->Configure(group->GetConfigurationContextType(),
group->GetMetadataContexts(), ccids)) {
LOG_ERROR(" failed to set ASE configuration");
diff --git a/system/bta/le_audio/state_machine.h b/system/bta/le_audio/state_machine.h
index e4d92e8af0..44a4d54877 100644
--- a/system/bta/le_audio/state_machine.h
+++ b/system/bta/le_audio/state_machine.h
@@ -45,8 +45,9 @@ class LeAudioGroupStateMachine {
static void Cleanup(void);
static LeAudioGroupStateMachine* Get(void);
- virtual bool AttachToStream(LeAudioDeviceGroup* group,
- LeAudioDevice* leAudioDevice) = 0;
+ virtual bool AttachToStream(
+ LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
+ types::BidirectionalPair<std::vector<uint8_t>> ccids) = 0;
virtual bool StartStream(
LeAudioDeviceGroup* group, types::LeAudioContextType context_type,
const types::BidirectionalPair<types::AudioContexts>&
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 89cf100377..d83a9077d2 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -3323,7 +3323,8 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
- LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice);
+ LeAudioGroupStateMachine::Get()->AttachToStream(
+ group, lastDevice, {.sink = {media_ccid}, .source = {}});
// Check if group keeps streaming
ASSERT_EQ(group->GetState(),
@@ -3404,8 +3405,8 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStreamDoNotAttach) {
LeAudioGroupStateMachine::Get()->StopStream(group);
testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
- ASSERT_FALSE(
- LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice));
+ ASSERT_FALSE(LeAudioGroupStateMachine::Get()->AttachToStream(
+ group, lastDevice, {.sink = {}, .source = {}}));
}
TEST_F(StateMachineTest, testReconfigureAfterLateDeviceAttached) {
@@ -3664,7 +3665,8 @@ TEST_F(StateMachineTest, testAttachDeviceToTheConversationalStream) {
EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
- LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice);
+ LeAudioGroupStateMachine::Get()->AttachToStream(
+ group, lastDevice, {.sink = {call_ccid}, .source = {call_ccid}});
// Check if group keeps streaming
ASSERT_EQ(group->GetState(),
@@ -4754,7 +4756,8 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStreamCisFailure) {
EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0);
- LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice);
+ LeAudioGroupStateMachine::Get()->AttachToStream(
+ group, lastDevice, {.sink = {media_ccid}, .source = {}});
// Check if group keeps streaming
ASSERT_EQ(group->GetState(),
diff --git a/system/bta/test/bta_dm_test.cc b/system/bta/test/bta_dm_test.cc
index 43c3c2a794..f40bb815d7 100644
--- a/system/bta/test/bta_dm_test.cc
+++ b/system/bta/test/bta_dm_test.cc
@@ -42,6 +42,7 @@
#include "test/mock/mock_osi_alarm.h"
#include "test/mock/mock_osi_allocator.h"
#include "test/mock/mock_stack_acl.h"
+#include "test/mock/mock_stack_btm.h"
#include "test/mock/mock_stack_btm_ble.h"
#include "test/mock/mock_stack_btm_inq.h"
#include "test/mock/mock_stack_btm_sec.h"
@@ -50,8 +51,6 @@
using namespace std::chrono_literals;
using ::testing::ElementsAre;
-extern struct btm_client_interface_t btm_client_interface;
-
namespace base {
class MessageLoop;
} // namespace base
@@ -106,6 +105,7 @@ class BtaDmTest : public testing::Test {
bluetooth::legacy::testing::bta_dm_deinit_cb();
post_on_bt_main([]() { LOG_INFO("Main thread shutting down"); });
main_thread_shut_down();
+ btm_client_interface = {};
}
std::unique_ptr<test::fake::FakeOsi> fake_osi_;
@@ -392,6 +392,11 @@ TEST_F(BtaDmTest, bta_dm_remname_cback__typical) {
strlcpy(reinterpret_cast<char*>(&name.remote_bd_name), kRemoteName,
strlen(kRemoteName));
+ btm_client_interface.security.BTM_SecDeleteRmtNameNotifyCallback =
+ [](tBTM_RMT_NAME_CALLBACK*) -> bool {
+ inc_func_call_count("BTM_SecDeleteRmtNameNotifyCallback");
+ return true;
+ };
bluetooth::legacy::testing::bta_dm_remname_cback(&name);
sync_main_handler();
@@ -416,6 +421,11 @@ TEST_F(BtaDmTest, bta_dm_remname_cback__wrong_address) {
strlcpy(reinterpret_cast<char*>(&name.remote_bd_name), kRemoteName,
strlen(kRemoteName));
+ btm_client_interface.security.BTM_SecDeleteRmtNameNotifyCallback =
+ [](tBTM_RMT_NAME_CALLBACK*) -> bool {
+ inc_func_call_count("BTM_SecDeleteRmtNameNotifyCallback");
+ return true;
+ };
bluetooth::legacy::testing::bta_dm_remname_cback(&name);
sync_main_handler();
@@ -440,6 +450,11 @@ TEST_F(BtaDmTest, bta_dm_remname_cback__HCI_ERR_CONNECTION_EXISTS) {
strlcpy(reinterpret_cast<char*>(&name.remote_bd_name), kRemoteName,
strlen(kRemoteName));
+ btm_client_interface.security.BTM_SecDeleteRmtNameNotifyCallback =
+ [](tBTM_RMT_NAME_CALLBACK*) -> bool {
+ inc_func_call_count("BTM_SecDeleteRmtNameNotifyCallback");
+ return true;
+ };
bluetooth::legacy::testing::bta_dm_remname_cback(&name);
sync_main_handler();
diff --git a/system/bta/test/bta_sdp_test.cc b/system/bta/test/bta_sdp_test.cc
index b91c44d51a..52775f0cc2 100644
--- a/system/bta/test/bta_sdp_test.cc
+++ b/system/bta/test/bta_sdp_test.cc
@@ -30,6 +30,10 @@
void BTA_dm_on_hw_on();
void BTA_dm_on_hw_off();
+namespace {
+const char kName[] = "Hello";
+}
+
class BtaSdpTest : public testing::Test {
protected:
void SetUp() override {
@@ -100,5 +104,9 @@ TEST_F(BtaSdpRegisteredTest, bta_dm_sdp_result_SDP_SUCCESS) {
.sdp_result = SDP_SUCCESS,
},
};
+ btm_client_interface.security.BTM_SecReadDevName =
+ [](const RawAddress& bd_addr) -> const char* { return kName; };
+ btm_client_interface.security.BTM_SecDeleteRmtNameNotifyCallback =
+ [](tBTM_RMT_NAME_CALLBACK*) -> bool { return true; };
bta_dm_sdp_result(&msg);
}
diff --git a/system/btcore/Android.bp b/system/btcore/Android.bp
index 0361a809c0..5e4b9de0dd 100644
--- a/system/btcore/Android.bp
+++ b/system/btcore/Android.bp
@@ -10,7 +10,7 @@ package {
cc_defaults {
name: "libbtcore_defaults",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
local_include_dirs: ["include"],
include_dirs: [
"packages/modules/Bluetooth/system",
@@ -36,7 +36,7 @@ cc_defaults {
cc_library_static {
name: "libbthalutils",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
srcs: [
"src/hal_util.cc",
],
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 08df424495..d7e341ed6d 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -39,9 +39,6 @@ cc_library {
generated_sources: ["statslog_bt.cpp"],
generated_headers: ["statslog_bt.h"],
export_generated_headers: ["statslog_bt.h"],
- shared_libs: [
- "libcutils",
- ],
apex_available: [
"com.android.btservices",
],
@@ -57,6 +54,7 @@ cc_library {
},
host: {
static_libs: [
+ "libbase",
"libstatssocket",
],
export_static_lib_headers: [
@@ -89,7 +87,7 @@ genrule {
cc_library_static {
name: "libbtif",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
include_dirs: btifCommonIncludes,
srcs: [
// AVRCP Target Service
@@ -162,13 +160,16 @@ cc_library_static {
cflags: [
"-DBUILDCFG",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
cc_library_static {
name: "libbtif-core",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
include_dirs: btifCommonIncludes,
srcs: [
// Callouts
@@ -237,6 +238,9 @@ cc_library_static {
"-DBUILDCFG",
"-fvisibility=default",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -269,7 +273,7 @@ cc_test {
"libutils",
],
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.bluetooth.a2dp@1.0",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.bluetooth@1.0",
@@ -286,7 +290,6 @@ cc_test {
"libbt-bta-core",
"libbt-common",
"libbt-hci",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbt-stack",
@@ -343,7 +346,6 @@ cc_test {
static_libs: [
"libbluetooth-types",
"libchrome",
- "libflatbuffers-cpp",
"libosi",
],
cflags: ["-DBUILDCFG"],
@@ -376,7 +378,6 @@ cc_test {
static_libs: [
"libbluetooth-types",
"libchrome",
- "libflatbuffers-cpp",
"libosi",
],
cflags: ["-DBUILDCFG"],
@@ -413,7 +414,6 @@ cc_test {
static_libs: [
"libbluetooth-types",
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libosi-AllocationTestHarness",
@@ -607,14 +607,13 @@ cc_test {
"libchrome",
"libcom.android.sysprop.bluetooth",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
],
cflags: ["-DBUILDCFG"],
target: {
android: {
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
@@ -739,14 +738,13 @@ cc_test {
"libchrome",
"libcom.android.sysprop.bluetooth",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
],
cflags: ["-DBUILDCFG"],
target: {
android: {
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc
index 42711010dc..e1724b21ea 100644
--- a/system/btif/co/bta_av_co.cc
+++ b/system/btif/co/bta_av_co.cc
@@ -406,13 +406,6 @@ class BtaAvCo {
const tA2DP_ENCODER_INTERFACE* GetSourceEncoderInterface();
/**
- * Get the Sink decoder interface for the current codec.
- *
- * @return the Sink decoder interface for the current codec
- */
- const tA2DP_DECODER_INTERFACE* GetSinkDecoderInterface();
-
- /**
* Set the codec user configuration.
*
* @param peer_address the peer address
@@ -1446,8 +1439,8 @@ void BtaAvCo::UpdateMtu(tBTA_AV_HNDL bta_av_handle,
}
bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
- VLOG(1) << __func__ << ": peer_address="
- << ADDRESS_TO_LOGGABLE_STR(peer_address);
+ LOG(INFO) << __func__
+ << ": peer_address=" << ADDRESS_TO_LOGGABLE_STR(peer_address);
std::lock_guard<std::recursive_mutex> lock(codec_lock_);
@@ -1505,12 +1498,6 @@ const tA2DP_ENCODER_INTERFACE* BtaAvCo::GetSourceEncoderInterface() {
return A2DP_GetEncoderInterface(codec_config_);
}
-const tA2DP_DECODER_INTERFACE* BtaAvCo::GetSinkDecoderInterface() {
- std::lock_guard<std::recursive_mutex> lock(codec_lock_);
-
- return A2DP_GetDecoderInterface(codec_config_);
-}
-
bool BtaAvCo::SetCodecUserConfig(
const RawAddress& peer_address,
const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
@@ -2263,10 +2250,6 @@ const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void) {
return bta_av_co_cb.GetSourceEncoderInterface();
}
-const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void) {
- return bta_av_co_cb.GetSinkDecoderInterface();
-}
-
bool bta_av_co_set_codec_user_config(
const RawAddress& peer_address,
const btav_a2dp_codec_config_t& codec_user_config, bool* p_restart_output) {
diff --git a/system/btif/include/btif_av_co.h b/system/btif/include/btif_av_co.h
index 1ce810f5e3..d7f80ab559 100644
--- a/system/btif/include/btif_av_co.h
+++ b/system/btif/include/btif_av_co.h
@@ -40,12 +40,6 @@ void bta_av_co_get_peer_params(const RawAddress& peer_addr,
// otherwise NULL.
const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void);
-// Gets the current A2DP decoder interface that can be used to decode received
-// A2DP packets - see |tA2DP_DECODER_INTERFACE|.
-// Returns the A2DP decoder interface if the current codec is setup, otherwise
-// NULL.
-const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void);
-
// Sets the user preferred codec configuration.
// The peer address is |peer_addr|.
// |codec_user_config| contains the preferred codec configuration.
diff --git a/system/btif/src/btif_a2dp_sink.cc b/system/btif/src/btif_a2dp_sink.cc
index 9a03a3cd44..4340e48375 100644
--- a/system/btif/src/btif_a2dp_sink.cc
+++ b/system/btif/src/btif_a2dp_sink.cc
@@ -631,7 +631,8 @@ static void btif_a2dp_sink_decoder_update_event(
btif_a2dp_sink_cb.rx_flush = false;
APPL_TRACE_DEBUG("%s: reset to Sink role", __func__);
- btif_a2dp_sink_cb.decoder_interface = bta_av_co_get_decoder_interface();
+ btif_a2dp_sink_cb.decoder_interface =
+ A2DP_GetDecoderInterface(p_buf->codec_info);
if (btif_a2dp_sink_cb.decoder_interface == nullptr) {
LOG_ERROR("%s: cannot stream audio: no source decoder interface", __func__);
return;
diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc
index 767279e724..23f78fd1e8 100644
--- a/system/btif/src/btif_av.cc
+++ b/system/btif/src/btif_av.cc
@@ -4035,9 +4035,9 @@ uint8_t btif_av_get_peer_sep(void) {
}
uint8_t peer_sep = peer->PeerSep();
- LOG_INFO("Peer %s SEP is %s (%d)",
- ADDRESS_TO_LOGGABLE_CSTR(peer->PeerAddress()),
- (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep);
+ BTIF_TRACE_DEBUG("Peer %s SEP is %s (%d)",
+ ADDRESS_TO_LOGGABLE_CSTR(peer->PeerAddress()),
+ (peer_sep == AVDT_TSEP_SRC) ? "Source" : "Sink", peer_sep);
return peer_sep;
}
diff --git a/system/btif/src/btif_avrcp_audio_track.cc b/system/btif/src/btif_avrcp_audio_track.cc
index 296c603c7f..2a732f372e 100644
--- a/system/btif/src/btif_avrcp_audio_track.cc
+++ b/system/btif/src/btif_avrcp_audio_track.cc
@@ -51,8 +51,8 @@ constexpr float kMinTrackGain = 0.0f;
void* BtifAvrcpAudioTrackCreate(int trackFreq, int bitsPerSample,
int channelCount) {
- LOG_VERBOSE("%s Track.cpp: btCreateTrack freq %d bps %d channel %d ",
- __func__, trackFreq, bitsPerSample, channelCount);
+ LOG_INFO("%s Track.cpp: btCreateTrack freq %d bps %d channel %d ", __func__,
+ trackFreq, bitsPerSample, channelCount);
AAudioStreamBuilder* builder;
AAudioStream* stream;
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 1521897450..bfe4c6f41a 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -602,11 +602,6 @@ static void bond_state_changed(bt_status_t status, const RawAddress& bd_addr,
GetInterfaceToProfiles()->events->invoke_bond_state_changed_cb(
status, bd_addr, state, pairing_cb.fail_reason);
- int dev_type;
- if (!btif_get_device_type(bd_addr, &dev_type)) {
- dev_type = BT_DEVICE_TYPE_BREDR;
- }
-
if ((state == BT_BOND_STATE_NONE) && (pairing_cb.bd_addr != bd_addr)
&& is_bonding_or_sdp()) {
LOG_WARN("Ignoring bond state changed for unexpected device: %s pairing: %s",
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index c5c00eb1e1..2daaacb1e3 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -3957,6 +3957,9 @@ static void handle_get_metadata_attr_response(tBTA_AV_META_MSG* pmeta_msg,
osi_free_and_reset((void**)&p_rsp->p_attrs[i].name.p_str);
}
}
+
+ osi_free_and_reset((void**)&p_rsp->p_attrs);
+
do_in_jni_thread(FROM_HERE,
base::Bind(bt_rc_ctrl_callbacks->track_changed_cb,
p_dev->rc_addr, p_rsp->num_attrs, p_attr));
diff --git a/system/btif/test/btif_rc_test.cc b/system/btif/test/btif_rc_test.cc
index c10cee2634..bd213221a4 100644
--- a/system/btif/test/btif_rc_test.cc
+++ b/system/btif/test/btif_rc_test.cc
@@ -426,3 +426,42 @@ TEST_F(BtifRcConnectionTest, BTA_AV_RC_OPEN_EVT) {
auto res = future.get();
CHECK(res.rc_state == true);
}
+
+class BtifTrackChangeCBTest : public BtifRcTest {
+ protected:
+ void SetUp() override {
+ BtifRcTest::SetUp();
+ init_ctrl(&btrc_ctrl_callbacks);
+ jni_thread.StartUp();
+ btrc_ctrl_callbacks.track_changed_cb = [](const RawAddress& bd_addr,
+ uint8_t num_attr, btrc_element_attr_val_t* p_attrs) {
+ btif_rc_cb.rc_multi_cb[0].rc_addr = bd_addr;
+ };
+ }
+
+ void TearDown() override {
+ btrc_ctrl_callbacks.track_changed_cb = [](const RawAddress& bd_addr,
+ uint8_t num_attr, btrc_element_attr_val_t* p_attrs) {};
+ BtifRcTest::TearDown();
+ }
+};
+
+TEST_F(BtifTrackChangeCBTest, handle_get_metadata_attr_response) {
+ tBTA_AV_META_MSG meta_msg = {
+ .rc_handle = 0,
+ };
+
+ tAVRC_GET_ATTRS_RSP rsp = {
+ .status = AVRC_STS_NO_ERROR,
+ .num_attrs = 0,
+ };
+
+ btif_rc_cb.rc_multi_cb[0].rc_handle = 0;
+ btif_rc_cb.rc_multi_cb[0].rc_addr = RawAddress::kEmpty;
+ btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED;
+ btif_rc_cb.rc_multi_cb[0].rc_connected = true;
+
+ handle_get_metadata_attr_response(&meta_msg, &rsp);
+
+ ASSERT_EQ(1, get_func_call_count("osi_free_and_reset"));
+}
diff --git a/system/build/Android.bp b/system/build/Android.bp
index 95b20a2ec7..76b9b476f3 100644
--- a/system/build/Android.bp
+++ b/system/build/Android.bp
@@ -19,7 +19,6 @@ cc_defaults {
// struct BT_HDR is defined as a variable-size header in a struct.
"-Wno-gnu-variable-sized-type-not-at-end",
],
- c_std: "c99",
product_variables: {
debuggable: {
cflags: [
@@ -57,7 +56,6 @@ cc_defaults {
"libbt_shim_bridge",
"libbt_shim_ffi",
],
- cpp_std: "c++17",
min_sdk_version: "current",
}
@@ -71,7 +69,6 @@ cc_defaults {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libcutils",
"libgmock",
"libosi",
@@ -86,22 +83,6 @@ cc_defaults {
}
cc_defaults {
- name: "fluoride_basic_defaults",
- defaults: [
- "fluoride_defaults",
- ],
- apex_available: [
- "//apex_available:platform",
- "com.android.btservices",
- ],
- static_libs: [
- "libbt-protos-lite",
- "libflatbuffers-cpp",
- "libprotobuf-cpp-lite",
- ],
-}
-
-cc_defaults {
name: "fluoride_defaults",
defaults: [
"fluoride_defaults_fuzzable",
diff --git a/system/common/Android.bp b/system/common/Android.bp
index 3c6a803dca..c40182d5b7 100644
--- a/system/common/Android.bp
+++ b/system/common/Android.bp
@@ -10,7 +10,7 @@ package {
cc_library_static {
name: "libbt-common",
defaults: [
- "fluoride_basic_defaults",
+ "fluoride_defaults",
],
host_supported: true,
include_dirs: [
@@ -28,9 +28,17 @@ cc_library_static {
"stop_watch_legacy.cc",
"time_util.cc",
],
+ proto: {
+ type: "lite",
+ canonical_path_from_root: false,
+ export_proto_headers: true,
+ },
target: {
android: {
- srcs: ["metrics.cc"],
+ srcs: [
+ ":bluetooth-metrics-proto",
+ "metrics.cc",
+ ],
},
host: {
srcs: ["metrics_linux.cc"],
@@ -40,6 +48,9 @@ cc_library_static {
"libcrypto",
"libcutils",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
min_sdk_version: "Tiramisu",
}
@@ -85,7 +96,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libgmock",
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 59b7a3d9b4..33a04833b0 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -10,7 +10,7 @@ package {
cc_library_static {
name: "libbtdevice",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
host_supported: true,
local_include_dirs: [
"include",
@@ -28,6 +28,9 @@ cc_library_static {
"src/esco_parameters.cc",
"src/interop.cc",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
min_sdk_version: "Tiramisu",
}
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index cdf6ae2c7d..70e52fe37e 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -30,7 +30,6 @@ cc_defaults {
enabled: false,
},
},
- cpp_std: "c++17",
cflags: [
"-DEXPORT_SYMBOL=__attribute__((visibility(\"default\")))",
"-DGOOGLE_PROTOBUF_NO_RTTI",
@@ -38,7 +37,6 @@ cc_defaults {
"-Wno-unused-result",
"-fvisibility=hidden",
],
- c_std: "c99",
header_libs: ["jni_headers"],
}
@@ -164,6 +162,9 @@ cc_defaults {
"libcrypto",
"libflatbuffers-cpp",
],
+ export_shared_lib_headers: [
+ "libflatbuffers-cpp",
+ ],
whole_static_libs: [
"libc++fs",
],
@@ -470,7 +471,6 @@ cc_defaults {
],
shared_libs: [
"libcrypto",
- "libflatbuffers-cpp",
"libgrpc++",
"libgrpc_wrap",
],
diff --git a/system/gd/btaa/activity_attribution.h b/system/gd/btaa/activity_attribution.h
index 93f5549dd3..7ad4ad42b3 100644
--- a/system/gd/btaa/activity_attribution.h
+++ b/system/gd/btaa/activity_attribution.h
@@ -36,17 +36,6 @@ struct BtaaAggregationEntry {
CreationTime creation_time;
};
-class ActivityAttributionCallback {
- public:
- virtual ~ActivityAttributionCallback() = default;
-
- // Callback when Bluetooth woke up the system
- virtual void OnWakeup(const Activity activity, const hci::Address& address) = 0;
-
- // Callback when Bluetooth activity logs are ready to be moved
- virtual void OnActivityLogsReady(const std::vector<BtaaAggregationEntry> logs) = 0;
-};
-
class ActivityAttribution : public bluetooth::Module {
public:
ActivityAttribution() = default;
@@ -59,7 +48,6 @@ class ActivityAttribution : public bluetooth::Module {
void OnWakelockAcquired();
void OnWakelockReleased();
void OnWakeup();
- void RegisterActivityAttributionCallback(ActivityAttributionCallback* callback);
void NotifyActivityAttributionInfo(int uid, const std::string& package_name, const std::string& device_address);
static const ModuleFactory Factory;
diff --git a/system/gd/btaa/android/activity_attribution.cc b/system/gd/btaa/android/activity_attribution.cc
index 79e0cea62d..e7a15bd9d6 100644
--- a/system/gd/btaa/android/activity_attribution.cc
+++ b/system/gd/btaa/android/activity_attribution.cc
@@ -158,10 +158,6 @@ struct ActivityAttribution::impl {
attribution_processor_.OnWakeup();
}
- void register_callback(ActivityAttributionCallback* callback) {
- callback_ = callback;
- }
-
void notify_activity_attribution_info(int uid, const std::string& package_name, const std::string& device_address) {
attribution_processor_.NotifyActivityAttributionInfo(uid, package_name, device_address);
}
@@ -171,7 +167,6 @@ struct ActivityAttribution::impl {
attribution_processor_.Dump(std::move(promise), fb_builder);
}
- ActivityAttributionCallback* callback_;
AttributionProcessor attribution_processor_;
HciProcessor hci_processor_;
WakelockProcessor wakelock_processor_;
@@ -213,10 +208,6 @@ void ActivityAttribution::OnWakeup() {
CallOn(pimpl_.get(), &impl::on_wakeup);
}
-void ActivityAttribution::RegisterActivityAttributionCallback(ActivityAttributionCallback* callback) {
- CallOn(pimpl_.get(), &impl::register_callback, callback);
-}
-
void ActivityAttribution::NotifyActivityAttributionInfo(
int uid, const std::string& package_name, const std::string& device_address) {
CallOn(pimpl_.get(), &impl::notify_activity_attribution_info, uid, package_name, device_address);
diff --git a/system/gd/btaa/host/activity_attribution.cc b/system/gd/btaa/host/activity_attribution.cc
index ec02daf108..20c7ac76cc 100644
--- a/system/gd/btaa/host/activity_attribution.cc
+++ b/system/gd/btaa/host/activity_attribution.cc
@@ -29,8 +29,6 @@ void ActivityAttribution::OnWakelockReleased() {}
void ActivityAttribution::OnWakeup() {}
-void ActivityAttribution::RegisterActivityAttributionCallback(ActivityAttributionCallback* callback) {}
-
void ActivityAttribution::NotifyActivityAttributionInfo(
int uid, const std::string& package_name, const std::string& device_address) {}
diff --git a/system/gd/btaa/linux/activity_attribution.cc b/system/gd/btaa/linux/activity_attribution.cc
index c4a4eb0aab..c39186750a 100644
--- a/system/gd/btaa/linux/activity_attribution.cc
+++ b/system/gd/btaa/linux/activity_attribution.cc
@@ -26,14 +26,10 @@ struct ActivityAttribution::impl {
impl(ActivityAttribution* module) {}
void on_hci_packet(hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length) {}
-
- void register_callback(ActivityAttributionCallback* callback) {}
};
void ActivityAttribution::Capture(const hal::HciPacket& packet, hal::SnoopLogger::PacketType type) {}
-void ActivityAttribution::RegisterActivityAttributionCallback(ActivityAttributionCallback* callback) {}
-
void ActivityAttribution::NotifyActivityAttributionInfo(
int uid, const std::string& package_name, const std::string& device_address) {}
diff --git a/system/gd/dumpsys/bundler/Android.bp b/system/gd/dumpsys/bundler/Android.bp
index f2f346a9ec..64059976bb 100644
--- a/system/gd/dumpsys/bundler/Android.bp
+++ b/system/gd/dumpsys/bundler/Android.bp
@@ -49,7 +49,6 @@ genrule {
cc_defaults {
name: "bluetooth_flatbuffer_bundler_defaults",
defaults: ["bluetooth_cflags"],
- cpp_std: "c++17",
generated_headers: [
"BluetoothGeneratedBundlerSchema_h_bfbs",
],
diff --git a/system/gd/hci/le_periodic_sync_manager.h b/system/gd/hci/le_periodic_sync_manager.h
index 9cd92cda7e..cb3b10fbb4 100644
--- a/system/gd/hci/le_periodic_sync_manager.h
+++ b/system/gd/hci/le_periodic_sync_manager.h
@@ -347,6 +347,10 @@ class PeriodicSyncManager {
LOG_DEBUG("[PSync]: sync_handle = %d", sync_handle);
callbacks_->OnPeriodicSyncLost(sync_handle);
auto periodic_sync = GetEstablishedSyncFromHandle(sync_handle);
+ if (periodic_sync == periodic_syncs_.end()) {
+ LOG_ERROR("[PSync]: index not found for handle %u", sync_handle);
+ return;
+ }
periodic_syncs_.erase(periodic_sync);
}
diff --git a/system/gd/proto/Android.bp b/system/gd/proto/Android.bp
index 538d8df347..6136cf84f6 100644
--- a/system/gd/proto/Android.bp
+++ b/system/gd/proto/Android.bp
@@ -14,7 +14,6 @@ java_library_static {
type: "lite",
},
srcs: [
- "bluetooth/bluetoothKeystore/keystore.proto",
"bluetooth/metrics/bluetooth.proto",
],
apex_available: [
@@ -24,21 +23,11 @@ java_library_static {
sdk_version: "current",
}
-cc_library_static {
- name: "libbt-protos-lite",
- host_supported: true,
- proto: {
- export_proto_headers: true,
- type: "lite",
- },
+filegroup {
+ name: "bluetooth-metrics-proto",
srcs: [
- "bluetooth/bluetoothKeystore/keystore.proto",
"bluetooth/metrics/bluetooth.proto",
],
- apex_available: [
- "com.android.btservices",
- ],
- min_sdk_version: "30",
}
cc_library_static {
@@ -50,7 +39,6 @@ cc_library_static {
include_dirs: ["external/protobuf/src"],
},
srcs: [
- "bluetooth/bluetoothKeystore/keystore.proto",
"bluetooth/metrics/bluetooth.proto",
],
apex_available: [
diff --git a/system/gd/rust/common/src/bridge.rs b/system/gd/rust/common/src/bridge.rs
index 6c3c281ee6..974ee8363c 100644
--- a/system/gd/rust/common/src/bridge.rs
+++ b/system/gd/rust/common/src/bridge.rs
@@ -2,6 +2,7 @@
pub use crate::parameter_provider::*;
+#[allow(unsafe_op_in_unsafe_fn)]
#[cxx::bridge(namespace = "bluetooth::fake_bluetooth_keystore")]
/// ffi extern module
pub mod ffi {
diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs
index bb6789d872..5d6ea21b69 100644
--- a/system/gd/rust/common/src/init_flags.rs
+++ b/system/gd/rust/common/src/init_flags.rs
@@ -366,6 +366,7 @@ init_flags!(
btaa_hci = true,
bta_dm_clear_conn_id_on_client_close = true,
btm_dm_flush_discovery_queue_on_search_cancel,
+ bta_dm_stop_discovery_on_search_cancel,
classic_discovery_only,
clear_hidd_interrupt_cid_on_disconnect = true,
delay_hidh_cleanup_until_hidh_ready_start = true,
diff --git a/system/gd/rust/common/src/parameter_provider.rs b/system/gd/rust/common/src/parameter_provider.rs
index d4d32a0295..50ac00fb5d 100644
--- a/system/gd/rust/common/src/parameter_provider.rs
+++ b/system/gd/rust/common/src/parameter_provider.rs
@@ -18,7 +18,11 @@ pub struct ParameterProvider {
lock: Mutex<i32>,
}
+// SAFETY: Nothing about `BluetoothKeystoreInterface` is bound to a specific thread, and all other
+// fields are `Send`.
unsafe impl Send for ParameterProvider {}
+
+// SAFETY: Nothing about `BluetoothKeystoreInterface` is bound to a specific thread.
unsafe impl Send for BluetoothKeystoreInterface {}
impl ParameterProvider {
@@ -243,6 +247,8 @@ mod tests {
assert_eq!(now, "/data/misc/bluetooth/logs/btsnooz_hci.log");
}
+ // TODO(b/290018030): Remove this and add proper safety comments.
+ #[allow(clippy::undocumented_unsafe_blocks)]
#[ignore]
#[tokio::test]
async fn test_bt_keystore_interface() {
@@ -292,11 +298,20 @@ mod tests {
}
let current = (*param_provider.lock().await).get_bt_keystore_interface().await;
- let address_current = format!("{:p}", &current);
- let reality = *hex_to_dec(address_current);
- let mut answer = *hex_to_dec(Arc::clone(&address1).lock().await.to_string());
+ let reality = current as i64;
+ // TODO: Fix unsoundness. `address1` contains a string representation of a pointer to a
+ // `cxx::UniquePtr` on the stack; either `choice_ptr1` (which is still in scope, so it is
+ // valid) or to `ptr1` (which is in another task which may have finished, so may not be
+ // valid). If it were valid, then transmuting the `cxx::UniquePtr` to an `i64` is probably
+ // fine, though a dubious thing to do.
+ let mut answer = unsafe { *(hex_to_dec(&address1.lock().await) as *const i64) };
if *choice.lock().await == "2" {
- answer = *hex_to_dec(Arc::clone(&address2).lock().await.to_string());
+ // TODO: Fix unsoundness. `address2` contains a string representation of a pointer to a
+ // `cxx::UniquePtr` on the stack; either `choice_ptr2` (which is still in scope, so it
+ // is valid) or to `ptr2` (which is in another task which may have finished, so may not
+ // be valid). If it were valid, then transmuting the `cxx::UniquePtr` to an `i64` is
+ // probably fine, though a dubious thing to do.
+ answer = unsafe { *(hex_to_dec(&address2.lock().await) as *const i64) };
}
assert_eq!(reality, answer);
}
@@ -382,11 +397,9 @@ mod tests {
assert_eq!(now, 0b11);
}
- fn hex_to_dec(origin: String) -> &'static i64 {
- let address = unsafe {
- let origin = origin.trim_start_matches("0x");
- &*(usize::from_str_radix(origin, 16).unwrap() as *const i64)
- };
- address
+ /// Converts a hex string to an integer
+ fn hex_to_dec(origin: &str) -> usize {
+ let origin = origin.trim_start_matches("0x");
+ usize::from_str_radix(origin, 16).unwrap()
}
}
diff --git a/system/gd/rust/facade/src/main.rs b/system/gd/rust/facade/src/main.rs
index 3bd53b05d4..67e7277e5e 100644
--- a/system/gd/rust/facade/src/main.rs
+++ b/system/gd/rust/facade/src/main.rs
@@ -13,7 +13,8 @@ use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;
fn main() {
- let sigint = install_sigint();
+ // SAFETY: There is no signal handler installed before this.
+ let sigint = unsafe { install_sigint() };
bt_common::init_logging();
let rt = Arc::new(Runtime::new().unwrap());
rt.block_on(async_main(Arc::clone(&rt), sigint));
@@ -70,7 +71,10 @@ async fn async_main(rt: Arc<Runtime>, mut sigint: mpsc::UnboundedReceiver<()>) {
}
// TODO: remove as this is a temporary nix-based hack to catch SIGINT
-fn install_sigint() -> mpsc::UnboundedReceiver<()> {
+/// # Safety
+///
+/// The old signal handler, if any, must be installed correctly.
+unsafe fn install_sigint() -> mpsc::UnboundedReceiver<()> {
let (tx, rx) = mpsc::unbounded();
*SIGINT_TX.lock().unwrap() = Some(tx);
@@ -79,6 +83,10 @@ fn install_sigint() -> mpsc::UnboundedReceiver<()> {
signal::SaFlags::empty(),
signal::SigSet::empty(),
);
+ // SAFETY: The caller guarantees that the old signal handler was installed correctly.
+ // TODO(b/292218119): Make sure `handle_sigint` only makes system calls that are safe for signal
+ // handlers, and only accesses global state through atomics. In particular, it must not take any
+ // shared locks.
unsafe {
signal::sigaction(signal::SIGINT, &sig_action).unwrap();
}
diff --git a/system/gd/rust/linux/client/src/dbus_iface.rs b/system/gd/rust/linux/client/src/dbus_iface.rs
index 5b87b9e01c..98e2e0cc5d 100644
--- a/system/gd/rust/linux/client/src/dbus_iface.rs
+++ b/system/gd/rust/linux/client/src/dbus_iface.rs
@@ -731,7 +731,7 @@ impl IBluetooth for BluetoothDBus {
dbus_generated!()
}
- fn init(&mut self, init_flags: Vec<String>) -> bool {
+ fn init(&mut self, _init_flags: Vec<String>) -> bool {
// Not implemented by server
true
}
diff --git a/system/gd/rust/linux/service/src/iface_bluetooth.rs b/system/gd/rust/linux/service/src/iface_bluetooth.rs
index e006675743..ea651d5966 100644
--- a/system/gd/rust/linux/service/src/iface_bluetooth.rs
+++ b/system/gd/rust/linux/service/src/iface_bluetooth.rs
@@ -441,7 +441,7 @@ impl IBluetooth for IBluetoothDBus {
}
// Not exposed over D-Bus. The stack is automatically initialized when the daemon starts.
- fn init(&mut self, init_flags: Vec<String>) -> bool {
+ fn init(&mut self, _init_flags: Vec<String>) -> bool {
dbus_generated!()
}
diff --git a/system/gd/rust/linux/service/src/main.rs b/system/gd/rust/linux/service/src/main.rs
index 13fa4db682..1f9a5864c1 100644
--- a/system/gd/rust/linux/service/src/main.rs
+++ b/system/gd/rust/linux/service/src/main.rs
@@ -20,7 +20,7 @@ use btstack::{
battery_manager::BatteryManager,
battery_provider_manager::BatteryProviderManager,
battery_service::BatteryService,
- bluetooth::{Bluetooth, IBluetooth},
+ bluetooth::{Bluetooth, IBluetooth, SigData},
bluetooth_admin::BluetoothAdmin,
bluetooth_gatt::BluetoothGatt,
bluetooth_logging::BluetoothLogging,
@@ -49,6 +49,8 @@ const ADMIN_SETTINGS_FILE_PATH: &str = "/var/lib/bluetooth/admin_policy.json";
// The maximum ACL disconnect timeout is 3.5s defined by BTA_DM_DISABLE_TIMER_MS
// and BTA_DM_DISABLE_TIMER_RETRIAL_MS
const STACK_TURN_OFF_TIMEOUT_MS: Duration = Duration::from_millis(4000);
+// Time bt_stack_manager waits for cleanup
+const STACK_CLEANUP_TIMEOUT_MS: Duration = Duration::from_millis(1000);
const VERBOSE_ONLY_LOG_TAGS: &[&str] = &[
"bt_bta_av", // AV apis
@@ -135,7 +137,12 @@ fn main() -> Result<(), Box<dyn Error>> {
init_flags.push(String::from("INIT_classic_discovery_only=true"));
let (tx, rx) = Stack::create_channel();
- let sig_notifier = Arc::new((Mutex::new(false), Condvar::new()));
+ let sig_notifier = Arc::new(SigData {
+ enabled: Mutex::new(false),
+ enabled_notify: Condvar::new(),
+ thread_attached: Mutex::new(false),
+ thread_notify: Condvar::new(),
+ });
let intf = Arc::new(Mutex::new(get_btinterface().unwrap()));
let bluetooth_gatt =
@@ -424,7 +431,7 @@ fn main() -> Result<(), Box<dyn Error>> {
lazy_static! {
/// Data needed for signal handling.
- static ref SIG_DATA: Mutex<Option<(Sender<Message>, Arc<(Mutex<bool>, Condvar)>)>> = Mutex::new(None);
+ static ref SIG_DATA: Mutex<Option<(Sender<Message>, Arc<SigData>)>> = Mutex::new(None);
}
extern "C" fn handle_sigterm(_signum: i32) {
@@ -437,10 +444,23 @@ extern "C" fn handle_sigterm(_signum: i32) {
let _ = txl.send(Message::Shutdown).await;
});
- let guard = notifier.0.lock().unwrap();
+ let guard = notifier.enabled.lock().unwrap();
if *guard {
log::debug!("Waiting for stack to turn off for {:?}", STACK_TURN_OFF_TIMEOUT_MS);
- let _ = notifier.1.wait_timeout(guard, STACK_TURN_OFF_TIMEOUT_MS);
+ let _ = notifier.enabled_notify.wait_timeout(guard, STACK_TURN_OFF_TIMEOUT_MS);
+ }
+
+ log::debug!("SIGTERM cleaning up the stack.");
+ let txl = tx.clone();
+ tokio::spawn(async move {
+ // Send the cleanup message here.
+ let _ = txl.send(Message::Cleanup).await;
+ });
+
+ let guard = notifier.thread_attached.lock().unwrap();
+ if *guard {
+ log::debug!("Waiting for stack to clean up for {:?}", STACK_CLEANUP_TIMEOUT_MS);
+ let _ = notifier.thread_notify.wait_timeout(guard, STACK_CLEANUP_TIMEOUT_MS);
}
}
diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs
index 9210aa978d..a62ec4ca54 100644
--- a/system/gd/rust/linux/stack/src/bluetooth.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth.rs
@@ -4,8 +4,8 @@ use bt_topshim::btif::{
BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BluetoothProperty, BtAclState,
BtBondState, BtConnectionDirection, BtConnectionState, BtDeviceType, BtDiscMode,
BtDiscoveryState, BtHciErrorCode, BtPinCode, BtPropertyType, BtScanMode, BtSspVariant, BtState,
- BtStatus, BtTransport, BtVendorProductInfo, DisplayAddress, RawAddress, ToggleableProfile,
- Uuid, Uuid128Bit,
+ BtStatus, BtThreadEvent, BtTransport, BtVendorProductInfo, DisplayAddress, RawAddress,
+ ToggleableProfile, Uuid, Uuid128Bit,
};
use bt_topshim::{
metrics,
@@ -382,6 +382,15 @@ impl BluetoothDeviceContext {
}
}
+/// Structure to track all the signals for SIGTERM.
+pub struct SigData {
+ pub enabled: Mutex<bool>,
+ pub enabled_notify: Condvar,
+
+ pub thread_attached: Mutex<bool>,
+ pub thread_notify: Condvar,
+}
+
/// The interface for adapter callbacks registered through `IBluetooth::register_callback`.
pub trait IBluetoothCallback: RPCProxy {
/// When any adapter property changes.
@@ -500,7 +509,7 @@ pub struct Bluetooth {
discoverable_timeout: Option<JoinHandle<()>>,
/// Used to notify signal handler that we have turned off the stack.
- sig_notifier: Arc<(Mutex<bool>, Condvar)>,
+ sig_notifier: Arc<SigData>,
}
impl Bluetooth {
@@ -509,7 +518,7 @@ impl Bluetooth {
adapter_index: i32,
hci_index: i32,
tx: Sender<Message>,
- sig_notifier: Arc<(Mutex<bool>, Condvar)>,
+ sig_notifier: Arc<SigData>,
intf: Arc<Mutex<BluetoothInterface>>,
bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,
@@ -1173,6 +1182,9 @@ pub(crate) trait BtifBluetoothCallbacks {
min_16_digit: bool,
) {
}
+
+ #[btif_callback(ThreadEvent)]
+ fn thread_event(&mut self, event: BtThreadEvent) {}
}
#[btif_callbacks_dispatcher(dispatch_hid_host_callbacks, HHCallbacks)]
@@ -1240,8 +1252,8 @@ impl BtifBluetoothCallbacks for Bluetooth {
}
// Let the signal notifier know we are turned off.
- *self.sig_notifier.0.lock().unwrap() = false;
- self.sig_notifier.1.notify_all();
+ *self.sig_notifier.enabled.lock().unwrap() = false;
+ self.sig_notifier.enabled_notify.notify_all();
}
BtState::On => {
@@ -1265,8 +1277,8 @@ impl BtifBluetoothCallbacks for Bluetooth {
self.set_connectable(true);
// Notify the signal notifier that we are turned on.
- *self.sig_notifier.0.lock().unwrap() = true;
- self.sig_notifier.1.notify_all();
+ *self.sig_notifier.enabled.lock().unwrap() = true;
+ self.sig_notifier.enabled_notify.notify_all();
// Signal that the stack is up and running.
match self.create_pid_file() {
@@ -1726,6 +1738,21 @@ impl BtifBluetoothCallbacks for Bluetooth {
None => (),
};
}
+
+ fn thread_event(&mut self, event: BtThreadEvent) {
+ match event {
+ BtThreadEvent::Associate => {
+ // Let the signal notifier know stack is initialized.
+ *self.sig_notifier.thread_attached.lock().unwrap() = true;
+ self.sig_notifier.thread_notify.notify_all();
+ }
+ BtThreadEvent::Disassociate => {
+ // Let the signal notifier know stack is done.
+ *self.sig_notifier.thread_attached.lock().unwrap() = false;
+ self.sig_notifier.thread_notify.notify_all();
+ }
+ }
+ }
}
struct BleDiscoveryCallbacks {
diff --git a/system/gd/rust/linux/stack/src/bluetooth_media.rs b/system/gd/rust/linux/stack/src/bluetooth_media.rs
index f26cc91006..d05f02738b 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -1095,6 +1095,15 @@ impl BluetoothMedia {
let sleep_duration = (first_conn_ts + total_duration).saturating_duration_since(now_ts);
sleep(sleep_duration).await;
+ Self::async_disconnect(fallback_tasks, device_states, txl, addr).await;
+ }
+
+ async fn async_disconnect(
+ fallback_tasks: &Arc<Mutex<HashMap<RawAddress, Option<(JoinHandle<()>, Instant)>>>>,
+ device_states: &Arc<Mutex<HashMap<RawAddress, DeviceConnectionStates>>>,
+ txl: &Sender<Message>,
+ addr: &RawAddress,
+ ) {
device_states.lock().unwrap().insert(*addr, DeviceConnectionStates::Disconnecting);
fallback_tasks.lock().unwrap().insert(*addr, None);
@@ -1119,6 +1128,16 @@ impl BluetoothMedia {
let _ = txl.send(Message::Media(MediaActions::ForceEnterConnected(addr.to_string()))).await;
}
+ fn is_bonded(&self, addr: &RawAddress) -> bool {
+ match &self.adapter {
+ Some(adapter) => {
+ BtBondState::Bonded
+ == adapter.lock().unwrap().get_bond_state_by_addr(&addr.to_string())
+ }
+ _ => false,
+ }
+ }
+
fn notify_media_capability_updated(&mut self, addr: RawAddress) {
let mut guard = self.fallback_tasks.lock().unwrap();
let mut states = self.device_states.lock().unwrap();
@@ -1135,8 +1154,22 @@ impl BluetoothMedia {
guard.insert(addr, None);
} else {
// The device is already added or is disconnecting.
- // Ignore unless all profiles are cleared.
+ // Ignore unless all profiles are cleared, where we need to do some clean up.
if !is_profile_cleared {
+ // Unbonded device is special, we need to reject the connection from them.
+ if !self.is_bonded(&addr) {
+ let tasks = self.fallback_tasks.clone();
+ let states = self.device_states.clone();
+ let txl = self.tx.clone();
+ let task = topstack::get_runtime().spawn(async move {
+ warn!(
+ "[{}]: Rejecting an unbonded device's attempt to connect media",
+ DisplayAddress(&addr)
+ );
+ BluetoothMedia::async_disconnect(&tasks, &states, &txl, &addr).await;
+ });
+ guard.insert(addr, Some((task, first_conn_ts)));
+ }
return;
}
}
@@ -1222,39 +1255,18 @@ impl BluetoothMedia {
}
DeviceConnectionStates::FullyConnected => {
// Rejecting the unbonded connection after we finished our profile
- // reconnectinglogic to avoid a collision.
- if let Some(adapter) = &self.adapter {
- if BtBondState::Bonded
- != adapter.lock().unwrap().get_bond_state_by_addr(&addr.to_string())
- {
- warn!(
- "[{}]: Rejecting a unbonded device's attempt to connect to media profiles",
- DisplayAddress(&addr));
- let fallback_tasks = self.fallback_tasks.clone();
- let device_states = self.device_states.clone();
- let txl = self.tx.clone();
- let task = topstack::get_runtime().spawn(async move {
- {
- device_states
- .lock()
- .unwrap()
- .insert(addr, DeviceConnectionStates::Disconnecting);
- fallback_tasks.lock().unwrap().insert(addr, None);
- }
-
- debug!(
- "[{}]: Device connection state: {:?}.",
- DisplayAddress(&addr),
- DeviceConnectionStates::Disconnecting
- );
+ // reconnecting logic to avoid a collision.
+ if !self.is_bonded(&addr) {
+ warn!(
+ "[{}]: Rejecting a unbonded device's attempt to connect to media profiles",
+ DisplayAddress(&addr)
+ );
- let _ = txl
- .send(Message::Media(MediaActions::Disconnect(addr.to_string())))
- .await;
- });
- guard.insert(addr, Some((task, first_conn_ts)));
- return;
- }
+ let task = topstack::get_runtime().spawn(async move {
+ BluetoothMedia::async_disconnect(&tasks, &device_states, &txl, &addr).await;
+ });
+ guard.insert(addr, Some((task, ts)));
+ return;
}
let cur_a2dp_caps = self.a2dp_caps.get(&addr);
diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs
index 9247167b66..a128782f85 100644
--- a/system/gd/rust/linux/stack/src/lib.rs
+++ b/system/gd/rust/linux/stack/src/lib.rs
@@ -64,6 +64,7 @@ use bt_topshim::{
pub enum Message {
// Shuts down the stack.
Shutdown,
+ Cleanup,
// Adapter is enabled and ready.
AdapterReady,
@@ -196,6 +197,9 @@ impl Stack {
match m.unwrap() {
Message::Shutdown => {
bluetooth.lock().unwrap().disable();
+ }
+
+ Message::Cleanup => {
bluetooth.lock().unwrap().cleanup();
}
diff --git a/system/gd/rust/linux/stack/src/socket_manager.rs b/system/gd/rust/linux/stack/src/socket_manager.rs
index 9fb8bae030..2893b76a30 100644
--- a/system/gd/rust/linux/stack/src/socket_manager.rs
+++ b/system/gd/rust/linux/stack/src/socket_manager.rs
@@ -842,7 +842,7 @@ impl BluetoothSocketManager {
SocketActions::OnIncomingSocketReady(
cbid,
cloned_socket_info,
- BtStatus::Fail,
+ BtStatus::Timeout,
),
))
.await;
diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs
index 39d4a38f7c..e40fdd59f2 100644
--- a/system/gd/rust/shim/src/init_flags.rs
+++ b/system/gd/rust/shim/src/init_flags.rs
@@ -1,4 +1,5 @@
#[cxx::bridge(namespace = bluetooth::common::init_flags)]
+#[allow(unsafe_op_in_unsafe_fn)]
mod ffi {
struct InitFlagWithValue {
flag: &'static str,
@@ -18,6 +19,7 @@ mod ffi {
fn bta_dm_clear_conn_id_on_client_close_is_enabled() -> bool;
fn delay_hidh_cleanup_until_hidh_ready_start_is_enabled() -> bool;
fn btm_dm_flush_discovery_queue_on_search_cancel_is_enabled() -> bool;
+ fn bta_dm_stop_discovery_on_search_cancel_is_enabled() -> bool;
fn classic_discovery_only_is_enabled() -> bool;
fn clear_hidd_interrupt_cid_on_disconnect_is_enabled() -> bool;
fn device_iot_config_logging_is_enabled() -> bool;
diff --git a/system/gd/rust/stack/src/hal/hidl_hal.rs b/system/gd/rust/stack/src/hal/hidl_hal.rs
index 542a4a38eb..5145f323dc 100644
--- a/system/gd/rust/stack/src/hal/hidl_hal.rs
+++ b/system/gd/rust/stack/src/hal/hidl_hal.rs
@@ -43,6 +43,7 @@ async fn provide_hidl_hal(rt: Arc<Runtime>) -> RawHal {
#[cxx::bridge(namespace = bluetooth::hal)]
// TODO Either use or remove these functions, this shouldn't be the long term state
#[allow(dead_code)]
+#[allow(unsafe_op_in_unsafe_fn)]
mod ffi {
unsafe extern "C++" {
include!("src/hal/ffi/hidl.h");
diff --git a/system/gd/rust/topshim/Android.bp b/system/gd/rust/topshim/Android.bp
index 786299c1c1..5f3e6e8f79 100644
--- a/system/gd/rust/topshim/Android.bp
+++ b/system/gd/rust/topshim/Android.bp
@@ -110,7 +110,8 @@ rust_bindgen {
wrapper_src: "bindings/wrapper.hpp",
crate_name: "bt_topshim_wrapper_bindgen",
source_stem: "bindings",
- cpp_std: "c++17",
+ defaults: ["bluetooth_cflags"],
+ c_std: "",
host_supported: true,
bindgen_flags: [
"--allowlist-function=bt_.*",
diff --git a/system/gd/rust/topshim/bindings/wrapper.hpp b/system/gd/rust/topshim/bindings/wrapper.hpp
index 7139de421e..4d6cde161e 100644
--- a/system/gd/rust/topshim/bindings/wrapper.hpp
+++ b/system/gd/rust/topshim/bindings/wrapper.hpp
@@ -1,5 +1,3 @@
-#pragma once
-
// Base
#include "hardware/bluetooth.h"
#include "include/hal_util.h"
diff --git a/system/gd/rust/topshim/facade/Android.bp b/system/gd/rust/topshim/facade/Android.bp
index b18b4eb9c7..37d71e7cc2 100644
--- a/system/gd/rust/topshim/facade/Android.bp
+++ b/system/gd/rust/topshim/facade/Android.bp
@@ -52,7 +52,6 @@ rust_defaults {
"libbt-bta-core",
"libbt-common",
"libbt-hci",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbt-stack",
diff --git a/system/gd/rust/topshim/facade/src/main.rs b/system/gd/rust/topshim/facade/src/main.rs
index e5b4ce30c7..a2c5d212fc 100644
--- a/system/gd/rust/topshim/facade/src/main.rs
+++ b/system/gd/rust/topshim/facade/src/main.rs
@@ -36,7 +36,8 @@ use bluetooth_core_rs_for_facade::*;
use bt_shim::*;
fn main() {
- let sigint = install_sigint();
+ // SAFETY: There is no signal handler installed before this.
+ let sigint = unsafe { install_sigint() };
bt_common::init_logging();
let rt = Arc::new(Runtime::new().unwrap());
rt.block_on(async_main(Arc::clone(&rt), sigint));
@@ -123,7 +124,10 @@ async fn async_main(rt: Arc<Runtime>, mut sigint: mpsc::UnboundedReceiver<()>) {
}
// TODO: remove as this is a temporary nix-based hack to catch SIGINT
-fn install_sigint() -> mpsc::UnboundedReceiver<()> {
+/// # Safety
+///
+/// The old signal handler, if any, must be installed correctly.
+unsafe fn install_sigint() -> mpsc::UnboundedReceiver<()> {
let (tx, rx) = mpsc::unbounded();
*SIGINT_TX.lock().unwrap() = Some(tx);
@@ -132,6 +136,10 @@ fn install_sigint() -> mpsc::UnboundedReceiver<()> {
signal::SaFlags::empty(),
signal::SigSet::empty(),
);
+ // SAFETY: The caller guarantees that the old signal handler was installed correctly.
+ // TODO(b/292218119): Make sure `handle_sigint` only makes system calls that are safe for signal
+ // handlers, and only accesses global state through atomics. In particular, it must not take any
+ // shared locks.
unsafe {
signal::sigaction(signal::SIGINT, &sig_action).unwrap();
}
diff --git a/system/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs
index ece8ebe5ac..d67be44737 100644
--- a/system/gd/rust/topshim/src/btif.rs
+++ b/system/gd/rust/topshim/src/btif.rs
@@ -201,6 +201,7 @@ pub enum BtStatus {
JniEnvironmentError,
JniThreadAttachError,
WakeLockError,
+ Timeout,
// Any statuses that couldn't be cleanly converted
Unknown = 0xff,
@@ -331,6 +332,19 @@ impl Into<u32> for BtDiscMode {
#[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)]
#[repr(u32)]
+pub enum BtThreadEvent {
+ Associate = 0,
+ Disassociate,
+}
+
+impl From<bindings::bt_cb_thread_evt> for BtThreadEvent {
+ fn from(item: bindings::bt_cb_thread_evt) -> Self {
+ BtThreadEvent::from_u32(item).unwrap_or(BtThreadEvent::Associate)
+ }
+}
+
+#[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)]
+#[repr(u32)]
pub enum BtIoCap {
Out,
InOut,
@@ -882,8 +896,8 @@ pub enum BaseCallbacks {
BtConnectionDirection,
u16,
),
+ ThreadEvent(BtThreadEvent),
// Unimplemented so far:
- // thread_evt_cb
// dut_mode_recv_cb
// le_test_mode_cb
// energy_info_cb
@@ -943,6 +957,8 @@ cb_variant!(BaseCb, le_address_associate_cb -> BaseCallbacks::LeAddressAssociate
let _1 = unsafe { *(_1 as *const RawAddress) };
});
+cb_variant!(BaseCb, thread_evt_cb -> BaseCallbacks::ThreadEvent, u32 -> BtThreadEvent);
+
cb_variant!(BaseCb, acl_state_cb -> BaseCallbacks::AclState,
u32 -> BtStatus, *mut RawAddress, bindings::bt_acl_state_t -> BtAclState, i32 -> BtTransport, bindings::bt_hci_error_code_t -> BtHciErrorCode, bindings::bt_conn_direction_t -> BtConnectionDirection, u16 -> u16, {
let _1 = unsafe { *(_1 as *const RawAddress) };
@@ -1071,7 +1087,7 @@ impl BluetoothInterface {
address_consolidate_cb: Some(address_consolidate_cb),
le_address_associate_cb: Some(le_address_associate_cb),
acl_state_changed_cb: Some(acl_state_cb),
- thread_evt_cb: None,
+ thread_evt_cb: Some(thread_evt_cb),
dut_mode_recv_cb: None,
le_test_mode_cb: None,
energy_info_cb: None,
diff --git a/system/hci/Android.bp b/system/hci/Android.bp
index cf60675ee3..3212482c83 100644
--- a/system/hci/Android.bp
+++ b/system/hci/Android.bp
@@ -10,7 +10,7 @@ package {
// HCI static library for target
cc_library_static {
name: "libbt-hci",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
srcs: [
"src/buffer_allocator.cc",
"src/packet_fragmenter.cc",
@@ -32,6 +32,9 @@ cc_library_static {
"packages/modules/Bluetooth/system/stack/include",
"system/libhwbinder/include",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -42,7 +45,7 @@ cc_test {
test_suites: ["device-tests"],
defaults: [
"bluetooth_gtest_x86_asan_workaround",
- "fluoride_basic_defaults",
+ "fluoride_defaults",
"fluoride_test_defaults",
"mts_defaults",
],
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 00396f9b11..429d1d7747 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -105,7 +105,8 @@ typedef enum {
BT_STATUS_AUTH_REJECTED,
BT_STATUS_JNI_ENVIRONMENT_ERROR,
BT_STATUS_JNI_THREAD_ATTACH_ERROR,
- BT_STATUS_WAKELOCK_ERROR
+ BT_STATUS_WAKELOCK_ERROR,
+ BT_STATUS_TIMEOUT
} bt_status_t;
inline std::string bt_status_text(const bt_status_t& status) {
@@ -140,6 +141,8 @@ inline std::string bt_status_text(const bt_status_t& status) {
return std::string("jni_thread_error");
case BT_STATUS_WAKELOCK_ERROR:
return std::string("wakelock_error");
+ case BT_STATUS_TIMEOUT:
+ return std::string("timeout_error");
default:
return std::string("UNKNOWN");
}
diff --git a/system/include/hardware/bt_activity_attribution.h b/system/include/hardware/bt_activity_attribution.h
index a9c37c4751..c8f56bc61a 100644
--- a/system/include/hardware/bt_activity_attribution.h
+++ b/system/include/hardware/bt_activity_attribution.h
@@ -24,51 +24,10 @@
namespace bluetooth {
namespace activity_attribution {
-class ActivityAttributionCallbacks {
- public:
- enum class Activity : uint8_t {
- UNKNOWN = 0,
- ACL,
- ADVERTISE,
- CONNECT,
- CONTROL,
- HFP,
- ISO,
- SCAN,
- VENDOR,
- };
-
- struct BtaaAggregationEntry {
- RawAddress address;
- Activity activity;
- uint16_t wakeup_count;
- uint32_t byte_count;
- uint32_t wakelock_duration;
- };
-
- virtual ~ActivityAttributionCallbacks() = default;
-
- /** Callback when Bluetooth woke up the system */
- virtual void OnWakeup(const Activity activity, const RawAddress& address) = 0;
-
- /** Callback when Bluetooth activity logs are ready to be moved */
- virtual void OnActivityLogsReady(
- const std::vector<BtaaAggregationEntry> logs) = 0;
-};
-
class ActivityAttributionInterface {
public:
virtual ~ActivityAttributionInterface() = default;
- /** Init the interface. */
- virtual void Init(void) = 0;
-
- /** Register JNI callbacks with the interface. */
- virtual void RegisterCallbacks(ActivityAttributionCallbacks* callbacks) = 0;
-
- /** Closes the interface. */
- virtual void Cleanup(void) = 0;
-
/** Notify the UID and package name of the app, and the address of associated
* active device */
virtual void NotifyActivityAttributionInfo(
diff --git a/system/main/Android.bp b/system/main/Android.bp
index d84e98b03e..123d749bc0 100644
--- a/system/main/Android.bp
+++ b/system/main/Android.bp
@@ -20,7 +20,7 @@ filegroup {
cc_library_static {
name: "libbte",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
srcs: [
":LibBluetoothShimSources",
":LibBluetoothSources",
@@ -52,6 +52,9 @@ cc_library_static {
"BluetoothGeneratedDumpsysDataSchema_h",
"BluetoothGeneratedPackets_h",
],
+ apex_available: [
+ "com.android.btservices",
+ ],
host_supported: true,
min_sdk_version: "Tiramisu",
}
@@ -63,7 +66,7 @@ cc_library {
"//packages/modules/Bluetooth:__subpackages__",
"//vendor:__subpackages__",
],
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
header_libs: ["libbluetooth_headers"],
export_header_lib_headers: ["libbluetooth_headers"],
include_dirs: [
@@ -143,7 +146,6 @@ cc_library_static {
"-DBUILDCFG",
],
shared_libs: [
- "libflatbuffers-cpp",
],
whole_static_libs: [
"libbluetooth_gd", // Gabeldorsche
@@ -203,7 +205,6 @@ cc_test {
static_libs: [
"libbluetooth-dumpsys",
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libchrome",
"libevent",
diff --git a/system/main/shim/activity_attribution.cc b/system/main/shim/activity_attribution.cc
index 4695309b74..12eed9de56 100644
--- a/system/main/shim/activity_attribution.cc
+++ b/system/main/shim/activity_attribution.cc
@@ -23,9 +23,7 @@
#include "main/shim/entry.h"
#include "types/raw_address.h"
-class ActivityAttributionInterfaceImpl
- : public ActivityAttributionInterface,
- public bluetooth::activity_attribution::ActivityAttributionCallback {
+class ActivityAttributionInterfaceImpl : public ActivityAttributionInterface {
public:
~ActivityAttributionInterfaceImpl() override = default;
@@ -35,17 +33,6 @@ class ActivityAttributionInterfaceImpl
return instance;
}
- void Init() override {
- bluetooth::shim::GetActivityAttribution()
- ->RegisterActivityAttributionCallback(this);
- }
-
- void RegisterCallbacks(ActivityAttributionCallbacks* callbacks) override {
- this->callbacks = callbacks;
- }
-
- void Cleanup(void) override{};
-
void NotifyActivityAttributionInfo(
int uid, const std::string& package_name,
const std::string& device_address) override {
@@ -53,44 +40,12 @@ class ActivityAttributionInterfaceImpl
uid, package_name, device_address);
}
- void OnWakeup(const Activity activity,
- const bluetooth::hci::Address& address) override {
- do_in_jni_thread(
- FROM_HERE, base::Bind(&ActivityAttributionCallbacks::OnWakeup,
- base::Unretained(callbacks),
- (ActivityAttributionCallbacks::Activity)activity,
- bluetooth::ToRawAddress(address)));
- }
-
- void OnActivityLogsReady(
- const std::vector<BtaaAggregationEntry> logs) override {
- std::vector<ActivityAttributionCallbacks::BtaaAggregationEntry>
- callback_logs;
- for (auto& it : logs) {
- ActivityAttributionCallbacks::BtaaAggregationEntry entry{
- bluetooth::ToRawAddress(it.address),
- (ActivityAttributionCallbacks::Activity)it.activity, it.wakeup_count,
- it.byte_count, it.wakelock_duration_ms};
- callback_logs.push_back(entry);
- }
- do_in_jni_thread(
- FROM_HERE,
- base::Bind(&ActivityAttributionCallbacks::OnActivityLogsReady,
- base::Unretained(callbacks), callback_logs));
- }
-
private:
// Private constructor to prevent construction.
ActivityAttributionInterfaceImpl() {}
-
- ActivityAttributionCallbacks* callbacks;
};
ActivityAttributionInterface*
bluetooth::shim::get_activity_attribution_instance() {
return ActivityAttributionInterfaceImpl::GetInstance();
}
-
-void bluetooth::shim::init_activity_attribution() {
- bluetooth::shim::get_activity_attribution_instance()->Init();
-}
diff --git a/system/main/shim/activity_attribution.h b/system/main/shim/activity_attribution.h
index 00d8099971..0f1253415e 100644
--- a/system/main/shim/activity_attribution.h
+++ b/system/main/shim/activity_attribution.h
@@ -28,7 +28,5 @@ namespace shim {
ActivityAttributionInterface* get_activity_attribution_instance();
-void init_activity_attribution();
-
} // namespace shim
} // namespace bluetooth
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 40dc243024..017b954168 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -148,9 +148,6 @@ void Stack::StartEverything() {
!common::init_flags::gd_core_is_enabled()) {
L2CA_UseLegacySecurityModule();
}
- if (common::init_flags::btaa_hci_is_enabled()) {
- bluetooth::shim::init_activity_attribution();
- }
}
void Stack::Start(ModuleList* modules) {
diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc
index fcaf32b4c5..83c6f4350f 100644
--- a/system/main/test/main_shim_test.cc
+++ b/system/main/test/main_shim_test.cc
@@ -345,8 +345,6 @@ class MockLeAclConnection
namespace bluetooth {
namespace shim {
-void init_activity_attribution() {}
-
namespace testing {
extern os::Handler* mock_handler_;
diff --git a/system/osi/Android.bp b/system/osi/Android.bp
index da4f9cf2e1..f5c6fc8e68 100644
--- a/system/osi/Android.bp
+++ b/system/osi/Android.bp
@@ -65,7 +65,7 @@ cc_library_static {
"include_internal",
],
defaults: [
- "fluoride_basic_defaults",
+ "fluoride_defaults",
"fluoride_osi_defaults",
],
// TODO(mcchou): Remove socket_utils sources after platform specific
@@ -160,7 +160,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libc++fs",
"libchrome",
"libevent",
diff --git a/system/osi/test/fuzzers/alarm/Android.bp b/system/osi/test/fuzzers/alarm/Android.bp
index 4bcef92af9..4e09043cce 100644
--- a/system/osi/test/fuzzers/alarm/Android.bp
+++ b/system/osi/test/fuzzers/alarm/Android.bp
@@ -23,7 +23,6 @@ cc_fuzz {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libgmock",
"libosi",
diff --git a/system/rust/src/connection/ffi.rs b/system/rust/src/connection/ffi.rs
index 651af1ff36..08506d546b 100644
--- a/system/rust/src/connection/ffi.rs
+++ b/system/rust/src/connection/ffi.rs
@@ -19,12 +19,14 @@ use super::{
ConnectionManagerClient, LeConnection,
};
+// SAFETY: `LeAclManagerShim` can be passed between threads.
unsafe impl Send for LeAclManagerShim {}
#[cxx::bridge]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::too_many_arguments)]
#[allow(missing_docs)]
+#[allow(unsafe_op_in_unsafe_fn)]
mod inner {
impl UniquePtr<LeAclManagerShim> {}
diff --git a/system/rust/src/core/ffi.rs b/system/rust/src/core/ffi.rs
index 83d10d0673..e69002def9 100644
--- a/system/rust/src/core/ffi.rs
+++ b/system/rust/src/core/ffi.rs
@@ -12,12 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// TODO(b/290018030): Remove this and add proper safety comments.
+#![allow(clippy::undocumented_unsafe_blocks)]
+
use crate::core::{start, stop};
use cxx::{type_id, ExternType};
pub use inner::*;
+// SAFETY: `GattServerCallbacks` can be passed between threads.
unsafe impl Send for GattServerCallbacks {}
+
+// SAFETY: `future_t` can be passed between threads.
unsafe impl Send for Future {}
unsafe impl ExternType for Uuid {
@@ -30,7 +36,7 @@ unsafe impl ExternType for AddressWithType {
type Kind = cxx::kind::Trivial;
}
-#[allow(dead_code, missing_docs)]
+#[allow(dead_code, missing_docs, unsafe_op_in_unsafe_fn)]
#[cxx::bridge]
mod inner {
#[derive(Debug)]
diff --git a/system/rust/src/gatt/arbiter.rs b/system/rust/src/gatt/arbiter.rs
index 52a0aa7207..66841bd977 100644
--- a/system/rust/src/gatt/arbiter.rs
+++ b/system/rust/src/gatt/arbiter.rs
@@ -52,6 +52,11 @@ pub fn with_arbiter<T>(f: impl FnOnce(&mut IsolationManager) -> T) -> T {
f(ARBITER.read().unwrap().as_ref().expect("Rust stack is not started").lock().as_mut().unwrap())
}
+/// Check if the Arbiter is initialized.
+pub fn has_arbiter() -> bool {
+ ARBITER.read().unwrap().is_some()
+}
+
/// Test to see if a buffer contains a valid ATT packet with an opcode we
/// are interested in intercepting (those intended for servers that are isolated)
fn try_parse_att_server_packet(
@@ -89,6 +94,13 @@ fn on_le_connect(tcb_idx: u8, advertiser: u8) {
}
fn on_le_disconnect(tcb_idx: u8) {
+ // Disconnection events may be received after a FactoryReset
+ // is initiated for Bluetooth and the rust arbiter is taken
+ // down.
+ if !has_arbiter() {
+ return;
+ }
+
let tcb_idx = TransportIndex(tcb_idx);
let was_isolated = with_arbiter(|arbiter| arbiter.is_connection_isolated(tcb_idx));
if was_isolated {
diff --git a/system/rust/src/gatt/ffi.rs b/system/rust/src/gatt/ffi.rs
index 4f06f0b60b..5a0f3b548e 100644
--- a/system/rust/src/gatt/ffi.rs
+++ b/system/rust/src/gatt/ffi.rs
@@ -39,6 +39,7 @@ use super::{
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::too_many_arguments)]
#[allow(missing_docs)]
+#[allow(unsafe_op_in_unsafe_fn)]
mod inner {
impl UniquePtr<GattServerCallbacks> {}
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 97619c7269..894ce03266 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -32,7 +32,7 @@ cc_test_library {
// Bluetooth stack static library for target
cc_library_static {
name: "libbt-stack",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
local_include_dirs: [
"avct",
"avdt",
@@ -125,10 +125,10 @@ cc_library_static {
"bnep/bnep_api.cc",
"bnep/bnep_main.cc",
"bnep/bnep_utils.cc",
- "btm/hfp_msbc_decoder.cc",
- "btm/hfp_msbc_encoder.cc",
"btm/hfp_lc3_decoder.cc",
"btm/hfp_lc3_encoder.cc",
+ "btm/hfp_msbc_decoder.cc",
+ "btm/hfp_msbc_encoder.cc",
"hid/hidd_api.cc",
"hid/hidd_conn.cc",
"hid/hidh_api.cc",
@@ -169,7 +169,7 @@ filegroup {
cc_library_static {
name: "libbt-stack-core",
- defaults: ["fluoride_basic_defaults"],
+ defaults: ["fluoride_defaults"],
local_include_dirs: [
"avct",
"avdt",
@@ -643,7 +643,7 @@ cc_test {
"libz",
],
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.bluetooth.a2dp@1.0",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.bluetooth@1.0",
@@ -660,7 +660,6 @@ cc_test {
"libbt-bta-core",
"libbt-common",
"libbt-hci",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbt-stack",
@@ -739,10 +738,8 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
@@ -818,7 +815,6 @@ cc_test {
static_libs: [
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
@@ -932,7 +928,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libgmock",
@@ -997,7 +992,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"liblog",
@@ -1104,7 +1098,6 @@ cc_test {
static_libs: [
"libFraunhoferAAC",
"libbt-common",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libchrome",
@@ -1156,7 +1149,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"liblog",
"libosi",
@@ -1212,7 +1204,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libgmock",
@@ -1319,7 +1310,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libchrome",
"libevent",
"libgmock",
@@ -1417,28 +1407,27 @@ cc_test {
"btm/btm_sco_hci.cc",
"btm/btm_sco_hfp_hal.cc",
"btm/btm_sec.cc",
- "btm/hfp_msbc_decoder.cc",
- "btm/hfp_msbc_encoder.cc",
"btm/hfp_lc3_decoder.cc",
"btm/hfp_lc3_encoder.cc",
+ "btm/hfp_msbc_decoder.cc",
+ "btm/hfp_msbc_encoder.cc",
"metrics/stack_metrics_logging.cc",
+ "test/btm/btm_scn_test.cc",
"test/btm/peer_packet_types_test.cc",
"test/btm/sco_hci_test.cc",
+ "test/btm/sco_pkt_status_test.cc",
"test/btm/stack_btm_regression_tests.cc",
"test/btm/stack_btm_test.cc",
- "test/btm/sco_pkt_status_test.cc",
"test/common/mock_eatt.cc",
"test/stack_include_test.cc",
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbtdevice",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblc3",
"liblog",
@@ -1486,7 +1475,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libbte",
"libchrome",
@@ -1542,7 +1530,6 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libbte",
"libchrome",
@@ -1614,11 +1601,9 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
@@ -1696,11 +1681,9 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
@@ -1778,11 +1761,9 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
@@ -1864,11 +1845,9 @@ cc_test {
],
static_libs: [
"libbt-common",
- "libbt-protos-lite",
"libbtdevice",
"libchrome",
"libevent",
- "libflatbuffers-cpp",
"libgmock",
"liblog",
"libosi",
diff --git a/system/stack/btm/btm_int_types.h b/system/stack/btm/btm_int_types.h
index 9ad5388ac6..ced48d04e3 100644
--- a/system/stack/btm/btm_int_types.h
+++ b/system/stack/btm/btm_int_types.h
@@ -18,6 +18,8 @@
#ifndef BTM_INT_TYPES_H
#define BTM_INT_TYPES_H
+#include <gtest/gtest_prod.h>
+
#include <cstdint>
#include <memory>
#include <string>
@@ -396,6 +398,14 @@ typedef struct tBTM_CB {
friend bool BTM_FreeSCN(uint8_t scn);
uint8_t btm_scn[BTM_MAX_SCN_];
uint8_t btm_available_index;
+
+ // give access to private method for test:
+ friend class BtmAllocateSCNTest;
+ FRIEND_TEST(BtmAllocateSCNTest, can_allocate_all_scns);
+ FRIEND_TEST(BtmAllocateSCNTest, only_last_scn_available);
+ FRIEND_TEST(BtmAllocateSCNTest, scn_available_after_available_index);
+ FRIEND_TEST(BtmAllocateSCNTest, scn_available_before_available_index);
+ FRIEND_TEST(BtmAllocateSCNTest, no_scn_available);
} tBTM_CB;
/* security action for L2CAP COC channels */
diff --git a/system/stack/btm/btm_scn.cc b/system/stack/btm/btm_scn.cc
index feec0f5fe7..8e87ccc730 100644
--- a/system/stack/btm/btm_scn.cc
+++ b/system/stack/btm/btm_scn.cc
@@ -34,8 +34,11 @@ extern tBTM_CB btm_cb;
uint8_t BTM_AllocateSCN(void) {
BTM_TRACE_DEBUG("BTM_AllocateSCN");
- // stack reserves scn 1 for HFP, HSP we still do the correct way
- for (uint8_t x = btm_cb.btm_available_index; x < PORT_MAX_RFC_PORTS; x++) {
+ // stack reserves scn 1 for HFP, HSP we still do the correct way.
+ // SCN can be allocated in the range of [1, PORT_MAX_RFC_PORTS). Since (x + 1)
+ // is returned, we iterate to less than PORT_MAX_RFC_PORTS - 1.
+ for (uint8_t x = btm_cb.btm_available_index; x < PORT_MAX_RFC_PORTS - 1;
+ x++) {
if (!btm_cb.btm_scn[x]) {
btm_cb.btm_scn[x] = true;
btm_cb.btm_available_index = (x + 1);
@@ -43,10 +46,10 @@ uint8_t BTM_AllocateSCN(void) {
}
}
- // In order to avoid OOB, btm_available_index must be less than or equal to
- // PORT_MAX_RFC_PORTS
+ // In order to avoid OOB, btm_available_index must be less than
+ // PORT_MAX_RFC_PORTS.
btm_cb.btm_available_index =
- std::min(btm_cb.btm_available_index, (uint8_t)PORT_MAX_RFC_PORTS);
+ std::min(btm_cb.btm_available_index, (uint8_t)(PORT_MAX_RFC_PORTS - 1));
// If there's no empty SCN from _last_index to BTM_MAX_SCN.
for (uint8_t y = 1; y < btm_cb.btm_available_index; y++) {
diff --git a/system/stack/btm/btm_scn.h b/system/stack/btm/btm_scn.h
index fe9dcb7ad6..733173297b 100644
--- a/system/stack/btm/btm_scn.h
+++ b/system/stack/btm/btm_scn.h
@@ -18,6 +18,6 @@
#include <cstdint>
+uint8_t BTM_AllocateSCN(void);
bool BTM_FreeSCN(uint8_t scn);
bool BTM_TryAllocateSCN(uint8_t scn);
-bool BTM_TryAllocateSCN(uint8_t scn);
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 6a64d88b61..809f9e6261 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -729,6 +729,11 @@ bool L2CA_ConnectCreditBasedRsp(const RawAddress& p_bd_addr, uint8_t id,
*/
tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(p_lcb, p_lcb->pending_lead_cid);
+ if (!p_ccb) {
+ L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, p_lcb->pending_lead_cid);
+ return false;
+ }
+
for (uint16_t cid : accepted_lcids) {
tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
if (temp_p_ccb == NULL) {
@@ -838,7 +843,8 @@ std::vector<uint16_t> L2CA_ConnectCreditBasedReq(uint16_t psm,
for (int i = 0; i < p_cfg->number_of_channels; i++) {
/* Allocate a channel control block */
- tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ tL2C_CCB* p_ccb =
+ l2cu_allocate_ccb(p_lcb, 0, psm == BT_PSM_EATT /* is_eatt */);
if (p_ccb == NULL) {
if (i == 0) {
L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm);
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index b18acb39fa..3a53d47abd 100644
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -657,7 +657,8 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED;
} else {
/* Allocate a ccb for this.*/
- temp_p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ temp_p_ccb = l2cu_allocate_ccb(
+ p_lcb, 0, con_info.psm == BT_PSM_EATT /* is_eatt */);
if (temp_p_ccb == NULL) {
LOG_ERROR("L2CAP - unable to allocate CCB");
p_lcb->pending_ecoc_connection_cids[i] = 0;
@@ -1004,7 +1005,8 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
}
/* Allocate a ccb for this.*/
- p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ p_ccb = l2cu_allocate_ccb(p_lcb, 0,
+ con_info.psm == BT_PSM_EATT /* is_eatt */);
if (p_ccb == NULL) {
L2CAP_TRACE_ERROR("L2CAP - unable to allocate CCB");
l2cu_reject_ble_connection(p_ccb, id, L2CAP_CONN_NO_RESOURCES);
diff --git a/system/stack/l2cap/l2c_csm.cc b/system/stack/l2cap/l2c_csm.cc
index 32c8ab9a55..44c4c99076 100644
--- a/system/stack/l2cap/l2c_csm.cc
+++ b/system/stack/l2cap/l2c_csm.cc
@@ -100,9 +100,13 @@ static void l2c_csm_indicate_connection_open(tL2C_CCB* p_ccb) {
if (p_ccb->connection_initiator == L2CAP_INITIATOR_LOCAL) {
(*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(p_ccb->local_cid, L2CAP_CONN_OK);
} else {
- (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb)(
- p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid, p_ccb->p_rcb->psm,
- p_ccb->remote_id);
+ if (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) {
+ (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb)(
+ p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid, p_ccb->p_rcb->psm,
+ p_ccb->remote_id);
+ } else {
+ LOG_WARN("pL2CA_ConnectInd_Cb is null");
+ }
}
if (p_ccb->chnl_state == CST_OPEN && !p_ccb->p_lcb->is_transport_ble()) {
(*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(
diff --git a/system/stack/l2cap/l2c_int.h b/system/stack/l2cap/l2c_int.h
index 14a0a20202..d44e16a9b4 100644
--- a/system/stack/l2cap/l2c_int.h
+++ b/system/stack/l2cap/l2c_int.h
@@ -719,7 +719,8 @@ void l2cu_enqueue_ccb(tL2C_CCB* p_ccb);
void l2cu_dequeue_ccb(tL2C_CCB* p_ccb);
void l2cu_change_pri_ccb(tL2C_CCB* p_ccb, tL2CAP_CHNL_PRIORITY priority);
-tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid);
+tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid,
+ bool is_eatt = false);
void l2cu_release_ccb(tL2C_CCB* p_ccb);
tL2C_CCB* l2cu_find_ccb_by_cid(tL2C_LCB* p_lcb, uint16_t local_cid);
tL2C_CCB* l2cu_find_ccb_by_remote_cid(tL2C_LCB* p_lcb, uint16_t remote_cid);
diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc
index d2d76bda9c..f64782ca5b 100644
--- a/system/stack/l2cap/l2c_utils.cc
+++ b/system/stack/l2cap/l2c_utils.cc
@@ -1351,7 +1351,7 @@ void l2cu_change_pri_ccb(tL2C_CCB* p_ccb, tL2CAP_CHNL_PRIORITY priority) {
* Returns pointer to CCB, or NULL if none
*
******************************************************************************/
-tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {
+tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid, bool is_eatt) {
LOG_DEBUG("is_dynamic = %d, cid 0x%04x", p_lcb != nullptr, cid);
if (!l2cb.p_free_ccb_first) {
LOG_ERROR("First free ccb is null for cid 0x%04x", cid);
@@ -1471,7 +1471,12 @@ tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {
if (p_lcb != NULL) {
// once a dynamic channel is opened, timeouts become active
- p_lcb->with_active_local_clients = true;
+ // the exception for this is EATT, since that is managed by GATT clients,
+ // not by the L2CAP layer (GATT will keep the idle timeout at infinity while
+ // clients are active)
+ if (!is_eatt) {
+ p_lcb->with_active_local_clients = true;
+ }
}
return p_ccb;
diff --git a/system/stack/test/btm/btm_scn_test.cc b/system/stack/test/btm/btm_scn_test.cc
new file mode 100644
index 0000000000..38294e312d
--- /dev/null
+++ b/system/stack/test/btm/btm_scn_test.cc
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "stack/btm/btm_scn.h"
+
+#include <gtest/gtest.h>
+
+#include "stack/btm/btm_int_types.h" // tBTM_CB
+#include "stack/include/rfcdefs.h" // PORT_MAX_RFC_PORTS
+
+extern tBTM_CB btm_cb;
+
+using testing::Test;
+
+class BtmAllocateSCNTest : public Test {
+ public:
+ protected:
+ void SetUp() override {
+ btm_cb.btm_available_index = 1;
+ for (int i = 0; i < PORT_MAX_RFC_PORTS; i++) {
+ btm_cb.btm_scn[i] = false;
+ }
+ }
+
+ void TearDown() override {}
+};
+
+TEST_F(BtmAllocateSCNTest, scn_available_after_available_index) {
+ btm_cb.btm_available_index = 5;
+ uint8_t occupied_idx[] = {1, 2, 3, 4, 5, 6, 7};
+ for (uint8_t idx : occupied_idx) {
+ btm_cb.btm_scn[idx] = true;
+ }
+
+ uint8_t scn = BTM_AllocateSCN();
+ ASSERT_EQ(scn, 9); // All indexes up to 7 are occupied; hence index 8 i.e.
+ // scn 9 should return
+}
+
+TEST_F(BtmAllocateSCNTest, scn_available_before_available_index) {
+ btm_cb.btm_available_index = 28;
+ uint8_t occupied_idx[] = {26, 27, 28, 29};
+ for (uint8_t idx : occupied_idx) {
+ btm_cb.btm_scn[idx] = true;
+ }
+
+ uint8_t scn = BTM_AllocateSCN();
+ ASSERT_EQ(scn, 2); // All SCN from available to 30 are occupied; hence cycle
+ // to beginning.
+}
+
+TEST_F(BtmAllocateSCNTest, can_allocate_all_scns) {
+ for (uint8_t scn = 2; scn < PORT_MAX_RFC_PORTS; scn++) {
+ EXPECT_EQ(BTM_AllocateSCN(), scn);
+ }
+}
+
+TEST_F(BtmAllocateSCNTest, only_last_scn_available) {
+ // Fill all relevants SCN except the last
+ for (uint8_t scn = 2; scn < PORT_MAX_RFC_PORTS - 1; scn++) {
+ btm_cb.btm_scn[scn - 1] = true;
+ }
+
+ EXPECT_EQ(BTM_AllocateSCN(), PORT_MAX_RFC_PORTS - 1);
+}
+
+TEST_F(BtmAllocateSCNTest, no_scn_available) {
+ btm_cb.btm_available_index = 2;
+ for (int i = 1; i < PORT_MAX_RFC_PORTS - 1;
+ i++) { // Fill all relevants SCN indexes (1 to 29)
+ btm_cb.btm_scn[i] = true;
+ }
+
+ uint8_t scn = BTM_AllocateSCN();
+ EXPECT_EQ(scn, 0) << "scn = " << scn << "and not 0";
+} \ No newline at end of file
diff --git a/system/stack/test/fuzzers/Android.bp b/system/stack/test/fuzzers/Android.bp
index 822bc50312..d187b961ed 100644
--- a/system/stack/test/fuzzers/Android.bp
+++ b/system/stack/test/fuzzers/Android.bp
@@ -29,7 +29,6 @@ cc_defaults {
"libbt-bta-core",
"libbt-common",
"libbt-hci",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbt-stack",
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
index aad71a9995..e1f667cc34 100644
--- a/system/test/headless/Android.bp
+++ b/system/test/headless/Android.bp
@@ -92,7 +92,6 @@ cc_binary {
"libbt-bta-core",
"libbt-common",
"libbt-hci",
- "libbt-protos-lite",
"libbt-sbc-decoder",
"libbt-sbc-encoder",
"libbt-stack",
diff --git a/system/test/mock/mock_bta_dm_act.cc b/system/test/mock/mock_bta_dm_act.cc
index 0cf293fa1d..42365fbd72 100644
--- a/system/test/mock/mock_bta_dm_act.cc
+++ b/system/test/mock/mock_bta_dm_act.cc
@@ -80,7 +80,6 @@ struct bta_dm_free_sdp_db bta_dm_free_sdp_db;
struct bta_dm_inq_cmpl bta_dm_inq_cmpl;
struct bta_dm_is_search_request_queued bta_dm_is_search_request_queued;
struct bta_dm_pin_reply bta_dm_pin_reply;
-struct bta_dm_proc_open_evt bta_dm_proc_open_evt;
struct bta_dm_process_remove_device bta_dm_process_remove_device;
struct bta_dm_queue_disc bta_dm_queue_disc;
struct bta_dm_queue_search bta_dm_queue_search;
@@ -295,10 +294,6 @@ void bta_dm_pin_reply(std::unique_ptr<tBTA_DM_API_PIN_REPLY> msg) {
inc_func_call_count(__func__);
test::mock::bta_dm_act::bta_dm_pin_reply(std::move(msg));
}
-void bta_dm_proc_open_evt(tBTA_GATTC_OPEN* p_data) {
- inc_func_call_count(__func__);
- test::mock::bta_dm_act::bta_dm_proc_open_evt(p_data);
-}
void bta_dm_process_remove_device(const RawAddress& bd_addr) {
inc_func_call_count(__func__);
test::mock::bta_dm_act::bta_dm_process_remove_device(bd_addr);
diff --git a/system/test/mock/mock_bta_dm_act.h b/system/test/mock/mock_bta_dm_act.h
index 661a7537d5..e58a58d7c2 100644
--- a/system/test/mock/mock_bta_dm_act.h
+++ b/system/test/mock/mock_bta_dm_act.h
@@ -585,16 +585,6 @@ struct bta_dm_pin_reply {
};
extern struct bta_dm_pin_reply bta_dm_pin_reply;
-// Name: bta_dm_proc_open_evt
-// Params: tBTA_GATTC_OPEN* p_data
-// Return: void
-struct bta_dm_proc_open_evt {
- std::function<void(tBTA_GATTC_OPEN* p_data)> body{
- [](tBTA_GATTC_OPEN* p_data) {}};
- void operator()(tBTA_GATTC_OPEN* p_data) { body(p_data); };
-};
-extern struct bta_dm_proc_open_evt bta_dm_proc_open_evt;
-
// Name: bta_dm_process_remove_device
// Params: const RawAddress& bd_addr
// Return: void
diff --git a/system/test/mock/mock_btif_co_bta_av_co.cc b/system/test/mock/mock_btif_co_bta_av_co.cc
index 15612db986..0241237208 100644
--- a/system/test/mock/mock_btif_co_bta_av_co.cc
+++ b/system/test/mock/mock_btif_co_bta_av_co.cc
@@ -49,7 +49,6 @@ struct bta_av_co_audio_source_data_path bta_av_co_audio_source_data_path;
struct bta_av_co_audio_start bta_av_co_audio_start;
struct bta_av_co_audio_stop bta_av_co_audio_stop;
struct bta_av_co_audio_update_mtu bta_av_co_audio_update_mtu;
-struct bta_av_co_get_decoder_interface bta_av_co_get_decoder_interface;
struct bta_av_co_get_encoder_effective_frame_size
bta_av_co_get_encoder_effective_frame_size;
struct bta_av_co_get_encoder_interface bta_av_co_get_encoder_interface;
@@ -76,8 +75,6 @@ namespace btif_co_bta_av_co {
tA2DP_STATUS bta_av_co_audio_getconfig::return_value = 0;
bool bta_av_co_audio_init::return_value = false;
BT_HDR* bta_av_co_audio_source_data_path::return_value = nullptr;
-const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface::return_value =
- nullptr;
int bta_av_co_get_encoder_effective_frame_size::return_value = 0;
const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface::return_value =
nullptr;
@@ -180,10 +177,6 @@ void bta_av_co_audio_update_mtu(tBTA_AV_HNDL bta_av_handle,
test::mock::btif_co_bta_av_co::bta_av_co_audio_update_mtu(bta_av_handle,
peer_address, mtu);
}
-const tA2DP_DECODER_INTERFACE* bta_av_co_get_decoder_interface(void) {
- inc_func_call_count(__func__);
- return test::mock::btif_co_bta_av_co::bta_av_co_get_decoder_interface();
-}
int bta_av_co_get_encoder_effective_frame_size() {
inc_func_call_count(__func__);
return test::mock::btif_co_bta_av_co::
diff --git a/system/test/mock/mock_btif_co_bta_av_co.h b/system/test/mock/mock_btif_co_bta_av_co.h
index 9f977ce53f..c817f8359c 100644
--- a/system/test/mock/mock_btif_co_bta_av_co.h
+++ b/system/test/mock/mock_btif_co_bta_av_co.h
@@ -260,17 +260,6 @@ struct bta_av_co_audio_update_mtu {
};
extern struct bta_av_co_audio_update_mtu bta_av_co_audio_update_mtu;
-// Name: bta_av_co_get_decoder_interface
-// Params: void
-// Return: const tA2DP_DECODER_INTERFACE*
-struct bta_av_co_get_decoder_interface {
- static const tA2DP_DECODER_INTERFACE* return_value;
- std::function<const tA2DP_DECODER_INTERFACE*(void)> body{
- [](void) { return return_value; }};
- const tA2DP_DECODER_INTERFACE* operator()(void) { return body(); };
-};
-extern struct bta_av_co_get_decoder_interface bta_av_co_get_decoder_interface;
-
// Name: bta_av_co_get_encoder_effective_frame_size
// Params:
// Return: int
diff --git a/system/test/mock/mock_main_shim_activity_attribution.cc b/system/test/mock/mock_main_shim_activity_attribution.cc
index 3b67f5aac9..83cfaa3872 100644
--- a/system/test/mock/mock_main_shim_activity_attribution.cc
+++ b/system/test/mock/mock_main_shim_activity_attribution.cc
@@ -36,6 +36,3 @@ bluetooth::shim::get_activity_attribution_instance() {
inc_func_call_count(__func__);
return nullptr;
}
-void bluetooth::shim::init_activity_attribution() {
- inc_func_call_count(__func__);
-}
diff --git a/system/test/mock/mock_stack_l2cap_utils.cc b/system/test/mock/mock_stack_l2cap_utils.cc
index f1166f2947..035591b80f 100644
--- a/system/test/mock/mock_stack_l2cap_utils.cc
+++ b/system/test/mock/mock_stack_l2cap_utils.cc
@@ -70,7 +70,7 @@ bool l2cu_start_post_bond_timer(uint16_t handle) {
inc_func_call_count(__func__);
return false;
}
-tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {
+tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid, bool is_eatt) {
inc_func_call_count(__func__);
return nullptr;
}
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index 29fe4ecc35..0158173745 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -117,7 +117,7 @@ cc_test {
"gatt/gatt_unittest.cc",
],
static_libs: [
- "android.hardware.audio.common-V1-ndk",
+ "android.hardware.audio.common-V2-ndk",
"android.hardware.bluetooth.audio-V3-ndk",
"android.hardware.bluetooth@1.0",
"android.hardware.bluetooth@1.1",
diff --git a/system/vendor_libs/linux/interface/Android.bp b/system/vendor_libs/linux/interface/Android.bp
index 26e11fbcb8..a47a57dbd8 100644
--- a/system/vendor_libs/linux/interface/Android.bp
+++ b/system/vendor_libs/linux/interface/Android.bp
@@ -46,7 +46,6 @@ cc_binary {
static_libs: [
"async_fd_watcher",
],
- c_std: "c99",
init_rc: ["android.hardware.bluetooth@1.1-service.btlinux.rc"],
}
diff --git a/tools/rootcanal/Android.bp b/tools/rootcanal/Android.bp
index a451f6a54c..67dd3afa78 100644
--- a/tools/rootcanal/Android.bp
+++ b/tools/rootcanal/Android.bp
@@ -47,13 +47,11 @@ cc_defaults {
"-fvisibility=hidden",
],
include_dirs: [
- "packages/modules/Bluetooth/system/gd",
- ],
- local_include_dirs: [
- "include",
+ "packages/modules/Bluetooth/tools/rootcanal/include",
],
header_libs: [
"libbase_headers",
+ "pdl_cxx_packet_runtime",
],
generated_headers: [
"rootcanal_bredr_bb_packets_cxx_gen",
@@ -67,7 +65,6 @@ filegroup {
srcs: [
"lib/crypto/crypto.cc",
"lib/hci/address.cc",
- "lib/hci/class_of_device.cc",
"lib/hci/pcap_filter.cc",
"lib/log.cc",
],
@@ -82,7 +79,6 @@ cc_library_static {
host_supported: true,
proprietary: true,
srcs: [
- ":BluetoothPacketSources",
":lib_sources",
"model/controller/acl_connection.cc",
"model/controller/acl_connection_handler.cc",
@@ -114,6 +110,9 @@ cc_library_static {
"net/posix/posix_async_socket_connector.cc",
"net/posix/posix_async_socket_server.cc",
],
+ export_header_lib_headers: [
+ "pdl_cxx_packet_runtime",
+ ],
export_include_dirs: [
".",
"include",
@@ -136,7 +135,6 @@ cc_library_static {
static_libs: [
"libscriptedbeaconpayload-protos-lite",
],
- include_dirs: ["packages/modules/Bluetooth/system/gd"],
}
// This library implements a foreigh function interface over DualModeController
@@ -150,7 +148,6 @@ cc_library_host_shared {
address: false,
},
srcs: [
- ":BluetoothPacketSources",
":lib_sources",
"model/controller/acl_connection.cc",
"model/controller/acl_connection_handler.cc",
@@ -181,7 +178,6 @@ cc_library_host_shared {
cflags: [
"-fexceptions",
],
- include_dirs: ["packages/modules/Bluetooth/system/gd"],
}
// Generate the python parser+serializer backend for
@@ -225,7 +221,7 @@ genrule {
" $(location :pdl_python_generator)" +
" --output $(out) --custom-type-location py.bluetooth",
srcs: [
- "packets/hci/hci_packets.pdl",
+ "packets/hci_packets.pdl",
],
out: [
"hci_packets.py",
@@ -295,7 +291,6 @@ cc_test_host {
static_libs: [
"libbt-rootcanal",
],
- include_dirs: ["packages/modules/Bluetooth/system/gd"],
}
// Implement the Bluetooth official LL test suite for root-canal.
@@ -315,8 +310,8 @@ python_test_host {
"test/LL/CON_/PER/*.py",
"test/LL/DDI/ADV/*.py",
"test/LL/DDI/SCN/*.py",
- "test/LMP/LIH/*.py",
"test/LMP/*.py",
+ "test/LMP/LIH/*.py",
"test/main.py",
],
data: [
@@ -369,7 +364,6 @@ cc_test_host {
enabled: false,
},
},
- include_dirs: ["packages/modules/Bluetooth/system/gd"],
}
// Linux RootCanal Executable
@@ -411,29 +405,41 @@ cc_binary_host {
enabled: false,
},
},
- include_dirs: ["packages/modules/Bluetooth/system/gd"],
}
genrule {
name: "rootcanal_hci_packets_cxx_gen",
tools: [
- "bluetooth_packetgen",
- ],
- cmd: "$(location bluetooth_packetgen) --include=packages/modules/Bluetooth/tools/rootcanal/packets --out=$(genDir) $(in)",
+ ":pdl_cxx_generator",
+ ":pdlc",
+ ],
+ cmd: "set -o pipefail;" +
+ " $(location :pdlc) $(in) |" +
+ " $(location :pdl_cxx_generator)" +
+ " --namespace bluetooth::hci" +
+ " --include-header hci/address.h" +
+ " --output $(out)",
srcs: [
- "packets/hci/hci_packets.pdl",
+ "packets/hci_packets.pdl",
],
out: [
- "hci/hci_packets.h",
+ "packets/hci_packets.h",
],
}
genrule {
name: "rootcanal_link_layer_packets_cxx_gen",
tools: [
- "bluetooth_packetgen",
- ],
- cmd: "$(location bluetooth_packetgen) --root_namespace=model --include=packages/modules/Bluetooth/tools/rootcanal --out=$(genDir) $(in)",
+ ":pdl_cxx_generator",
+ ":pdlc",
+ ],
+ cmd: "set -o pipefail;" +
+ " $(location :pdlc) $(in) |" +
+ " $(location :pdl_cxx_generator)" +
+ " --namespace model::packets" +
+ " --include-header hci/address.h" +
+ " --using-namespace bluetooth::hci" +
+ " --output $(out)",
srcs: [
"packets/link_layer_packets.pdl",
],
@@ -445,9 +451,16 @@ genrule {
genrule {
name: "rootcanal_bredr_bb_packets_cxx_gen",
tools: [
- "bluetooth_packetgen",
- ],
- cmd: "$(location bluetooth_packetgen) --root_namespace=bredr_bb --include=packages/modules/Bluetooth/tools/rootcanal/packets --out=$(genDir) $(in)",
+ ":pdl_cxx_generator",
+ ":pdlc",
+ ],
+ cmd: "set -o pipefail;" +
+ " $(location :pdlc) $(in) |" +
+ " $(location :pdl_cxx_generator)" +
+ " --namespace bredr_bb" +
+ " --include-header hci/address.h" +
+ " --using-namespace bluetooth::hci" +
+ " --output $(out)",
srcs: [
"packets/bredr_bb.pdl",
],
@@ -459,6 +472,6 @@ genrule {
genrule {
name: "rootcanal_hci_packets_rust_gen",
defaults: ["pdl_rust_generator_defaults"],
- srcs: ["packets/hci/hci_packets.pdl"],
+ srcs: ["packets/hci_packets.pdl"],
out: ["hci_packets.rs"],
}
diff --git a/tools/rootcanal/CMakeLists.txt b/tools/rootcanal/CMakeLists.txt
index 6324bc19d6..53aec54b73 100644
--- a/tools/rootcanal/CMakeLists.txt
+++ b/tools/rootcanal/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(BT_ROOT ${AOSP_ROOT}/packages/modules/Bluetooth/system)
+set(BT_ROOT ${AOSP_ROOT}/packages/modules/Bluetooth)
set(ROOTCANAL_ROOT ${AOSP_ROOT}/packages/modules/Bluetooth/tools/rootcanal)
set(PDL_ROOT ${AOSP_ROOT}/external/rust/crates/pdl-compiler)
@@ -28,7 +28,8 @@ android_license(
function(pdl_gen)
# Parse arguments.
set(options)
- set(oneValueArgs NAME INPUT OUTPUT LANG)
+ set(oneValueArgs NAME INPUT OUTPUT LANG NAMESPACE)
+ set(multiValueArgs USING INCLUDE)
cmake_parse_arguments(pdl "${options}" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN})
@@ -66,102 +67,62 @@ function(pdl_gen)
VERBATIM
DEPENDS pdlc ${pdl_INPUT_ABSOLUTE})
endif()
-endfunction()
-
-# ~~~
-# ! foobar : Compile .pdl files to C++/Rust!
-#
-# This function allows you to compile a set of .pdl files
-# to rust or C++. It will generate a set of headers and
-# sources.
-#
-# :GENERATED: The set of sources that are generated.
-# Add these sources to the library you are building
-# :SRC: The set of .pdl files to be compiled.
-# :INCLUDES: Include directory used when generating sources.
-# :LANG: Optional parameter indicating which language to use,
-# either rust or c++, defaults to c++
-# :NAMESPACE: Root name space to use for the generated c++ classes.
-# :OUTPUT_DIR: Optional paramater of the directory where the generated
-# sources will be placed, defaults to CMAKE_CURRENT_BINARY_DIR/gens
-# :SOURCE_DIR: Root directory where sources can be found,
-# defaults to CMAKE_CURRENT_SOURCE_DIR
-# ~~~
-function(android_bluetooth_packet_gen)
- # Parse arguments
- set(options)
- set(oneValueArgs OUTPUT_DIR GENERATED SOURCE_DIR INCLUDES NAMESPACE LANG)
- set(multiValueArgs SRC)
- cmake_parse_arguments(packet_gen "${options}" "${oneValueArgs}"
- "${multiValueArgs}" ${ARGN})
-
- if(NOT packet_gen_OUTPUT_DIR)
- set(packet_gen_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/packet_gen)
- endif()
-
- if(packet_gen_NAMESPACE)
- set(packet_gen_NAMESPACE "--root_namespace=${packet_gen_NAMESPACE}")
- endif()
- if(NOT packet_gen_SOURCE_DIR)
- set(packet_gen_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
- endif()
-
- if(NOT packet_gen_SRC)
- message(
- FATAL_ERROR
- "Error: android_packet_gen_compile() called without any .yy files")
- return()
- endif()
-
- set(SUFFIX_GEN "h")
+ if(pdl_LANG STREQUAL "c++")
+ if(NOT pdl_NAMESPACE)
+ message(FATAL_ERROR "Error: namespace not specified")
+ endif()
- if(packet_gen_LANG STREQUAL "rust")
- set(SUFFIX_GEN "rs")
- set(packet_gen_LANG "--rust")
- endif()
-
- # Configure packet_gen
- android_compile_for_host(
- bluetooth_packetgen ${ANDROID_QEMU2_TOP_DIR}/android/bluetooth/packet_gen
- bluetooth_packetgen_EXECUTABLE)
+ foreach(namespace ${pdl_USING})
+ list(APPEND pdl_FLAGS --using-namespace)
+ list(APPEND pdl_FLAGS ${namespace})
+ endforeach()
+ foreach(header ${pdl_INCLUDE})
+ list(APPEND pdl_FLAGS --include-header)
+ list(APPEND pdl_FLAGS ${header})
+ endforeach()
- set(BLUE_GEN "")
- file(MAKE_DIRECTORY ${packet_gen_OUTPUT_DIR})
+ add_custom_command(
+ OUTPUT "${pdl_OUTPUT_ABSOLUTE}.json"
+ COMMAND
+ ${pdlc_EXECUTABLE}
+ --output-format json
+ "${pdl_INPUT_ABSOLUTE}"
+ > "${pdl_OUTPUT_ABSOLUTE}.json"
+ COMMENT "Analyzing ${pdl_INPUT}"
+ VERBATIM
+ DEPENDS pdlc ${pdl_INPUT_ABSOLUTE})
- foreach(FIL ${packet_gen_SRC})
- get_filename_component(
- ABS_FIL ${packet_gen_SOURCE_DIR}/${packet_gen_INCLUDES}/${FIL} ABSOLUTE)
- get_filename_component(FIL_WE ${FIL} NAME_WE)
- get_filename_component(FIL_DIR ${FIL} DIRECTORY)
- set(FIL_GEN "${packet_gen_OUTPUT_DIR}/${FIL_DIR}/${FIL_WE}.${SUFFIX_GEN}")
add_custom_command(
- OUTPUT "${FIL_GEN}"
- COMMAND
- ${bluetooth_packetgen_EXECUTABLE} ${packet_gen_NAMESPACE}
- "--include=${packet_gen_INCLUDES}" "--out=${packet_gen_OUTPUT_DIR}"
- ${packet_gen_INCLUDES}/${FIL} ${packet_gen_LANG}
- COMMENT "Creating bluetooth packet headers from ${ABS_FIL}"
- WORKING_DIRECTORY ${packet_gen_SOURCE_DIR}
- VERBATIM
- DEPENDS ${bluetooth_packetgen_EXECUTABLE} ${ABS_FIL})
- list(APPEND BLUE_GEN ${FIL_GEN})
- set_source_files_properties(${FIL_GEN} PROPERTIES GENERATED TRUE)
- endforeach()
-
- # Make the library available
- if(packet_gen_GENERATED)
- set(${packet_gen_GENERATED} "${BLUE_GEN}" PARENT_SCOPE)
+ OUTPUT "${pdl_OUTPUT_ABSOLUTE}"
+ COMMAND
+ ${PDL_ROOT}/scripts/generate_cxx_backend.py
+ --input "${pdl_OUTPUT_ABSOLUTE}.json"
+ --output "${pdl_OUTPUT_ABSOLUTE}"
+ --namespace ${pdl_NAMESPACE}
+ ${pdl_FLAGS}
+ COMMENT "Generating c++ header from ${pdl_INPUT}"
+ VERBATIM
+ DEPENDS pdlc ${pdl_OUTPUT_ABSOLUTE}.json)
endif()
endfunction()
-android_bluetooth_packet_gen(
- GENERATED BluetoothGeneratedPackets_h INCLUDES tools/rootcanal/packets
- SRC hci/hci_packets.pdl SOURCE_DIR ${BT_ROOT}/..)
-
-android_bluetooth_packet_gen(
- GENERATED RootCanalGeneratedPackets_h INCLUDES tools/rootcanal NAMESPACE model
- SRC packets/link_layer_packets.pdl SOURCE_DIR ${BT_ROOT}/..)
+pdl_gen(
+ NAME BluetoothGeneratedPackets_h
+ INPUT ${ROOTCANAL_ROOT}/packets/hci_packets.pdl
+ OUTPUT packets/hci_packets.h
+ LANG c++
+ NAMESPACE "bluetooth::hci"
+ INCLUDE "hci/address.h")
+
+pdl_gen(
+ NAME RootCanalGeneratedPackets_h
+ INPUT ${ROOTCANAL_ROOT}/packets/link_layer_packets.pdl
+ OUTPUT packets/link_layer_packets.h
+ LANG c++
+ NAMESPACE model::packets
+ INCLUDE "hci/address.h"
+ USING "bluetooth::hci")
android_add_library(
TARGET libscriptedbeaconpayload-protos-lite LICENSE Apache-2.0
@@ -205,7 +166,7 @@ target_include_directories(libbt-rootcanal.headers INTERFACE ${ROOTCANAL_ROOT})
target_link_libraries(libbt-rootcanal.headers
INTERFACE android-emu-base-headers)
android_license(TARGET "libbt-rootcanal.headers" LIBNAME None SPDX Apache-2.0
- LICENSE Apache-2.0 LOCAL "${BT_ROOT}/../NOTICE")
+ LICENSE Apache-2.0 LOCAL "${BT_ROOT}/NOTICE")
android_add_library(
TARGET librootcanal_log
@@ -220,30 +181,6 @@ target_include_directories(
${AOSP_ROOT}/external/fmtlib/include)
android_add_library(
- TARGET BluetoothPacketSources
- LICENSE Apache-2.0
- SOURCE_DIR ${BT_ROOT}/gd/packet
- SRC bit_inserter.cc
- byte_inserter.cc
- byte_observer.cc
- fragmenting_inserter.cc
- iterator.cc
- packet_view.cc
- raw_builder.cc
- view.cc
- DEPS android-emu-base android-emu-base-headers librootcanal_log)
-
-target_include_directories(
- BluetoothPacketSources PUBLIC
- ${ROOTCANAL_ROOT}
- ${ROOTCANAL_ROOT}/include
- ${BT_ROOT}/gd
- ${BT_ROOT})
-
-target_compile_options(BluetoothPacketSources
- PUBLIC -Wno-inconsistent-missing-override)
-
-android_add_library(
TARGET libbt-rootcanal
LICENSE Apache-2.0
SOURCE_DIR ${ROOTCANAL_ROOT}
@@ -251,7 +188,6 @@ android_add_library(
${RootCanalGeneratedPackets_h}
lib/crypto/crypto.cc
lib/hci/address.cc
- lib/hci/class_of_device.cc
lib/hci/pcap_filter.cc
lib/log.cc
model/controller/acl_connection.cc
@@ -287,7 +223,6 @@ android_add_library(
DEPS android-emu-base
android-emu-base-headers
android-emu-base-logging
- BluetoothPacketSources
crypto
librootcanal_config
libscriptedbeaconpayload-protos-lite)
@@ -300,9 +235,8 @@ target_include_directories(
libbt-rootcanal
PUBLIC ${ROOTCANAL_ROOT}/include
${ROOTCANAL_ROOT}
- ${BT_ROOT}/gd
- ${BT_ROOT}
- ${CMAKE_CURRENT_BINARY_DIR}/packet_gen
+ ${PDL_ROOT}/scripts
+ ${CMAKE_CURRENT_BINARY_DIR}/pdl_gen
${CMAKE_CURRENT_BINARY_DIR}/config)
target_compile_options(libbt-rootcanal
diff --git a/system/test/rootcanal/Android.bp b/tools/rootcanal/hal/Android.bp
index 7145768ec8..f211b70b9c 100644
--- a/system/test/rootcanal/Android.bp
+++ b/tools/rootcanal/hal/Android.bp
@@ -24,9 +24,7 @@ package {
cc_binary {
name: "android.hardware.bluetooth@1.1-service.sim",
- defaults: [
- "gd_defaults",
- ],
+ defaults: ["rootcanal_defaults"],
proprietary: true,
relative_install_path: "hw",
srcs: [
@@ -36,7 +34,6 @@ cc_binary {
visibility: [
"//platform_testing/libraries/sts-common-util/host-side/rootcanal",
],
- header_libs: ["libbluetooth_headers"],
shared_libs: [
"android.hardware.bluetooth@1.0",
"android.hardware.bluetooth@1.1",
@@ -44,7 +41,6 @@ cc_binary {
"libcrypto",
"libcutils",
"libhidlbase",
- "libjsoncpp",
"liblog",
"libprotobuf-cpp-full",
"libutils",
@@ -52,9 +48,6 @@ cc_binary {
cflags: [
"-fvisibility=hidden",
],
- generated_headers: [
- "libbt_init_flags_bridge_header",
- ],
whole_static_libs: [
"libbt-rootcanal",
],
@@ -63,20 +56,12 @@ cc_binary {
"android.hardware.bluetooth-hci",
"libscriptedbeaconpayload-protos-lite",
],
- include_dirs: [
- "packages/modules/Bluetooth/system",
- "packages/modules/Bluetooth/system/gd",
- "packages/modules/Bluetooth/system/internal_include",
- "packages/modules/Bluetooth/system/stack/include",
- ],
init_rc: ["android.hardware.bluetooth@1.1-service.sim.rc"],
}
cc_library_shared {
name: "android.hardware.bluetooth@1.1-impl-sim",
- defaults: [
- "gd_defaults",
- ],
+ defaults: ["rootcanal_defaults"],
proprietary: true,
relative_install_path: "hw",
srcs: [
@@ -85,7 +70,6 @@ cc_library_shared {
visibility: [
"//platform_testing/libraries/sts-common-util/host-side/rootcanal",
],
- header_libs: ["libbluetooth_headers"],
shared_libs: [
"android.hardware.bluetooth@1.0",
"android.hardware.bluetooth@1.1",
@@ -93,14 +77,10 @@ cc_library_shared {
"libcrypto",
"libcutils",
"libhidlbase",
- "libjsoncpp",
"liblog",
"libprotobuf-cpp-full",
"libutils",
],
- generated_headers: [
- "libbt_init_flags_bridge_header",
- ],
whole_static_libs: [
"libbt-rootcanal",
],
@@ -109,10 +89,4 @@ cc_library_shared {
"android.hardware.bluetooth-hci",
"libscriptedbeaconpayload-protos-lite",
],
- include_dirs: [
- "packages/modules/Bluetooth/system",
- "packages/modules/Bluetooth/system/gd",
- "packages/modules/Bluetooth/system/internal_include",
- "packages/modules/Bluetooth/system/stack/include",
- ],
}
diff --git a/system/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc b/tools/rootcanal/hal/android.hardware.bluetooth@1.1-service.sim.rc
index 262684136e..262684136e 100644
--- a/system/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc
+++ b/tools/rootcanal/hal/android.hardware.bluetooth@1.1-service.sim.rc
diff --git a/system/test/rootcanal/bluetooth_hci.cc b/tools/rootcanal/hal/bluetooth_hci.cc
index bab7be1139..d63b870843 100644
--- a/system/test/rootcanal/bluetooth_hci.cc
+++ b/tools/rootcanal/hal/bluetooth_hci.cc
@@ -69,7 +69,7 @@ class BluetoothDeathRecipient : public hidl_death_recipient {
void setHasDied(bool has_died) { has_died_ = has_died; }
private:
- bool has_died_;
+ bool has_died_{false};
};
BluetoothHci::BluetoothHci()
diff --git a/system/test/rootcanal/bluetooth_hci.h b/tools/rootcanal/hal/bluetooth_hci.h
index 1bceda8812..1bceda8812 100644
--- a/system/test/rootcanal/bluetooth_hci.h
+++ b/tools/rootcanal/hal/bluetooth_hci.h
diff --git a/system/test/rootcanal/service.cc b/tools/rootcanal/hal/service.cc
index 7856dcfa5b..7856dcfa5b 100644
--- a/system/test/rootcanal/service.cc
+++ b/tools/rootcanal/hal/service.cc
diff --git a/tools/rootcanal/include/hci/address.h b/tools/rootcanal/include/hci/address.h
index ff273cfa53..a46653ada0 100644
--- a/tools/rootcanal/include/hci/address.h
+++ b/tools/rootcanal/include/hci/address.h
@@ -16,19 +16,20 @@
#pragma once
+#include <packet_runtime.h>
+
#include <array>
#include <cstring>
#include <initializer_list>
#include <optional>
#include <ostream>
#include <string>
-
-#include "packet/custom_field_fixed_size_interface.h"
+#include <vector>
namespace bluetooth {
namespace hci {
-class Address final : public packet::CustomFieldFixedSizeInterface<Address> {
+class Address final : public pdl::packet::Builder {
public:
static constexpr size_t kLength = 6;
@@ -43,10 +44,6 @@ class Address final : public packet::CustomFieldFixedSizeInterface<Address> {
Address(const uint8_t (&address)[kLength]);
Address(std::initializer_list<uint8_t> l);
- // CustomFieldFixedSizeInterface methods
- inline uint8_t* data() override { return address.data(); }
- inline const uint8_t* data() const override { return address.data(); }
-
// storage::Serializable methods
std::string ToString() const;
static std::optional<Address> FromString(const std::string& from);
@@ -59,6 +56,17 @@ class Address final : public packet::CustomFieldFixedSizeInterface<Address> {
bool operator!=(const Address& rhs) const { return !(*this == rhs); }
bool IsEmpty() const { return *this == kEmpty; }
+ uint8_t* data() { return address.data(); }
+ uint8_t const* data() const { return address.data(); }
+
+ // Packet parser interface.
+ static bool Parse(pdl::packet::slice& input, Address* output);
+
+ // Packet builder interface.
+ size_t GetSize() const override { return kLength; }
+ void Serialize(std::vector<uint8_t>& output) const override {
+ output.insert(output.end(), address.begin(), address.end());
+ }
// Converts |string| to Address and places it in |to|. If |from| does
// not represent a Bluetooth address, |to| is not modified and this function
diff --git a/tools/rootcanal/include/hci/address_with_type.h b/tools/rootcanal/include/hci/address_with_type.h
index 1de5203ae2..51ddfd9f1a 100644
--- a/tools/rootcanal/include/hci/address_with_type.h
+++ b/tools/rootcanal/include/hci/address_with_type.h
@@ -22,7 +22,7 @@
#include "crypto/crypto.h"
#include "hci/address.h"
-#include "hci/hci_packets.h"
+#include "packets/hci_packets.h"
namespace bluetooth {
namespace hci {
diff --git a/tools/rootcanal/include/hci/class_of_device.h b/tools/rootcanal/include/hci/class_of_device.h
deleted file mode 100644
index 8a1d1f04df..0000000000
--- a/tools/rootcanal/include/hci/class_of_device.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/******************************************************************************
- *
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************/
-
-#pragma once
-
-#include <array>
-#include <optional>
-#include <string>
-
-#include "packet/custom_field_fixed_size_interface.h"
-
-namespace bluetooth {
-namespace hci {
-
-class ClassOfDevice final
- : public packet::CustomFieldFixedSizeInterface<ClassOfDevice> {
- public:
- static constexpr size_t kLength = 3;
-
- std::array<uint8_t, kLength> cod = {};
-
- ClassOfDevice() = default;
- ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
-
- // packet::CustomFieldFixedSizeInterface methods
- inline uint8_t* data() override { return cod.data(); }
- inline const uint8_t* data() const override { return cod.data(); }
-
- uint32_t ToUint32() const;
- std::string ToString() const;
- static std::optional<ClassOfDevice> FromString(const std::string& str);
-
- bool operator<(const ClassOfDevice& rhs) const { return cod < rhs.cod; }
- bool operator==(const ClassOfDevice& rhs) const { return cod == rhs.cod; }
- bool operator>(const ClassOfDevice& rhs) const { return (rhs < *this); }
- bool operator<=(const ClassOfDevice& rhs) const { return !(*this > rhs); }
- bool operator>=(const ClassOfDevice& rhs) const { return !(*this < rhs); }
- bool operator!=(const ClassOfDevice& rhs) const { return !(*this == rhs); }
-
- // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
- // not represent a Class of Device, |to| is not modified and this function
- // returns false. Otherwise, it returns true.
- static bool FromString(const std::string& from, ClassOfDevice& to);
-
- // Copies |from| raw Class of Device octets to the local object.
- // Returns the number of copied octets (always ClassOfDevice::kLength)
- size_t FromOctets(const uint8_t* from);
-
- static bool IsValid(const std::string& class_of_device);
-};
-
-inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
- os << c.ToString();
- return os;
-}
-
-} // namespace hci
-} // namespace bluetooth
diff --git a/tools/rootcanal/include/hci/pcap_filter.h b/tools/rootcanal/include/hci/pcap_filter.h
index a9897a270f..01c60eb0bf 100644
--- a/tools/rootcanal/include/hci/pcap_filter.h
+++ b/tools/rootcanal/include/hci/pcap_filter.h
@@ -16,7 +16,7 @@
#pragma once
-#include <hci/hci_packets.h>
+#include <packets/hci_packets.h>
#include <unordered_map>
diff --git a/tools/rootcanal/lib/hci/address.cc b/tools/rootcanal/lib/hci/address.cc
index dbb0288972..690a1b4ff3 100644
--- a/tools/rootcanal/lib/hci/address.cc
+++ b/tools/rootcanal/lib/hci/address.cc
@@ -45,6 +45,20 @@ Address::Address(std::initializer_list<uint8_t> l) {
std::copy(l.begin(), std::min(l.begin() + kLength, l.end()), data());
}
+bool Address::Parse(pdl::packet::slice& input, Address* output) {
+ if (input.size() < kLength) {
+ return false;
+ }
+
+ std::array<uint8_t, kLength> address{
+ input.read_le<uint8_t>(), input.read_le<uint8_t>(),
+ input.read_le<uint8_t>(), input.read_le<uint8_t>(),
+ input.read_le<uint8_t>(), input.read_le<uint8_t>(),
+ };
+ *output = Address(address);
+ return true;
+}
+
std::string Address::ToString() const {
std::stringstream ss;
for (auto it = address.rbegin(); it != address.rend(); it++) {
diff --git a/tools/rootcanal/lib/hci/class_of_device.cc b/tools/rootcanal/lib/hci/class_of_device.cc
deleted file mode 100644
index 97ab807a21..0000000000
--- a/tools/rootcanal/lib/hci/class_of_device.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-/******************************************************************************
- *
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************/
-
-#include "hci/class_of_device.h"
-
-#include <algorithm>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <sstream>
-#include <vector>
-
-#include "log.h"
-
-namespace bluetooth {
-namespace hci {
-
-// ClassOfDevice cannot initialize member variables as it is a POD type
-// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
-ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
- std::copy(class_of_device, class_of_device + kLength, cod.data());
-}
-
-uint32_t ClassOfDevice::ToUint32() const {
- return (cod[2]) | (cod[1] << 8) | (cod[0] << 16);
-}
-
-std::string ClassOfDevice::ToString() const {
- char buffer[] = "000-0-00";
- std::snprintf(&buffer[0], sizeof(buffer), "%03x-%01x-%02x",
- (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4,
- cod[1] & 0x0f, cod[0]);
- std::string str(buffer);
- return str;
-}
-
-std::optional<ClassOfDevice> ClassOfDevice::FromString(const std::string& str) {
- if (str.length() != 8) {
- return std::nullopt;
- }
-
- std::istringstream stream(str);
- std::string token;
- int index = 0;
- uint16_t values[3];
-
- ClassOfDevice new_cod{};
- while (getline(stream, token, '-')) {
- if (index >= 3) {
- return std::nullopt;
- }
-
- if (index == 0 && token.length() != 3) {
- return std::nullopt;
- }
- if (index == 1 && token.length() != 1) {
- return std::nullopt;
- }
- if (index == 2 && token.length() != 2) {
- return std::nullopt;
- }
- char* temp = nullptr;
- values[index] = std::strtol(token.c_str(), &temp, 16);
- if (*temp != '\0') {
- return std::nullopt;
- }
-
- index++;
- }
-
- if (index != 3) {
- return std::nullopt;
- }
-
- new_cod.cod[0] = values[2];
- new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
- new_cod.cod[2] = values[0] >> 4;
-
- return new_cod;
-}
-
-bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
- auto new_cod = FromString(from);
- if (!new_cod) {
- to = {};
- return false;
- }
- to = std::move(*new_cod);
- return true;
-}
-
-bool ClassOfDevice::IsValid(const std::string& class_of_device) {
- return ClassOfDevice::FromString(class_of_device).has_value();
-}
-
-} // namespace hci
-} // namespace bluetooth
diff --git a/tools/rootcanal/lib/hci/pcap_filter.cc b/tools/rootcanal/lib/hci/pcap_filter.cc
index edb81e42f3..7dbdedabdc 100644
--- a/tools/rootcanal/lib/hci/pcap_filter.cc
+++ b/tools/rootcanal/lib/hci/pcap_filter.cc
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-#include <hci/hci_packets.h>
#include <hci/pcap_filter.h>
-#include <packet/raw_builder.h>
+#include <packets/hci_packets.h>
+
+#include "log.h"
using namespace bluetooth::hci;
-using namespace bluetooth::packet;
namespace rootcanal {
-static PacketView<kLittleEndian> create_packet_view(
+static pdl::packet::slice create_packet_view(
std::vector<uint8_t> const& packet) {
// Wrap the reference to the packet in a shared_ptr with created
// a no-op deleter. The packet view will be short lived so there is no
// risk of the reference leaking.
- return PacketView<kLittleEndian>(std::shared_ptr<std::vector<uint8_t> const>(
+ return pdl::packet::slice(std::shared_ptr<std::vector<uint8_t> const>(
&packet, [](std::vector<uint8_t> const* /* ptr */) {}));
}
@@ -140,8 +140,7 @@ static std::vector<uint8_t> FilterHciAcl(std::vector<uint8_t> const& packet) {
payload.resize(acl.GetPayload().size());
ASSERT(acl.IsValid());
return AclBuilder::Create(acl.GetHandle(), acl.GetPacketBoundaryFlag(),
- acl.GetBroadcastFlag(),
- std::make_unique<RawBuilder>(payload))
+ acl.GetBroadcastFlag(), std::move(payload))
->SerializeToBytes();
}
@@ -160,8 +159,7 @@ static std::vector<uint8_t> FilterHciIso(std::vector<uint8_t> const& packet) {
payload.resize(iso.GetPayload().size());
ASSERT(iso.IsValid());
return IsoBuilder::Create(iso.GetConnectionHandle(), iso.GetPbFlag(),
- iso.GetTsFlag(),
- std::make_unique<RawBuilder>(payload))
+ iso.GetTsFlag(), std::move(payload))
->SerializeToBytes();
}
diff --git a/tools/rootcanal/model/controller/acl_connection_handler.cc b/tools/rootcanal/model/controller/acl_connection_handler.cc
index fabac2abe7..5642c327be 100644
--- a/tools/rootcanal/model/controller/acl_connection_handler.cc
+++ b/tools/rootcanal/model/controller/acl_connection_handler.cc
@@ -16,7 +16,7 @@
#include "acl_connection_handler.h"
-#include <hci/hci_packets.h>
+#include <packets/hci_packets.h>
#include "hci/address.h"
#include "log.h"
diff --git a/tools/rootcanal/model/controller/controller_properties.h b/tools/rootcanal/model/controller/controller_properties.h
index eabc91604a..5eb4d48163 100644
--- a/tools/rootcanal/model/controller/controller_properties.h
+++ b/tools/rootcanal/model/controller/controller_properties.h
@@ -23,7 +23,7 @@
#include <vector>
#include "hci/address.h"
-#include "hci/hci_packets.h"
+#include "packets/hci_packets.h"
#include "rootcanal/configuration.pb.h"
namespace rootcanal {
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.cc b/tools/rootcanal/model/controller/dual_mode_controller.cc
index 8b633efc52..175f08aa03 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.cc
+++ b/tools/rootcanal/model/controller/dual_mode_controller.cc
@@ -21,7 +21,6 @@
#include "crypto/crypto.h"
#include "log.h"
-#include "packet/raw_builder.h"
using bluetooth::hci::ErrorCode;
using bluetooth::hci::LoopbackMode;
@@ -61,8 +60,8 @@ void DualModeController::SendCommandCompleteUnknownOpCodeEvent(
bluetooth::hci::OpCode op_code) const {
send_event_(bluetooth::hci::CommandCompleteBuilder::Create(
kNumCommandPackets, op_code,
- std::make_unique<bluetooth::packet::RawBuilder>(std::vector<uint8_t>{
- static_cast<uint8_t>(ErrorCode::UNKNOWN_HCI_COMMAND)})));
+ std::vector<uint8_t>{
+ static_cast<uint8_t>(ErrorCode::UNKNOWN_HCI_COMMAND)}));
}
DualModeController::DualModeController(ControllerProperties properties)
@@ -89,18 +88,15 @@ void DualModeController::ForwardToLl(CommandView command) {
void DualModeController::HandleAcl(
std::shared_ptr<std::vector<uint8_t>> packet) {
- bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
- auto acl_packet = bluetooth::hci::AclView::Create(raw_packet);
+ auto acl_packet = bluetooth::hci::AclView::Create(pdl::packet::slice(packet));
ASSERT(acl_packet.IsValid());
if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
uint16_t handle = acl_packet.GetHandle();
- std::vector<uint8_t> payload{acl_packet.GetPayload().begin(),
- acl_packet.GetPayload().end()};
+ std::vector<uint8_t> payload(acl_packet.GetPayload());
send_acl_(bluetooth::hci::AclBuilder::Create(
handle, acl_packet.GetPacketBoundaryFlag(),
- acl_packet.GetBroadcastFlag(),
- std::make_unique<bluetooth::packet::RawBuilder>(payload)));
+ acl_packet.GetBroadcastFlag(), std::move(payload)));
std::vector<bluetooth::hci::CompletedPackets> completed_packets;
bluetooth::hci::CompletedPackets cp;
@@ -117,8 +113,7 @@ void DualModeController::HandleAcl(
void DualModeController::HandleSco(
std::shared_ptr<std::vector<uint8_t>> packet) {
- bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
- auto sco_packet = bluetooth::hci::ScoView::Create(raw_packet);
+ auto sco_packet = bluetooth::hci::ScoView::Create(pdl::packet::slice(packet));
ASSERT(sco_packet.IsValid());
if (loopback_mode_ == LoopbackMode::ENABLE_LOCAL) {
uint16_t handle = sco_packet.GetHandle();
@@ -143,16 +138,15 @@ void DualModeController::HandleSco(
void DualModeController::HandleIso(
std::shared_ptr<std::vector<uint8_t>> packet) {
- bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
- auto iso = bluetooth::hci::IsoView::Create(raw_packet);
+ auto iso = bluetooth::hci::IsoView::Create(pdl::packet::slice(packet));
ASSERT(iso.IsValid());
link_layer_controller_.HandleIso(iso);
}
void DualModeController::HandleCommand(
std::shared_ptr<std::vector<uint8_t>> packet) {
- auto command_packet = bluetooth::hci::CommandView::Create(
- bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian>(packet));
+ auto command_packet =
+ bluetooth::hci::CommandView::Create(pdl::packet::slice(packet));
ASSERT(command_packet.IsValid());
OpCode op_code = command_packet.GetOpCode();
@@ -184,17 +178,13 @@ void DualModeController::HandleCommand(
op_code != OpCode::READ_BUFFER_SIZE &&
op_code != OpCode::READ_LOOPBACK_MODE &&
op_code != OpCode::WRITE_LOOPBACK_MODE) {
- std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
- std::make_unique<bluetooth::packet::RawBuilder>(255);
- raw_builder_ptr->AddOctets(*packet);
send_event_(bluetooth::hci::LoopbackCommandBuilder::Create(
- std::move(raw_builder_ptr)));
+ std::vector<uint8_t>(packet->begin(), packet->end())));
}
// Quirk to reset the host stack when a command is received before the Hci
// Reset command.
else if (properties_.quirks.hardware_error_before_reset &&
- !controller_reset_ &&
- op_code != OpCode::RESET) {
+ !controller_reset_ && op_code != OpCode::RESET) {
WARNING(id_,
"Received command {} before HCI Reset; sending the Hardware"
" Error event",
@@ -233,10 +223,8 @@ void DualModeController::RegisterEventChannel(
send_event_ =
[send_event](std::shared_ptr<bluetooth::hci::EventBuilder> event) {
auto bytes = std::make_shared<std::vector<uint8_t>>();
- bluetooth::packet::BitInserter bit_inserter(*bytes);
- bytes->reserve(event->size());
- event->Serialize(bit_inserter);
- send_event(std::move(bytes));
+ event->Serialize(*bytes);
+ send_event(bytes);
};
link_layer_controller_.RegisterEventChannel(send_event_);
}
@@ -246,10 +234,8 @@ void DualModeController::RegisterAclChannel(
send_acl) {
send_acl_ = [send_acl](std::shared_ptr<bluetooth::hci::AclBuilder> acl_data) {
auto bytes = std::make_shared<std::vector<uint8_t>>();
- bluetooth::packet::BitInserter bit_inserter(*bytes);
- bytes->reserve(acl_data->size());
- acl_data->Serialize(bit_inserter);
- send_acl(std::move(bytes));
+ acl_data->Serialize(*bytes);
+ send_acl(bytes);
};
link_layer_controller_.RegisterAclChannel(send_acl_);
}
@@ -259,10 +245,8 @@ void DualModeController::RegisterScoChannel(
send_sco) {
send_sco_ = [send_sco](std::shared_ptr<bluetooth::hci::ScoBuilder> sco_data) {
auto bytes = std::make_shared<std::vector<uint8_t>>();
- bluetooth::packet::BitInserter bit_inserter(*bytes);
- bytes->reserve(sco_data->size());
- sco_data->Serialize(bit_inserter);
- send_sco(std::move(bytes));
+ sco_data->Serialize(*bytes);
+ send_sco(bytes);
};
link_layer_controller_.RegisterScoChannel(send_sco_);
}
@@ -272,10 +256,8 @@ void DualModeController::RegisterIsoChannel(
send_iso) {
send_iso_ = [send_iso](std::shared_ptr<bluetooth::hci::IsoBuilder> iso_data) {
auto bytes = std::make_shared<std::vector<uint8_t>>();
- bluetooth::packet::BitInserter bit_inserter(*bytes);
- bytes->reserve(iso_data->size());
- iso_data->Serialize(bit_inserter);
- send_iso(std::move(bytes));
+ iso_data->Serialize(*bytes);
+ send_iso(bytes);
};
link_layer_controller_.RegisterIsoChannel(send_iso_);
}
@@ -409,7 +391,7 @@ void DualModeController::ReadRemoteVersionInformation(CommandView command) {
DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
- OpCode::READ_REMOTE_VERSION_INFORMATION, command.GetPayload(),
+ OpCode::READ_REMOTE_VERSION_INFORMATION, command.bytes(),
command_view.GetConnectionHandle());
send_event_(bluetooth::hci::ReadRemoteVersionInformationStatusBuilder::Create(
@@ -487,7 +469,7 @@ void DualModeController::ReadRemoteExtendedFeatures(CommandView command) {
DEBUG(id_, " page_number={}", command_view.GetPageNumber());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
- OpCode::READ_REMOTE_EXTENDED_FEATURES, command_view.GetPayload(),
+ OpCode::READ_REMOTE_EXTENDED_FEATURES, command_view.bytes(),
command_view.GetConnectionHandle());
send_event_(bluetooth::hci::ReadRemoteExtendedFeaturesStatusBuilder::Create(
@@ -518,7 +500,7 @@ void DualModeController::ReadRemoteSupportedFeatures(CommandView command) {
DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle());
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
- OpCode::READ_REMOTE_SUPPORTED_FEATURES, command_view.GetPayload(),
+ OpCode::READ_REMOTE_SUPPORTED_FEATURES, command_view.bytes(),
command_view.GetConnectionHandle());
send_event_(bluetooth::hci::ReadRemoteSupportedFeaturesStatusBuilder::Create(
@@ -535,7 +517,7 @@ void DualModeController::ReadClockOffset(CommandView command) {
uint16_t handle = command_view.GetConnectionHandle();
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
- OpCode::READ_CLOCK_OFFSET, command_view.GetPayload(), handle);
+ OpCode::READ_CLOCK_OFFSET, command_view.bytes(), handle);
send_event_(bluetooth::hci::ReadClockOffsetStatusBuilder::Create(
status, kNumCommandPackets));
@@ -1200,8 +1182,7 @@ void DualModeController::WriteClassOfDevice(CommandView command) {
ASSERT(command_view.IsValid());
DEBUG(id_, "<< Write Class of Device");
- DEBUG(id_, " class_of_device={}",
- command_view.GetClassOfDevice().ToString());
+ DEBUG(id_, " class_of_device=0x{:x}", command_view.GetClassOfDevice());
link_layer_controller_.SetClassOfDevice(command_view.GetClassOfDevice());
send_event_(bluetooth::hci::WriteClassOfDeviceCompleteBuilder::Create(
@@ -1797,8 +1778,7 @@ void DualModeController::RemoteNameRequest(CommandView command) {
DEBUG(id_, " bd_addr={}", bd_addr);
auto status = link_layer_controller_.SendCommandToRemoteByAddress(
- OpCode::REMOTE_NAME_REQUEST, command_view.GetPayload(), GetAddress(),
- bd_addr);
+ OpCode::REMOTE_NAME_REQUEST, command_view.bytes(), GetAddress(), bd_addr);
send_event_(bluetooth::hci::RemoteNameRequestStatusBuilder::Create(
status, kNumCommandPackets));
@@ -2081,10 +2061,10 @@ void DualModeController::LeCreateConnection(CommandView command) {
command_view.GetPeerAddress(),
command_view.GetPeerAddressType(),
},
- command_view.GetOwnAddressType(), command_view.GetConnIntervalMin(),
- command_view.GetConnIntervalMax(), command_view.GetConnLatency(),
- command_view.GetSupervisionTimeout(), command_view.GetMinimumCeLength(),
- command_view.GetMaximumCeLength());
+ command_view.GetOwnAddressType(), command_view.GetConnectionIntervalMin(),
+ command_view.GetConnectionIntervalMax(), command_view.GetMaxLatency(),
+ command_view.GetSupervisionTimeout(), command_view.GetMinCeLength(),
+ command_view.GetMaxCeLength());
send_event_(bluetooth::hci::LeCreateConnectionStatusBuilder::Create(
status, kNumCommandPackets));
}
@@ -2109,8 +2089,9 @@ void DualModeController::LeConnectionUpdate(CommandView command) {
DEBUG(id_, " connection_handle=0x{:x}", command_view.GetConnectionHandle());
ErrorCode status = link_layer_controller_.LeConnectionUpdate(
- command_view.GetConnectionHandle(), command_view.GetConnIntervalMin(),
- command_view.GetConnIntervalMax(), command_view.GetConnLatency(),
+ command_view.GetConnectionHandle(),
+ command_view.GetConnectionIntervalMin(),
+ command_view.GetConnectionIntervalMax(), command_view.GetMaxLatency(),
command_view.GetSupervisionTimeout());
send_event_(bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
@@ -2628,7 +2609,7 @@ void DualModeController::LeSetExtendedScanParameters(CommandView command) {
ErrorCode status = link_layer_controller_.LeSetExtendedScanParameters(
command_view.GetOwnAddressType(), command_view.GetScanningFilterPolicy(),
- command_view.GetScanningPhys(), command_view.GetParameters());
+ command_view.GetScanningPhys(), command_view.GetScanningPhyParameters());
send_event_(
bluetooth::hci::LeSetExtendedScanParametersCompleteBuilder::Create(
kNumCommandPackets, status));
@@ -2659,18 +2640,30 @@ void DualModeController::LeExtendedCreateConnection(CommandView command) {
DEBUG(id_, "<< LE Extended Create Connection");
DEBUG(id_, " peer_address={}", command_view.GetPeerAddress());
DEBUG(id_, " peer_address_type={}",
- bluetooth::hci::AddressTypeText(command_view.GetPeerAddressType()));
+ bluetooth::hci::PeerAddressTypeText(command_view.GetPeerAddressType()));
DEBUG(id_, " initiator_filter_policy={}",
bluetooth::hci::InitiatorFilterPolicyText(
command_view.GetInitiatorFilterPolicy()));
+ AddressType peer_address_type;
+ switch (command_view.GetPeerAddressType()) {
+ case PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
+ default:
+ peer_address_type = AddressType::PUBLIC_DEVICE_ADDRESS;
+ break;
+ case PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
+ peer_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
+ break;
+ }
+
ErrorCode status = link_layer_controller_.LeExtendedCreateConnection(
command_view.GetInitiatorFilterPolicy(), command_view.GetOwnAddressType(),
AddressWithType{
command_view.GetPeerAddress(),
- command_view.GetPeerAddressType(),
+ peer_address_type,
},
- command_view.GetInitiatingPhys(), command_view.GetPhyScanParameters());
+ command_view.GetInitiatingPhys(),
+ command_view.GetInitiatingPhyParameters());
send_event_(bluetooth::hci::LeExtendedCreateConnectionStatusBuilder::Create(
status, kNumCommandPackets));
}
@@ -2704,7 +2697,7 @@ void DualModeController::LeReadRemoteFeatures(CommandView command) {
DEBUG(id_, " connection_handle=0x{:x}", handle);
auto status = link_layer_controller_.SendCommandToRemoteByHandle(
- OpCode::LE_READ_REMOTE_FEATURES, command_view.GetPayload(), handle);
+ OpCode::LE_READ_REMOTE_FEATURES, command_view.bytes(), handle);
send_event_(bluetooth::hci::LeReadRemoteFeaturesStatusBuilder::Create(
status, kNumCommandPackets));
@@ -2794,20 +2787,19 @@ void DualModeController::LeGetVendorCapabilities(CommandView command) {
return;
}
+ std::vector<uint8_t> return_parameters = {
+ static_cast<uint8_t>(ErrorCode::SUCCESS)};
+ return_parameters.insert(return_parameters.end(),
+ properties_.le_vendor_capabilities.begin(),
+ properties_.le_vendor_capabilities.end());
// Ensure a minimal size for vendor capabilities.
- vector<uint8_t> vendor_capabilities = properties_.le_vendor_capabilities;
- if (vendor_capabilities.size() < 8) {
- vendor_capabilities.resize(8);
+ if (return_parameters.size() < 9) {
+ return_parameters.resize(9);
}
- std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
- std::make_unique<bluetooth::packet::RawBuilder>();
- raw_builder_ptr->AddOctets1(static_cast<uint8_t>(ErrorCode::SUCCESS));
- raw_builder_ptr->AddOctets(vendor_capabilities);
-
send_event_(bluetooth::hci::CommandCompleteBuilder::Create(
kNumCommandPackets, OpCode::LE_GET_VENDOR_CAPABILITIES,
- std::move(raw_builder_ptr)));
+ std::move(return_parameters)));
}
void DualModeController::LeMultiAdv(CommandView command) {
@@ -2848,8 +2840,7 @@ void DualModeController::CsrVendorCommand(CommandView command) {
//
// BlueZ has a reference implementation of the CSR vendor command.
- std::vector<uint8_t> parameters(command.GetPayload().begin(),
- command.GetPayload().end());
+ std::vector<uint8_t> parameters(command.GetPayload());
uint16_t type = 0;
uint16_t length = 0;
@@ -2928,8 +2919,7 @@ complete:
parameters[1] = 0x1;
parameters[2] = 0x0;
send_event_(bluetooth::hci::EventBuilder::Create(
- bluetooth::hci::EventCode::VENDOR_SPECIFIC,
- std::make_unique<bluetooth::packet::RawBuilder>(std::move(parameters))));
+ bluetooth::hci::EventCode::VENDOR_SPECIFIC, std::move(parameters)));
}
void DualModeController::CsrReadVarid(CsrVarid varid,
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.h b/tools/rootcanal/model/controller/dual_mode_controller.h
index c591ca98ae..d8a71eb484 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.h
+++ b/tools/rootcanal/model/controller/dual_mode_controller.h
@@ -27,10 +27,10 @@
#include "controller_properties.h"
#include "hci/address.h"
-#include "hci/hci_packets.h"
#include "link_layer_controller.h"
#include "model/controller/vendor_commands/csr.h"
#include "model/devices/device.h"
+#include "packets/hci_packets.h"
namespace rootcanal {
diff --git a/tools/rootcanal/model/controller/ffi.cc b/tools/rootcanal/model/controller/ffi.cc
index 81162ed9dd..888f198a26 100644
--- a/tools/rootcanal/model/controller/ffi.cc
+++ b/tools/rootcanal/model/controller/ffi.cc
@@ -117,9 +117,7 @@ __attribute__((visibility("default"))) void ffi_controller_receive_ll(
std::shared_ptr<std::vector<uint8_t>> bytes =
std::make_shared<std::vector<uint8_t>>(data, data + data_len);
model::packets::LinkLayerPacketView packet =
- model::packets::LinkLayerPacketView::Create(
- bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(
- bytes));
+ model::packets::LinkLayerPacketView::Create(pdl::packet::slice(bytes));
if (!packet.IsValid()) {
std::cerr << "Dropping malformed LL packet" << std::endl;
return;
diff --git a/tools/rootcanal/model/controller/le_advertiser.h b/tools/rootcanal/model/controller/le_advertiser.h
index 19af1125d4..80fced0440 100644
--- a/tools/rootcanal/model/controller/le_advertiser.h
+++ b/tools/rootcanal/model/controller/le_advertiser.h
@@ -23,7 +23,7 @@
#include <ratio>
#include "hci/address_with_type.h"
-#include "hci/hci_packets.h"
+#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"
namespace rootcanal {
diff --git a/tools/rootcanal/model/controller/link_layer_controller.cc b/tools/rootcanal/model/controller/link_layer_controller.cc
index a0a87b0733..adafc72b99 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.cc
+++ b/tools/rootcanal/model/controller/link_layer_controller.cc
@@ -16,13 +16,11 @@
#include "link_layer_controller.h"
-#include <hci/hci_packets.h>
-
#include <algorithm>
#include "crypto/crypto.h"
#include "log.h"
-#include "packet/raw_builder.h"
+#include "packets/hci_packets.h"
#include "rootcanal_rs.h"
using namespace std::chrono;
@@ -684,7 +682,7 @@ ErrorCode LinkLayerController::LeAddDeviceToResolvingList(
for (auto const& entry : le_resolving_list_) {
if ((entry.peer_identity_address_type == peer_identity_address_type &&
entry.peer_identity_address == peer_identity_address) ||
- entry.peer_irk == peer_irk) {
+ (entry.peer_irk == peer_irk && !irk_is_zero(peer_irk))) {
INFO(id_, "device is already present in the resolving list");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
@@ -1283,7 +1281,8 @@ ErrorCode LinkLayerController::LeSetExtendedScanParameters(
bluetooth::hci::OwnAddressType own_address_type,
bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy,
uint8_t scanning_phys,
- std::vector<bluetooth::hci::PhyScanParameters> scanning_phy_parameters) {
+ std::vector<bluetooth::hci::ScanningPhyParameters>
+ scanning_phy_parameters) {
uint8_t supported_phys = properties_.LeSupportedPhys();
// Extended advertising commands are disallowed when legacy advertising
@@ -1678,7 +1677,7 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy,
bluetooth::hci::OwnAddressType own_address_type,
AddressWithType peer_address, uint8_t initiating_phys,
- std::vector<bluetooth::hci::LeCreateConnPhyScanParameters>
+ std::vector<bluetooth::hci::InitiatingPhyParameters>
initiating_phy_parameters) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
@@ -1753,36 +1752,39 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
// Note: no explicit error code stated for invalid connection interval
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
- if (parameter.conn_interval_min_ < 0x6 ||
- parameter.conn_interval_min_ > 0x0c80 ||
- parameter.conn_interval_max_ < 0x6 ||
- parameter.conn_interval_max_ > 0x0c80) {
+ if (parameter.connection_interval_min_ < 0x6 ||
+ parameter.connection_interval_min_ > 0x0c80 ||
+ parameter.connection_interval_max_ < 0x6 ||
+ parameter.connection_interval_max_ > 0x0c80) {
INFO(id_,
"connection_interval_min (0x{:04x}) and/or "
"connection_interval_max (0x{:04x}) are outside the range"
" of supported values (0x6 - 0x0c80)",
- parameter.conn_interval_min_, parameter.conn_interval_max_);
+ parameter.connection_interval_min_,
+ parameter.connection_interval_max_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Connection_Interval_Min parameter shall not be greater than the
// Connection_Interval_Max parameter.
- if (parameter.conn_interval_max_ < parameter.conn_interval_min_) {
+ if (parameter.connection_interval_max_ <
+ parameter.connection_interval_min_) {
INFO(id_,
"connection_interval_min (0x{:04x}) is larger than"
" connection_interval_max (0x{:04x})",
- parameter.conn_interval_min_, parameter.conn_interval_max_);
+ parameter.connection_interval_min_,
+ parameter.connection_interval_max_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for invalid max_latency
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
- if (parameter.conn_latency_ > 0x01f3) {
+ if (parameter.max_latency_ > 0x01f3) {
INFO(id_,
"max_latency (0x{:04x}) is outside the range"
" of supported values (0x0 - 0x01f3)",
- parameter.conn_latency_);
+ parameter.max_latency_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
@@ -1802,8 +1804,8 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
// (1 + Max_Latency) * Connection_Interval_Max * 2, where
// Connection_Interval_Max is given in milliseconds.
milliseconds min_supervision_timeout = duration_cast<milliseconds>(
- (1 + parameter.conn_latency_) *
- slots(2 * parameter.conn_interval_max_) * 2);
+ (1 + parameter.max_latency_) *
+ slots(2 * parameter.connection_interval_max_) * 2);
if (parameter.supervision_timeout_ * 10ms < min_supervision_timeout) {
INFO(
id_,
@@ -1864,10 +1866,10 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
- initiating_phy_parameters[offset].conn_interval_min_,
+ initiating_phy_parameters[offset].connection_interval_min_,
.connection_interval_max =
- initiating_phy_parameters[offset].conn_interval_max_,
- .max_latency = initiating_phy_parameters[offset].conn_latency_,
+ initiating_phy_parameters[offset].connection_interval_max_,
+ .max_latency = initiating_phy_parameters[offset].max_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
@@ -1882,10 +1884,10 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
- initiating_phy_parameters[offset].conn_interval_min_,
+ initiating_phy_parameters[offset].connection_interval_min_,
.connection_interval_max =
- initiating_phy_parameters[offset].conn_interval_max_,
- .max_latency = initiating_phy_parameters[offset].conn_latency_,
+ initiating_phy_parameters[offset].connection_interval_max_,
+ .max_latency = initiating_phy_parameters[offset].max_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
@@ -1900,10 +1902,10 @@ ErrorCode LinkLayerController::LeExtendedCreateConnection(
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
- initiating_phy_parameters[offset].conn_interval_min_,
+ initiating_phy_parameters[offset].connection_interval_min_,
.connection_interval_max =
- initiating_phy_parameters[offset].conn_interval_max_,
- .max_latency = initiating_phy_parameters[offset].conn_latency_,
+ initiating_phy_parameters[offset].connection_interval_max_,
+ .max_latency = initiating_phy_parameters[offset].max_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
@@ -2034,11 +2036,8 @@ LinkLayerController::LinkLayerController(const Address& address,
auto controller = static_cast<LinkLayerController*>(user);
auto event_code = static_cast<EventCode>(data[0]);
- auto payload = std::make_unique<bluetooth::packet::RawBuilder>(
- std::vector(data + 2, data + len));
-
controller->send_event_(bluetooth::hci::EventBuilder::Create(
- event_code, std::move(payload)));
+ event_code, std::vector(data + 2, data + len)));
},
.send_lmp_packet =
@@ -2046,22 +2045,17 @@ LinkLayerController::LinkLayerController(const Address& address,
uintptr_t len) {
auto controller = static_cast<LinkLayerController*>(user);
- auto payload = std::make_unique<bluetooth::packet::RawBuilder>(
- std::vector(data, data + len));
-
Address source = controller->GetAddress();
Address dest(*to);
controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create(
- source, dest, std::move(payload)));
+ source, dest, std::vector(data, data + len)));
},
.send_llcp_packet =
[](void* user, uint16_t acl_connection_handle, const uint8_t* data,
uintptr_t len) {
auto controller = static_cast<LinkLayerController*>(user);
- auto payload = std::make_unique<bluetooth::packet::RawBuilder>(
- std::vector(data, data + len));
if (!controller->connections_.HasHandle(acl_connection_handle)) {
ERROR(
@@ -2078,7 +2072,7 @@ LinkLayerController::LinkLayerController(const Address& address,
Address destination = connection.GetAddress().GetAddress();
controller->SendLinkLayerPacket(model::packets::LlcpBuilder::Create(
- source, destination, std::move(payload)));
+ source, destination, std::vector(data, data + len)));
}};
lm_.reset(link_manager_create(controller_ops_));
@@ -2124,8 +2118,8 @@ ErrorCode LinkLayerController::SendLeCommandToRemoteByAddress(
}
ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
- OpCode opcode, bluetooth::packet::PacketView<true> args,
- const Address& own_address, const Address& peer_address) {
+ OpCode opcode, pdl::packet::slice args, const Address& own_address,
+ const Address& peer_address) {
switch (opcode) {
case (OpCode::REMOTE_NAME_REQUEST):
// LMP features get requested with remote name requests.
@@ -2140,8 +2134,8 @@ ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
own_address, peer_address));
break;
case (OpCode::READ_REMOTE_EXTENDED_FEATURES): {
- uint8_t page_number =
- (args.begin() + 2).extract<uint8_t>(); // skip the handle
+ pdl::packet::slice page_number_slice = args.subrange(5, 2);
+ uint8_t page_number = page_number_slice.read_le<uint8_t>();
SendLinkLayerPacket(
model::packets::ReadRemoteExtendedFeaturesBuilder::Create(
own_address, peer_address, page_number));
@@ -2165,7 +2159,7 @@ ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
}
ErrorCode LinkLayerController::SendCommandToRemoteByHandle(
- OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle) {
+ OpCode opcode, pdl::packet::slice args, uint16_t handle) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
@@ -2231,8 +2225,7 @@ ErrorCode LinkLayerController::SendScoToRemote(
std::vector<uint8_t> sco_data_bytes(sco_data.begin(), sco_data.end());
SendLinkLayerPacket(model::packets::ScoBuilder::Create(
- source, destination,
- std::make_unique<bluetooth::packet::RawBuilder>(sco_data_bytes)));
+ source, destination, std::move(sco_data_bytes)));
return ErrorCode::SUCCESS;
}
@@ -2479,7 +2472,7 @@ void LinkLayerController::IncomingAclPacket(
auto acl_packet = bluetooth::hci::AclBuilder::Create(
connection_handle, packet_boundary_flag, broadcast_flag,
- std::make_unique<bluetooth::packet::RawBuilder>(std::move(fragment)));
+ std::move(fragment));
send_acl_(std::move(acl_packet));
@@ -2803,6 +2796,7 @@ void LinkLayerController::IncomingInquiryResponsePacket(
inquiry_response.GetClockOffset(), inquiry_response.GetRssi(),
inquiry_response.GetExtendedInquiryResponse()));
} break;
+
default:
WARNING(id_, "Unhandled Incoming Inquiry Response of type {}",
static_cast<int>(basic_inquiry_response.GetInquiryType()));
@@ -2843,6 +2837,10 @@ Address LinkLayerController::generate_rpa(
return rpa;
}
+bool LinkLayerController::irk_is_zero(std::array<uint8_t, LinkLayerController::kIrkSize> irk) {
+ return std::all_of(irk.begin(), irk.end(), [](uint8_t b) { return b == 0; });
+}
+
// Handle legacy advertising PDUs while in the Scanning state.
void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu(
model::packets::LeLegacyAdvertisingPduView& pdu, uint8_t rssi) {
@@ -2963,10 +2961,10 @@ void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu(
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
- if (scanner_.IsPacketInHistory(pdu)) {
+ if (scanner_.IsPacketInHistory(pdu.bytes())) {
should_send_advertising_report = false;
} else {
- scanner_.AddPacketToHistory(pdu);
+ scanner_.AddPacketToHistory(pdu.bytes());
}
}
@@ -3290,6 +3288,7 @@ void LinkLayerController::ConnectIncomingLeLegacyAdvertisingPdu(
}
initiator_.pending_connect_request = advertising_address;
+ initiator_.initiating_address = initiating_address.GetAddress();
INFO(id_, "Sending LE Connect request to {} with initiating address {}",
resolved_advertising_address, initiating_address);
@@ -3425,10 +3424,10 @@ void LinkLayerController::ScanIncomingLeExtendedAdvertisingPdu(
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
- if (scanner_.IsPacketInHistory(pdu)) {
+ if (scanner_.IsPacketInHistory(pdu.bytes())) {
should_send_advertising_report = false;
} else {
- scanner_.AddPacketToHistory(pdu);
+ scanner_.AddPacketToHistory(pdu.bytes());
}
}
@@ -3716,6 +3715,7 @@ void LinkLayerController::ConnectIncomingLeExtendedAdvertisingPdu(
}
initiator_.pending_connect_request = advertising_address;
+ initiator_.initiating_address = initiating_address.GetAddress();
INFO(id_, "Sending LE Connect request to {} with initiating address {}",
resolved_advertising_address, initiating_address);
@@ -4091,7 +4091,7 @@ void LinkLayerController::IncomingLeConnectedIsochronousPdu(
send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create(
cis_connection_handle, packet_boundary_flag, pdu.GetSequenceNumber(),
iso_sdu_length, bluetooth::hci::IsoPacketStatusFlag::VALID,
- std::make_unique<bluetooth::packet::RawBuilder>(std::move(fragment))));
+ std::move(fragment)));
remaining_size -= fragment_size;
offset += fragment_size;
@@ -4917,10 +4917,10 @@ void LinkLayerController::IncomingLeScanResponsePacket(
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
- if (scanner_.IsPacketInHistory(incoming)) {
+ if (scanner_.IsPacketInHistory(incoming.bytes())) {
should_send_advertising_report = false;
} else {
- scanner_.AddPacketToHistory(incoming);
+ scanner_.AddPacketToHistory(incoming.bytes());
}
}
@@ -5171,12 +5171,12 @@ void LinkLayerController::RegisterRemoteChannel(
}
void LinkLayerController::ForwardToLm(bluetooth::hci::CommandView command) {
- auto packet = std::vector(command.begin(), command.end());
+ auto packet = command.bytes().bytes();
ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size()));
}
void LinkLayerController::ForwardToLl(bluetooth::hci::CommandView command) {
- auto packet = std::vector(command.begin(), command.end());
+ auto packet = command.bytes().bytes();
ASSERT(link_layer_ingest_hci(ll_.get(), packet.data(), packet.size()));
}
@@ -5993,7 +5993,7 @@ void LinkLayerController::Reset() {
sco_flow_control_enable_ = false;
local_name_.fill(0);
extended_inquiry_response_.fill(0);
- class_of_device_ = ClassOfDevice({0, 0, 0});
+ class_of_device_ = 0;
min_encryption_key_size_ = 16;
event_mask_ = 0x00001fffffffffff;
event_mask_page_2_ = 0x0;
@@ -6322,12 +6322,9 @@ TaskId LinkLayerController::StartScoStream(Address address) {
connections_.GetScoHandle(address), PacketStatusFlag::CORRECTLY_RECEIVED,
{0, 0, 0, 0, 0});
- auto bytes = std::make_shared<std::vector<uint8_t>>();
- bluetooth::packet::BitInserter bit_inserter(*bytes);
- sco_builder->Serialize(bit_inserter);
- auto raw_view =
- bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian>(bytes);
- auto sco_view = bluetooth::hci::ScoView::Create(raw_view);
+ auto sco_bytes = sco_builder->SerializeToBytes();
+ auto sco_view = bluetooth::hci::ScoView::Create(pdl::packet::slice(
+ std::make_shared<std::vector<uint8_t>>(std::move(sco_bytes))));
ASSERT(sco_view.IsValid());
return SchedulePeriodicTask(0ms, 20ms, [this, address, sco_view]() {
diff --git a/tools/rootcanal/model/controller/link_layer_controller.h b/tools/rootcanal/model/controller/link_layer_controller.h
index 58abc822d0..3e036faff6 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.h
+++ b/tools/rootcanal/model/controller/link_layer_controller.h
@@ -23,11 +23,11 @@
#include <vector>
#include "hci/address.h"
-#include "hci/hci_packets.h"
#include "include/phy.h"
#include "model/controller/acl_connection_handler.h"
#include "model/controller/controller_properties.h"
#include "model/controller/le_advertiser.h"
+#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"
#include "rootcanal_rs.h"
@@ -36,7 +36,6 @@ namespace rootcanal {
using ::bluetooth::hci::Address;
using ::bluetooth::hci::AddressType;
using ::bluetooth::hci::AuthenticationEnable;
-using ::bluetooth::hci::ClassOfDevice;
using ::bluetooth::hci::ErrorCode;
using ::bluetooth::hci::FilterAcceptListAddressType;
using ::bluetooth::hci::OpCode;
@@ -64,18 +63,21 @@ class LinkLayerController {
static Address generate_rpa(
std::array<uint8_t, LinkLayerController::kIrkSize> irk);
+ // Return true if the input IRK is all 0s.
+ static bool irk_is_zero(std::array<uint8_t, LinkLayerController::kIrkSize> irk);
+
LinkLayerController(const Address& address,
const ControllerProperties& properties, int id = 0);
~LinkLayerController();
- ErrorCode SendCommandToRemoteByAddress(
- OpCode opcode, bluetooth::packet::PacketView<true> args,
- const Address& own_address, const Address& peer_address);
+ ErrorCode SendCommandToRemoteByAddress(OpCode opcode, pdl::packet::slice args,
+ const Address& own_address,
+ const Address& peer_address);
ErrorCode SendLeCommandToRemoteByAddress(OpCode opcode,
const Address& own_address,
const Address& peer_address);
- ErrorCode SendCommandToRemoteByHandle(
- OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle);
+ ErrorCode SendCommandToRemoteByHandle(OpCode opcode, pdl::packet::slice args,
+ uint16_t handle);
ErrorCode SendScoToRemote(bluetooth::hci::ScoView sco_packet);
ErrorCode SendAclToRemote(bluetooth::hci::AclView acl_packet);
@@ -481,7 +483,8 @@ class LinkLayerController {
bluetooth::hci::OwnAddressType own_address_type,
bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy,
uint8_t scanning_phys,
- std::vector<bluetooth::hci::PhyScanParameters> scanning_phy_parameters);
+ std::vector<bluetooth::hci::ScanningPhyParameters>
+ scanning_phy_parameters);
// HCI command LE_Set_Extended_Scan_Enable (Vol 4, Part E § 7.8.65).
ErrorCode LeSetExtendedScanEnable(
@@ -510,7 +513,7 @@ class LinkLayerController {
bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy,
bluetooth::hci::OwnAddressType own_address_type,
AddressWithType peer_address, uint8_t initiating_phys,
- std::vector<bluetooth::hci::LeCreateConnPhyScanParameters>
+ std::vector<bluetooth::hci::InitiatingPhyParameters>
initiating_phy_parameters);
// Periodic Advertising
@@ -719,7 +722,7 @@ class LinkLayerController {
}
uint16_t GetVoiceSetting() const { return voice_setting_; }
- const ClassOfDevice& GetClassOfDevice() const { return class_of_device_; }
+ uint32_t GetClassOfDevice() const { return class_of_device_; }
uint8_t GetMaxLmpFeaturesPageNumber() {
return properties_.lmp_features.size() - 1;
@@ -738,14 +741,8 @@ class LinkLayerController {
void SetExtendedInquiryResponse(
std::vector<uint8_t> const& extended_inquiry_response);
- void SetClassOfDevice(ClassOfDevice class_of_device) {
- class_of_device_ = class_of_device;
- }
-
void SetClassOfDevice(uint32_t class_of_device) {
- class_of_device_.cod[0] = class_of_device & UINT8_MAX;
- class_of_device_.cod[1] = (class_of_device >> 8) & UINT8_MAX;
- class_of_device_.cod[2] = (class_of_device >> 16) & UINT8_MAX;
+ class_of_device_ = class_of_device;
}
void SetAuthenticationEnable(AuthenticationEnable enable) {
@@ -874,7 +871,7 @@ class LinkLayerController {
extended_inquiry_response_{};
// Class of Device (Vol 4, Part E § 6.26).
- ClassOfDevice class_of_device_{{0, 0, 0}};
+ uint32_t class_of_device_{0};
// Other configuration parameters.
@@ -1005,20 +1002,17 @@ class LinkLayerController {
std::optional<std::chrono::steady_clock::time_point> periodical_timeout;
// Packet History
- std::vector<model::packets::LinkLayerPacketView> history;
+ std::vector<pdl::packet::slice> history;
bool IsEnabled() const { return scan_enable; }
- bool IsPacketInHistory(model::packets::LinkLayerPacketView packet) const {
+ bool IsPacketInHistory(pdl::packet::slice const& packet) const {
return std::any_of(
history.begin(), history.end(),
- [packet](model::packets::LinkLayerPacketView const& a) {
- return a.size() == packet.size() &&
- std::equal(a.begin(), a.end(), packet.begin());
- });
+ [packet](pdl::packet::slice const& a) { return a == packet; });
}
- void AddPacketToHistory(model::packets::LinkLayerPacketView packet) {
+ void AddPacketToHistory(pdl::packet::slice packet) {
history.push_back(packet);
}
};
diff --git a/tools/rootcanal/model/controller/sco_connection.cc b/tools/rootcanal/model/controller/sco_connection.cc
index 827e60df8b..3286a1cdaa 100644
--- a/tools/rootcanal/model/controller/sco_connection.cc
+++ b/tools/rootcanal/model/controller/sco_connection.cc
@@ -16,8 +16,8 @@
#include "sco_connection.h"
-#include <hci/hci_packets.h>
#include <log.h>
+#include <packets/hci_packets.h>
#include <vector>
diff --git a/tools/rootcanal/model/devices/baseband_sniffer.cc b/tools/rootcanal/model/devices/baseband_sniffer.cc
index 50b1b345d8..056f7c3884 100644
--- a/tools/rootcanal/model/devices/baseband_sniffer.cc
+++ b/tools/rootcanal/model/devices/baseband_sniffer.cc
@@ -17,7 +17,6 @@
#include "baseband_sniffer.h"
#include "log.h"
-#include "packet/raw_builder.h"
#include "pcap.h"
using std::vector;
@@ -38,11 +37,7 @@ BaseBandSniffer::BaseBandSniffer(const std::string& filename) {
void BaseBandSniffer::AppendRecord(
std::unique_ptr<bredr_bb::BaseBandPacketBuilder> packet) {
- auto bytes = std::vector<uint8_t>();
- bytes.reserve(packet->size());
- bluetooth::packet::BitInserter i(bytes);
- packet->Serialize(i);
-
+ std::vector<uint8_t> bytes = packet->SerializeToBytes();
pcap::WriteRecordHeader(output_, bytes.size());
output_.write((char*)bytes.data(), bytes.size());
output_.flush();
@@ -143,7 +138,7 @@ void BaseBandSniffer::ReceiveLinkLayerPacket(
0, // eir
0, // sr
0, // sp
- uap, nap, page_view.GetClassOfDevice().ToUint32(),
+ uap, nap, page_view.GetClassOfDevice(),
1, // lt_addr
0, // clk
0, // page_scan_mode
@@ -152,9 +147,7 @@ void BaseBandSniffer::ReceiveLinkLayerPacket(
} else if (packet_type == model::packets::PacketType::LMP) {
auto lmp_view = model::packets::LmpView::Create(packet);
ASSERT(lmp_view.IsValid());
- auto lmp_bytes = std::vector<uint8_t>(lmp_view.GetPayload().begin(),
- lmp_view.GetPayload().end());
-
+ auto lmp_bytes = lmp_view.GetPayload();
uint8_t bt_packet_type = 0b0011; // DM1
AppendRecord(bredr_bb::DM1AclPacketBuilder::Create(
@@ -165,7 +158,7 @@ void BaseBandSniffer::ReceiveLinkLayerPacket(
flags,
0x3, // llid
1, // flow
- std::make_unique<bluetooth::packet::RawBuilder>(lmp_bytes),
+ std::move(lmp_bytes),
0 // crc
));
}
diff --git a/tools/rootcanal/model/devices/baseband_sniffer.h b/tools/rootcanal/model/devices/baseband_sniffer.h
index 496d15312a..d27619946e 100644
--- a/tools/rootcanal/model/devices/baseband_sniffer.h
+++ b/tools/rootcanal/model/devices/baseband_sniffer.h
@@ -24,9 +24,7 @@
namespace rootcanal {
namespace bredr_bb {
-namespace {
class BaseBandPacketBuilder;
-}
} // namespace bredr_bb
using ::bluetooth::hci::Address;
diff --git a/tools/rootcanal/model/devices/beacon.h b/tools/rootcanal/model/devices/beacon.h
index e83bdfdcd4..5a387bc299 100644
--- a/tools/rootcanal/model/devices/beacon.h
+++ b/tools/rootcanal/model/devices/beacon.h
@@ -18,6 +18,7 @@
#include <chrono>
#include <cstdint>
+#include <memory>
#include <vector>
#include "device.h"
diff --git a/tools/rootcanal/model/devices/device.cc b/tools/rootcanal/model/devices/device.cc
index bfe86b056a..e7aeabbd9f 100644
--- a/tools/rootcanal/model/devices/device.cc
+++ b/tools/rootcanal/model/devices/device.cc
@@ -18,8 +18,12 @@
#include <vector>
+#include "log.h"
+
namespace rootcanal {
+Device::Device() { ASSERT(Address::FromString("BB:BB:BB:BB:BB:AD", address_)); }
+
std::string Device::ToString() const {
return GetTypeString() + "@" + address_.ToString();
}
diff --git a/tools/rootcanal/model/devices/device.h b/tools/rootcanal/model/devices/device.h
index a7fb13887c..c9deec67bc 100644
--- a/tools/rootcanal/model/devices/device.h
+++ b/tools/rootcanal/model/devices/device.h
@@ -18,6 +18,7 @@
#include <chrono>
#include <cstdint>
+#include <functional>
#include <map>
#include <string>
#include <vector>
@@ -34,7 +35,7 @@ using ::bluetooth::hci::Address;
// - Provide Get*() and Set*() functions for device attributes.
class Device {
public:
- Device() { ASSERT(Address::FromString("BB:BB:BB:BB:BB:AD", address_)); }
+ Device();
virtual ~Device() = default;
// Return a string representation of the type of device.
diff --git a/tools/rootcanal/model/devices/hci_device.cc b/tools/rootcanal/model/devices/hci_device.cc
index ef2abac20b..710bd3a598 100644
--- a/tools/rootcanal/model/devices/hci_device.cc
+++ b/tools/rootcanal/model/devices/hci_device.cc
@@ -54,33 +54,41 @@ HciDevice::HciDevice(std::shared_ptr<HciTransport> transport,
}));
RegisterEventChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
- transport_->SendEvent(*packet);
+ transport_->Send(PacketType::EVENT, *packet);
});
RegisterAclChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
- transport_->SendAcl(*packet);
+ transport_->Send(PacketType::ACL, *packet);
});
RegisterScoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
- transport_->SendSco(*packet);
+ transport_->Send(PacketType::SCO, *packet);
});
RegisterIsoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
- transport_->SendIso(*packet);
+ transport_->Send(PacketType::ISO, *packet);
});
transport_->RegisterCallbacks(
- [this](const std::shared_ptr<std::vector<uint8_t>> command) {
- HandleCommand(command);
- },
- [this](const std::shared_ptr<std::vector<uint8_t>> acl) {
- HandleAcl(acl);
- },
- [this](const std::shared_ptr<std::vector<uint8_t>> sco) {
- HandleSco(sco);
- },
- [this](const std::shared_ptr<std::vector<uint8_t>> iso) {
- HandleIso(iso);
+ [this](PacketType packet_type,
+ const std::shared_ptr<std::vector<uint8_t>> packet) {
+ switch (packet_type) {
+ case PacketType::COMMAND:
+ HandleCommand(packet);
+ break;
+ case PacketType::ACL:
+ HandleAcl(packet);
+ break;
+ case PacketType::SCO:
+ HandleSco(packet);
+ break;
+ case PacketType::ISO:
+ HandleIso(packet);
+ break;
+ default:
+ ASSERT(false);
+ break;
+ }
},
[this]() {
- INFO("HCI transport closed");
+ INFO(id_, "HCI transport closed");
Close();
});
}
diff --git a/tools/rootcanal/model/devices/link_layer_socket_device.cc b/tools/rootcanal/model/devices/link_layer_socket_device.cc
index 9936318f20..92de96e8e0 100644
--- a/tools/rootcanal/model/devices/link_layer_socket_device.cc
+++ b/tools/rootcanal/model/devices/link_layer_socket_device.cc
@@ -16,15 +16,12 @@
#include "link_layer_socket_device.h"
+#include <packet_runtime.h>
+
#include <type_traits> // for remove_extent_t
#include "log.h"
-#include "packet/bit_inserter.h" // for BitInserter
-#include "packet/iterator.h" // for Iterator
-#include "packet/packet_view.h" // for PacketView, kLittleEndian
-#include "packet/raw_builder.h" // for RawBuilder
-#include "packet/view.h" // for View
-#include "phy.h" // for Phy, Phy::Type
+#include "phy.h" // for Phy, Phy::Type
using std::vector;
@@ -55,9 +52,8 @@ void LinkLayerSocketDevice::Tick() {
offset_ += bytes_received;
return;
}
- bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> size(
- {bluetooth::packet::View(size_bytes_, 0, kSizeBytes)});
- bytes_left_ = size.begin().extract<uint32_t>();
+ pdl::packet::slice size(std::move(size_bytes_));
+ bytes_left_ = size.read_le<uint32_t>();
received_ = std::make_shared<std::vector<uint8_t>>(bytes_left_);
offset_ = 0;
receiving_size_ = false;
@@ -95,15 +91,12 @@ void LinkLayerSocketDevice::Close() {
void LinkLayerSocketDevice::ReceiveLinkLayerPacket(
model::packets::LinkLayerPacketView packet, Phy::Type /*type*/,
int8_t /*rssi*/) {
- auto size_packet = bluetooth::packet::RawBuilder();
- size_packet.AddOctets4(packet.size());
+ std::vector<uint8_t> packet_bytes = packet.bytes().bytes();
std::vector<uint8_t> size_bytes;
- bluetooth::packet::BitInserter bit_inserter(size_bytes);
- size_packet.Serialize(bit_inserter);
+ pdl::packet::Builder::write_le<uint32_t>(size_bytes, packet_bytes.size());
if (socket_->Send(size_bytes.data(), size_bytes.size()) == kSizeBytes) {
- std::vector<uint8_t> payload_bytes{packet.begin(), packet.end()};
- socket_->Send(payload_bytes.data(), payload_bytes.size());
+ socket_->Send(packet_bytes.data(), packet_bytes.size());
}
}
diff --git a/tools/rootcanal/model/hci/hci_sniffer.cc b/tools/rootcanal/model/hci/hci_sniffer.cc
index f0dbc139f0..ea3e2cbeac 100644
--- a/tools/rootcanal/model/hci/hci_sniffer.cc
+++ b/tools/rootcanal/model/hci/hci_sniffer.cc
@@ -73,32 +73,14 @@ void HciSniffer::AppendRecord(PacketDirection packet_direction,
output_->flush();
}
-void HciSniffer::RegisterCallbacks(PacketCallback command_callback,
- PacketCallback acl_callback,
- PacketCallback sco_callback,
- PacketCallback iso_callback,
+void HciSniffer::RegisterCallbacks(PacketCallback packet_callback,
CloseCallback close_callback) {
transport_->RegisterCallbacks(
- [this,
- command_callback](const std::shared_ptr<std::vector<uint8_t>> command) {
- AppendRecord(PacketDirection::HOST_TO_CONTROLLER, PacketType::COMMAND,
- *command);
- command_callback(command);
- },
- [this, acl_callback](const std::shared_ptr<std::vector<uint8_t>> acl) {
- AppendRecord(PacketDirection::HOST_TO_CONTROLLER, PacketType::ACL,
- *acl);
- acl_callback(acl);
- },
- [this, sco_callback](const std::shared_ptr<std::vector<uint8_t>> sco) {
- AppendRecord(PacketDirection::HOST_TO_CONTROLLER, PacketType::SCO,
- *sco);
- sco_callback(sco);
- },
- [this, iso_callback](const std::shared_ptr<std::vector<uint8_t>> iso) {
- AppendRecord(PacketDirection::HOST_TO_CONTROLLER, PacketType::ISO,
- *iso);
- iso_callback(iso);
+ [this, packet_callback](
+ PacketType packet_type,
+ const std::shared_ptr<std::vector<uint8_t>> packet) {
+ AppendRecord(PacketDirection::HOST_TO_CONTROLLER, packet_type, *packet);
+ packet_callback(packet_type, packet);
},
close_callback);
}
@@ -112,23 +94,10 @@ void HciSniffer::Close() {
}
}
-void HciSniffer::SendEvent(const std::vector<uint8_t>& packet) {
- AppendRecord(PacketDirection::CONTROLLER_TO_HOST, PacketType::EVENT, packet);
- transport_->SendEvent(packet);
-}
-
-void HciSniffer::SendAcl(const std::vector<uint8_t>& packet) {
- AppendRecord(PacketDirection::CONTROLLER_TO_HOST, PacketType::ACL, packet);
- transport_->SendAcl(packet);
-}
-
-void HciSniffer::SendSco(const std::vector<uint8_t>& packet) {
- AppendRecord(PacketDirection::CONTROLLER_TO_HOST, PacketType::SCO, packet);
- transport_->SendSco(packet);
+void HciSniffer::Send(PacketType packet_type,
+ const std::vector<uint8_t>& packet) {
+ AppendRecord(PacketDirection::CONTROLLER_TO_HOST, packet_type, packet);
+ transport_->Send(packet_type, packet);
}
-void HciSniffer::SendIso(const std::vector<uint8_t>& packet) {
- AppendRecord(PacketDirection::CONTROLLER_TO_HOST, PacketType::ISO, packet);
- transport_->SendIso(packet);
-}
} // namespace rootcanal
diff --git a/tools/rootcanal/model/hci/hci_sniffer.h b/tools/rootcanal/model/hci/hci_sniffer.h
index 206414e2f1..5ad47fbe7b 100644
--- a/tools/rootcanal/model/hci/hci_sniffer.h
+++ b/tools/rootcanal/model/hci/hci_sniffer.h
@@ -52,22 +52,13 @@ class HciSniffer : public HciTransport {
void SetOutputStream(std::shared_ptr<std::ostream> outputStream);
void SetPcapFilter(std::shared_ptr<PcapFilter> filter);
- void SendEvent(const std::vector<uint8_t>& packet) override;
+ void Send(PacketType packet_type,
+ const std::vector<uint8_t>& packet) override;
- void SendAcl(const std::vector<uint8_t>& packet) override;
-
- void SendSco(const std::vector<uint8_t>& packet) override;
-
- void SendIso(const std::vector<uint8_t>& packet) override;
-
- void RegisterCallbacks(PacketCallback command_callback,
- PacketCallback acl_callback,
- PacketCallback sco_callback,
- PacketCallback iso_callback,
+ void RegisterCallbacks(PacketCallback packet_callback,
CloseCallback close_callback) override;
void Tick() override;
-
void Close() override;
private:
diff --git a/tools/rootcanal/model/hci/hci_socket_transport.cc b/tools/rootcanal/model/hci/hci_socket_transport.cc
index c018b0ef1a..82632e466d 100644
--- a/tools/rootcanal/model/hci/hci_socket_transport.cc
+++ b/tools/rootcanal/model/hci/hci_socket_transport.cc
@@ -23,44 +23,41 @@ namespace rootcanal {
HciSocketTransport::HciSocketTransport(std::shared_ptr<AsyncDataChannel> socket)
: socket_(socket) {}
-void HciSocketTransport::RegisterCallbacks(PacketCallback command_callback,
- PacketCallback acl_callback,
- PacketCallback sco_callback,
- PacketCallback iso_callback,
+void HciSocketTransport::RegisterCallbacks(PacketCallback packet_callback,
CloseCallback close_callback) {
// TODO: Avoid the copy here by using new buffer in H4DataChannel
h4_ = H4DataChannelPacketizer(
socket_,
- [command_callback](const std::vector<uint8_t>& raw_command) {
+ [packet_callback](const std::vector<uint8_t>& raw_command) {
std::shared_ptr<std::vector<uint8_t>> packet_copy =
std::make_shared<std::vector<uint8_t>>(raw_command);
- command_callback(packet_copy);
+ packet_callback(PacketType::COMMAND, packet_copy);
},
[](const std::vector<uint8_t>&) {
FATAL("Unexpected Event in HciSocketTransport!");
},
- [acl_callback](const std::vector<uint8_t>& raw_acl) {
+ [packet_callback](const std::vector<uint8_t>& raw_acl) {
std::shared_ptr<std::vector<uint8_t>> packet_copy =
std::make_shared<std::vector<uint8_t>>(raw_acl);
- acl_callback(packet_copy);
+ packet_callback(PacketType::ACL, packet_copy);
},
- [sco_callback](const std::vector<uint8_t>& raw_sco) {
+ [packet_callback](const std::vector<uint8_t>& raw_sco) {
std::shared_ptr<std::vector<uint8_t>> packet_copy =
std::make_shared<std::vector<uint8_t>>(raw_sco);
- sco_callback(packet_copy);
+ packet_callback(PacketType::SCO, packet_copy);
},
- [iso_callback](const std::vector<uint8_t>& raw_iso) {
+ [packet_callback](const std::vector<uint8_t>& raw_iso) {
std::shared_ptr<std::vector<uint8_t>> packet_copy =
std::make_shared<std::vector<uint8_t>>(raw_iso);
- iso_callback(packet_copy);
+ packet_callback(PacketType::ISO, packet_copy);
},
close_callback);
}
void HciSocketTransport::Tick() { h4_.OnDataReady(socket_); }
-void HciSocketTransport::SendHci(PacketType packet_type,
- const std::vector<uint8_t>& packet) {
+void HciSocketTransport::Send(PacketType packet_type,
+ const std::vector<uint8_t>& packet) {
if (!socket_ || !socket_->Connected()) {
INFO("Closed socket. Dropping packet of type {}", packet_type);
return;
@@ -69,22 +66,6 @@ void HciSocketTransport::SendHci(PacketType packet_type,
h4_.Send(type, packet.data(), packet.size());
}
-void HciSocketTransport::SendEvent(const std::vector<uint8_t>& packet) {
- SendHci(PacketType::EVENT, packet);
-}
-
-void HciSocketTransport::SendAcl(const std::vector<uint8_t>& packet) {
- SendHci(PacketType::ACL, packet);
-}
-
-void HciSocketTransport::SendSco(const std::vector<uint8_t>& packet) {
- SendHci(PacketType::SCO, packet);
-}
-
-void HciSocketTransport::SendIso(const std::vector<uint8_t>& packet) {
- SendHci(PacketType::ISO, packet);
-}
-
void HciSocketTransport::Close() { socket_->Close(); }
} // namespace rootcanal
diff --git a/tools/rootcanal/model/hci/hci_socket_transport.h b/tools/rootcanal/model/hci/hci_socket_transport.h
index 6bd2d8e7e5..e6e4a356ac 100644
--- a/tools/rootcanal/model/hci/hci_socket_transport.h
+++ b/tools/rootcanal/model/hci/hci_socket_transport.h
@@ -36,27 +36,16 @@ class HciSocketTransport : public HciTransport {
return std::make_shared<HciSocketTransport>(socket);
}
- void SendEvent(const std::vector<uint8_t>& packet) override;
+ void Send(PacketType packet_type,
+ const std::vector<uint8_t>& packet) override;
- void SendAcl(const std::vector<uint8_t>& packet) override;
-
- void SendSco(const std::vector<uint8_t>& packet) override;
-
- void SendIso(const std::vector<uint8_t>& packet) override;
-
- void RegisterCallbacks(PacketCallback command_callback,
- PacketCallback acl_callback,
- PacketCallback sco_callback,
- PacketCallback iso_callback,
+ void RegisterCallbacks(PacketCallback packet_callback,
CloseCallback close_callback) override;
void Tick() override;
-
void Close() override;
private:
- void SendHci(PacketType packet_type, const std::vector<uint8_t>& packet);
-
std::shared_ptr<AsyncDataChannel> socket_;
H4DataChannelPacketizer h4_{socket_,
[](const std::vector<uint8_t>&) {},
diff --git a/tools/rootcanal/model/hci/hci_transport.h b/tools/rootcanal/model/hci/hci_transport.h
index e682801255..5aa10f160c 100644
--- a/tools/rootcanal/model/hci/hci_transport.h
+++ b/tools/rootcanal/model/hci/hci_transport.h
@@ -20,32 +20,28 @@
#include <memory>
#include <vector>
+#include "model/hci/h4.h"
+
namespace rootcanal {
-using PacketCallback =
- std::function<void(const std::shared_ptr<std::vector<uint8_t>>)>;
+using PacketCallback = std::function<void(
+ PacketType, const std::shared_ptr<std::vector<uint8_t>>)>;
using CloseCallback = std::function<void()>;
class HciTransport {
public:
virtual ~HciTransport() = default;
- virtual void SendEvent(const std::vector<uint8_t>& packet) = 0;
-
- virtual void SendAcl(const std::vector<uint8_t>& packet) = 0;
-
- virtual void SendSco(const std::vector<uint8_t>& packet) = 0;
+ /// Send the input HCI packet with the selected H4 packet type.
+ /// The packet data contains the H4 header but not the IDC byte.
+ virtual void Send(PacketType packet_type,
+ std::vector<uint8_t> const& packet) = 0;
- virtual void SendIso(const std::vector<uint8_t>& packet) = 0;
-
- virtual void RegisterCallbacks(PacketCallback command_callback,
- PacketCallback acl_callback,
- PacketCallback sco_callback,
- PacketCallback iso_callback,
+ /// Register the handler for received HCI packets.
+ virtual void RegisterCallbacks(PacketCallback packet_callback,
CloseCallback close_callback) = 0;
virtual void Tick() = 0;
-
virtual void Close() = 0;
};
diff --git a/tools/rootcanal/model/setup/phy_device.cc b/tools/rootcanal/model/setup/phy_device.cc
index 25718614f5..bd2cd78250 100644
--- a/tools/rootcanal/model/setup/phy_device.cc
+++ b/tools/rootcanal/model/setup/phy_device.cc
@@ -16,6 +16,8 @@
#include "phy_device.h"
+#include <log.h>
+
#include "phy_layer.h"
namespace rootcanal {
@@ -45,8 +47,7 @@ void PhyDevice::Receive(std::vector<uint8_t> const& packet, Phy::Type type,
std::make_shared<std::vector<uint8_t>>(packet);
model::packets::LinkLayerPacketView packet_view =
model::packets::LinkLayerPacketView::Create(
- bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(
- packet_copy));
+ pdl::packet::slice(packet_copy));
if (packet_view.IsValid()) {
device_->ReceiveLinkLayerPacket(std::move(packet_view), type, rssi);
} else {
diff --git a/tools/rootcanal/model/setup/test_model.cc b/tools/rootcanal/model/setup/test_model.cc
index b80fde6cd3..56ac1694fc 100644
--- a/tools/rootcanal/model/setup/test_model.cc
+++ b/tools/rootcanal/model/setup/test_model.cc
@@ -208,7 +208,7 @@ PhyDevice::Identifier TestModel::AddHciConnection(
}};
device->SetAddress(bluetooth_address);
- INFO("Initialized device with address {}", bluetooth_address.ToString());
+ INFO(device->id_, "Initialized device with address {}", bluetooth_address.ToString());
for (auto& [_, phy_layer] : phy_layers_) {
phy_layer->Register(phy_devices_[device_id]);
diff --git a/tools/rootcanal/packets/hci/hci_packets.pdl b/tools/rootcanal/packets/hci_packets.pdl
index 25ecfe435f..a99774499b 100644
--- a/tools/rootcanal/packets/hci/hci_packets.pdl
+++ b/tools/rootcanal/packets/hci_packets.pdl
@@ -1,7 +1,6 @@
little_endian_packets
custom_field Address : 48 "hci/"
-custom_field ClassOfDevice : 24 "hci/"
enum Enable : 8 {
DISABLED = 0x00,
@@ -1745,8 +1744,8 @@ packet SetEventFilterInquiryResultAllDevices : SetEventFilterInquiryResult (filt
}
packet SetEventFilterInquiryResultClassOfDevice : SetEventFilterInquiryResult (filter_condition_type = CLASS_OF_DEVICE) {
- class_of_device : ClassOfDevice,
- class_of_device_mask : ClassOfDevice,
+ class_of_device : 24,
+ class_of_device_mask : 24,
}
packet SetEventFilterInquiryResultAddress : SetEventFilterInquiryResult (filter_condition_type = ADDRESS) {
@@ -1769,8 +1768,8 @@ packet SetEventFilterConnectionSetupAllDevices : SetEventFilterConnectionSetup (
}
packet SetEventFilterConnectionSetupClassOfDevice : SetEventFilterConnectionSetup (filter_condition_type = CLASS_OF_DEVICE) {
- class_of_device : ClassOfDevice,
- class_of_device_mask : ClassOfDevice,
+ class_of_device : 24,
+ class_of_device_mask : 24,
auto_accept_flag : AutoAcceptFlag,
}
@@ -2022,11 +2021,11 @@ packet ReadClassOfDevice : Command (op_code = READ_CLASS_OF_DEVICE) {
packet ReadClassOfDeviceComplete : CommandComplete (command_op_code = READ_CLASS_OF_DEVICE) {
status : ErrorCode,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
}
packet WriteClassOfDevice : Command (op_code = WRITE_CLASS_OF_DEVICE) {
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
}
packet WriteClassOfDeviceComplete : CommandComplete (command_op_code = WRITE_CLASS_OF_DEVICE) {
@@ -3280,12 +3279,12 @@ packet LeCreateConnection : Command (op_code = LE_CREATE_CONNECTION) {
peer_address_type : AddressType,
peer_address : Address,
own_address_type : OwnAddressType,
- conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_latency : 16, // 0x0006-0x01F3
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
- minimum_ce_length : 16, // 0.625ms
- maximum_ce_length : 16, // 0.625ms
+ min_ce_length : 16, // 0.625ms
+ max_ce_length : 16, // 0.625ms
}
packet LeCreateConnectionStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION) {
@@ -3348,12 +3347,12 @@ packet LeRemoveDeviceFromFilterAcceptListComplete : CommandComplete (command_op_
packet LeConnectionUpdate : Command (op_code = LE_CONNECTION_UPDATE) {
connection_handle : 12,
_reserved_ : 4,
- conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_latency : 16, // 0x0006-0x01F3
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
- minimum_ce_length : 16, // 0.625ms
- maximum_ce_length : 16, // 0.625ms
+ min_ce_length : 16, // 0.625ms
+ max_ce_length : 16, // 0.625ms
}
packet LeConnectionUpdateStatus : CommandStatus (command_op_code = LE_CONNECTION_UPDATE) {
@@ -3989,7 +3988,7 @@ packet LeSetPeriodicAdvertisingEnableComplete : CommandComplete (command_op_code
status : ErrorCode,
}
-struct PhyScanParameters {
+struct ScanningPhyParameters {
le_scan_type : LeScanType,
le_scan_interval : 16, // 0x0004-0xFFFF Default 0x10 (10ms)
le_scan_window : 16, // 0x004-0xFFFF Default 0x10 (10ms)
@@ -3999,7 +3998,7 @@ packet LeSetExtendedScanParameters : Command (op_code = LE_SET_EXTENDED_SCAN_PAR
own_address_type : OwnAddressType,
scanning_filter_policy : LeScanningFilterPolicy,
scanning_phys : 8,
- parameters : PhyScanParameters[],
+ scanning_phy_parameters : ScanningPhyParameters[],
}
test LeSetExtendedScanParameters {
@@ -4041,12 +4040,12 @@ test LeSetExtendedScanEnableComplete {
"\x0e\x04\x01\x42\x20\x00",
}
-struct LeCreateConnPhyScanParameters {
+struct InitiatingPhyParameters {
scan_interval : 16, // 0x0004-0xFFFF
scan_window : 16, // < = LeScanInterval
- conn_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
- conn_latency : 16, // 0x0006-0x01F3
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
min_ce_length : 16, // 0.625ms
max_ce_length : 16, // 0.625ms
@@ -4055,10 +4054,10 @@ struct LeCreateConnPhyScanParameters {
packet LeExtendedCreateConnection : Command (op_code = LE_EXTENDED_CREATE_CONNECTION) {
initiator_filter_policy : InitiatorFilterPolicy,
own_address_type : OwnAddressType,
- peer_address_type : AddressType,
+ peer_address_type : PeerAddressType,
peer_address : Address,
initiating_phys : 8,
- phy_scan_parameters : LeCreateConnPhyScanParameters[],
+ initiating_phy_parameters : InitiatingPhyParameters[],
}
test LeExtendedCreateConnection {
@@ -5190,7 +5189,7 @@ struct InquiryResponse {
page_scan_repetition_mode : PageScanRepetitionMode,
_reserved_ : 8,
_reserved_ : 8,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
clock_offset : 15,
_reserved_ : 1,
}
@@ -5223,7 +5222,7 @@ enum ConnectionRequestLinkType : 8 {
packet ConnectionRequest : Event (event_code = CONNECTION_REQUEST) {
bd_addr : Address,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
link_type : ConnectionRequestLinkType,
}
@@ -5427,7 +5426,7 @@ struct InquiryResponseWithRssi {
address : Address,
page_scan_repetition_mode : PageScanRepetitionMode,
_reserved_ : 8,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
clock_offset : 15,
_reserved_ : 1,
rssi : 8,
@@ -5514,7 +5513,7 @@ packet ExtendedInquiryResult : Event (event_code = EXTENDED_INQUIRY_RESULT) {
address : Address,
page_scan_repetition_mode : PageScanRepetitionMode,
_reserved_ : 8,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
clock_offset : 15,
_reserved_ : 1,
rssi : 8,
@@ -5626,8 +5625,8 @@ packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE)
role : Role,
peer_address_type : AddressType,
peer_address : Address,
- conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
- conn_latency : 16, // Number of connection events
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16,
supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
central_clock_accuracy : ClockAccuracy,
}
@@ -5658,8 +5657,8 @@ packet LeConnectionUpdateComplete : LeMetaEvent (subevent_code = CONNECTION_UPDA
status : ErrorCode,
connection_handle : 12,
_reserved_ : 4,
- conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
- conn_latency : 16, // Number of connection events
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16, // Number of connection events
supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
}
@@ -5714,8 +5713,8 @@ packet LeEnhancedConnectionComplete : LeMetaEvent (subevent_code = ENHANCED_CONN
peer_address : Address,
local_resolvable_private_address : Address,
peer_resolvable_private_address : Address,
- conn_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
- conn_latency : 16, // Number of connection events
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16,
supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
central_clock_accuracy : ClockAccuracy,
}
diff --git a/tools/rootcanal/packets/link_layer_packets.pdl b/tools/rootcanal/packets/link_layer_packets.pdl
index 39b4ed2f5e..d2fcbfca4f 100644
--- a/tools/rootcanal/packets/link_layer_packets.pdl
+++ b/tools/rootcanal/packets/link_layer_packets.pdl
@@ -1,7 +1,6 @@
little_endian_packets
custom_field Address : 48 "hci/"
-custom_field ClassOfDevice : 24 "hci/"
enum PacketType : 8 {
UNKNOWN = 0x00,
@@ -110,7 +109,7 @@ packet Inquiry : LinkLayerPacket (type = INQUIRY) {
packet BasicInquiryResponse : LinkLayerPacket(type = INQUIRY_RESPONSE) {
inquiry_type : InquiryType,
page_scan_repetition_mode : 8,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
clock_offset : 15,
_reserved_ : 1,
_body_,
@@ -213,7 +212,7 @@ packet LeScanResponse : LinkLayerPacket (type = LE_SCAN_RESPONSE) {
}
packet Page : LinkLayerPacket (type = PAGE) {
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
allow_role_switch : 8,
}
@@ -323,7 +322,7 @@ packet ScoConnectionRequest : LinkLayerPacket (type = SCO_CONNECTION_REQUEST) {
_reserved_ : 6,
retransmission_effort : 8,
packet_type : 16,
- class_of_device : ClassOfDevice,
+ class_of_device : 24,
}
packet ScoConnectionResponse : LinkLayerPacket (type = SCO_CONNECTION_RESPONSE) {
diff --git a/tools/rootcanal/py/controller.py b/tools/rootcanal/py/controller.py
index 9c3e2a85fd..b4bbecaac0 100644
--- a/tools/rootcanal/py/controller.py
+++ b/tools/rootcanal/py/controller.py
@@ -373,13 +373,13 @@ class ControllerTest(unittest.IsolatedAsyncioTestCase):
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
initiating_phys=0x1,
- phy_scan_parameters=[
- hci.LeCreateConnPhyScanParameters(
+ initiating_phy_parameters=[
+ hci.InitiatingPhyParameters(
scan_interval=0x200,
scan_window=0x100,
- conn_interval_min=0x200,
- conn_interval_max=0x200,
- conn_latency=0x6,
+ connection_interval_min=0x200,
+ connection_interval_max=0x200,
+ max_latency=0x6,
supervision_timeout=0xc80,
min_ce_length=0,
max_ce_length=0,
@@ -418,8 +418,8 @@ class ControllerTest(unittest.IsolatedAsyncioTestCase):
role=hci.Role.CENTRAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x6,
+ connection_interval=0x200,
+ peripheral_latency=0x6,
supervision_timeout=0xc80,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
@@ -470,8 +470,8 @@ class ControllerTest(unittest.IsolatedAsyncioTestCase):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x200,
+ connection_interval=0x200,
+ peripheral_latency=0x200,
supervision_timeout=0x200,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/rust/CMakeLists.txt b/tools/rootcanal/rust/CMakeLists.txt
index 48751eacff..2e7c7cbaa9 100644
--- a/tools/rootcanal/rust/CMakeLists.txt
+++ b/tools/rootcanal/rust/CMakeLists.txt
@@ -2,7 +2,7 @@ message(STATUS "Enabling bluetooth LMP module.")
pdl_gen(
NAME hci_packets_rs
- INPUT ${ROOTCANAL_ROOT}/packets/hci/hci_packets.pdl
+ INPUT ${ROOTCANAL_ROOT}/packets/hci_packets.pdl
OUTPUT hci_packets.rs
LANG rust)
diff --git a/tools/rootcanal/rust/build.rs b/tools/rootcanal/rust/build.rs
index cd927f6ee2..793e1f93e1 100644
--- a/tools/rootcanal/rust/build.rs
+++ b/tools/rootcanal/rust/build.rs
@@ -31,7 +31,7 @@ fn main() {
install_generated_module(
"hci_packets.rs",
"HCI_PACKETS_PREBUILT",
- &PathBuf::from("../packets/hci/hci_packets.pdl").canonicalize().unwrap(),
+ &PathBuf::from("../packets/hci_packets.pdl").canonicalize().unwrap(),
);
}
@@ -60,8 +60,8 @@ fn generate_module(in_file: &PathBuf) {
// Find the pdl tool. Expecting it at CARGO_HOME/bin
let pdl = match env::var("CARGO_HOME") {
- Ok(dir) => PathBuf::from(dir).join("bin").join("pdl"),
- Err(_) => PathBuf::from("pdl"),
+ Ok(dir) => PathBuf::from(dir).join("bin").join("pdlc"),
+ Err(_) => PathBuf::from("pdlc"),
};
if !Path::new(pdl.as_os_str()).exists() {
diff --git a/tools/rootcanal/rust/src/ffi.rs b/tools/rootcanal/rust/src/ffi.rs
index 18a229c8b0..0024bbc707 100644
--- a/tools/rootcanal/rust/src/ffi.rs
+++ b/tools/rootcanal/rust/src/ffi.rs
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// TODO(b/290018030): Remove this and add proper safety comments.
+#![allow(clippy::undocumented_unsafe_blocks)]
+
use std::convert::TryFrom;
use std::mem::ManuallyDrop;
use std::rc::Rc;
@@ -105,8 +108,8 @@ pub unsafe extern "C" fn link_manager_add_link(
lm: *const LinkManager,
peer: *const [u8; 6],
) -> bool {
- let lm = ManuallyDrop::new(Rc::from_raw(lm));
- lm.add_link(hci::Address::from(&*peer)).is_ok()
+ let lm = ManuallyDrop::new(unsafe { Rc::from_raw(lm) });
+ unsafe { lm.add_link(hci::Address::from(&*peer)).is_ok() }
}
/// Unregister a link with a peer inside the link manager
@@ -123,8 +126,8 @@ pub unsafe extern "C" fn link_manager_remove_link(
lm: *const LinkManager,
peer: *const [u8; 6],
) -> bool {
- let lm = ManuallyDrop::new(Rc::from_raw(lm));
- lm.remove_link(hci::Address::from(&*peer)).is_ok()
+ let lm = ManuallyDrop::new(unsafe { Rc::from_raw(lm) });
+ unsafe { lm.remove_link(hci::Address::from(&*peer)).is_ok() }
}
/// Run the Link Manager procedures
@@ -135,7 +138,7 @@ pub unsafe extern "C" fn link_manager_remove_link(
/// - `lm` must be a valid pointer
#[no_mangle]
pub unsafe extern "C" fn link_manager_tick(lm: *const LinkManager) {
- let lm = ManuallyDrop::new(Rc::from_raw(lm));
+ let lm = ManuallyDrop::new(unsafe { Rc::from_raw(lm) });
lm.as_ref().tick();
}
@@ -155,8 +158,8 @@ pub unsafe extern "C" fn link_manager_ingest_hci(
data: *const u8,
len: usize,
) -> bool {
- let lm = ManuallyDrop::new(Rc::from_raw(lm));
- let data = slice::from_raw_parts(data, len);
+ let lm = ManuallyDrop::new(unsafe { Rc::from_raw(lm) });
+ let data = unsafe { slice::from_raw_parts(data, len) };
if let Ok(packet) = hci::Command::parse(data) {
lm.ingest_hci(packet).is_ok()
@@ -184,11 +187,11 @@ pub unsafe extern "C" fn link_manager_ingest_lmp(
data: *const u8,
len: usize,
) -> bool {
- let lm = ManuallyDrop::new(Rc::from_raw(lm));
- let data = slice::from_raw_parts(data, len);
+ let lm = ManuallyDrop::new(unsafe { Rc::from_raw(lm) });
+ let data = unsafe { slice::from_raw_parts(data, len) };
if let Ok(packet) = lmp::LmpPacket::parse(data) {
- lm.ingest_lmp(hci::Address::from(&*from), packet).is_ok()
+ unsafe { lm.ingest_lmp(hci::Address::from(&*from), packet).is_ok() }
} else {
false
}
@@ -202,7 +205,9 @@ pub unsafe extern "C" fn link_manager_ingest_lmp(
/// - `lm` must be a valid pointers and must not be reused afterwards
#[no_mangle]
pub unsafe extern "C" fn link_manager_destroy(lm: *const LinkManager) {
- let _ = Rc::from_raw(lm);
+ unsafe {
+ let _ = Rc::from_raw(lm);
+ }
}
/// Create a new link manager instance
@@ -231,10 +236,10 @@ pub unsafe extern "C" fn link_layer_add_link(
peer_address: *const [u8; 6],
role: u8,
) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
let role = hci::Role::try_from(role).unwrap_or(hci::Role::Peripheral);
- ll.add_link(handle, hci::Address::from(&*peer_address), role).is_ok()
+ unsafe { ll.add_link(handle, hci::Address::from(&*peer_address), role).is_ok() }
}
/// Unregister a link with a peer inside the link layer
@@ -248,7 +253,7 @@ pub unsafe extern "C" fn link_layer_add_link(
/// - `peer` must be valid for reads for 6 bytes
#[no_mangle]
pub unsafe extern "C" fn link_layer_remove_link(ll: *const LinkLayer, handle: u16) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
ll.remove_link(handle).is_ok()
}
@@ -261,7 +266,7 @@ pub unsafe extern "C" fn link_layer_remove_link(ll: *const LinkLayer, handle: u1
/// - `ll` must be a valid pointer
#[no_mangle]
pub unsafe extern "C" fn link_layer_tick(ll: *const LinkLayer) {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
ll.tick();
}
@@ -282,9 +287,9 @@ pub unsafe extern "C" fn link_layer_ingest_hci(
data: *const u8,
len: usize,
) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
- let data = slice::from_raw_parts(data, len);
+ let data = unsafe { slice::from_raw_parts(data, len) };
if let Ok(packet) = hci::Command::parse(data) {
ll.ingest_hci(packet).is_ok()
@@ -311,9 +316,9 @@ pub unsafe extern "C" fn link_layer_ingest_llcp(
data: *const u8,
len: usize,
) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
- let data = slice::from_raw_parts(data, len);
+ let data = unsafe { slice::from_raw_parts(data, len) };
if let Ok(packet) = llcp::LlcpPacket::parse(data) {
ll.ingest_llcp(handle, packet).is_ok()
@@ -340,10 +345,10 @@ pub unsafe extern "C" fn link_layer_get_cis_connection_handle(
cis_id: u8,
cis_connection_handle: *mut u16,
) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
ll.get_cis_connection_handle(cig_id, cis_id)
- .map(|handle| {
+ .map(|handle| unsafe {
*cis_connection_handle = handle;
})
.is_some()
@@ -369,16 +374,20 @@ pub unsafe extern "C" fn link_layer_get_cis_information(
cis_id: *mut u8,
max_sdu_tx: *mut u16,
) -> bool {
- let mut ll = ManuallyDrop::new(Rc::from_raw(ll));
+ let mut ll = ManuallyDrop::new(unsafe { Rc::from_raw(ll) });
let ll = Rc::get_mut(&mut ll).unwrap();
ll.get_cis(cis_connection_handle)
.map(|cis| {
if let Some(handle) = cis.acl_connection_handle {
- *acl_connection_handle = handle;
+ unsafe {
+ *acl_connection_handle = handle;
+ }
+ }
+ unsafe {
+ *cig_id = cis.cig_id;
+ *cis_id = cis.cis_id;
+ *max_sdu_tx = cis.max_sdu_tx().unwrap_or(0);
}
- *cig_id = cis.cig_id;
- *cis_id = cis.cis_id;
- *max_sdu_tx = cis.max_sdu_tx().unwrap_or(0);
})
.is_some()
}
@@ -391,5 +400,7 @@ pub unsafe extern "C" fn link_layer_get_cis_information(
/// - `ll` must be a valid pointers and must not be reused afterwards
#[no_mangle]
pub unsafe extern "C" fn link_layer_destroy(ll: *const LinkLayer) {
- let _ = Rc::from_raw(ll);
+ unsafe {
+ let _ = Rc::from_raw(ll);
+ }
}
diff --git a/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs b/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs
index 0c3c4e31a8..543219691f 100644
--- a/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs
+++ b/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs
@@ -143,11 +143,14 @@ async fn receive_public_key(ctx: &impl Context, transaction_id: u8) -> PublicKey
const COMMITMENT_VALUE_SIZE: usize = 16;
const NONCE_SIZE: usize = 16;
-async fn receive_commitment(ctx: &impl Context, skip_first: bool) {
- let commitment_value = [0; COMMITMENT_VALUE_SIZE];
+fn build_commitment(_ctx: &impl Context) -> [u8; COMMITMENT_VALUE_SIZE] {
+ [0; COMMITMENT_VALUE_SIZE]
+}
+
+async fn receive_commitment(ctx: &impl Context, confirm: Option<lmp::SimplePairingConfirm>) {
+ let commitment_value = build_commitment(ctx);
- if !skip_first {
- let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ if let Some(confirm) = confirm {
if confirm.get_commitment_value() != &commitment_value {
todo!();
}
@@ -177,16 +180,8 @@ async fn receive_commitment(ctx: &impl Context, skip_first: bool) {
.await;
}
-async fn send_commitment(ctx: &impl Context, skip_first: bool) {
- let commitment_value = [0; COMMITMENT_VALUE_SIZE];
-
- if !skip_first {
- ctx.send_lmp_packet(
- lmp::SimplePairingConfirmBuilder { transaction_id: 0, commitment_value }.build(),
- );
- }
-
- let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+async fn send_commitment(ctx: &impl Context, confirm: lmp::SimplePairingConfirm) {
+ let commitment_value = build_commitment(ctx);
if confirm.get_commitment_value() != &commitment_value {
todo!();
@@ -437,14 +432,33 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
match auth_method {
AuthenticationMethod::NumericComparisonJustWork
| AuthenticationMethod::NumericComparisonUserConfirm => {
- send_commitment(ctx, true).await;
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ send_commitment(ctx, confirm).await;
- user_confirmation_request(ctx).await?;
+ if user_confirmation_request(ctx).await.is_err() {
+ ctx.send_lmp_packet(
+ lmp::NumericComparisonFailedBuilder { transaction_id: 0 }.build(),
+ );
+ Err(())?;
+ }
Ok(())
}
AuthenticationMethod::PasskeyEntry => {
- if initiator.io_capability == hci::IoCapability::KeyboardOnly {
- user_passkey_request(ctx).await?;
+ let confirm = if initiator.io_capability == hci::IoCapability::KeyboardOnly {
+ if user_passkey_request(ctx).await.is_err() {
+ ctx.send_lmp_packet(
+ lmp::PasskeyFailedBuilder { transaction_id: 0 }.build(),
+ );
+ Err(())?;
+ }
+ ctx.send_lmp_packet(
+ lmp::SimplePairingConfirmBuilder {
+ transaction_id: 0,
+ commitment_value: build_commitment(ctx),
+ }
+ .build(),
+ );
+ ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await
} else {
ctx.send_hci_event(
hci::UserPasskeyNotificationBuilder {
@@ -453,9 +467,32 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
}
.build(),
);
- }
- for _ in 0..PASSKEY_ENTRY_REPEAT_NUMBER {
- send_commitment(ctx, false).await;
+ ctx.send_lmp_packet(
+ lmp::SimplePairingConfirmBuilder {
+ transaction_id: 0,
+ commitment_value: build_commitment(ctx),
+ }
+ .build(),
+ );
+ match ctx
+ .receive_lmp_packet::<Either<lmp::SimplePairingConfirm, lmp::NotAccepted>>()
+ .await
+ {
+ Either::Left(confirm) => confirm,
+ Either::Right(_) => Err(())?,
+ }
+ };
+ send_commitment(ctx, confirm).await;
+ for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER {
+ ctx.send_lmp_packet(
+ lmp::SimplePairingConfirmBuilder {
+ transaction_id: 0,
+ commitment_value: build_commitment(ctx),
+ }
+ .build(),
+ );
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ send_commitment(ctx, confirm).await;
}
Ok(())
}
@@ -464,7 +501,15 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
remote_oob_data_request(ctx).await?;
}
- send_commitment(ctx, false).await;
+ ctx.send_lmp_packet(
+ lmp::SimplePairingConfirmBuilder {
+ transaction_id: 0,
+ commitment_value: build_commitment(ctx),
+ }
+ .build(),
+ );
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ send_commitment(ctx, confirm).await;
Ok(())
}
}
@@ -472,7 +517,6 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
.await;
if result.is_err() {
- ctx.send_lmp_packet(lmp::NumericComparisonFailedBuilder { transaction_id: 0 }.build());
ctx.send_hci_event(
hci::SimplePairingCompleteBuilder {
status: hci::ErrorCode::AuthenticationFailure,
@@ -644,37 +688,78 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReq) -> Resul
// Authentication Stage 1
let auth_method = authentication_method(initiator, responder);
- let negative_user_confirmation = match auth_method {
- AuthenticationMethod::NumericComparisonJustWork
- | AuthenticationMethod::NumericComparisonUserConfirm => {
- receive_commitment(ctx, true).await;
+ let result: Result<bool, ()> = async {
+ match auth_method {
+ AuthenticationMethod::NumericComparisonJustWork
+ | AuthenticationMethod::NumericComparisonUserConfirm => {
+ receive_commitment(ctx, None).await;
- let user_confirmation = user_confirmation_request(ctx).await;
- user_confirmation.is_err()
- }
- AuthenticationMethod::PasskeyEntry => {
- if responder.io_capability == hci::IoCapability::KeyboardOnly {
- // TODO: handle error
- let _user_passkey = user_passkey_request(ctx).await;
- } else {
- ctx.send_hci_event(
- hci::UserPasskeyNotificationBuilder { bd_addr: ctx.peer_address(), passkey: 0 }
+ let user_confirmation = user_confirmation_request(ctx).await;
+ Ok(user_confirmation.is_err())
+ }
+ AuthenticationMethod::PasskeyEntry => {
+ let confirm = if responder.io_capability == hci::IoCapability::KeyboardOnly {
+ let user_passkey = user_passkey_request(ctx).await;
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ if user_passkey.is_err() {
+ ctx.send_lmp_packet(
+ lmp::NotAcceptedBuilder {
+ transaction_id: 0,
+ not_accepted_opcode: lmp::Opcode::SimplePairingConfirm,
+ error_code: hci::ErrorCode::AuthenticationFailure.into(),
+ }.build(),
+ );
+ return Err(());
+ }
+ confirm
+ } else {
+ ctx.send_hci_event(
+ hci::UserPasskeyNotificationBuilder {
+ bd_addr: ctx.peer_address(),
+ passkey: 0,
+ }
.build(),
- );
+ );
+ match ctx
+ .receive_lmp_packet::<Either<lmp::SimplePairingConfirm, lmp::PasskeyFailed>>()
+ .await
+ {
+ Either::Left(confirm) => confirm,
+ Either::Right(_) => Err(())?,
+ }
+ };
+ receive_commitment(ctx, Some(confirm)).await;
+ for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER {
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ receive_commitment(ctx, Some(confirm)).await;
+ }
+ Ok(false)
}
- for _ in 0..PASSKEY_ENTRY_REPEAT_NUMBER {
- receive_commitment(ctx, false).await;
+ AuthenticationMethod::OutOfBand => {
+ if responder.oob_data_present != hci::OobDataPresent::NotPresent {
+ // TODO: handle error
+ let _remote_oob_data = remote_oob_data_request(ctx).await;
+ }
+
+ let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
+ receive_commitment(ctx, Some(confirm)).await;
+ Ok(false)
}
- false
}
- AuthenticationMethod::OutOfBand => {
- if responder.oob_data_present != hci::OobDataPresent::NotPresent {
- // TODO: handle error
- let _remote_oob_data = remote_oob_data_request(ctx).await;
- }
+ }
+ .await;
- receive_commitment(ctx, false).await;
- false
+ let negative_user_confirmation = match result {
+ Ok(negative_user_confirmation) => negative_user_confirmation,
+ Err(_) => {
+ ctx.send_hci_event(
+ hci::SimplePairingCompleteBuilder {
+ status: hci::ErrorCode::AuthenticationFailure,
+ bd_addr: ctx.peer_address(),
+ }
+ .build(),
+ );
+ return Err(());
}
};
@@ -867,8 +952,7 @@ mod tests {
}
#[test]
- #[should_panic] // TODO: make the test pass
- fn passkey_entry_initiator_failure_on_initiating_side() {
+ fn passkey_entry_initiator_negative_reply_on_initiating_side() {
let context = TestContext::new();
let procedure = initiate;
@@ -876,8 +960,15 @@ mod tests {
}
#[test]
- #[should_panic] // TODO: make the test pass
- fn passkey_entry_responder_failure_on_initiating_side() {
+ fn passkey_entry_responder_negative_reply_on_responding_side() {
+ let context = TestContext::new();
+ let procedure = respond;
+
+ include!("../../../test/SP/BV-14bis-C.in");
+ }
+
+ #[test]
+ fn passkey_entry_responder_negative_reply_on_initiating_side() {
let context = TestContext::new();
let procedure = respond;
@@ -885,6 +976,14 @@ mod tests {
}
#[test]
+ fn passkey_entry_initiator_negative_reply_on_responding_side() {
+ let context = TestContext::new();
+ let procedure = initiate;
+
+ include!("../../../test/SP/BV-15bis-C.in");
+ }
+
+ #[test]
#[should_panic] // TODO: make the test pass
fn passkey_entry_initiator_failure_on_responding_side() {
let context = TestContext::new();
diff --git a/tools/rootcanal/rust/src/packets.rs b/tools/rootcanal/rust/src/packets.rs
index dba39c9aa5..249c4c9912 100644
--- a/tools/rootcanal/rust/src/packets.rs
+++ b/tools/rootcanal/rust/src/packets.rs
@@ -54,18 +54,6 @@ pub mod hci {
}
}
- impl fmt::Display for ClassOfDevice {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{:03X}-{:01X}-{:02X}",
- (self.0 >> 12) & 0xfff,
- (self.0 >> 8) & 0xf,
- self.0 & 0xff,
- )
- }
- }
-
pub fn command_remote_device_address(command: &Command) -> Option<Address> {
use CommandChild::*;
#[allow(unused_imports)]
diff --git a/tools/rootcanal/rust/test/SP/BV-14bis-C.in b/tools/rootcanal/rust/test/SP/BV-14bis-C.in
new file mode 100644
index 0000000000..b98f45096d
--- /dev/null
+++ b/tools/rootcanal/rust/test/SP/BV-14bis-C.in
@@ -0,0 +1,106 @@
+// Passkey entry responder, negative reply on responding side:
+// - Test case not present in LMP.TS, but other permutations are described in SP/BV-14-C, SP/BV-15-C
+// - IUT is KeyboardOnly, responder
+// - Lower Tester is Display, initiator
+// - IUT fails passkey entry with User_Passkey_Request_NegativeReply, responds Not Accepted to the SimplePairingConfirm
+sequence! { procedure, context,
+ // ACL Connection Established
+ Lower Tester -> IUT: IoCapabilityReq {
+ transaction_id: 0,
+ io_capabilities: 0x00,
+ oob_authentication_data: 0x00,
+ authentication_requirement: 0x01,
+ }
+ IUT -> Upper Tester: IoCapabilityResponse {
+ bd_addr: context.peer_address(),
+ io_capability: IoCapability::DisplayOnly,
+ oob_data_present: OobDataPresent::NotPresent,
+ authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+ }
+ IUT -> Upper Tester: IoCapabilityRequest {
+ bd_addr: context.peer_address(),
+ }
+ Upper Tester -> IUT: IoCapabilityRequestReply {
+ bd_addr: context.peer_address(),
+ io_capability: IoCapability::KeyboardOnly,
+ oob_present: OobDataPresent::NotPresent,
+ authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+ }
+ IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+ num_hci_command_packets: 1,
+ status: ErrorCode::Success,
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Lower Tester: IoCapabilityRes {
+ transaction_id: 0,
+ io_capabilities: 0x02,
+ oob_authentication_data: 0x00,
+ authentication_requirement: 0x01,
+ }
+ // Public Key Exchange
+ Lower Tester -> IUT: EncapsulatedHeader {
+ transaction_id: 0,
+ major_type: 1,
+ minor_type: 1,
+ payload_length: 48,
+ }
+ IUT -> Lower Tester: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedHeader,
+ }
+ repeat 3 times with (part in peer_p192_public_key()) {
+ Lower Tester -> IUT: EncapsulatedPayload {
+ transaction_id: 0,
+ data: part,
+ }
+ IUT -> Lower Tester: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedPayload,
+ }
+ }
+ IUT -> Lower Tester: EncapsulatedHeader {
+ transaction_id: 0,
+ major_type: 1,
+ minor_type: 1,
+ payload_length: 48,
+ }
+ Lower Tester -> IUT: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedHeader,
+ }
+ repeat 3 times with (part in local_p192_public_key(&context)) {
+ IUT -> Lower Tester: EncapsulatedPayload {
+ transaction_id: 0,
+ data: part,
+ }
+ Lower Tester -> IUT: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedPayload,
+ }
+ }
+ // Authentication Stage 1: Passkey Entry Protocol
+ IUT -> Upper Tester: UserPasskeyRequest {
+ bd_addr: context.peer_address(),
+ }
+ Upper Tester -> IUT: UserPasskeyRequestNegativeReply {
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Upper Tester: UserPasskeyRequestNegativeReplyComplete {
+ num_hci_command_packets: 1,
+ status: ErrorCode::Success,
+ bd_addr: context.peer_address(),
+ }
+ Lower Tester -> IUT: SimplePairingConfirm {
+ transaction_id: 0,
+ commitment_value: [0; 16],
+ }
+ IUT -> Lower Tester: NotAccepted {
+ transaction_id: 0,
+ not_accepted_opcode: Opcode::SimplePairingConfirm,
+ error_code: ErrorCode::AuthenticationFailure.into(),
+ }
+ IUT -> Upper Tester: SimplePairingComplete {
+ status: ErrorCode::AuthenticationFailure,
+ bd_addr: context.peer_address(),
+ }
+}
diff --git a/tools/rootcanal/rust/test/SP/BV-15bis-C.in b/tools/rootcanal/rust/test/SP/BV-15bis-C.in
new file mode 100644
index 0000000000..f9fda299c8
--- /dev/null
+++ b/tools/rootcanal/rust/test/SP/BV-15bis-C.in
@@ -0,0 +1,118 @@
+// Passkey entry initiator, negative reply on responding side:
+// - Test case not present in LMP.TS, but other permutations are described in SP/BV-14-C, SP/BV-15-C
+// - IUT is DisplayOnly, initiator
+// - Lower Tester is KeyboardOnly, responder
+// - Lower Tester fails passkey entry with User_Passkey_Request_NegativeReply, responds Not Accepted to the SimplePairingConfirm
+sequence! { procedure, context,
+ // ACL Connection Established
+ Upper Tester -> IUT: AuthenticationRequested {
+ connection_handle: context.peer_handle()
+ }
+ IUT -> Upper Tester: AuthenticationRequestedStatus {
+ num_hci_command_packets: 1,
+ status: ErrorCode::Success,
+ }
+ IUT -> Upper Tester: LinkKeyRequest {
+ bd_addr: context.peer_address(),
+ }
+ Upper Tester -> IUT: LinkKeyRequestNegativeReply {
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete {
+ num_hci_command_packets: 1,
+ status: ErrorCode::Success,
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Upper Tester: IoCapabilityRequest {
+ bd_addr: context.peer_address(),
+ }
+ Upper Tester -> IUT: IoCapabilityRequestReply {
+ bd_addr: context.peer_address(),
+ io_capability: IoCapability::DisplayOnly,
+ oob_present: OobDataPresent::NotPresent,
+ authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+ }
+ IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+ num_hci_command_packets: 1,
+ status: ErrorCode::Success,
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Lower Tester: IoCapabilityReq {
+ transaction_id: 0,
+ io_capabilities: 0x00,
+ oob_authentication_data: 0x00,
+ authentication_requirement: 0x01,
+ }
+ Lower Tester -> IUT: IoCapabilityRes {
+ transaction_id: 0,
+ io_capabilities: 0x02,
+ oob_authentication_data: 0x00,
+ authentication_requirement: 0x01,
+ }
+ IUT -> Upper Tester: IoCapabilityResponse {
+ bd_addr: context.peer_address(),
+ io_capability: IoCapability::KeyboardOnly,
+ oob_data_present: OobDataPresent::NotPresent,
+ authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+ }
+ // Public Key Exchange
+ IUT -> Lower Tester: EncapsulatedHeader {
+ transaction_id: 0,
+ major_type: 1,
+ minor_type: 1,
+ payload_length: 48,
+ }
+ Lower Tester -> IUT: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedHeader,
+ }
+ repeat 3 times with (part in local_p192_public_key(&context)) {
+ IUT -> Lower Tester: EncapsulatedPayload {
+ transaction_id: 0,
+ data: part,
+ }
+ Lower Tester -> IUT: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedPayload,
+ }
+ }
+ Lower Tester -> IUT: EncapsulatedHeader {
+ transaction_id: 0,
+ major_type: 1,
+ minor_type: 1,
+ payload_length: 48,
+ }
+ IUT -> Lower Tester: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedHeader,
+ }
+ repeat 3 times with (part in peer_p192_public_key()) {
+ Lower Tester -> IUT: EncapsulatedPayload {
+ transaction_id: 0,
+ data: part,
+ }
+ IUT -> Lower Tester: Accepted {
+ transaction_id: 0,
+ accepted_opcode: Opcode::EncapsulatedPayload,
+ }
+ }
+ // Authentication Stage 1: Passkey Entry Protocol
+ IUT -> Upper Tester: UserPasskeyNotification { bd_addr: context.peer_address(), passkey: 0 }
+ IUT -> Lower Tester: SimplePairingConfirm {
+ transaction_id: 0,
+ commitment_value: [0; 16],
+ }
+ Lower Tester -> IUT: NotAccepted {
+ transaction_id: 0,
+ not_accepted_opcode: Opcode::SimplePairingConfirm,
+ error_code: ErrorCode::AuthenticationFailure.into(),
+ }
+ IUT -> Upper Tester: SimplePairingComplete {
+ status: ErrorCode::AuthenticationFailure,
+ bd_addr: context.peer_address(),
+ }
+ IUT -> Upper Tester: AuthenticationComplete {
+ status: ErrorCode::AuthenticationFailure,
+ connection_handle: context.peer_handle(),
+ }
+}
diff --git a/tools/rootcanal/test/LL/CON_/CEN/BV_41_C.py b/tools/rootcanal/test/LL/CON_/CEN/BV_41_C.py
index c3b719082e..9d377ef54a 100644
--- a/tools/rootcanal/test/LL/CON_/CEN/BV_41_C.py
+++ b/tools/rootcanal/test/LL/CON_/CEN/BV_41_C.py
@@ -46,13 +46,13 @@ class Test(ControllerTest):
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
initiating_phys=0x1,
- phy_scan_parameters=[
- hci.LeCreateConnPhyScanParameters(
+ initiating_phy_parameters=[
+ hci.InitiatingPhyParameters(
scan_interval=0x200,
scan_window=0x100,
- conn_interval_min=0x200,
- conn_interval_max=0x200,
- conn_latency=0x6,
+ connection_interval_min=0x200,
+ connection_interval_max=0x200,
+ max_latency=0x6,
supervision_timeout=0xc80,
min_ce_length=0,
max_ce_length=0,
@@ -91,8 +91,8 @@ class Test(ControllerTest):
role=hci.Role.CENTRAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x6,
+ connection_interval=0x200,
+ peripheral_latency=0x6,
supervision_timeout=0xc80,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/CON_/CEN/BV_43_C.py b/tools/rootcanal/test/LL/CON_/CEN/BV_43_C.py
index ec5b68ba4a..914df4cfe9 100644
--- a/tools/rootcanal/test/LL/CON_/CEN/BV_43_C.py
+++ b/tools/rootcanal/test/LL/CON_/CEN/BV_43_C.py
@@ -43,13 +43,13 @@ class Test(ControllerTest):
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
initiating_phys=0x1,
- phy_scan_parameters=[
- hci.LeCreateConnPhyScanParameters(
+ initiating_phy_parameters=[
+ hci.InitiatingPhyParameters(
scan_interval=0x200,
scan_window=0x100,
- conn_interval_min=0x200,
- conn_interval_max=0x200,
- conn_latency=0x6,
+ connection_interval_min=0x200,
+ connection_interval_max=0x200,
+ max_latency=0x6,
supervision_timeout=0xc80,
min_ce_length=0,
max_ce_length=0,
@@ -88,8 +88,8 @@ class Test(ControllerTest):
role=hci.Role.CENTRAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x6,
+ connection_interval=0x200,
+ peripheral_latency=0x6,
supervision_timeout=0xc80,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/CON_/PER/BV_40_C.py b/tools/rootcanal/test/LL/CON_/PER/BV_40_C.py
index 201beeb3cd..045bca32a3 100644
--- a/tools/rootcanal/test/LL/CON_/PER/BV_40_C.py
+++ b/tools/rootcanal/test/LL/CON_/PER/BV_40_C.py
@@ -77,8 +77,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x200,
+ connection_interval=0x200,
+ peripheral_latency=0x200,
supervision_timeout=0x200,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/CON_/PER/BV_42_C.py b/tools/rootcanal/test/LL/CON_/PER/BV_42_C.py
index 4f19d701c5..ae067c2269 100644
--- a/tools/rootcanal/test/LL/CON_/PER/BV_42_C.py
+++ b/tools/rootcanal/test/LL/CON_/PER/BV_42_C.py
@@ -77,8 +77,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=0x200,
- conn_latency=0x200,
+ connection_interval=0x200,
+ peripheral_latency=0x200,
supervision_timeout=0x200,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
index f850c57143..f52edd3deb 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_06_C.py
@@ -95,8 +95,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=LL_initiator_connInterval,
- conn_latency=LL_initiator_connPeripheralLatency,
+ connection_interval=LL_initiator_connInterval,
+ peripheral_latency=LL_initiator_connPeripheralLatency,
supervision_timeout=LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_07_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_07_C.py
index c4b10cdc8e..271a4fc564 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_07_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_07_C.py
@@ -125,8 +125,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=LL_initiator_connInterval,
- conn_latency=LL_initiator_connPeripheralLatency,
+ connection_interval=LL_initiator_connInterval,
+ peripheral_latency=LL_initiator_connPeripheralLatency,
supervision_timeout=LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_09_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_09_C.py
index d583ef4cf3..719ddc76e3 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_09_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_09_C.py
@@ -310,8 +310,8 @@ class Test(ControllerTest):
peer_address_type=(hci.AddressType.PUBLIC_DEVICE_ADDRESS if peer_address_type == ll.AddressType.PUBLIC
else hci.AddressType.RANDOM_DEVICE_ADDRESS),
peer_address=peer_address,
- conn_interval=self.LL_initiator_connInterval,
- conn_latency=self.LL_initiator_connPeripheralLatency,
+ connection_interval=self.LL_initiator_connInterval,
+ peripheral_latency=self.LL_initiator_connPeripheralLatency,
supervision_timeout=self.LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
@@ -380,8 +380,8 @@ class Test(ControllerTest):
peer_address_type=(hci.AddressType.PUBLIC_DEVICE_ADDRESS if peer_address_type == ll.AddressType.PUBLIC
else hci.AddressType.RANDOM_DEVICE_ADDRESS),
peer_address=peer_address,
- conn_interval=self.LL_initiator_connInterval,
- conn_latency=self.LL_initiator_connPeripheralLatency,
+ connection_interval=self.LL_initiator_connInterval,
+ peripheral_latency=self.LL_initiator_connPeripheralLatency,
supervision_timeout=self.LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_11_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_11_C.py
index 1b0953ac51..014417f9be 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_11_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_11_C.py
@@ -137,8 +137,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=peer_address,
- conn_interval=self.LL_initiator_connInterval,
- conn_latency=self.LL_initiator_connPeripheralLatency,
+ connection_interval=self.LL_initiator_connInterval,
+ peripheral_latency=self.LL_initiator_connPeripheralLatency,
supervision_timeout=self.LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_17_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_17_C.py
index 8795116d66..c27619c2da 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_17_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_17_C.py
@@ -69,7 +69,7 @@ class Test(ControllerTest):
await self.steps_4_6(peer_address=peer_address, scan_response_data=scan_response_data)
# 7. Configure Scan Response Data in the IUT using device name length of 31 as response data.
- scan_response_data = [31] + [0] * 31
+ scan_response_data = [31] + [0] * 30
controller.send_cmd(hci.LeSetScanResponseData(advertising_data=scan_response_data))
await self.expect_evt(hci.LeSetScanResponseDataComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
@@ -104,7 +104,7 @@ class Test(ControllerTest):
await self.steps_4_6(peer_address=peer_address, scan_response_data=scan_response_data)
# 12. Configure Scan Response Data in the IUT using device name length of 31 as response data.
- scan_response_data = [31] + [0] * 31
+ scan_response_data = [31] + [0] * 30
controller.send_cmd(hci.LeSetScanResponseData(advertising_data=scan_response_data))
await self.expect_evt(hci.LeSetScanResponseDataComplete(status=ErrorCode.SUCCESS, num_hci_command_packets=1))
diff --git a/tools/rootcanal/test/LL/DDI/ADV/BV_19_C.py b/tools/rootcanal/test/LL/DDI/ADV/BV_19_C.py
index ba996c7ad6..9247324a4f 100644
--- a/tools/rootcanal/test/LL/DDI/ADV/BV_19_C.py
+++ b/tools/rootcanal/test/LL/DDI/ADV/BV_19_C.py
@@ -112,8 +112,8 @@ class Test(ControllerTest):
role=hci.Role.PERIPHERAL,
peer_address_type=hci.AddressType.PUBLIC_DEVICE_ADDRESS,
peer_address=public_peer_address,
- conn_interval=self.LL_initiator_connInterval,
- conn_latency=self.LL_initiator_connPeripheralLatency,
+ connection_interval=self.LL_initiator_connInterval,
+ peripheral_latency=self.LL_initiator_connPeripheralLatency,
supervision_timeout=self.LL_initiator_connSupervisionTimeout,
central_clock_accuracy=hci.ClockAccuracy.PPM_500))
diff --git a/tools/rootcanal/test/LL/DDI/SCN/BV_19_C.py b/tools/rootcanal/test/LL/DDI/SCN/BV_19_C.py
index 04bda10551..41c3db5b94 100644
--- a/tools/rootcanal/test/LL/DDI/SCN/BV_19_C.py
+++ b/tools/rootcanal/test/LL/DDI/SCN/BV_19_C.py
@@ -82,10 +82,10 @@ class Test(ControllerTest):
hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL,
scanning_phys=0x1,
- parameters=[
- hci.PhyScanParameters(le_scan_type=hci.LeScanType.PASSIVE,
- le_scan_interval=0x0010,
- le_scan_window=0x0010)
+ scanning_phy_parameters=[
+ hci.ScanningPhyParameters(le_scan_type=hci.LeScanType.PASSIVE,
+ le_scan_interval=0x0010,
+ le_scan_window=0x0010)
]))
await self.expect_evt(
diff --git a/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py b/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py
index a27b0a1058..80a6245c68 100644
--- a/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py
+++ b/tools/rootcanal/test/LL/DDI/SCN/BV_79_C.py
@@ -52,10 +52,10 @@ class Test(ControllerTest):
hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.PUBLIC_DEVICE_ADDRESS,
scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL,
scanning_phys=0x1,
- parameters=[
- hci.PhyScanParameters(le_scan_type=hci.LeScanType.PASSIVE,
- le_scan_interval=0x0010,
- le_scan_window=0x0010)
+ scanning_phy_parameters=[
+ hci.ScanningPhyParameters(le_scan_type=hci.LeScanType.PASSIVE,
+ le_scan_interval=0x0010,
+ le_scan_window=0x0010)
]))
await self.expect_evt(
diff --git a/tools/rootcanal/test/controller/le/le_add_device_to_resolving_list_test.cc b/tools/rootcanal/test/controller/le/le_add_device_to_resolving_list_test.cc
index 427861e0e9..b2a44d4812 100644
--- a/tools/rootcanal/test/controller/le/le_add_device_to_resolving_list_test.cc
+++ b/tools/rootcanal/test/controller/le/le_add_device_to_resolving_list_test.cc
@@ -130,4 +130,16 @@ TEST_F(LeAddDeviceToResolvingListTest, PeerIrkDuplicate) {
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
}
+TEST_F(LeAddDeviceToResolvingListTest, EmptyPeerIrkDuplicate) {
+ ASSERT_EQ(controller_.LeAddDeviceToResolvingList(
+ PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, Address{1},
+ std::array<uint8_t, 16>{0}, std::array<uint8_t, 16>{1}),
+ ErrorCode::SUCCESS);
+
+ ASSERT_EQ(controller_.LeAddDeviceToResolvingList(
+ PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS, Address{1},
+ std::array<uint8_t, 16>{0}, std::array<uint8_t, 16>{1}),
+ ErrorCode::SUCCESS);
+}
+
} // namespace rootcanal
diff --git a/tools/rootcanal/test/controller/le/le_scanning_filter_duplicates_test.cc b/tools/rootcanal/test/controller/le/le_scanning_filter_duplicates_test.cc
index a37dafc4d8..feed4edcd7 100644
--- a/tools/rootcanal/test/controller/le/le_scanning_filter_duplicates_test.cc
+++ b/tools/rootcanal/test/controller/le/le_scanning_filter_duplicates_test.cc
@@ -23,10 +23,8 @@
#include <vector>
#include "hci/address.h"
-#include "hci/hci_packets.h"
#include "model/controller/link_layer_controller.h"
-#include "packet/bit_inserter.h"
-#include "packet/packet_view.h"
+#include "packets/hci_packets.h"
#include "packets/link_layer_packets.h"
namespace rootcanal {
@@ -73,7 +71,7 @@ class LeScanningFilterDuplicates : public ::testing::Test {
void StartExtendedScan(FilterDuplicates filter_duplicates,
uint16_t duration = 0, uint16_t period = 0) {
- bluetooth::hci::PhyScanParameters param;
+ bluetooth::hci::ScanningPhyParameters param;
param.le_scan_type_ = LeScanType::ACTIVE;
param.le_scan_interval_ = 0x4;
param.le_scan_window_ = 0x4;
@@ -95,7 +93,7 @@ class LeScanningFilterDuplicates : public ::testing::Test {
/// Helper for building ScanResponse packets
static model::packets::LinkLayerPacketView LeScanResponse(
std::vector<uint8_t> const data = {}) {
- return FromBuilder(*model::packets::LeScanResponseBuilder::Create(
+ return FromBuilder(model::packets::LeScanResponseBuilder::Create(
Address::kEmpty, Address::kEmpty, model::packets::AddressType::PUBLIC,
data));
}
@@ -103,7 +101,7 @@ class LeScanningFilterDuplicates : public ::testing::Test {
/// Helper for building LeLegacyAdvertisingPdu packets
static model::packets::LinkLayerPacketView LeLegacyAdvertisingPdu(
std::vector<uint8_t> const data = {}) {
- return FromBuilder(*model::packets::LeLegacyAdvertisingPduBuilder::Create(
+ return FromBuilder(model::packets::LeLegacyAdvertisingPduBuilder::Create(
Address::kEmpty, Address::kEmpty, model::packets::AddressType::PUBLIC,
model::packets::AddressType::PUBLIC,
model::packets::LegacyAdvertisingType::ADV_IND, data));
@@ -112,13 +110,21 @@ class LeScanningFilterDuplicates : public ::testing::Test {
/// Helper for building LeExtendedAdvertisingPdu packets
static model::packets::LinkLayerPacketView LeExtendedAdvertisingPdu(
std::vector<uint8_t> const data = {}) {
- return FromBuilder(*model::packets::LeExtendedAdvertisingPduBuilder::Create(
+ return FromBuilder(model::packets::LeExtendedAdvertisingPduBuilder::Create(
Address::kEmpty, Address::kEmpty, model::packets::AddressType::PUBLIC,
model::packets::AddressType::PUBLIC, 0, 1, 0, 0, 0,
model::packets::PrimaryPhyType::LE_1M,
model::packets::SecondaryPhyType::LE_1M, 0, data));
}
+ static model::packets::LinkLayerPacketView FromBuilder(
+ std::unique_ptr<pdl::packet::Builder> builder) {
+ auto data =
+ std::make_shared<std::vector<uint8_t>>(builder->SerializeToBytes());
+ return model::packets::LinkLayerPacketView::Create(
+ pdl::packet::slice(data));
+ }
+
enum Filtered {
kFiltered,
kReported,
@@ -154,18 +160,6 @@ class LeScanningFilterDuplicates : public ::testing::Test {
static void remote_listener_(
std::shared_ptr<model::packets::LinkLayerPacketBuilder> /* packet */,
Phy::Type /* phy */, int8_t /* tx_power */) {}
-
- /// Helper for building packet view from packet builder
- static model::packets::LinkLayerPacketView FromBuilder(
- model::packets::LinkLayerPacketBuilder& builder) {
- std::shared_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>);
- auto bit_inserter = bluetooth::packet::BitInserter(*buffer);
-
- builder.Serialize(bit_inserter);
-
- return model::packets::LinkLayerPacketView::Create(
- PacketView<kLittleEndian>(buffer));
- }
};
unsigned LeScanningFilterDuplicates::event_listener_called_ = 0;
diff --git a/tools/rootcanal/test/controller/le/le_set_extended_scan_enable_test.cc b/tools/rootcanal/test/controller/le/le_set_extended_scan_enable_test.cc
index 79b9663619..278b9d06aa 100644
--- a/tools/rootcanal/test/controller/le/le_set_extended_scan_enable_test.cc
+++ b/tools/rootcanal/test/controller/le/le_set_extended_scan_enable_test.cc
@@ -33,10 +33,10 @@ class LeSetExtendedScanEnableTest : public ::testing::Test {
LinkLayerController controller_{address_, properties_};
};
-static PhyScanParameters MakePhyScanParameters(LeScanType scan_type,
- uint16_t scan_interval,
- uint16_t scan_window) {
- PhyScanParameters parameters;
+static ScanningPhyParameters MakeScanningPhyParameters(LeScanType scan_type,
+ uint16_t scan_interval,
+ uint16_t scan_window) {
+ ScanningPhyParameters parameters;
parameters.le_scan_type_ = scan_type;
parameters.le_scan_interval_ = scan_interval;
parameters.le_scan_window_ = scan_window;
@@ -44,22 +44,24 @@ static PhyScanParameters MakePhyScanParameters(LeScanType scan_type,
}
TEST_F(LeSetExtendedScanEnableTest, EnableUsingPublicAddress) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
ErrorCode::SUCCESS);
}
TEST_F(LeSetExtendedScanEnableTest, EnableUsingRandomAddress) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::RANDOM_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::RANDOM_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetRandomAddress(Address{1}), ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
@@ -67,11 +69,12 @@ TEST_F(LeSetExtendedScanEnableTest, EnableUsingRandomAddress) {
}
TEST_F(LeSetExtendedScanEnableTest, EnableUsingResolvableAddress) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetRandomAddress(Address{1}), ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
@@ -79,11 +82,12 @@ TEST_F(LeSetExtendedScanEnableTest, EnableUsingResolvableAddress) {
}
TEST_F(LeSetExtendedScanEnableTest, ResetEachPeriod) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::RESET_EACH_PERIOD, 100, 1000),
ErrorCode::SUCCESS);
@@ -96,11 +100,12 @@ TEST_F(LeSetExtendedScanEnableTest, Disable) {
}
TEST_F(LeSetExtendedScanEnableTest, ValidDuration) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 127, 1),
@@ -108,11 +113,12 @@ TEST_F(LeSetExtendedScanEnableTest, ValidDuration) {
}
TEST_F(LeSetExtendedScanEnableTest, InvalidDuration) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::RESET_EACH_PERIOD, 0, 0),
@@ -123,20 +129,22 @@ TEST_F(LeSetExtendedScanEnableTest, InvalidDuration) {
}
TEST_F(LeSetExtendedScanEnableTest, NoRandomAddress) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::RANDOM_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::RANDOM_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::SUCCESS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
diff --git a/tools/rootcanal/test/controller/le/le_set_extended_scan_parameters_test.cc b/tools/rootcanal/test/controller/le/le_set_extended_scan_parameters_test.cc
index 0c153ea1ac..3e1ab5390a 100644
--- a/tools/rootcanal/test/controller/le/le_set_extended_scan_parameters_test.cc
+++ b/tools/rootcanal/test/controller/le/le_set_extended_scan_parameters_test.cc
@@ -33,10 +33,10 @@ class LeSetExtendedScanParametersTest : public ::testing::Test {
LinkLayerController controller_{address_, properties_};
};
-static PhyScanParameters MakePhyScanParameters(LeScanType scan_type,
- uint16_t scan_interval,
- uint16_t scan_window) {
- PhyScanParameters parameters;
+static ScanningPhyParameters MakeScanningPhyParameters(LeScanType scan_type,
+ uint16_t scan_interval,
+ uint16_t scan_window) {
+ ScanningPhyParameters parameters;
parameters.le_scan_type_ = scan_type;
parameters.le_scan_interval_ = scan_interval;
parameters.le_scan_window_ = scan_window;
@@ -47,8 +47,8 @@ TEST_F(LeSetExtendedScanParametersTest, Success) {
ASSERT_EQ(controller_.LeSetExtendedScanParameters(
OwnAddressType::PUBLIC_DEVICE_ADDRESS,
LeScanningFilterPolicy::ACCEPT_ALL, 0x5,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200),
- MakePhyScanParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200),
+ MakeScanningPhyParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
ErrorCode::SUCCESS);
}
@@ -56,8 +56,8 @@ TEST_F(LeSetExtendedScanParametersTest, ScanningActive) {
ASSERT_EQ(controller_.LeSetExtendedScanParameters(
OwnAddressType::PUBLIC_DEVICE_ADDRESS,
LeScanningFilterPolicy::ACCEPT_ALL, 0x5,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200),
- MakePhyScanParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200),
+ MakeScanningPhyParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
ErrorCode::SUCCESS);
ASSERT_EQ(controller_.LeSetExtendedScanEnable(
true, FilterDuplicates::DISABLED, 0, 0),
@@ -66,17 +66,18 @@ TEST_F(LeSetExtendedScanParametersTest, ScanningActive) {
ASSERT_EQ(controller_.LeSetExtendedScanParameters(
OwnAddressType::PUBLIC_DEVICE_ADDRESS,
LeScanningFilterPolicy::ACCEPT_ALL, 0x5,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200),
- MakePhyScanParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200),
+ MakeScanningPhyParameters(LeScanType::ACTIVE, 0x2000, 0x200)}),
ErrorCode::COMMAND_DISALLOWED);
}
TEST_F(LeSetExtendedScanParametersTest, ReservedPhy) {
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x80,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x80,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
}
TEST_F(LeSetExtendedScanParametersTest, InvalidPhyParameters) {
@@ -85,19 +86,20 @@ TEST_F(LeSetExtendedScanParametersTest, InvalidPhyParameters) {
LeScanningFilterPolicy::ACCEPT_ALL, 0x1, {}),
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200),
- MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
- ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200),
+ MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x200)}),
+ ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
}
TEST_F(LeSetExtendedScanParametersTest, InvalidScanInterval) {
ASSERT_EQ(controller_.LeSetExtendedScanParameters(
OwnAddressType::PUBLIC_DEVICE_ADDRESS,
LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x0, 0x200)}),
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x0, 0x200)}),
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
}
@@ -105,14 +107,15 @@ TEST_F(LeSetExtendedScanParametersTest, InvalidScanWindow) {
ASSERT_EQ(controller_.LeSetExtendedScanParameters(
OwnAddressType::PUBLIC_DEVICE_ADDRESS,
LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x0)}),
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x0)}),
ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
- ASSERT_EQ(controller_.LeSetExtendedScanParameters(
- OwnAddressType::PUBLIC_DEVICE_ADDRESS,
- LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
- {MakePhyScanParameters(LeScanType::PASSIVE, 0x2000, 0x2001)}),
- ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
+ ASSERT_EQ(
+ controller_.LeSetExtendedScanParameters(
+ OwnAddressType::PUBLIC_DEVICE_ADDRESS,
+ LeScanningFilterPolicy::ACCEPT_ALL, 0x1,
+ {MakeScanningPhyParameters(LeScanType::PASSIVE, 0x2000, 0x2001)}),
+ ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
}
} // namespace rootcanal
diff --git a/tools/rootcanal/test/controller/le/test_helpers.h b/tools/rootcanal/test/controller/le/test_helpers.h
index 074e258d5c..4c9016c22c 100644
--- a/tools/rootcanal/test/controller/le/test_helpers.h
+++ b/tools/rootcanal/test/controller/le/test_helpers.h
@@ -49,18 +49,18 @@ MakeAdvertisingEventProperties(unsigned mask = 0) {
return set;
}
-[[maybe_unused]] static bluetooth::hci::LeCreateConnPhyScanParameters
+[[maybe_unused]] static bluetooth::hci::InitiatingPhyParameters
MakeInitiatingPhyParameters(uint16_t scan_interval, uint16_t scan_window,
uint16_t connection_interval_min,
uint16_t connection_interval_max,
uint16_t max_latency, uint16_t supervision_timeout,
uint16_t min_ce_length, uint16_t max_ce_length) {
- bluetooth::hci::LeCreateConnPhyScanParameters parameters;
+ bluetooth::hci::InitiatingPhyParameters parameters;
parameters.scan_interval_ = scan_interval;
parameters.scan_window_ = scan_window;
- parameters.conn_interval_min_ = connection_interval_min;
- parameters.conn_interval_max_ = connection_interval_max;
- parameters.conn_latency_ = max_latency;
+ parameters.connection_interval_min_ = connection_interval_min;
+ parameters.connection_interval_max_ = connection_interval_max;
+ parameters.max_latency_ = max_latency;
parameters.supervision_timeout_ = supervision_timeout;
parameters.min_ce_length_ = min_ce_length;
parameters.max_ce_length_ = max_ce_length;
diff --git a/tools/rootcanal/test/pcap_filter_unittest.cc b/tools/rootcanal/test/pcap_filter_unittest.cc
index 88035402e4..41a80768a7 100644
--- a/tools/rootcanal/test/pcap_filter_unittest.cc
+++ b/tools/rootcanal/test/pcap_filter_unittest.cc
@@ -18,7 +18,7 @@
#include <gtest/gtest.h>
-#include "hci/hci_packets.h"
+#include "packets/hci_packets.h"
namespace rootcanal {