diff options
762 files changed, 17581 insertions, 28730 deletions
diff --git a/Android.bp b/Android.bp index 9142bd51ae..7ec7305d0a 100644 --- a/Android.bp +++ b/Android.bp @@ -115,6 +115,7 @@ java_defaults { "-Xep:ClassCanBeStatic:ERROR", "-Xep:DateFormatConstant:ERROR", "-Xep:DirectInvocationOnMock:ERROR", + "-Xep:DuplicateBranches:ERROR", "-Xep:EmptyBlockTag:ERROR", "-Xep:EmptyCatch:ERROR", "-Xep:EnumOrdinal:ERROR", @@ -152,6 +153,7 @@ java_defaults { "-Xep:NullableVoid:ERROR", "-Xep:ObjectEqualsForPrimitives:ERROR", "-Xep:OperatorPrecedence:ERROR", + "-Xep:RedundantControlFlow:ERROR", "-Xep:ReferenceEquality:ERROR", "-Xep:ReturnAtTheEndOfVoidFunction:ERROR", "-Xep:ReturnFromVoid:ERROR", diff --git a/OWNERS_leaudio b/OWNERS_leaudio index 0ba606bbf0..30db38d799 100644 --- a/OWNERS_leaudio +++ b/OWNERS_leaudio @@ -1,4 +1,3 @@ siyuanh@google.com jpawlowski@google.com rongxuan@google.com -yuyangh@google.com diff --git a/TEST_MAPPING b/TEST_MAPPING index bd98b7d91c..5fc31de514 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,4 +1,183 @@ { + "bt-mainline-presubmit": [ + { + "name": "CtsBluetoothTestCases" + }, + { + "name": "GoogleBluetoothInstrumentationTests" + }, + { + "name": "FrameworkBluetoothTests" + }, + { + "name": "ServiceBluetoothTests" + }, + { + "name": "net_test_audio_hearing_aid_hw" + }, + { + "name": "net_test_bluetooth" + }, + { + "name": "net_test_bta" + }, + { + "name": "net_test_bta_security" + }, + { + "name": "net_test_btif" + }, + { + "name": "net_test_btif_hf_client_service" + }, + { + "name": "net_test_btif_profile_queue" + }, + { + "name": "net_test_btif_avrcp_audio_track" + }, + { + "name": "net_test_device" + }, + { + "name": "net_test_device_iot_config" + }, + { + "name": "net_test_conn_multiplexing" + }, + { + "name": "net_test_hci" + }, + { + "name": "net_test_stack" + }, + { + "name": "net_test_stack_a2dp_codecs_native" + }, + { + "name": "net_test_stack_ad_parser" + }, + { + "name": "bluetooth_csis_test" + }, + { + "name": "bluetooth_flatbuffer_tests" + }, + { + "name": "bluetooth_groups_test" + }, + { + "name": "bluetooth_has_test" + }, + { + "name": "bluetooth_hh_test" + }, + { + "name": "bluetooth_le_audio_client_test" + }, + { + "name": "bluetooth_le_audio_test" + }, + { + "name": "bluetooth_packet_parser_test" + }, + { + "name": "bluetooth_test_broadcaster" + }, + { + "name": "bluetooth_test_broadcaster_state_machine" + }, + { + "name": "bluetooth_test_common" + }, + { + "name": "bluetooth_vc_test" + }, + { + "name": "libaptx_enc_tests" + }, + { + "name": "libaptxhd_enc_tests" + }, + { + "name": "net_test_avrcp" + }, + { + "name": "net_test_btcore" + }, + { + "name": "net_test_btif_hh" + }, + { + "name": "net_test_btif_rc" + }, + { + "name": "net_test_btif_stack" + }, + { + "name": "net_test_btm_iso" + }, + { + "name": "net_test_btpackets" + }, + { + "name": "net_test_eatt" + }, + { + "name": "net_test_main_shim" + }, + { + "name": "net_test_osi" + }, + { + "name": "net_test_performance" + }, + { + "name": "net_test_stack_a2dp_native" + }, + { + "name": "net_test_stack_acl" + }, + { + "name": "net_test_stack_avdtp" + }, + { + "name": "net_test_stack_btm" + }, + { + "name": "net_test_stack_btu" + }, + { + "name": "net_test_stack_gatt" + }, + { + "name": "net_test_stack_gatt_native" + }, + { + "name": "net_test_stack_gatt_sr_hash_native" + }, + { + "name": "net_test_stack_hci" + }, + { + "name": "net_test_stack_hid" + }, + { + "name": "net_test_stack_l2cap" + }, + { + "name": "net_test_stack_rfcomm" + }, + { + "name": "net_test_stack_sdp" + }, + { + "name": "net_test_stack_smp" + }, + { + "name": "net_test_types" + } + ], "presubmit": [ // android_test targets { diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java index 1dc753318e..61b412f2f8 100644 --- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java +++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/DistanceMeasurementInitiator.java @@ -66,7 +66,6 @@ class DistanceMeasurementInitiator { } } - private static final int DISTANCE_MEASUREMENT_DURATION_SEC = 3600; private static final List<Pair<Integer, String>> mDistanceMeasurementMethodMapping = List.of( new Pair<>(DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_AUTO, "AUTO"), @@ -151,8 +150,13 @@ class DistanceMeasurementInitiator { return List.of(Freq.MEDIUM.toString(), Freq.HIGH.toString(), Freq.LOW.toString()); } + List<String> getMeasureDurationsInSeconds() { + return List.of("3600", "300", "60", "10"); + } + @SuppressLint("MissingPermission") // permissions are checked upfront - void startDistanceMeasurement(String distanceMeasurementMethodName, String selectedFreq) { + void startDistanceMeasurement( + String distanceMeasurementMethodName, String selectedFreq, int duration) { if (mTargetDevice == null) { printLog("do Gatt connect first"); @@ -163,7 +167,7 @@ class DistanceMeasurementInitiator { DistanceMeasurementParams params = new DistanceMeasurementParams.Builder(mTargetDevice) - .setDurationSeconds(DISTANCE_MEASUREMENT_DURATION_SEC) + .setDurationSeconds(duration) .setFrequency(Freq.fromName(selectedFreq).getFreq()) .setMethodId(getDistanceMeasurementMethodId(distanceMeasurementMethodName)) .build(); diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java index b1b96d7bb1..cd26703e56 100644 --- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java +++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorFragment.java @@ -44,10 +44,12 @@ public class InitiatorFragment extends Fragment { private ArrayAdapter<String> mDmMethodArrayAdapter; private ArrayAdapter<String> mFreqArrayAdapter; + private ArrayAdapter<String> mDurationArrayAdapter; private TextView mDistanceText; private CanvasView mDistanceCanvasView; private Spinner mSpinnerDmMethod; private Spinner mSpinnerFreq; + private Spinner mSpinnerDuration; private Button mButtonCs; private LinearLayout mDistanceViewLayout; private TextView mLogText; @@ -66,6 +68,7 @@ public class InitiatorFragment extends Fragment { mButtonCs = (Button) root.findViewById(R.id.btn_cs); mSpinnerDmMethod = (Spinner) root.findViewById(R.id.spinner_dm_method); mSpinnerFreq = (Spinner) root.findViewById(R.id.spinner_freq); + mSpinnerDuration = (Spinner) root.findViewById(R.id.spinner_duration); mDistanceViewLayout = (LinearLayout) root.findViewById(R.id.layout_distance_view); mDistanceText = new TextView(getContext()); mDistanceViewLayout.addView(mDistanceText); @@ -93,6 +96,12 @@ public class InitiatorFragment extends Fragment { getContext(), android.R.layout.simple_spinner_item, new ArrayList<>()); mFreqArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mSpinnerFreq.setAdapter(mFreqArrayAdapter); + mDurationArrayAdapter = + new ArrayAdapter<String>( + getContext(), android.R.layout.simple_spinner_item, new ArrayList<>()); + mDurationArrayAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + mSpinnerDuration.setAdapter(mDurationArrayAdapter); mInitiatorViewModel = new ViewModelProvider(this).get(InitiatorViewModel.class); mBleConnectionViewModel = new ViewModelProvider(this).get(BleConnectionViewModel.class); @@ -143,14 +152,18 @@ public class InitiatorFragment extends Fragment { mDmMethodArrayAdapter.addAll(mInitiatorViewModel.getSupportedDmMethods()); mFreqArrayAdapter.addAll(mInitiatorViewModel.getMeasurementFreqs()); + mDurationArrayAdapter.addAll(mInitiatorViewModel.getMeasurementDurations()); mButtonCs.setOnClickListener( v -> { String methodName = mSpinnerDmMethod.getSelectedItem().toString(); String freq = mSpinnerFreq.getSelectedItem().toString(); + int duration = Integer.parseInt(mSpinnerDuration.getSelectedItem().toString()); + if (TextUtils.isEmpty(methodName)) { printLog("the device doesn't support any distance measurement methods."); } - mInitiatorViewModel.toggleCsStartStop(methodName, freq); + + mInitiatorViewModel.toggleCsStartStop(methodName, freq, duration); }); } diff --git a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java index e07e7a1593..1203f9e488 100644 --- a/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java +++ b/android/ChannelSoundingTestApp/app/src/main/java/com/android/bluetooth/channelsoundingtestapp/InitiatorViewModel.java @@ -75,10 +75,14 @@ public class InitiatorViewModel extends AndroidViewModel { return mDistanceMeasurementInitiator.getMeasurementFreqs(); } - void toggleCsStartStop(String distanceMeasurementMethodName, String freq) { + List<String> getMeasurementDurations() { + return mDistanceMeasurementInitiator.getMeasureDurationsInSeconds(); + } + + void toggleCsStartStop(String distanceMeasurementMethodName, String freq, int duration) { if (!mCsStarted.getValue()) { mDistanceMeasurementInitiator.startDistanceMeasurement( - distanceMeasurementMethodName, freq); + distanceMeasurementMethodName, freq, duration); } else { mDistanceMeasurementInitiator.stopDistanceMeasurement(); } diff --git a/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml b/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml index 39b0246c05..78e17927fd 100644 --- a/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml +++ b/android/ChannelSoundingTestApp/app/src/main/res/layout/fragment_initiator.xml @@ -16,49 +16,81 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" - app:layout_constraintBottom_toTopOf="@id/spinner_dm_method" + app:layout_constraintBottom_toTopOf="@id/layout_methods" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - <TextView - android:id="@+id/dm_method_label" - android:layout_width="wrap_content" + <LinearLayout android:id="@+id/layout_methods" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:text= "Method" - app:layout_constraintBottom_toTopOf="@id/btn_cs" - app:layout_constraintEnd_toStartOf="@id/spinner_dm_method" - app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" - /> - <Spinner - android:id="@+id/spinner_dm_method" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:padding="10dp" - app:layout_constraintEnd_toStartOf="@id/freq_label" - app:layout_constraintStart_toEndOf="@id/dm_method_label" - app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" /> - <TextView - android:id="@+id/freq_label" - android:layout_width="wrap_content" + app:layout_constraintBottom_toTopOf="@id/layout_parameters" + android:weightSum="4" + android:orientation="horizontal"> + <TextView + android:id="@+id/dm_method_label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="0.6" + android:text= "Method" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/spinner_dm_method" + /> + <Spinner + android:id="@+id/spinner_dm_method" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1.4" + android:padding="10dp" + app:layout_constraintStart_toEndOf="@id/dm_method_label" + app:layout_constraintEnd_toEndOf="parent" + /> + </LinearLayout> + <LinearLayout android:id="@+id/layout_parameters" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:text= "Freq" + app:layout_constraintTop_toBottomOf="@id/layout_methods" app:layout_constraintBottom_toTopOf="@id/btn_cs" - app:layout_constraintEnd_toStartOf="@id/spinner_freq" - app:layout_constraintStart_toEndOf="@id/spinner_dm_method" - app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" - /> - <Spinner - android:id="@+id/spinner_freq" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:padding="10dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@id/freq_label" - app:layout_constraintTop_toBottomOf="@id/init_ble_connection_container" /> + android:weightSum="4" + android:orientation="horizontal"> + <TextView + android:id="@+id/freq_label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="0.6" + android:text= "Freq" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/spinner_freq" + /> + <Spinner + android:id="@+id/spinner_freq" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1.4" + android:padding="10dp" + app:layout_constraintStart_toEndOf="@id/freq_label" + app:layout_constraintEnd_toStartOf="@id/duration_label" + /> + <TextView + android:id="@+id/duration_label" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="0.7" + android:text= "Duration (s)" + app:layout_constraintStart_toEndOf="@id/spinner_freq" + app:layout_constraintEnd_toStartOf="@id/spinner_duration" + /> + <Spinner + android:id="@+id/spinner_duration" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1.3" + android:padding="10dp" + app:layout_constraintStart_toEndOf="@id/duration_label" + app:layout_constraintEnd_toEndOf="parent" + /> + </LinearLayout> <Button android:id="@+id/btn_cs" android:layout_width="wrap_content" @@ -69,7 +101,7 @@ android:text="Start Distance Measurement" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/spinner_dm_method" /> + app:layout_constraintTop_toBottomOf="@id/layout_parameters" /> <TextView android:id="@+id/text_log" android:layout_width="match_parent" diff --git a/android/app/Android.bp b/android/app/Android.bp index 09d8a91865..db85914522 100644 --- a/android/app/Android.bp +++ b/android/app/Android.bp @@ -108,7 +108,6 @@ cc_library_shared { "libaconfig_storage_read_api_cc", "libbase", "libbluetooth", - "libbluetooth-dumpsys", "libbluetooth-types", "libbluetooth_core_rs", "libbluetooth_core_rs_bridge", diff --git a/android/app/OWNERS b/android/app/OWNERS index 6f7b40e8f5..9edeffc18e 100644 --- a/android/app/OWNERS +++ b/android/app/OWNERS @@ -9,4 +9,3 @@ okamil@google.com poahlo@google.com siyuanh@google.com wescande@google.com -yuyangh@google.com diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl index e619073fd9..158e37823f 100644 --- a/android/app/aidl/android/bluetooth/IBluetooth.aidl +++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl @@ -31,6 +31,7 @@ import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothSinkAudioPolicy; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDevice.BluetoothAddress; import android.bluetooth.BluetoothQualityReport; import android.bluetooth.IncomingRfcommSocketInfo; import android.bluetooth.OobData; @@ -40,6 +41,8 @@ import android.os.ParcelUuid; import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; +parcelable BluetoothDevice.BluetoothAddress; + /** * System private API for talking with the Bluetooth service. * @@ -63,6 +66,8 @@ interface IBluetooth boolean setName(in String name, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") String getIdentityAddress(in String address); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") + BluetoothDevice.BluetoothAddress getIdentityAddressWithType(in String address); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)") String getName(in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)") @@ -333,4 +338,10 @@ interface IBluetooth @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)") oneway void killBluetoothProcess(); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)") + boolean isLeCocSocketOffloadSupported(in AttributionSource source); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)") + boolean isRfcommSocketOffloadSupported(in AttributionSource source); } diff --git a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl index f9436a7fa2..9b2de3e169 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothGatt.aidl @@ -81,7 +81,7 @@ interface IBluetoothGatt { void setAdvertisingData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)") void setScanResponseData(in int advertiserId, in AdvertiseData data, in AttributionSource attributionSource); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)") void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)") void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters, in AttributionSource attributionSource); @@ -176,11 +176,8 @@ interface IBluetoothGatt { void disconnectAll(in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)") int numHwTrackFiltersAvailable(in AttributionSource attributionSource); - @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, - in int contNumber, in int supervisionTimeout, in AttributionSource attributionSource); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)") - void subrateModeRequest(in int clientIf, in String address, in int subrateMode, in AttributionSource attributionSource); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)") + int subrateModeRequest(in int clientIf, in BluetoothDevice device, in int subrateMode, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") List<DistanceMeasurementMethod> getSupportedDistanceMeasurementMethods(in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") diff --git a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl index 1bbf692cda..05f647d4a9 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl @@ -29,8 +29,12 @@ interface IBluetoothSocketManager { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)") @nullable ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in @nullable ParcelUuid uuid, int port, int flag); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") + @nullable ParcelFileDescriptor connectSocketwithOffload(in BluetoothDevice device, int type, in @nullable ParcelUuid uuid, int port, int flag, int dataPath, in String socketName, long hubId, long endpointId, int maximumPacketSize); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)") @nullable ParcelFileDescriptor createSocketChannel(int type, in @nullable String serviceName, in @nullable ParcelUuid uuid, int port, int flag); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") + @nullable ParcelFileDescriptor createSocketChannelWithOffload(int type, in @nullable String serviceName, in @nullable ParcelUuid uuid, int port, int flag, int dataPath, in String socketName, long hubId, long endpointId, int maximumPacketSize); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)") void requestMaximumTxDataLength(in BluetoothDevice device); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") diff --git a/android/app/jni/com_android_bluetooth_a2dp.cpp b/android/app/jni/com_android_bluetooth_a2dp.cpp index 2d6e255d15..df467e8427 100644 --- a/android/app/jni/com_android_bluetooth_a2dp.cpp +++ b/android/app/jni/com_android_bluetooth_a2dp.cpp @@ -36,15 +36,23 @@ #include "types/raw_address.h" namespace android { -static jmethodID method_onConnectionStateChanged; -static jmethodID method_onAudioStateChanged; -static jmethodID method_onCodecConfigChanged; -static jmethodID method_isMandatoryCodecPreferred; + +static struct { + jfieldID mNativeCallback; +} android_bluetooth_A2dpNativeInterface; + +static struct { + jmethodID onConnectionStateChanged; + jmethodID onAudioStateChanged; + jmethodID onCodecConfigChanged; + jmethodID isMandatoryCodecPreferred; +} android_bluetooth_A2dpNativeCallback; static struct { jclass clazz; jmethodID constructor; jmethodID getCodecType; + jmethodID getExtendedCodecType; jmethodID getCodecPriority; jmethodID getSampleRate; jmethodID getBitsPerSample; @@ -55,6 +63,12 @@ static struct { jmethodID getCodecSpecific4; } android_bluetooth_BluetoothCodecConfig; +static struct { + jclass clazz; + jmethodID constructor; + jmethodID getCodecId; +} android_bluetooth_BluetoothCodecType; + static std::vector<btav_a2dp_codec_info_t> supported_codecs; static std::shared_timed_mutex interface_mutex; @@ -81,8 +95,9 @@ static void bta2dp_connection_state_callback(const RawAddress& bd_addr, sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), reinterpret_cast<const jbyte*>(bd_addr.address)); - sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, addr.get(), - (jint)state); + sCallbackEnv->CallVoidMethod(mCallbacksObj, + android_bluetooth_A2dpNativeCallback.onConnectionStateChanged, + addr.get(), (jint)state); } static void bta2dp_audio_state_callback(const RawAddress& bd_addr, btav_audio_state_t state) { @@ -103,7 +118,9 @@ static void bta2dp_audio_state_callback(const RawAddress& bd_addr, btav_audio_st sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), reinterpret_cast<const jbyte*>(bd_addr.address)); - sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, addr.get(), (jint)state); + sCallbackEnv->CallVoidMethod(mCallbacksObj, + android_bluetooth_A2dpNativeCallback.onAudioStateChanged, addr.get(), + (jint)state); } static void bta2dp_audio_config_callback( @@ -168,9 +185,9 @@ static void bta2dp_audio_config_callback( sCallbackEnv->SetByteArrayRegion(addr.get(), 0, RawAddress::kLength, reinterpret_cast<const jbyte*>(bd_addr.address)); - sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged, addr.get(), - codecConfigObj, local_capabilities_array, - selectable_capabilities_array); + sCallbackEnv->CallVoidMethod( + mCallbacksObj, android_bluetooth_A2dpNativeCallback.onCodecConfigChanged, addr.get(), + codecConfigObj, local_capabilities_array, selectable_capabilities_array); } static bool bta2dp_mandatory_codec_preferred_callback(const RawAddress& bd_addr) { @@ -190,8 +207,9 @@ static bool bta2dp_mandatory_codec_preferred_callback(const RawAddress& bd_addr) } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, RawAddress::kLength, reinterpret_cast<const jbyte*>(bd_addr.address)); - return sCallbackEnv->CallBooleanMethod(mCallbacksObj, method_isMandatoryCodecPreferred, - addr.get()); + return sCallbackEnv->CallBooleanMethod( + mCallbacksObj, android_bluetooth_A2dpNativeCallback.isMandatoryCodecPreferred, + addr.get()); } static btav_source_callbacks_t sBluetoothA2dpCallbacks = { @@ -216,6 +234,7 @@ static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences( log::error("Invalid BluetoothCodecConfig instance"); continue; } + jint codecType = env->CallIntMethod(jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType); jint codecPriority = env->CallIntMethod( @@ -268,7 +287,8 @@ static void initNative(JNIEnv* env, jobject object, jint maxConnectedAudioDevice mCallbacksObj = nullptr; } - if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) { + if ((mCallbacksObj = env->NewGlobalRef(env->GetObjectField( + object, android_bluetooth_A2dpNativeInterface.mNativeCallback))) == nullptr) { log::error("Failed to allocate Global Ref for A2DP Callbacks"); return; } @@ -280,6 +300,13 @@ static void initNative(JNIEnv* env, jobject object, jint maxConnectedAudioDevice return; } + android_bluetooth_BluetoothCodecType.clazz = + (jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/BluetoothCodecType")); + if (android_bluetooth_BluetoothCodecType.clazz == nullptr) { + log::error("Failed to allocate Global Ref for BluetoothCodecType class"); + return; + } + std::vector<btav_a2dp_codec_config_t> codec_priorities = prepareCodecPreferences(env, object, codecConfigArray); @@ -316,23 +343,8 @@ static void cleanupNative(JNIEnv* env, jobject /* object */) { } static jobjectArray getSupportedCodecTypesNative(JNIEnv* env) { - jclass android_bluetooth_BluetoothCodecType_clazz = - (jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/BluetoothCodecType")); - if (android_bluetooth_BluetoothCodecType_clazz == nullptr) { - log::error("Failed to allocate Global Ref for BluetoothCodecType class"); - return nullptr; - } - - jmethodID init = env->GetMethodID(android_bluetooth_BluetoothCodecType_clazz, "<init>", - "(IJLjava/lang/String;)V"); - - if (init == nullptr) { - log::error("Failed to find method <init> of BluetoothCodecType class"); - return nullptr; - } - jobjectArray result = env->NewObjectArray(supported_codecs.size(), - android_bluetooth_BluetoothCodecType_clazz, nullptr); + android_bluetooth_BluetoothCodecType.clazz, nullptr); if (result == nullptr) { log::error("Failed to allocate result array of BluetoothCodecType"); @@ -341,7 +353,8 @@ static jobjectArray getSupportedCodecTypesNative(JNIEnv* env) { for (size_t index = 0; index < supported_codecs.size(); index++) { jobject codec_type = env->NewObject( - android_bluetooth_BluetoothCodecType_clazz, init, + android_bluetooth_BluetoothCodecType.clazz, + android_bluetooth_BluetoothCodecType.constructor, (jint)supported_codecs[index].codec_type, (jlong)supported_codecs[index].codec_id, env->NewStringUTF(supported_codecs[index].codec_name.c_str())); env->SetObjectArrayElement(result, index, codec_type); @@ -443,6 +456,7 @@ static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object, jbyt bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr)); std::vector<btav_a2dp_codec_config_t> codec_preferences = prepareCodecPreferences(env, object, codecConfigArray); + log::info("{}: {}", bd_addr, btav_a2dp_codec_config_t::PrintCodecs(codec_preferences)); bt_status_t status = btif_av_source_set_codec_config_preference(bd_addr, codec_preferences); if (status != BT_STATUS_SUCCESS) { @@ -475,20 +489,32 @@ int register_com_android_bluetooth_a2dp(JNIEnv* env) { } const JNIJavaMethod javaMethods[] = { - {"onConnectionStateChanged", "([BI)V", &method_onConnectionStateChanged}, - {"onAudioStateChanged", "([BI)V", &method_onAudioStateChanged}, + {"onConnectionStateChanged", "([BI)V", + &android_bluetooth_A2dpNativeCallback.onConnectionStateChanged}, + {"onAudioStateChanged", "([BI)V", + &android_bluetooth_A2dpNativeCallback.onAudioStateChanged}, {"onCodecConfigChanged", "([BLandroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;" "[Landroid/bluetooth/BluetoothCodecConfig;)V", - &method_onCodecConfigChanged}, - {"isMandatoryCodecPreferred", "([B)Z", &method_isMandatoryCodecPreferred}, + &android_bluetooth_A2dpNativeCallback.onCodecConfigChanged}, + {"isMandatoryCodecPreferred", "([B)Z", + &android_bluetooth_A2dpNativeCallback.isMandatoryCodecPreferred}, }; - GET_JAVA_METHODS(env, "com/android/bluetooth/a2dp/A2dpNativeInterface", javaMethods); + GET_JAVA_METHODS(env, "com/android/bluetooth/a2dp/A2dpNativeCallback", javaMethods); + + jclass jniA2dpNativeInterfaceClass = + env->FindClass("com/android/bluetooth/a2dp/A2dpNativeInterface"); + android_bluetooth_A2dpNativeInterface.mNativeCallback = + env->GetFieldID(jniA2dpNativeInterfaceClass, "mNativeCallback", + "Lcom/android/bluetooth/a2dp/A2dpNativeCallback;"); + env->DeleteLocalRef(jniA2dpNativeInterfaceClass); const JNIJavaMethod codecConfigCallbacksMethods[] = { {"<init>", "(IIIIIJJJJ)V", &android_bluetooth_BluetoothCodecConfig.constructor}, {"getCodecType", "()I", &android_bluetooth_BluetoothCodecConfig.getCodecType}, + {"getExtendedCodecType", "()Landroid/bluetooth/BluetoothCodecType;", + &android_bluetooth_BluetoothCodecConfig.getExtendedCodecType}, {"getCodecPriority", "()I", &android_bluetooth_BluetoothCodecConfig.getCodecPriority}, {"getSampleRate", "()I", &android_bluetooth_BluetoothCodecConfig.getSampleRate}, {"getBitsPerSample", "()I", &android_bluetooth_BluetoothCodecConfig.getBitsPerSample}, @@ -500,6 +526,12 @@ int register_com_android_bluetooth_a2dp(JNIEnv* env) { }; GET_JAVA_METHODS(env, "android/bluetooth/BluetoothCodecConfig", codecConfigCallbacksMethods); + const JNIJavaMethod bluetoothCodecTypeMethods[] = { + {"<init>", "(IJLjava/lang/String;)V", &android_bluetooth_BluetoothCodecType.constructor}, + {"getCodecId", "()J", &android_bluetooth_BluetoothCodecType.getCodecId}, + }; + GET_JAVA_METHODS(env, "android/bluetooth/BluetoothCodecType", bluetoothCodecTypeMethods); + return 0; } } // namespace android diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp index 3a0a8d1aac..805e22b0df 100644 --- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp @@ -1782,11 +1782,14 @@ static jboolean setBufferLengthMillisNative(JNIEnv* /* env */, jobject /* obj */ } static jint connectSocketNative(JNIEnv* env, jobject /* obj */, jbyteArray address, jint type, - jbyteArray uuid, jint port, jint flag, jint callingUid) { + jbyteArray uuid, jint port, jint flag, jint callingUid, + jint dataPath, jstring socketName, jlong hubId, jlong endPointId, + jint maxRxPacketSize) { int socket_fd = INVALID_FD; jbyte* addr = nullptr; jbyte* uuidBytes = nullptr; Uuid btUuid; + const char* nativeSocketName = nullptr; if (!sBluetoothSocketInterface) { goto done; @@ -1799,9 +1802,13 @@ static jint connectSocketNative(JNIEnv* env, jobject /* obj */, jbyteArray addre } btUuid = Uuid::From128BitBE(reinterpret_cast<uint8_t*>(uuidBytes)); + if (socketName != nullptr) { + nativeSocketName = env->GetStringUTFChars(socketName, nullptr); + } if (sBluetoothSocketInterface->connect(reinterpret_cast<RawAddress*>(addr), (btsock_type_t)type, - &btUuid, port, &socket_fd, flag, - callingUid) != BT_STATUS_SUCCESS) { + &btUuid, port, &socket_fd, flag, callingUid, + (btsock_data_path_t)dataPath, nativeSocketName, hubId, + endPointId, maxRxPacketSize) != BT_STATUS_SUCCESS) { socket_fd = INVALID_FD; } @@ -1812,16 +1819,21 @@ done: if (uuidBytes) { env->ReleaseByteArrayElements(uuid, uuidBytes, 0); } + if (nativeSocketName) { + env->ReleaseStringUTFChars(socketName, nativeSocketName); + } return socket_fd; } static jint createSocketChannelNative(JNIEnv* env, jobject /* obj */, jint type, jstring serviceName, jbyteArray uuid, jint port, jint flag, - jint callingUid) { + jint callingUid, jint dataPath, jstring socketName, + jlong hubId, jlong endPointId, jint maxRxPacketSize) { int socket_fd = INVALID_FD; jbyte* uuidBytes = nullptr; Uuid btUuid; const char* nativeServiceName = nullptr; + const char* nativeSocketName = nullptr; if (!sBluetoothSocketInterface) { goto done; @@ -1835,9 +1847,14 @@ static jint createSocketChannelNative(JNIEnv* env, jobject /* obj */, jint type, goto done; } btUuid = Uuid::From128BitBE(reinterpret_cast<uint8_t*>(uuidBytes)); + if (socketName != nullptr) { + nativeSocketName = env->GetStringUTFChars(socketName, nullptr); + } if (sBluetoothSocketInterface->listen((btsock_type_t)type, nativeServiceName, &btUuid, port, - &socket_fd, flag, callingUid) != BT_STATUS_SUCCESS) { + &socket_fd, flag, callingUid, (btsock_data_path_t)dataPath, + nativeSocketName, hubId, endPointId, + maxRxPacketSize) != BT_STATUS_SUCCESS) { socket_fd = INVALID_FD; } @@ -1848,6 +1865,9 @@ done: if (nativeServiceName) { env->ReleaseStringUTFChars(serviceName, nativeServiceName); } + if (nativeSocketName) { + env->ReleaseStringUTFChars(socketName, nativeSocketName); + } return socket_fd; } @@ -2267,8 +2287,9 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { {"setBufferLengthMillisNative", "(II)Z", reinterpret_cast<void*>(setBufferLengthMillisNative)}, {"getMetricIdNative", "([B)I", reinterpret_cast<void*>(getMetricIdNative)}, - {"connectSocketNative", "([BI[BIII)I", reinterpret_cast<void*>(connectSocketNative)}, - {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I", + {"connectSocketNative", "([BI[BIIIILjava/lang/String;JJI)I", + reinterpret_cast<void*>(connectSocketNative)}, + {"createSocketChannelNative", "(ILjava/lang/String;[BIIIILjava/lang/String;JJI)I", reinterpret_cast<void*>(createSocketChannelNative)}, {"requestMaximumTxDataLengthNative", "([B)V", reinterpret_cast<void*>(requestMaximumTxDataLengthNative)}, diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp index 54e97282a1..80e334e844 100644 --- a/android/app/jni/com_android_bluetooth_gatt.cpp +++ b/android/app/jni/com_android_bluetooth_gatt.cpp @@ -247,28 +247,6 @@ void btgattc_register_app_cb(int status, int clientIf, const Uuid& app_uuid) { UUID_PARAMS(app_uuid)); } -void btgattc_scan_result_cb(uint16_t event_type, uint8_t addr_type, RawAddress* bda, - uint8_t primary_phy, uint8_t secondary_phy, uint8_t advertising_sid, - int8_t tx_power, int8_t rssi, uint16_t periodic_adv_int, - std::vector<uint8_t> adv_data, RawAddress* original_bda) { - std::shared_lock<std::shared_mutex> lock(callbacks_mutex); - CallbackEnv sCallbackEnv(__func__); - if (!sCallbackEnv.valid() || !mScanCallbacksObj) { - return; - } - - ScopedLocalRef<jstring> address(sCallbackEnv.get(), bdaddr2newjstr(sCallbackEnv.get(), bda)); - ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), sCallbackEnv->NewByteArray(adv_data.size())); - sCallbackEnv->SetByteArrayRegion(jb.get(), 0, adv_data.size(), (jbyte*)adv_data.data()); - - ScopedLocalRef<jstring> original_address(sCallbackEnv.get(), - bdaddr2newjstr(sCallbackEnv.get(), original_bda)); - - sCallbackEnv->CallVoidMethod(mScanCallbacksObj, method_onScanResult, event_type, addr_type, - address.get(), primary_phy, secondary_phy, advertising_sid, tx_power, - rssi, periodic_adv_int, jb.get(), original_address.get()); -} - void btgattc_open_cb(int conn_id, int status, int clientIf, const RawAddress& bda) { std::shared_lock<std::shared_mutex> lock(callbacks_mutex); CallbackEnv sCallbackEnv(__func__); @@ -442,65 +420,6 @@ void btgattc_congestion_cb(int conn_id, bool congested) { sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientCongestion, conn_id, congested); } -void btgattc_batchscan_reports_cb(int client_if, int status, int report_format, int num_records, - std::vector<uint8_t> data) { - std::shared_lock<std::shared_mutex> lock(callbacks_mutex); - CallbackEnv sCallbackEnv(__func__); - if (!sCallbackEnv.valid() || !mScanCallbacksObj) { - return; - } - ScopedLocalRef<jbyteArray> jb(sCallbackEnv.get(), sCallbackEnv->NewByteArray(data.size())); - sCallbackEnv->SetByteArrayRegion(jb.get(), 0, data.size(), (jbyte*)data.data()); - - sCallbackEnv->CallVoidMethod(mScanCallbacksObj, method_onBatchScanReports, status, client_if, - report_format, num_records, jb.get()); -} - -void btgattc_batchscan_threshold_cb(int client_if) { - std::shared_lock<std::shared_mutex> lock(callbacks_mutex); - CallbackEnv sCallbackEnv(__func__); - if (!sCallbackEnv.valid() || !mScanCallbacksObj) { - return; - } - sCallbackEnv->CallVoidMethod(mScanCallbacksObj, method_onBatchScanThresholdCrossed, client_if); -} - -void btgattc_track_adv_event_cb(btgatt_track_adv_info_t* p_adv_track_info) { - std::shared_lock<std::shared_mutex> lock(callbacks_mutex); - CallbackEnv sCallbackEnv(__func__); - if (!sCallbackEnv.valid() || !mScanCallbacksObj) { - return; - } - - ScopedLocalRef<jstring> address(sCallbackEnv.get(), - bdaddr2newjstr(sCallbackEnv.get(), &p_adv_track_info->bd_addr)); - - ScopedLocalRef<jbyteArray> jb_adv_pkt(sCallbackEnv.get(), - sCallbackEnv->NewByteArray(p_adv_track_info->adv_pkt_len)); - ScopedLocalRef<jbyteArray> jb_scan_rsp( - sCallbackEnv.get(), sCallbackEnv->NewByteArray(p_adv_track_info->scan_rsp_len)); - - sCallbackEnv->SetByteArrayRegion(jb_adv_pkt.get(), 0, p_adv_track_info->adv_pkt_len, - (jbyte*)p_adv_track_info->p_adv_pkt_data); - - sCallbackEnv->SetByteArrayRegion(jb_scan_rsp.get(), 0, p_adv_track_info->scan_rsp_len, - (jbyte*)p_adv_track_info->p_scan_rsp_data); - - ScopedLocalRef<jobject> trackadv_obj( - sCallbackEnv.get(), - sCallbackEnv->CallObjectMethod( - mScanCallbacksObj, method_createOnTrackAdvFoundLostObject, - p_adv_track_info->client_if, p_adv_track_info->adv_pkt_len, jb_adv_pkt.get(), - p_adv_track_info->scan_rsp_len, jb_scan_rsp.get(), p_adv_track_info->filt_index, - p_adv_track_info->advertiser_state, p_adv_track_info->advertiser_info_present, - address.get(), p_adv_track_info->addr_type, p_adv_track_info->tx_power, - p_adv_track_info->rssi_value, p_adv_track_info->time_stamp)); - - if (NULL != trackadv_obj.get()) { - sCallbackEnv->CallVoidMethod(mScanCallbacksObj, method_onTrackAdvFoundLost, trackadv_obj.get()); - } -} - void fillGattDbElementArray(JNIEnv* env, jobject* array, const btgatt_db_element_t* db, int count) { // Because JNI uses a different class loader in the callback context, we // cannot simply get the class. @@ -628,13 +547,6 @@ void btgattc_subrate_change_cb(int conn_id, uint16_t subrate_factor, uint16_t la latency, cont_num, timeout, status); } -static const btgatt_scanner_callbacks_t sGattScannerCallbacks = { - btgattc_scan_result_cb, - btgattc_batchscan_reports_cb, - btgattc_batchscan_threshold_cb, - btgattc_track_adv_event_cb, -}; - static const btgatt_client_callbacks_t sGattClientCallbacks = { btgattc_register_app_cb, btgattc_open_cb, @@ -919,7 +831,6 @@ static const btgatt_callbacks_t sGattCallbacks = { sizeof(btgatt_callbacks_t), &sGattClientCallbacks, &sGattServerCallbacks, - &sGattScannerCallbacks, }; class JniAdvertisingCallbacks : AdvertisingCallbacks { @@ -2046,14 +1957,16 @@ static void gattConnectionParameterUpdateNative(JNIEnv* env, jobject /* object * (uint16_t)max_ce_len); } -static void gattSubrateRequestNative(JNIEnv* env, jobject /* object */, jint /* client_if */, - jstring address, jint subrate_min, jint subrate_max, - jint max_latency, jint cont_num, jint sup_timeout) { +static int gattSubrateRequestNative(JNIEnv* env, jobject /* object */, jint /* client_if */, + jstring address, jint subrate_min, jint subrate_max, + jint max_latency, jint cont_num, jint sup_timeout) { if (!sGattIf) { - return; + return 1; // BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED } + // TODO does bt_status_t align with BluetoothStatusCodes ? sGattIf->client->subrate_request(str2addr(env, address), subrate_min, subrate_max, max_latency, cont_num, sup_timeout); + return 0; // BluetoothStatusCodes.SUCCESS } void batchscan_cfg_storage_cb(uint8_t client_if, uint8_t status) { @@ -3060,7 +2973,7 @@ static int register_com_android_bluetooth_gatt_(JNIEnv* env) { {"gattServerSendIndicationNative", "(III[B)V", (void*)gattServerSendIndicationNative}, {"gattServerSendNotificationNative", "(III[B)V", (void*)gattServerSendNotificationNative}, {"gattServerSendResponseNative", "(IIIIII[BI)V", (void*)gattServerSendResponseNative}, - {"gattSubrateRequestNative", "(ILjava/lang/String;IIIII)V", + {"gattSubrateRequestNative", "(ILjava/lang/String;IIIII)I", (void*)gattSubrateRequestNative}, {"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void*)gattTestNative}, diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp index 4ce436e5c4..3a3d8ceed6 100644 --- a/android/app/jni/com_android_bluetooth_le_audio.cpp +++ b/android/app/jni/com_android_bluetooth_le_audio.cpp @@ -706,7 +706,7 @@ static void setUnicastMonitorModeNative(JNIEnv* /* env */, jobject /* object */, sLeAudioClientInterface->SetUnicastMonitorMode(direction, enable); } -static void sendAudioProfilePreferencesNative(JNIEnv* /* env */, jint groupId, +static void sendAudioProfilePreferencesNative(JNIEnv* /* env */, jobject /* object */, jint groupId, jboolean isOutputPreferenceLeAudio, jboolean isDuplexPreferenceLeAudio) { std::shared_lock<std::shared_timed_mutex> lock(interface_mutex); diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java index f6d065eb76..e73c4f99ba 100644 --- a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java @@ -100,7 +100,13 @@ public class BluetoothMethodProxy { final String selection, final String[] selectionArgs, final String sortOrder) { - return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder); + try { + return contentResolver.query( + contentUri, projection, selection, selectionArgs, sortOrder); + } catch (Exception e) { + Log.e(TAG, "Exception happened" + e + "\n" + Log.getStackTraceString(new Throwable())); + return null; + } } /** Proxies {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}. */ @@ -110,7 +116,12 @@ public class BluetoothMethodProxy { final String[] projection, final Bundle queryArgs, final CancellationSignal cancellationSignal) { - return contentResolver.query(contentUri, projection, queryArgs, cancellationSignal); + try { + return contentResolver.query(contentUri, projection, queryArgs, cancellationSignal); + } catch (Exception e) { + Log.e(TAG, "Exception happened " + e + "\n" + Log.getStackTraceString(new Throwable())); + return null; + } } /** Proxies {@link ContentResolver#insert(Uri, ContentValues)}. */ diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java index 663227f899..ca897af46e 100644 --- a/android/app/src/com/android/bluetooth/Utils.java +++ b/android/app/src/com/android/bluetooth/Utils.java @@ -1290,7 +1290,11 @@ public final class Utils { long elapsedRealtime(); } - public static final TimeProvider sSystemClock = new SystemClockTimeProvider(); + private static final TimeProvider sSystemClock = new SystemClockTimeProvider(); + + public static TimeProvider getSystemClock() { + return sSystemClock; + } private static final class SystemClockTimeProvider implements TimeProvider { @Override diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeCallback.java b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeCallback.java new file mode 100644 index 0000000000..4ea0fbe96e --- /dev/null +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeCallback.java @@ -0,0 +1,95 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.a2dp; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothCodecConfig; +import android.bluetooth.BluetoothCodecStatus; +import android.bluetooth.BluetoothDevice; +import android.util.Log; + +import com.android.bluetooth.btservice.AdapterService; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; + +class A2dpNativeCallback { + static final String TAG = A2dpNativeCallback.class.getSimpleName(); + + private final AdapterService mAdapterService; + private final A2dpService mA2dpService; + + @VisibleForTesting + A2dpNativeCallback(@NonNull AdapterService adapterService, @NonNull A2dpService a2dpService) { + mAdapterService = requireNonNull(adapterService); + mA2dpService = requireNonNull(a2dpService); + } + + private BluetoothDevice getDevice(byte[] address) { + return mAdapterService.getDeviceFromByte(address); + } + + @VisibleForTesting + void onConnectionStateChanged(byte[] address, int state) { + A2dpStackEvent event = + new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); + event.device = getDevice(address); + event.valueInt = state; + + Log.d(TAG, "onConnectionStateChanged: " + event); + mA2dpService.messageFromNative(event); + } + + @VisibleForTesting + void onAudioStateChanged(byte[] address, int state) { + A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); + event.device = getDevice(address); + event.valueInt = state; + + Log.d(TAG, "onAudioStateChanged: " + event); + mA2dpService.messageFromNative(event); + } + + @VisibleForTesting + void onCodecConfigChanged( + byte[] address, + BluetoothCodecConfig newCodecConfig, + BluetoothCodecConfig[] codecsLocalCapabilities, + BluetoothCodecConfig[] codecsSelectableCapabilities) { + A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); + event.device = getDevice(address); + event.codecStatus = + new BluetoothCodecStatus( + newCodecConfig, + Arrays.asList(codecsLocalCapabilities), + Arrays.asList(codecsSelectableCapabilities)); + + Log.d(TAG, "onCodecConfigChanged: " + event); + mA2dpService.messageFromNative(event); + } + + @VisibleForTesting + boolean isMandatoryCodecPreferred(byte[] address) { + int enabled = mA2dpService.getOptionalCodecsEnabled(getDevice(address)); + + Log.d(TAG, "isMandatoryCodecPreferred: optional preference " + enabled); + return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; + } +} diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java index 2de9f0d8fa..709dc954c5 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java @@ -21,65 +21,37 @@ */ package com.android.bluetooth.a2dp; -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothAdapter; +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; import android.bluetooth.BluetoothCodecConfig; -import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothCodecType; import android.bluetooth.BluetoothDevice; -import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.flags.Flags; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Native; + import java.util.Arrays; import java.util.List; -import java.util.Objects; /** A2DP Native Interface to/from JNI. */ public class A2dpNativeInterface { private static final String TAG = A2dpNativeInterface.class.getSimpleName(); - private BluetoothAdapter mAdapter; - private AdapterService mAdapterService; - @GuardedBy("INSTANCE_LOCK") - private static A2dpNativeInterface sInstance; + private final AdapterService mAdapterService; + @Native private final A2dpNativeCallback mNativeCallback; - private static BluetoothCodecType[] sSupportedCodecTypes; - - private static final Object INSTANCE_LOCK = new Object(); + private BluetoothCodecType[] mSupportedCodecTypes; @VisibleForTesting - private A2dpNativeInterface() { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (mAdapter == null) { - Log.wtf(TAG, "No Bluetooth Adapter Available"); - } - mAdapterService = - Objects.requireNonNull( - AdapterService.getAdapterService(), - "AdapterService cannot be null when A2dpNativeInterface init"); - } - - /** Get singleton instance. */ - public static A2dpNativeInterface getInstance() { - synchronized (INSTANCE_LOCK) { - if (sInstance == null) { - sInstance = new A2dpNativeInterface(); - } - return sInstance; - } - } - - /** Set singleton instance. */ - @VisibleForTesting - public static void setInstance(A2dpNativeInterface instance) { - synchronized (INSTANCE_LOCK) { - sInstance = instance; - } + A2dpNativeInterface( + @NonNull AdapterService adapterService, @NonNull A2dpNativeCallback nativeCallback) { + mAdapterService = requireNonNull(adapterService); + mNativeCallback = requireNonNull(nativeCallback); } /** @@ -103,10 +75,10 @@ public class A2dpNativeInterface { /** Returns the list of locally supported codec types. */ public List<BluetoothCodecType> getSupportedCodecTypes() { - if (sSupportedCodecTypes == null) { - sSupportedCodecTypes = getSupportedCodecTypesNative(); + if (mSupportedCodecTypes == null) { + mSupportedCodecTypes = getSupportedCodecTypesNative(); } - return Arrays.asList(sSupportedCodecTypes); + return Arrays.asList(mSupportedCodecTypes); } /** @@ -161,10 +133,6 @@ public class A2dpNativeInterface { return setCodecConfigPreferenceNative(getByteAddress(device), codecConfigArray); } - private BluetoothDevice getDevice(byte[] address) { - return mAdapterService.getDeviceFromByte(address); - } - private byte[] getByteAddress(BluetoothDevice device) { if (device == null) { return Utils.getBytesFromAddress("00:00:00:00:00:00"); @@ -176,68 +144,6 @@ public class A2dpNativeInterface { } } - private void sendMessageToService(A2dpStackEvent event) { - A2dpService service = A2dpService.getA2dpService(); - if (service != null) { - service.messageFromNative(event); - } else { - Log.w(TAG, "Event ignored, service not available: " + event); - } - } - - // 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 onConnectionStateChanged(byte[] address, int state) { - A2dpStackEvent event = - new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); - event.device = getDevice(address); - event.valueInt = state; - - Log.d(TAG, "onConnectionStateChanged: " + event); - sendMessageToService(event); - } - - private void onAudioStateChanged(byte[] address, int state) { - A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); - event.device = getDevice(address); - event.valueInt = state; - - Log.d(TAG, "onAudioStateChanged: " + event); - sendMessageToService(event); - } - - private void onCodecConfigChanged( - byte[] address, - BluetoothCodecConfig newCodecConfig, - BluetoothCodecConfig[] codecsLocalCapabilities, - BluetoothCodecConfig[] codecsSelectableCapabilities) { - A2dpStackEvent event = new A2dpStackEvent(A2dpStackEvent.EVENT_TYPE_CODEC_CONFIG_CHANGED); - event.device = getDevice(address); - event.codecStatus = - new BluetoothCodecStatus( - newCodecConfig, - Arrays.asList(codecsLocalCapabilities), - Arrays.asList(codecsSelectableCapabilities)); - Log.d(TAG, "onCodecConfigChanged: " + event); - sendMessageToService(event); - } - - private boolean isMandatoryCodecPreferred(byte[] address) { - A2dpService service = A2dpService.getA2dpService(); - if (service != null) { - int enabled = service.getOptionalCodecsEnabled(getDevice(address)); - Log.d(TAG, "isMandatoryCodecPreferred: optional preference " + enabled); - // Optional codecs are more preferred if possible - return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; - } else { - Log.w(TAG, "isMandatoryCodecPreferred: service not available"); - return false; - } - } - - // Native methods that call into the JNI interface private native void initNative( int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities, diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index 355cb36929..136a0d4621 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java @@ -24,6 +24,7 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static com.android.bluetooth.Utils.checkCallerTargetSdk; import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElseGet; import android.annotation.NonNull; import android.annotation.RequiresPermission; @@ -92,7 +93,6 @@ public class A2dpService extends ProfileService { private final CompanionDeviceManager mCompanionDeviceManager; private final Looper mLooper; private final Handler mHandler; - private final HandlerThread mStateMachinesThread; // Upper limit of all A2DP devices that are Connected or Connecting private final int mMaxConnectedAudioDevices; @@ -108,9 +108,6 @@ public class A2dpService extends ProfileService { // Protect setActiveDevice()/removeActiveDevice() so all invoked is handled sequentially private final Object mActiveSwitchingGuard = new Object(); - // Timeout for state machine thread join, to prevent potential ANR. - private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000; - // Upper limit of all A2DP devices: Bonded or Connected private static final int MAX_A2DP_STATE_MACHINES = 50; // A2DP Offload Enabled in platform @@ -120,14 +117,20 @@ public class A2dpService extends ProfileService { new AudioManagerAudioDeviceCallback(); public A2dpService(AdapterService adapterService) { - this(adapterService, A2dpNativeInterface.getInstance(), Looper.getMainLooper()); + this(adapterService, null, Looper.getMainLooper()); } @VisibleForTesting A2dpService(AdapterService adapterService, A2dpNativeInterface nativeInterface, Looper looper) { super(requireNonNull(adapterService)); mAdapterService = adapterService; - mNativeInterface = requireNonNull(nativeInterface); + mNativeInterface = + requireNonNullElseGet( + nativeInterface, + () -> + new A2dpNativeInterface( + adapterService, + new A2dpNativeCallback(adapterService, this))); mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); mAudioManager = requireNonNull(getSystemService(AudioManager.class)); mCompanionDeviceManager = requireNonNull(getSystemService(CompanionDeviceManager.class)); @@ -137,13 +140,6 @@ public class A2dpService extends ProfileService { mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); - if (!Flags.a2dpServiceLooper()) { - mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); - mStateMachinesThread.start(); - } else { - mStateMachinesThread = null; - } - mA2dpCodecConfig = new A2dpCodecConfig(this, mNativeInterface); mNativeInterface.init( @@ -200,15 +196,6 @@ public class A2dpService extends ProfileService { mStateMachines.clear(); } - if (mStateMachinesThread != null) { - try { - mStateMachinesThread.quitSafely(); - mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); - } catch (InterruptedException e) { - // Do not rethrow as we are shutting down anyway - } - } - mHandler.removeCallbacksAndMessages(null); } @@ -899,9 +886,12 @@ public class A2dpService extends ProfileService { // Handle messages from native (JNI) to Java void messageFromNative(A2dpStackEvent stackEvent) { - requireNonNull(stackEvent.device); + if (!isAvailable()) { + Log.w(TAG, "messageFromNative(): service is not available"); + return; + } + BluetoothDevice device = requireNonNull(stackEvent.device); synchronized (mStateMachines) { - BluetoothDevice device = stackEvent.device; A2dpStateMachine sm = mStateMachines.get(device); if (sm == null) { if (stackEvent.type == A2dpStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { @@ -1015,7 +1005,7 @@ public class A2dpService extends ProfileService { device, mNativeInterface, mA2dpOffloadEnabled, - Flags.a2dpServiceLooper() ? mLooper : mStateMachinesThread.getLooper()); + mLooper); mStateMachines.put(device, sm); return sm; } @@ -1170,7 +1160,12 @@ public class A2dpService extends ProfileService { if (sm == null) { return; } - if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { + + // Bond removal implies that the ACL is disconnected and device properties are removed. + // If pseudo address is not same as the identity address, all further events from the + // native stack would get ignored. So the state machine must be removed right away. + if (!Flags.a2dpCleanupOnRemoveDevice() + && sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { return; } } diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java index f0b5a028ac..a2e4d0b4d2 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java @@ -328,7 +328,14 @@ final class A2dpStateMachine extends StateMachine { + messageWhatToString(message.what)); switch (message.what) { - case MESSAGE_CONNECT -> deferMessage(message); + case MESSAGE_CONNECT -> { + if (Flags.a2dpSmIgnoreConnectEventsInConnectingState() + && !hasDeferredMessages(MESSAGE_DISCONNECT)) { + Log.w(TAG, "Connecting: CONNECT ignored: " + mDevice); + } else { + deferMessage(message); + } + } case MESSAGE_CONNECT_TIMEOUT -> { Log.w(TAG, "Connecting connection timeout: " + mDevice); mA2dpNativeInterface.disconnectA2dp(mDevice); diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java index e9e0f8c714..b04f684f34 100644 --- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java +++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java @@ -378,10 +378,7 @@ public class MediaPlayerList { cb.run(playerId, false, "", 0); return; } - - mBrowsingPlayerId = playerId; MediaBrowserWrapper wrapper = mMediaBrowserWrappers.get(playerId); - // If player is different than actual or if the given path is wrong, process rootId if (playerId != mBrowsingPlayerId || currentPath.equals("")) { wrapper.getRootId( @@ -399,6 +396,7 @@ public class MediaPlayerList { cb.run(playerId, true, currentPath, itemList.size()); }); } + mBrowsingPlayerId = playerId; } else { // Fix PTS AVRCP/TG/MCN/CB/BI-02-C if (Utils.isPtsTestMode()) { diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java index 93579a845c..b6aa7cf8b6 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java @@ -66,7 +66,6 @@ public class AvrcpTargetService extends ProfileService { // Cover Art Service (Storage + BIP Server) private final AvrcpCoverArtService mAvrcpCoverArtService; - private final AdapterService mAdapterService; private final AvrcpVersion mAvrcpVersion; private final MediaPlayerList mMediaPlayerList; private final PlayerSettingsManager mPlayerSettingsManager; @@ -100,12 +99,29 @@ public class AvrcpTargetService extends ProfileService { private static AvrcpTargetService sInstance = null; public AvrcpTargetService(AdapterService adapterService) { + this( + requireNonNull(adapterService), + adapterService.getSystemService(AudioManager.class), + AvrcpNativeInterface.getInstance(), + new AvrcpVolumeManager( + requireNonNull(adapterService), + adapterService.getSystemService(AudioManager.class), + AvrcpNativeInterface.getInstance()), + Looper.myLooper()); + } + + @VisibleForTesting + AvrcpTargetService( + AdapterService adapterService, + AudioManager audioManager, + AvrcpNativeInterface nativeInterface, + AvrcpVolumeManager volumeManager, + Looper looper) { super(requireNonNull(adapterService)); - mAdapterService = adapterService; - mAudioManager = requireNonNull(getSystemService(AudioManager.class)); - mNativeInterface = requireNonNull(AvrcpNativeInterface.getInstance()); + mAudioManager = requireNonNull(audioManager); + mNativeInterface = requireNonNull(nativeInterface); - mMediaPlayerList = new MediaPlayerList(Looper.myLooper(), this); + mMediaPlayerList = new MediaPlayerList(looper, this); IntentFilter userFilter = new IntentFilter(); userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); @@ -119,7 +135,7 @@ public class AvrcpTargetService extends ProfileService { mNativeInterface.init(this); mAvrcpVersion = AvrcpVersion.getCurrentSystemPropertiesValue(); - mVolumeManager = new AvrcpVolumeManager(mAdapterService, mAudioManager, mNativeInterface); + mVolumeManager = requireNonNull(volumeManager); UserManager userManager = getApplicationContext().getSystemService(UserManager.class); if (userManager.isUserUnlocked()) { @@ -257,6 +273,7 @@ public class AvrcpTargetService extends ProfileService { mPlayerSettingsManager.cleanup(); mMediaPlayerList.cleanup(); mNativeInterface.cleanup(); + mVolumeManager.cleanup(); getApplicationContext().unregisterReceiver(mUserUnlockedReceiver); } diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java index 36301c2f54..b11aeebe24 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java @@ -421,6 +421,11 @@ class AvrcpVolumeManager extends AudioDeviceCallback { mDeviceMap.remove(device); } + /** Cleans up and unregisters any registered callbacks. */ + void cleanup() { + mAudioManager.unregisterAudioDeviceCallback(this); + } + public void dump(StringBuilder sb) { sb.append("AvrcpVolumeManager:\n"); sb.append(" mCurrentDevice: ").append(mCurrentDevice).append("\n"); diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java index 3038c5ee19..67b2b4a005 100644 --- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java +++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java @@ -118,7 +118,7 @@ public class BatteryStateMachine extends StateMachine { } synchronized boolean isConnected() { - return getCurrentState() == mConnected; + return mLastConnectionState == BluetoothProfile.STATE_CONNECTED; } private static String messageWhatToString(int what) { @@ -153,17 +153,7 @@ public class BatteryStateMachine extends StateMachine { @BluetoothProfile.BtProfileState int getConnectionState() { - String currentState = getCurrentState().getName(); - return switch (currentState) { - case "Disconnected" -> BluetoothProfile.STATE_DISCONNECTED; - case "Connecting" -> BluetoothProfile.STATE_CONNECTING; - case "Connected" -> BluetoothProfile.STATE_CONNECTED; - case "Disconnecting" -> BluetoothProfile.STATE_DISCONNECTING; - default -> { - Log.e(TAG, "Bad currentState: " + currentState); - yield BluetoothProfile.STATE_DISCONNECTED; - } - }; + return mLastConnectionState; } void dispatchConnectionStateChanged(int toState) { 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 72648cbcfd..581951caa6 100644 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java @@ -19,6 +19,7 @@ package com.android.bluetooth.bass_client; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.Manifest.permission.BLUETOOTH_SCAN; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; @@ -27,6 +28,7 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiGetLocalMetad import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper; +import static com.android.bluetooth.flags.Flags.leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator; import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails; import android.annotation.RequiresPermission; @@ -99,6 +101,7 @@ import java.util.Optional; import java.util.PriorityQueue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; /** Broadcast Assistant Scan Service */ @@ -167,8 +170,8 @@ public class BassClientService extends ProfileService { private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources = new ConcurrentHashMap<>(); private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); - private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap = - new ConcurrentHashMap<>(); + private final Map<BluetoothDevice, Map<Integer, BluetoothLeBroadcastMetadata>> + mBroadcastMetadataMap = new ConcurrentHashMap<>(); private final HashSet<BluetoothDevice> mPausedBroadcastSinks = new HashSet<>(); private final Map<BluetoothDevice, Pair<Integer, Integer>> mSinksWaitingForPast = new HashMap<>(); @@ -285,7 +288,7 @@ public class BassClientService extends ProfileService { break; } Handler handler = getOrCreateHandler(broadcastId); - if (!handler.hasMessagesOrCallbacks()) { + if (!hasAnyMessagesOrCallbacks(handler)) { mHandlers.remove(broadcastId); } } @@ -308,7 +311,7 @@ public class BassClientService extends ProfileService { } Handler handler = getOrCreateHandler(broadcastId); handler.removeMessages(msg); - if (!handler.hasMessagesOrCallbacks()) { + if (!hasAnyMessagesOrCallbacks(handler)) { mHandlers.remove(broadcastId); } } @@ -326,7 +329,7 @@ public class BassClientService extends ProfileService { Map.Entry<Integer, Handler> entry = iterator.next(); Handler handler = entry.getValue(); handler.removeMessages(msg); - if (!handler.hasMessagesOrCallbacks()) { + if (!hasAnyMessagesOrCallbacks(handler)) { iterator.remove(); } } @@ -339,6 +342,16 @@ public class BassClientService extends ProfileService { Handler handler = getOrCreateHandler(broadcastId); return handler.hasMessages(msg); } + + private boolean hasAnyMessagesOrCallbacks(Handler handler) { + if (android.os.Flags.mainlineVcnPlatformApi()) { + return handler.hasMessagesOrCallbacks(); + } else { + return handler.hasMessages(MESSAGE_SYNC_LOST_TIMEOUT) + || handler.hasMessages(MESSAGE_BROADCAST_MONITOR_TIMEOUT) + || handler.hasMessages(MESSAGE_BIG_MONITOR_TIMEOUT); + } + } } public BassClientService(Context ctx) { @@ -907,10 +920,16 @@ public class BassClientService extends ProfileService { private void enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj) { log("enqueueSourceGroupOp device: " + sink + ", msgId: " + msgId); - if (!mPendingGroupOp.containsKey(sink)) { - mPendingGroupOp.put(sink, new ArrayList()); - } - mPendingGroupOp.get(sink).add(new Pair<Integer, Object>(msgId, obj)); + mPendingGroupOp.compute( + sink, + (key, opsToModify) -> { + List<Pair<Integer, Object>> operations = + (opsToModify == null) + ? new ArrayList<>() + : new ArrayList<>(opsToModify); + operations.add(new Pair<>(msgId, obj)); + return operations; + }); } private boolean isSuccess(int status) { @@ -957,67 +976,73 @@ public class BassClientService extends ProfileService { + ", reqMsg: " + reqMsg); - List<Pair<Integer, Object>> operations = mPendingGroupOp.get(sink); - if (operations == null) { - return; - } + AtomicBoolean shouldUpdateAssistantActive = new AtomicBoolean(false); - switch (reqMsg) { - case BassClientStateMachine.ADD_BCAST_SOURCE: - if (obj == null) { - return; - } - // Identify the operation by operation type and broadcastId - if (isSuccess(reason)) { - BluetoothLeBroadcastReceiveState sourceState = - (BluetoothLeBroadcastReceiveState) obj; - boolean removed = - operations.removeIf( - m -> - (m.first.equals( - BassClientStateMachine - .ADD_BCAST_SOURCE)) - && (sourceState.getBroadcastId() - == ((BluetoothLeBroadcastMetadata) - m.second) - .getBroadcastId())); - if (removed) { - setSourceGroupManaged(sink, sourceState.getSourceId(), true); - } - } else { - BluetoothLeBroadcastMetadata metadata = (BluetoothLeBroadcastMetadata) obj; - operations.removeIf( - m -> - (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE)) - && (metadata.getBroadcastId() - == ((BluetoothLeBroadcastMetadata) m.second) - .getBroadcastId())); - - if (!isAnyPendingAddSourceOperation() - && mIsAssistantActive - && mPausedBroadcastSinks.isEmpty()) { - LeAudioService leAudioService = mServiceFactory.getLeAudioService(); - mIsAssistantActive = false; - mUnicastSourceStreamStatus = Optional.empty(); - - if (leAudioService != null) { - leAudioService.activeBroadcastAssistantNotification(false); - } + mPendingGroupOp.computeIfPresent( + sink, + (key, opsToModify) -> { + List<Pair<Integer, Object>> operations = new ArrayList<>(opsToModify); + + switch (reqMsg) { + case BassClientStateMachine.ADD_BCAST_SOURCE: + if (obj == null) { + return operations; + } + // Identify the operation by operation type and broadcastId + if (isSuccess(reason)) { + BluetoothLeBroadcastReceiveState sourceState = + (BluetoothLeBroadcastReceiveState) obj; + if (removeMatchingOperation(operations, reqMsg, obj)) { + setSourceGroupManaged(sink, sourceState.getSourceId(), true); + } + } else { + removeMatchingOperation(operations, reqMsg, obj); + shouldUpdateAssistantActive.set(true); + } + break; + case BassClientStateMachine.REMOVE_BCAST_SOURCE: + // Identify the operation by operation type and sourceId + removeMatchingOperation(operations, reqMsg, obj); + Integer sourceId = (Integer) obj; + setSourceGroupManaged(sink, sourceId, false); + break; + default: + break; } - } - break; - case BassClientStateMachine.REMOVE_BCAST_SOURCE: - // Identify the operation by operation type and sourceId - Integer sourceId = (Integer) obj; - operations.removeIf( - m -> - m.first.equals(BassClientStateMachine.REMOVE_BCAST_SOURCE) - && (sourceId.equals((Integer) m.second))); - setSourceGroupManaged(sink, sourceId, false); - break; - default: - break; + return operations; + }); + + if (shouldUpdateAssistantActive.get() + && !isAnyPendingAddSourceOperation() + && mIsAssistantActive + && mPausedBroadcastSinks.isEmpty()) { + LeAudioService leAudioService = mServiceFactory.getLeAudioService(); + mIsAssistantActive = false; + mUnicastSourceStreamStatus = Optional.empty(); + + if (leAudioService != null) { + leAudioService.activeBroadcastAssistantNotification(false); + } + } + } + + private boolean removeMatchingOperation( + List<Pair<Integer, Object>> operations, int reqMsg, Object obj) { + return operations.removeIf( + m -> m.first.equals(reqMsg) && isMatchingOperation(m.second, obj)); + } + + private boolean isMatchingOperation(Object operationData, Object obj) { + if (obj instanceof BluetoothLeBroadcastReceiveState) { + return ((BluetoothLeBroadcastMetadata) operationData).getBroadcastId() + == ((BluetoothLeBroadcastReceiveState) obj).getBroadcastId(); + } else if (obj instanceof BluetoothLeBroadcastMetadata) { + return ((BluetoothLeBroadcastMetadata) operationData).getBroadcastId() + == ((BluetoothLeBroadcastMetadata) obj).getBroadcastId(); + } else if (obj instanceof Integer) { + return obj.equals(operationData); } + return false; } private boolean isDevicePartOfActiveUnicastGroup(BluetoothDevice device) { @@ -1159,8 +1184,12 @@ public class BassClientService extends ProfileService { return; } - boolean isAssistantActive = - areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()); + boolean isAssistantActive; + if (leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) { + isAssistantActive = hasPrimaryDeviceManagedExternalBroadcast(); + } else { + isAssistantActive = areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()); + } if (isAssistantActive) { /* Assistant become active */ @@ -1325,10 +1354,15 @@ public class BassClientService extends ProfileService { return false; } boolean isRoomAvailable = false; - for (BluetoothLeBroadcastReceiveState recvState : stateMachine.getAllSources()) { - if (isEmptyBluetoothDevice(recvState.getSourceDevice())) { - isRoomAvailable = true; - break; + List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources(); + if (sources.size() < stateMachine.getMaximumSourceCapacity()) { + isRoomAvailable = true; + } else { + for (BluetoothLeBroadcastReceiveState recvState : sources) { + if (isEmptyBluetoothDevice(recvState.getSourceDevice())) { + isRoomAvailable = true; + break; + } } } log("isRoomAvailable: " + isRoomAvailable); @@ -1514,7 +1548,7 @@ public class BassClientService extends ProfileService { return BluetoothStatusCodes.ERROR_BAD_PARAMETERS; } - if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { + if (getConnectionState(device) != STATE_CONNECTED) { log("validateParameters: device is not connected, device: " + device); return BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR; } @@ -1643,10 +1677,10 @@ public class BassClientService extends ProfileService { } /* Restore allowed context mask for unicast in case if last connected broadcast - * delegator device which has external source disconnectes. + * delegator device which has external source disconnects. */ checkAndResetGroupAllowedContextMask(); - } else if (toState == BluetoothProfile.STATE_CONNECTED) { + } else if (toState == STATE_CONNECTED) { handleReconnectingAudioSharingModeDevice(device); } } @@ -2860,6 +2894,77 @@ public class BassClientService extends ProfileService { } } + private void storeSinkMetadata( + BluetoothDevice device, int broadcastId, BluetoothLeBroadcastMetadata metadata) { + if (device == null + || broadcastId == BassConstants.INVALID_BROADCAST_ID + || metadata == null) { + Log.e( + TAG, + "Failed to store Sink Metadata, invalid parameters (device: " + + device + + ", broadcastId: " + + broadcastId + + ", metadata: " + + metadata + + ")"); + return; + } + + mBroadcastMetadataMap.compute( + device, + (key, existingMap) -> { + if (existingMap == null) { + existingMap = new ConcurrentHashMap<>(); + } + existingMap.put(broadcastId, metadata); + return existingMap; + }); + } + + private void removeSinkMetadata(BluetoothDevice device, int broadcastId) { + if (device == null || broadcastId == BassConstants.INVALID_BROADCAST_ID) { + Log.e( + TAG, + "Failed to remove Sink Metadata, invalid parameters (device: " + + device + + ", broadcastId: " + + broadcastId + + ")"); + return; + } + + mBroadcastMetadataMap.compute( + device, + (key, existingMap) -> { + if (existingMap != null) { + existingMap.remove(broadcastId); + if (existingMap.isEmpty()) { + return null; + } + } else { + Log.w( + TAG, + "There is no metadata related to sink (device: " + + device + + ", broadcastId: " + + broadcastId); + } + return existingMap; + }); + } + + private void removeSinkMetadata(BluetoothDevice device) { + if (device == null) { + Log.e( + TAG, + "Failed to remove Sink Metadata, invalid parameters (device: " + device + ")"); + return; + } + + mBroadcastMetadataMap.remove(device); + } + /** * Add a Broadcast Source to the Broadcast Sink * @@ -3029,7 +3134,7 @@ public class BassClientService extends ProfileService { } /* Store metadata for sink device */ - mBroadcastMetadataMap.put(device, sourceMetadata); + storeSinkMetadata(device, sourceMetadata.getBroadcastId(), sourceMetadata); Message message = stateMachine.obtainMessage(BassClientStateMachine.SWITCH_BCAST_SOURCE); @@ -3057,7 +3162,7 @@ public class BassClientService extends ProfileService { } /* Store metadata for sink device */ - mBroadcastMetadataMap.put(device, sourceMetadata); + storeSinkMetadata(device, sourceMetadata.getBroadcastId(), sourceMetadata); if (isGroupOp) { enqueueSourceGroupOp( @@ -3121,7 +3226,7 @@ public class BassClientService extends ProfileService { } /* Update metadata for sink device */ - mBroadcastMetadataMap.put(sink, updatedMetadata); + storeSinkMetadata(sink, updatedMetadata.getBroadcastId(), updatedMetadata); byte[] code = updatedMetadata.getBroadcastCode(); for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { @@ -3190,16 +3295,13 @@ public class BassClientService extends ProfileService { Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second; for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { BluetoothDevice device = deviceSourceIdPair.getKey(); - /* Removes metadata for sink device if not paused */ - if (!mPausedBroadcastSinks.contains(device)) { - mBroadcastMetadataMap.remove(device); - } Integer deviceSourceId = deviceSourceIdPair.getValue(); BassClientStateMachine stateMachine = getOrCreateStateMachine(device); int statusCode = validateParametersForSourceOperation(stateMachine, device, deviceSourceId); if (statusCode != BluetoothStatusCodes.SUCCESS) { + removeSinkMetadata(device); mCallbacks.notifySourceRemoveFailed(device, sourceId, statusCode); continue; } @@ -3207,6 +3309,15 @@ public class BassClientService extends ProfileService { BluetoothLeBroadcastMetadata metaData = stateMachine.getCurrentBroadcastMetadata(sourceId); + /* Removes metadata for sink device if not paused */ + if (!mPausedBroadcastSinks.contains(device)) { + if (metaData != null) { + removeSinkMetadata(device, metaData.getBroadcastId()); + } else { + removeSinkMetadata(device); + } + } + if (metaData != null) { stopBigMonitoring(metaData.getBroadcastId(), true); } @@ -3555,13 +3666,13 @@ public class BassClientService extends ProfileService { continue; } - mBroadcastMetadataMap.remove(sink); + removeSinkMetadata(sink); /* Check if there is any other primary device receiving this broadcast */ if (devices.stream() .anyMatch( d -> - ((getConnectionState(d) == BluetoothProfile.STATE_CONNECTED) + ((getConnectionState(d) == STATE_CONNECTED) && leAudioService.isPrimaryDevice(d)))) { continue; } @@ -3582,8 +3693,7 @@ public class BassClientService extends ProfileService { d -> !d.equals(sink) && (getConnectionState(d) - == BluetoothProfile - .STATE_CONNECTED))) { + == STATE_CONNECTED))) { iterator.remove(); leAudioService.stopBroadcast(broadcastId); continue; @@ -3594,47 +3704,54 @@ public class BassClientService extends ProfileService { .anyMatch( d -> !d.equals(sink) - && (getConnectionState(d) - == BluetoothProfile.STATE_CONNECTED))) { + && (getConnectionState(d) == STATE_CONNECTED))) { continue; - } else { - Log.d( - TAG, - "handleUnintendedDeviceDisconnection: No more potential broadcast " - + "(broadcast ID: " - + broadcastId - + ") receivers - stopping broadcast"); - mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(broadcastId); - mHandler.postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); } + Log.d( + TAG, + "handleUnintendedDeviceDisconnection: No more potential broadcast " + + "(broadcast ID: " + + broadcastId + + ") receivers - stopping broadcast"); + mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(broadcastId); + mHandler.postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); } } } /** Handle device newly connected and its peer device still has active source */ private void checkAndResumeBroadcast(BluetoothDevice sink) { - BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink); - if (metadata == null) { - Log.d(TAG, "checkAndResumeBroadcast: no metadata available"); + Map<Integer, BluetoothLeBroadcastMetadata> entry = mBroadcastMetadataMap.get(sink); + + if (entry == null) { + Log.d(TAG, "checkAndResumeBroadcast: no entry for device: " + sink + ", available"); return; } - for (BluetoothDevice groupDevice : getTargetDeviceList(sink, true)) { - if (groupDevice.equals(sink)) { + + for (Map.Entry<Integer, BluetoothLeBroadcastMetadata> idMetadataIdPair : entry.entrySet()) { + BluetoothLeBroadcastMetadata metadata = idMetadataIdPair.getValue(); + if (metadata == null) { + Log.d(TAG, "checkAndResumeBroadcast: no metadata available"); continue; } - // Check peer device - Optional<BluetoothLeBroadcastReceiveState> receiver = - getOrCreateStateMachine(groupDevice).getAllSources().stream() - .filter(e -> e.getBroadcastId() == metadata.getBroadcastId()) - .findAny(); - if (receiver.isPresent() - && !getAllSources(sink).stream() - .anyMatch( - rs -> - (rs.getBroadcastId() - == receiver.get().getBroadcastId()))) { - Log.d(TAG, "checkAndResumeBroadcast: restore the source for device: " + sink); - addSource(sink, metadata, false); + for (BluetoothDevice groupDevice : getTargetDeviceList(sink, true)) { + if (groupDevice.equals(sink)) { + continue; + } + // Check peer device + Optional<BluetoothLeBroadcastReceiveState> receiver = + getOrCreateStateMachine(groupDevice).getAllSources().stream() + .filter(e -> e.getBroadcastId() == metadata.getBroadcastId()) + .findAny(); + if (receiver.isPresent() + && !getAllSources(sink).stream() + .anyMatch( + rs -> + (rs.getBroadcastId() + == receiver.get().getBroadcastId()))) { + Log.d(TAG, "checkAndResumeBroadcast: restore the source for device: " + sink); + addSource(sink, metadata, false); + } } } } @@ -3768,65 +3885,67 @@ public class BassClientService extends ProfileService { while (iterator.hasNext()) { BluetoothDevice sink = iterator.next(); sEventLogger.logd(TAG, "Remove broadcast sink from paused cache: " + sink); - BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink); + Map<Integer, BluetoothLeBroadcastMetadata> entry = + mBroadcastMetadataMap.getOrDefault(sink, Collections.emptyMap()); - if (leaudioBroadcastAssistantPeripheralEntrustment() - || leaudioBroadcastResyncHelper()) { - if (metadata == null) { - Log.w( - TAG, - "resumeReceiversSourceSynchronization: failed to get metadata to" - + " resume sink: " - + sink); - // remove the device from mPausedBroadcastSinks - iterator.remove(); - continue; - } + for (BluetoothLeBroadcastMetadata metadata : entry.values()) { - mPausedBroadcastIds.remove(metadata.getBroadcastId()); + if (leaudioBroadcastAssistantPeripheralEntrustment() + || leaudioBroadcastResyncHelper()) { + if (metadata == null) { + Log.w( + TAG, + "resumeReceiversSourceSynchronization: failed to get metadata to" + + " resume sink: " + + sink); + continue; + } - // For each device, find the source ID having this broadcast ID - BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); - List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources(); - Optional<BluetoothLeBroadcastReceiveState> receiveState = - sources.stream() - .filter(e -> e.getBroadcastId() == metadata.getBroadcastId()) - .findAny(); + mPausedBroadcastIds.remove(metadata.getBroadcastId()); - if (leaudioBroadcastResyncHelper() - && receiveState.isPresent() - && (receiveState.get().getPaSyncState() - == BluetoothLeBroadcastReceiveState - .PA_SYNC_STATE_SYNCINFO_REQUEST - || receiveState.get().getPaSyncState() - == BluetoothLeBroadcastReceiveState - .PA_SYNC_STATE_SYNCHRONIZED)) { - iterator.remove(); - continue; - } + // For each device, find the source ID having this broadcast ID + BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); + List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources(); + Optional<BluetoothLeBroadcastReceiveState> receiveState = + sources.stream() + .filter(e -> e.getBroadcastId() == metadata.getBroadcastId()) + .findAny(); - List<Integer> activeSyncedSrc = getActiveSyncedSources(); + if (leaudioBroadcastResyncHelper() + && receiveState.isPresent() + && (receiveState.get().getPaSyncState() + == BluetoothLeBroadcastReceiveState + .PA_SYNC_STATE_SYNCINFO_REQUEST + || receiveState.get().getPaSyncState() + == BluetoothLeBroadcastReceiveState + .PA_SYNC_STATE_SYNCHRONIZED)) { + continue; + } - if (receiveState.isPresent() - && (!leaudioBroadcastResyncHelper() - || isLocalBroadcast(metadata) - || activeSyncedSrc.contains( - getSyncHandleForBroadcastId(metadata.getBroadcastId())))) { - int sourceId = receiveState.get().getSourceId(); - updateSourceToResumeBroadcast(sink, sourceId, metadata); - } else { - addSource(sink, metadata, false); - } - } else { - if (metadata != null) { - mPausedBroadcastIds.remove(metadata.getBroadcastId()); - addSource(sink, metadata, false); + List<Integer> activeSyncedSrc = getActiveSyncedSources(); + + if (receiveState.isPresent() + && (!leaudioBroadcastResyncHelper() + || isLocalBroadcast(metadata) + || activeSyncedSrc.contains( + getSyncHandleForBroadcastId( + metadata.getBroadcastId())))) { + int sourceId = receiveState.get().getSourceId(); + updateSourceToResumeBroadcast(sink, sourceId, metadata); + } else { + addSource(sink, metadata, false); + } } else { - Log.w( - TAG, - "resumeReceiversSourceSynchronization: failed to get metadata to" - + " resume sink: " - + sink); + if (metadata != null) { + mPausedBroadcastIds.remove(metadata.getBroadcastId()); + addSource(sink, metadata, false); + } else { + Log.w( + TAG, + "resumeReceiversSourceSynchronization: failed to get metadata to" + + " resume sink: " + + sink); + } } } // remove the device from mPausedBroadcastSinks @@ -3928,10 +4047,7 @@ public class BassClientService extends ProfileService { for (BluetoothDevice device : devices) { for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { for (int i = 0; i < receiveState.getNumSubgroups(); i++) { - Long syncState = receiveState.getBisSyncState().get(i); - /* Synced to BIS */ - if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS - && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) { + if (isSyncedToBroadcastStream(receiveState.getBisSyncState().get(i))) { return true; } } @@ -3941,6 +4057,35 @@ public class BassClientService extends ProfileService { return false; } + public boolean hasPrimaryDeviceManagedExternalBroadcast() { + LeAudioService leAudioService = mServiceFactory.getLeAudioService(); + + if (leAudioService == null) { + Log.e(TAG, "no LeAudioService"); + return false; + } + + for (BluetoothDevice device : getConnectedDevices()) { + if (!leAudioService.isPrimaryDevice(device)) { + continue; + } + + Map<Integer, BluetoothLeBroadcastMetadata> entry = mBroadcastMetadataMap.get(device); + + /* null means that this source was not added or modified by assistant */ + if (entry == null) { + continue; + } + + /* Assistant manages some external broadcast */ + if (entry.values().stream().anyMatch(e -> !isLocalBroadcast(e))) { + return true; + } + } + + return false; + } + /** Check if any sink receivers are receiving broadcast stream */ public boolean areReceiversReceivingOnlyExternalBroadcast(List<BluetoothDevice> devices) { boolean isReceivingExternalBroadcast = false; @@ -3948,10 +4093,7 @@ public class BassClientService extends ProfileService { for (BluetoothDevice device : devices) { for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { for (int i = 0; i < receiveState.getNumSubgroups(); i++) { - Long syncState = receiveState.getBisSyncState().get(i); - /* Synced to BIS */ - if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS - && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) { + if (isSyncedToBroadcastStream(receiveState.getBisSyncState().get(i))) { if (isLocalBroadcast(receiveState)) { return false; } else { @@ -3981,10 +4123,7 @@ public class BassClientService extends ProfileService { return true; } else { for (int i = 0; i < receiveState.getNumSubgroups(); i++) { - Long syncState = receiveState.getBisSyncState().get(i); - /* Synced to BIS */ - if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS - && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) { + if (isSyncedToBroadcastStream(receiveState.getBisSyncState().get(i))) { return true; } } @@ -4034,14 +4173,7 @@ public class BassClientService extends ProfileService { .anyMatch( receiveState -> (receiveState.getBisSyncState().stream() - .anyMatch( - syncState -> - syncState - != BassConstants - .BIS_SYNC_NOT_SYNC_TO_BIS - && syncState - != BassConstants - .BIS_SYNC_FAILED_SYNC_TO_BIG)))) { + .anyMatch(this::isSyncedToBroadcastStream)))) { activeSinks.add(device); } } @@ -4049,6 +4181,11 @@ public class BassClientService extends ProfileService { return activeSinks; } + private boolean isSyncedToBroadcastStream(Long syncState) { + return syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS + && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG; + } + /** Handle broadcast state changed */ public void notifyBroadcastStateChanged(int state, int broadcastId) { switch (state) { @@ -4178,7 +4315,7 @@ public class BassClientService extends ProfileService { try { invokeCallback(callback, msg); } catch (RemoteException e) { - continue; + // Ignore exception } } mCallbacksList.finishBroadcast(); 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 5954b827b0..efdc0dcae7 100644 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; +import static com.android.bluetooth.flags.Flags.leaudioBroadcastReceiveStateProcessingRefactor; import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper; import android.annotation.Nullable; @@ -155,6 +156,7 @@ class BassClientStateMachine extends StateMachine { private final Map<Integer, Boolean> mFirstTimeBisDiscoveryMap; private int mPASyncRetryCounter = 0; @VisibleForTesting int mNumOfBroadcastReceiverStates = 0; + int mNumOfReadyBroadcastReceiverStates = 0; @VisibleForTesting int mPendingOperation = -1; @VisibleForTesting byte mPendingSourceId = -1; @VisibleForTesting BluetoothLeBroadcastMetadata mPendingMetadata = null; @@ -975,10 +977,15 @@ class BassClientStateMachine extends StateMachine { } private boolean isSourceAbsent(BluetoothLeBroadcastReceiveState recvState) { - return recvState.getSourceDevice() == null + return recvState == null + || recvState.getSourceDevice() == null || recvState.getSourceDevice().getAddress().equals("00:00:00:00:00:00"); } + private boolean isSourcePresent(BluetoothLeBroadcastReceiveState recvState) { + return !isSourceAbsent(recvState); + } + private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) { log("checkAndUpdateBroadcastCode"); // Whenever receive state indicated code requested, assistant should set the broadcast code @@ -999,7 +1006,8 @@ class BassClientStateMachine extends StateMachine { } } - private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(byte[] receiverState) { + private BluetoothLeBroadcastReceiveState parseBroadcastReceiverStateObsolete( + byte[] receiverState) { byte sourceId = 0; if (receiverState.length > 0) { sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX]; @@ -1140,10 +1148,11 @@ class BassClientStateMachine extends StateMachine { return recvState; } - private void processBroadcastReceiverState( + private void processBroadcastReceiverStateObsolete( byte[] receiverState, BluetoothGattCharacteristic characteristic) { log("processBroadcastReceiverState: characteristic:" + characteristic); - BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState(receiverState); + BluetoothLeBroadcastReceiveState recvState = + parseBroadcastReceiverStateObsolete(receiverState); if (recvState == null) { log("processBroadcastReceiverState: Null recvState"); return; @@ -1249,6 +1258,226 @@ class BassClientStateMachine extends StateMachine { broadcastReceiverState(recvState, sourceId); } + private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState( + byte[] receiverState, int previousSourceId) { + log("parseBroadcastReceiverState: receiverState length: " + receiverState.length); + + BluetoothLeBroadcastReceiveState recvState = null; + if (receiverState.length == 0) { + byte[] emptyBluetoothDeviceAddress = Utils.getBytesFromAddress("00:00:00:00:00:00"); + if (previousSourceId != BassConstants.INVALID_SOURCE_ID) { + recvState = + new BluetoothLeBroadcastReceiveState( + previousSourceId, + BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType + mAdapterService.getDeviceFromByte( + emptyBluetoothDeviceAddress), // sourceDev + 0, // sourceAdvertisingSid + 0, // broadcastId + BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState + // bigEncryptionState + BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, + null, // badCode + 0, // numSubgroups + Arrays.asList(new Long[0]), // bisSyncState + Arrays.asList( + new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata + ); + } else { + log("parseBroadcastReceiverState: unknown sourceId"); + } + } else { + byte sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX]; + byte paSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX]; + byte bigEncryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX]; + byte[] badBroadcastCode = null; + int badBroadcastCodeLen = 0; + if (bigEncryptionStatus + == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) { + badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE]; + System.arraycopy( + receiverState, + BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX, + badBroadcastCode, + 0, + BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE); + badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE; + } + byte numSubGroups = + receiverState[ + BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen]; + int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen + 1; + ArrayList<BluetoothLeAudioContentMetadata> metadataList = + new ArrayList<BluetoothLeAudioContentMetadata>(); + ArrayList<Long> bisSyncState = new ArrayList<Long>(); + for (int i = 0; i < numSubGroups; i++) { + byte[] bisSyncIndex = new byte[Long.BYTES]; + System.arraycopy( + receiverState, + offset, + bisSyncIndex, + 0, + BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE); + offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE; + bisSyncState.add((long) Utils.byteArrayToLong(bisSyncIndex)); + + int metaDataLength = receiverState[offset++] & 0xFF; + if (metaDataLength > 0) { + log("metadata of length: " + metaDataLength + "is available"); + byte[] metaData = new byte[metaDataLength]; + System.arraycopy(receiverState, offset, metaData, 0, metaDataLength); + offset += metaDataLength; + metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData)); + } else { + metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0])); + } + } + byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength]; + System.arraycopy( + receiverState, + BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX, + broadcastIdBytes, + 0, + mBroadcastSourceIdLength); + int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes); + byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE]; + System.arraycopy( + receiverState, + BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX, + sourceAddress, + 0, + BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE); + byte sourceAddressType = + receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX]; + BassUtils.reverse(sourceAddress); + String address = Utils.getAddressStringFromByte(sourceAddress); + BluetoothDevice device = + BluetoothAdapter.getDefaultAdapter() + .getRemoteLeDevice(address, sourceAddressType); + byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX]; + recvState = + new BluetoothLeBroadcastReceiveState( + sourceId, + (int) sourceAddressType, + device, + sourceAdvSid, + broadcastId, + (int) paSyncState, + (int) bigEncryptionStatus, + badBroadcastCode, + numSubGroups, + bisSyncState, + metadataList); + } + return recvState; + } + + private void processBroadcastReceiverState( + byte[] receiverState, BluetoothGattCharacteristic characteristic) { + log( + "processBroadcastReceiverState: characteristic:" + + characteristic + + ", instanceId:" + + characteristic.getInstanceId()); + + BluetoothLeBroadcastReceiveState prevRecvState = + mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId()); + if (prevRecvState == null + && (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates)) { + Log.e(TAG, "processBroadcastReceiverState: reached the Max SourceInfos"); + return; + } + + int prevSourceId = BassConstants.INVALID_SOURCE_ID; + if (prevRecvState != null) { + prevSourceId = prevRecvState.getSourceId(); + } + + BluetoothLeBroadcastReceiveState recvState = + parseBroadcastReceiverState(receiverState, prevSourceId); + if (recvState == null) { + log("processBroadcastReceiverState: Null recvState"); + return; + } + + log("processBroadcastReceiverState: Updated receiver state: " + recvState); + mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState); + int sourceId = recvState.getSourceId(); + + if (isSourceAbsent(prevRecvState) && isSourcePresent(recvState)) { + log("processBroadcastReceiverState: Source Addition"); + removeMessages(CANCEL_PENDING_SOURCE_OPERATION); + if (mPendingMetadata != null) { + setCurrentBroadcastMetadata(sourceId, mPendingMetadata); + mPendingMetadata = null; + } + if (mPendingOperation == ADD_BCAST_SOURCE) { + mService.getCallbacks() + .notifySourceAdded( + mDevice, recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); + } else { + mService.getCallbacks() + .notifySourceAdded( + mDevice, recvState, BluetoothStatusCodes.REASON_REMOTE_REQUEST); + } + checkAndUpdateBroadcastCode(recvState); + processPASyncState(recvState); + processSyncStateChangeStats(recvState); + } else if (isSourcePresent(prevRecvState) && isSourcePresent(recvState)) { + log("processBroadcastReceiverState: Source Update"); + removeMessages(CANCEL_PENDING_SOURCE_OPERATION); + if (mPendingMetadata != null) { + setCurrentBroadcastMetadata(sourceId, mPendingMetadata); + mPendingMetadata = null; + } + mService.getCallbacks() + .notifySourceModified( + mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); + checkAndUpdateBroadcastCode(recvState); + processPASyncState(recvState); + processSyncStateChangeStats(recvState); + + if (isPendingRemove(sourceId) && !isSyncedToTheSource(sourceId)) { + Message message = obtainMessage(REMOVE_BCAST_SOURCE); + message.arg1 = sourceId; + sendMessage(message); + } + } else if (isSourcePresent(prevRecvState) && isSourceAbsent(recvState)) { + BluetoothDevice removedDevice = prevRecvState.getSourceDevice(); + log("processBroadcastReceiverState: Source Removal " + removedDevice); + if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { + cancelActiveSync(mService.getSyncHandleForBroadcastId(recvState.getBroadcastId())); + } + BluetoothLeBroadcastMetadata metaData = getCurrentBroadcastMetadata(sourceId); + if (metaData != null) { + logBroadcastSyncStatsWithStatus( + metaData.getBroadcastId(), + BluetoothStatsLog + .BROADCAST_AUDIO_SYNC_REPORTED__SYNC_STATUS__SYNC_STATUS_UNKNOWN); + } + setCurrentBroadcastMetadata(sourceId, null); + if (mPendingSourceToSwitch != null) { + // Source remove is triggered by switch source request + mService.getCallbacks() + .notifySourceRemoved( + mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); + log("processBroadcastReceiverState: Source Switching"); + Message message = obtainMessage(ADD_BCAST_SOURCE); + message.obj = mPendingSourceToSwitch; + sendMessage(message); + } else if (mPendingOperation == REMOVE_BCAST_SOURCE) { + mService.getCallbacks() + .notifySourceRemoved( + mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); + } else { + mService.getCallbacks() + .notifySourceRemoved( + mDevice, sourceId, BluetoothStatusCodes.REASON_REMOTE_REQUEST); + } + } + broadcastReceiverState(recvState, sourceId); + } + // Implements callback methods for GATT events that the app cares about. // For example, connection change and services discovered. final class GattCallback extends BluetoothGattCallback { @@ -1325,7 +1554,17 @@ class BassClientStateMachine extends StateMachine { characteristic.getValue(), 0, characteristic.getValue().length); - processBroadcastReceiverState(characteristic.getValue(), characteristic); + if (leaudioBroadcastReceiveStateProcessingRefactor()) { + processBroadcastReceiverState(characteristic.getValue(), characteristic); + mNumOfReadyBroadcastReceiverStates++; + if (mNumOfReadyBroadcastReceiverStates == mNumOfBroadcastReceiverStates) { + // Notify service BASS state ready for operations + mService.getCallbacks().notifyBassStateReady(mDevice); + } + } else { + processBroadcastReceiverStateObsolete( + characteristic.getValue(), characteristic); + } } // switch to receiving notifications after initial characteristic read BluetoothGattDescriptor desc = @@ -1376,7 +1615,12 @@ class BassClientStateMachine extends StateMachine { Log.e(TAG, "Remote receiver state is NULL"); return; } - processBroadcastReceiverState(characteristic.getValue(), characteristic); + if (leaudioBroadcastReceiveStateProcessingRefactor()) { + processBroadcastReceiverState(characteristic.getValue(), characteristic); + } else { + processBroadcastReceiverStateObsolete( + characteristic.getValue(), characteristic); + } } } @@ -1625,6 +1869,7 @@ class BassClientStateMachine extends StateMachine { mBroadcastScanControlPoint = null; } mNumOfBroadcastReceiverStates = 0; + mNumOfReadyBroadcastReceiverStates = 0; if (mBluetoothLeBroadcastReceiveStates != null) { mBluetoothLeBroadcastReceiveStates.clear(); } diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java index f1f0b51691..981b2db0c7 100644 --- a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java +++ b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java @@ -49,6 +49,7 @@ public final class AbstractionLayer { static final int BT_PROPERTY_REMOTE_ASHA_CAPABILITY = 0X15; static final int BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID = 0X16; static final int BT_PROPERTY_REMOTE_MODEL_NUM = 0x17; + static final int BT_PROPERTY_LPP_OFFLOAD_FEATURES = 0x1B; public static final int BT_DEVICE_TYPE_BREDR = 0x01; public static final int BT_DEVICE_TYPE_BLE = 0x02; diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java index 6c79ab4789..6fb1169a9e 100644 --- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java +++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java @@ -412,7 +412,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac final LeAudioService leAudioService = mFactory.getLeAudioService(); setA2dpActiveDevice(null, true); setHfpActiveDevice(null); - if (Flags.admVerifyActiveFallbackDevice()) { + if (Flags.admVerifyActiveFallbackDevice() && leAudioService != null) { setLeAudioActiveDevice( null, !leAudioService.getActiveDevices().contains(device)); } else { @@ -709,7 +709,6 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac + device + ", mHfpActiveDevice=" + mHfpActiveDevice); - if (!Objects.equals(mHfpActiveDevice, device)) { if (device != null) { setHearingAidActiveDevice(null, true); @@ -717,10 +716,10 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac updateLeAudioActiveDeviceIfDualMode(mHfpActiveDevice, device); - if (!Utils.isDualModeAudioEnabled() || device == null) { + if ((!Utils.isDualModeAudioEnabled() && device == null)) { Log.d(TAG, "HFP active device is null. Try to fallback to the active device."); synchronized (mLock) { - setFallbackDeviceActiveLocked(null); + setFallbackDeviceActiveLocked(mHfpActiveDevice /* recentlyRemovedDevice */); } } } @@ -832,7 +831,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac if (device == null && !Utils.isDualModeAudioEnabled() && !isBroadcastingAudio()) { Log.d(TAG, "LE audio active device is null. Try to fallback to the active device."); synchronized (mLock) { - setFallbackDeviceActiveLocked(device); + setFallbackDeviceActiveLocked(mLeAudioActiveDevice /* recentlyRemovedDevice */); } } @@ -1136,12 +1135,18 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac Log.d(TAG, "setFallbackDeviceActive, recently removed: " + recentlyRemovedDevice); mDbManager = mAdapterService.getDatabase(); List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>(); + final LeAudioService leAudioService = mFactory.getLeAudioService(); if (!mHearingAidConnectedDevices.isEmpty()) { connectedHearingAidDevices.addAll(mHearingAidConnectedDevices); } - if (!mLeHearingAidConnectedDevices.isEmpty()) { - connectedHearingAidDevices.addAll(mLeHearingAidConnectedDevices); + if (!mLeHearingAidConnectedDevices.isEmpty() && leAudioService != null) { + for (BluetoothDevice dev : mLeHearingAidConnectedDevices) { + if (leAudioService.isGroupAvailableForStream(leAudioService.getGroupId(dev))) { + connectedHearingAidDevices.add(dev); + } + } } + if (!connectedHearingAidDevices.isEmpty()) { BluetoothDevice device = mDbManager.getMostRecentlyConnectedDevicesInList(connectedHearingAidDevices); @@ -1197,7 +1202,13 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac } List<BluetoothDevice> connectedDevices = new ArrayList<>(); - connectedDevices.addAll(mLeAudioConnectedDevices); + if (leAudioService != null) { + for (BluetoothDevice dev : mLeAudioConnectedDevices) { + if (leAudioService.isGroupAvailableForStream(leAudioService.getGroupId(dev))) { + connectedDevices.add(dev); + } + } + } Log.d(TAG, "Audio mode: " + mAudioManager.getMode()); switch (mAudioManager.getMode()) { case AudioManager.MODE_NORMAL: diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java index 33bf3c3f8f..64b5a0dfae 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java @@ -195,13 +195,56 @@ public class AdapterNativeInterface { return getMetricIdNative(address); } - int connectSocket(byte[] address, int type, byte[] uuid, int port, int flag, int callingUid) { - return connectSocketNative(address, type, uuid, port, flag, callingUid); + int connectSocket( + byte[] address, + int type, + byte[] uuid, + int port, + int flag, + int callingUid, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize) { + return connectSocketNative( + address, + type, + uuid, + port, + flag, + callingUid, + dataPath, + socketName, + hubId, + endpointId, + maximumPacketSize); } int createSocketChannel( - int type, String serviceName, byte[] uuid, int port, int flag, int callingUid) { - return createSocketChannelNative(type, serviceName, uuid, port, flag, callingUid); + int type, + String serviceName, + byte[] uuid, + int port, + int flag, + int callingUid, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize) { + return createSocketChannelNative( + type, + serviceName, + uuid, + port, + flag, + callingUid, + dataPath, + socketName, + hubId, + endpointId, + maximumPacketSize); } void requestMaximumTxDataLength(byte[] address) { @@ -359,10 +402,30 @@ public class AdapterNativeInterface { private native int getMetricIdNative(byte[] address); private native int connectSocketNative( - byte[] address, int type, byte[] uuid, int port, int flag, int callingUid); + byte[] address, + int type, + byte[] uuid, + int port, + int flag, + int callingUid, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize); private native int createSocketChannelNative( - int type, String serviceName, byte[] uuid, int port, int flag, int callingUid); + int type, + String serviceName, + byte[] uuid, + int port, + int flag, + int callingUid, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize); private native void requestMaximumTxDataLengthNative(byte[] address); diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java index 851df2c030..b99debdede 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -134,6 +134,9 @@ class AdapterProperties { private boolean mIsLeIsochronousBroadcasterSupported; private boolean mIsLeChannelSoundingSupported; + private int mNumberOfSupportedOffloadedLeCocSockets; + private int mNumberOfSupportedOffloadedRfcommSockets; + // Lock for all getters and setters. // If finer grained locking is needer, more locks // can be added here. @@ -908,6 +911,10 @@ class AdapterProperties { updateDynamicAudioBufferSupport(val); break; + case AbstractionLayer.BT_PROPERTY_LPP_OFFLOAD_FEATURES: + updateLppOffloadFeatureSupport(val); + break; + default: Log.e(TAG, "Property change not handled in Java land:" + type); } @@ -1022,6 +1029,37 @@ class AdapterProperties { mBufferConstraintList.complete(bufferConstraintList); } + /** + * @return the mNumberOfSupportedOffloadedLeCocSockets + */ + int getNumberOfSupportedOffloadedLeCocSockets() { + return mNumberOfSupportedOffloadedLeCocSockets; + } + + /** + * @return the mNumberOfSupportedOffloadedRfcommSockets + */ + int getNumberOfSupportedOffloadedRfcommSockets() { + return mNumberOfSupportedOffloadedRfcommSockets; + } + + private void updateLppOffloadFeatureSupport(byte[] val) { + if (val == null || val.length < 2) { + Log.e(TAG, "BT_PROPERTY_LPP_OFFLOAD_FEATURES: invalid value length"); + return; + } + mNumberOfSupportedOffloadedLeCocSockets = (0xFF & ((int) val[0])); + mNumberOfSupportedOffloadedRfcommSockets = (0xFF & ((int) val[1])); + + Log.d( + TAG, + "BT_PROPERTY_LPP_OFFLOAD_FEATURES: update from Offload HAL" + + " mNumberOfSupportedOffloadedLeCocSockets = " + + mNumberOfSupportedOffloadedLeCocSockets + + " mNumberOfSupportedOffloadedRfcommSockets = " + + mNumberOfSupportedOffloadedRfcommSockets); + } + void onBluetoothReady() { debugLog( "onBluetoothReady, state=" diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 77b2c4cf28..798f9e3a9d 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -38,6 +38,7 @@ import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser; import static com.android.bluetooth.Utils.getBytesFromAddress; import static com.android.bluetooth.Utils.isDualModeAudioEnabled; import static com.android.bluetooth.Utils.isPackageNameAccurate; +import static com.android.modules.utils.build.SdkLevel.isAtLeastV; import static java.util.Objects.requireNonNull; @@ -56,6 +57,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter.ActiveDeviceProfile; import android.bluetooth.BluetoothAdapter.ActiveDeviceUse; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDevice.BluetoothAddress; import android.bluetooth.BluetoothFrameworkInitializer; import android.bluetooth.BluetoothMap; import android.bluetooth.BluetoothProfile; @@ -89,7 +91,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.hardware.display.DisplayManager; +import android.hardware.devicestate.DeviceStateManager; import android.os.AsyncTask; import android.os.BatteryStatsManager; import android.os.Binder; @@ -277,10 +279,6 @@ public class AdapterService extends Service { private final BluetoothHciVendorSpecificDispatcher mBluetoothHciVendorSpecificDispatcher = new BluetoothHciVendorSpecificDispatcher(); - private final BluetoothHciVendorSpecificNativeInterface - mBluetoothHciVendorSpecificNativeInterface = - new BluetoothHciVendorSpecificNativeInterface( - mBluetoothHciVendorSpecificDispatcher); private final Looper mLooper; private final AdapterServiceHandler mHandler; @@ -345,6 +343,7 @@ public class AdapterService extends Service { private BassClientService mBassClientService; private BatteryService mBatteryService; private BluetoothQualityReportNativeInterface mBluetoothQualityReportNativeInterface; + private BluetoothHciVendorSpecificNativeInterface mBluetoothHciVendorSpecificNativeInterface; private GattService mGattService; private ScanController mScanController; @@ -695,7 +694,11 @@ public class AdapterService extends Service { mBluetoothQualityReportNativeInterface.init(); if (Flags.hciVendorSpecificExtension()) { - mBluetoothHciVendorSpecificNativeInterface.init(); + mBluetoothHciVendorSpecificNativeInterface = + requireNonNull( + mBluetoothHciVendorSpecificNativeInterface.getInstance(), + "mBluetoothHciVendorSpecificNativeInterface cannot be null"); + mBluetoothHciVendorSpecificNativeInterface.init(mBluetoothHciVendorSpecificDispatcher); } mSdpManager = new SdpManager(this, mLooper); @@ -729,10 +732,10 @@ public class AdapterService extends Service { mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this); - if (Flags.adapterSuspendMgmt()) { + if (Flags.adapterSuspendMgmt() && isAtLeastV()) { mAdapterSuspend = new AdapterSuspend( - mNativeInterface, mLooper, getSystemService(DisplayManager.class)); + mNativeInterface, mLooper, getSystemService(DeviceStateManager.class)); } invalidateBluetoothCaches(); @@ -1478,7 +1481,9 @@ public class AdapterService extends Service { } if (mAdapterSuspend != null) { - mAdapterSuspend.cleanup(); + if (Flags.adapterSuspendMgmt() && isAtLeastV()) { + mAdapterSuspend.cleanup(); + } mAdapterSuspend = null; } @@ -2388,6 +2393,23 @@ public class AdapterService extends Service { } @Override + @NonNull + public BluetoothAddress getIdentityAddressWithType(@NonNull String address) { + AdapterService service = getService(); + if (service == null + || !callerIsSystemOrActiveOrManagedUser( + service, TAG, "getIdentityAddressWithType") + || !Utils.checkConnectPermissionForDataDelivery( + service, + Utils.getCallingAttributionSource(mService), + "AdapterService getIdentityAddressWithType")) { + return new BluetoothAddress(null, BluetoothDevice.ADDRESS_TYPE_UNKNOWN); + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getIdentityAddressWithType(address); + } + + @Override public String getName(AttributionSource source) { AdapterService service = getService(); if (service == null @@ -4369,6 +4391,26 @@ public class AdapterService extends Service { service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); return service.mDatabaseManager.isMicrophonePreferredForCalls(device); } + + @Override + public boolean isLeCocSocketOffloadSupported(AttributionSource source) { + AdapterService service = getService(); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isLeCocSocketOffloadSupported(); + } + + @Override + public boolean isRfcommSocketOffloadSupported(AttributionSource source) { + AdapterService service = getService(); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isRfcommSocketOffloadSupported(); + } } /** @@ -4895,6 +4937,38 @@ public class AdapterService extends Service { } } + /** + * Returns the identity address and identity address type. + * + * @param address of remote device + * @return a {@link BluetoothDevice.BluetoothAddress} containing identity address and identity + * address type + */ + @NonNull + public BluetoothAddress getIdentityAddressWithType(@NonNull String address) { + BluetoothDevice device = + BluetoothAdapter.getDefaultAdapter().getRemoteDevice(Ascii.toUpperCase(address)); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + + String identityAddress = null; + int identityAddressType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN; + + if (deviceProp != null) { + if (deviceProp.getIdentityAddress() != null) { + identityAddress = deviceProp.getIdentityAddress(); + } + identityAddressType = deviceProp.getIdentityAddressType(); + } else { + if (Flags.identityAddressNullIfNotKnown()) { + identityAddress = null; + } else { + identityAddress = address; + } + } + + return new BluetoothAddress(identityAddress, identityAddressType); + } + private static class CallerInfo { public String callerPackageName; public UserHandle user; @@ -6355,14 +6429,7 @@ public class AdapterService extends Service { long idleTime, long energyUsed, UidTraffic[] data) { - if (Flags.btSystemContextReport()) { - energyInfoCallbackInternal( - status, ctrlState, txTime, rxTime, idleTime, energyUsed, data); - } else if (ctrlState >= BluetoothActivityEnergyInfo.BT_STACK_STATE_INVALID - && ctrlState <= BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_IDLE) { - energyInfoCallbackInternal( - status, ctrlState, txTime, rxTime, idleTime, energyUsed, data); - } + energyInfoCallbackInternal(status, ctrlState, txTime, rxTime, idleTime, energyUsed, data); Log.v( TAG, "energyInfoCallback()" @@ -7106,4 +7173,26 @@ public class AdapterService extends Service { Log.e(TAG, "Error happened while removing contents: ", e); } } + + /** Get the number of the supported offloaded LE COC sockets. */ + public int getNumberOfSupportedOffloadedLeCocSockets() { + return mAdapterProperties.getNumberOfSupportedOffloadedLeCocSockets(); + } + + /** Check if the offloaded LE COC socket is supported. */ + public boolean isLeCocSocketOffloadSupported() { + int val = getNumberOfSupportedOffloadedLeCocSockets(); + return val > 0; + } + + /** Get the number of the supported offloaded RFCOMM sockets. */ + public int getNumberOfSupportedOffloadedRfcommSockets() { + return mAdapterProperties.getNumberOfSupportedOffloadedRfcommSockets(); + } + + /** Check if the offloaded RFCOMM socket is supported. */ + public boolean isRfcommSocketOffloadSupported() { + int val = getNumberOfSupportedOffloadedRfcommSockets(); + return val > 0; + } } diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java b/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java index 751bb3d6dc..857121b1f9 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterSuspend.java @@ -21,16 +21,19 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE; import static java.util.Objects.requireNonNull; -import android.hardware.display.DisplayManager; +import android.annotation.NonNull; +import android.hardware.devicestate.DeviceState; +import android.hardware.devicestate.DeviceStateManager; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; -import android.view.Display; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.RequiresApi; -import java.util.Arrays; +import com.android.internal.annotations.VisibleForTesting; +@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) public class AdapterSuspend { private static final String TAG = "BtAdapterSuspend"; @@ -39,42 +42,78 @@ public class AdapterSuspend { private static final long MASK_DISCONNECT_CMPLT = 1 << 4; private static final long MASK_MODE_CHANGE = 1 << 19; - private boolean mSuspended = false; + private static final String DEVICE_STATE_LAPTOP = "LAPTOP"; + private static final String DEVICE_STATE_TABLET = "TABLET"; + private static final String DEVICE_STATE_DOCKED = "DOCKED"; + private static final String DEVICE_STATE_CLOSED = "CLOSED"; + private static final String DEVICE_STATE_DISPLAY_OFF = "DISPLAY_OFF"; - private final AdapterNativeInterface mAdapterNativeInterface; - private final Looper mLooper; - private final DisplayManager mDisplayManager; - private final DisplayManager.DisplayListener mDisplayListener = - new DisplayManager.DisplayListener() { - @Override - public void onDisplayAdded(int displayId) {} + private final DeviceStateManager mDeviceStateManager; + public final DeviceStateManager.DeviceStateCallback mDeviceStateCallback = + new DeviceStateManager.DeviceStateCallback() { @Override - public void onDisplayRemoved(int displayId) {} + public void onDeviceStateChanged(@NonNull DeviceState state) { + String nextState = state.getName(); + Log.d(TAG, "Handle state transition: " + mCurrentState + " => " + nextState); + if (mCurrentState.equals("None")) { + mCurrentState = nextState; + Log.i(TAG, "Initialize device state to " + nextState); + return; + } - @Override - public void onDisplayChanged(int displayId) { - if (isScreenOn()) { - handleResume(); - } else { - handleSuspend(); + switch (nextState) { + case DEVICE_STATE_CLOSED -> { + switch (mCurrentState) { + case DEVICE_STATE_DISPLAY_OFF -> + Log.d(TAG, "No action for state " + nextState); + default -> handleSuspend(false); + } + } + case DEVICE_STATE_DISPLAY_OFF -> { + switch (mCurrentState) { + case DEVICE_STATE_TABLET -> handleSuspend(false); + case DEVICE_STATE_DOCKED, DEVICE_STATE_LAPTOP -> + handleSuspend(true); + default -> Log.d(TAG, "No action for state " + nextState); + } + } + case DEVICE_STATE_LAPTOP, DEVICE_STATE_DOCKED, DEVICE_STATE_TABLET -> { + switch (mCurrentState) { + case DEVICE_STATE_CLOSED, DEVICE_STATE_DISPLAY_OFF -> + handleResume(); + default -> Log.d(TAG, "No action for state " + nextState); + } + } + default -> { + Log.wtf(TAG, "Unknown state transition to " + nextState); + return; + } } + mCurrentState = nextState; } }; + private boolean mSuspended = false; + + // Value should be initialized when registering the mDeviceStateCallback. + private String mCurrentState = "None"; + + private final AdapterNativeInterface mAdapterNativeInterface; + public AdapterSuspend( AdapterNativeInterface adapterNativeInterface, Looper looper, - DisplayManager displayManager) { + DeviceStateManager deviceStateManager) { mAdapterNativeInterface = requireNonNull(adapterNativeInterface); - mLooper = requireNonNull(looper); - mDisplayManager = requireNonNull(displayManager); + Handler handler = new Handler(requireNonNull(looper)); - mDisplayManager.registerDisplayListener(mDisplayListener, new Handler(mLooper)); + mDeviceStateManager = requireNonNull(deviceStateManager); + mDeviceStateManager.registerCallback(handler::post, mDeviceStateCallback); } void cleanup() { - mDisplayManager.unregisterDisplayListener(mDisplayListener); + mDeviceStateManager.unregisterCallback(mDeviceStateCallback); } @VisibleForTesting @@ -82,13 +121,8 @@ public class AdapterSuspend { return mSuspended; } - private boolean isScreenOn() { - return Arrays.stream(mDisplayManager.getDisplays()) - .anyMatch(display -> display.getState() == Display.STATE_ON); - } - @VisibleForTesting - void handleSuspend() { + void handleSuspend(boolean allowWakeByHid) { if (mSuspended) { return; } @@ -106,7 +140,11 @@ public class AdapterSuspend { mAdapterNativeInterface.clearEventFilter(); mAdapterNativeInterface.clearFilterAcceptList(); mAdapterNativeInterface.disconnectAllAcls(); - mAdapterNativeInterface.allowWakeByHid(); + + if (allowWakeByHid) { + mAdapterNativeInterface.allowWakeByHid(); + Log.i(TAG, "configure wake by hid"); + } Log.i(TAG, "ready to suspend"); } diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java index 7acf26f360..dc3517171c 100644 --- a/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java +++ b/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java @@ -16,16 +16,39 @@ package com.android.bluetooth.btservice; -class BluetoothHciVendorSpecificNativeInterface { +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +public class BluetoothHciVendorSpecificNativeInterface { private static final String TAG = "BluetoothHciVendorSpecificNativeInterface"; - private final BluetoothHciVendorSpecificDispatcher mDispatcher; - public BluetoothHciVendorSpecificNativeInterface( - BluetoothHciVendorSpecificDispatcher dispatcher) { - mDispatcher = dispatcher; + @GuardedBy("INSTANCE_LOCK") + private static BluetoothHciVendorSpecificNativeInterface sInstance; + + private static final Object INSTANCE_LOCK = new Object(); + + /** Get singleton instance. */ + public static BluetoothHciVendorSpecificNativeInterface getInstance() { + synchronized (INSTANCE_LOCK) { + if (sInstance == null) { + sInstance = new BluetoothHciVendorSpecificNativeInterface(); + } + return sInstance; + } } - void init() { + /** Set singleton instance. */ + @VisibleForTesting + static void setInstance(BluetoothHciVendorSpecificNativeInterface instance) { + synchronized (INSTANCE_LOCK) { + sInstance = instance; + } + } + + private BluetoothHciVendorSpecificDispatcher mDispatcher; + + void init(BluetoothHciVendorSpecificDispatcher dispatcher) { + mDispatcher = dispatcher; initNative(); } diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java index d3062c3707..d5309f6ad7 100644 --- a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java +++ b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; +import android.bluetooth.BluetoothSocketSettings; import android.bluetooth.IBluetoothSocketManager; import android.content.AttributionSource; import android.os.Binder; @@ -86,7 +87,82 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { Utils.uuidToByteArray(uuid), port, flag, - Binder.getCallingUid())); + Binder.getCallingUid(), + 0, + "", + 0, + 0, + 0)); + } + + @Override + public ParcelFileDescriptor connectSocketwithOffload( + BluetoothDevice device, + int type, + ParcelUuid uuid, + int port, + int flag, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize) { + + enforceActiveUser(); + + if (!Utils.checkConnectPermissionForPreflight(mService)) { + return null; + } + + if (dataPath != BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + mService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + enforceSocketOffloadSupport(type); + } + String brEdrAddress = + Flags.identityAddressNullIfNotKnown() + ? Utils.getBrEdrAddress(device) + : mService.getIdentityAddress(device.getAddress()); + + Log.i( + TAG, + "connectSocketwithOffload: device=" + + device + + ", type=" + + type + + ", uuid=" + + uuid + + ", port=" + + port + + ", from " + + Utils.getUidPidString() + + ", dataPath=" + + dataPath + + ", socketName=" + + socketName + + ", hubId=" + + hubId + + ", endpointId=" + + endpointId + + ", maximumPacketSize=" + + maximumPacketSize); + + return marshalFd( + mService.getNative() + .connectSocket( + Utils.getBytesFromAddress( + type == BluetoothSocket.TYPE_L2CAP_LE + ? device.getAddress() + : brEdrAddress), + type, + Utils.uuidToByteArray(uuid), + port, + flag, + Binder.getCallingUid(), + dataPath, + socketName, + hubId, + endpointId, + maximumPacketSize)); } @Override @@ -120,7 +196,75 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { Utils.uuidToByteArray(uuid), port, flag, - Binder.getCallingUid())); + Binder.getCallingUid(), + 0, + "", + 0, + 0, + 0)); + } + + @Override + public ParcelFileDescriptor createSocketChannelWithOffload( + int type, + String serviceName, + ParcelUuid uuid, + int port, + int flag, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize) { + + enforceActiveUser(); + + if (!Utils.checkConnectPermissionForPreflight(mService)) { + return null; + } + + if (dataPath != BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + mService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + enforceSocketOffloadSupport(type); + } + + Log.i( + TAG, + "createSocketChannelWithOffload: type=" + + type + + ", serviceName=" + + serviceName + + ", uuid=" + + uuid + + ", port=" + + port + + ", from " + + Utils.getUidPidString() + + ", dataPath=" + + dataPath + + ", socketName=" + + socketName + + ", hubId=" + + hubId + + ", endpointId=" + + endpointId + + ", maximumPacketSize=" + + maximumPacketSize); + + return marshalFd( + mService.getNative() + .createSocketChannel( + type, + serviceName, + Utils.uuidToByteArray(uuid), + port, + flag, + Binder.getCallingUid(), + dataPath, + socketName, + hubId, + endpointId, + maximumPacketSize)); } @Override @@ -169,6 +313,14 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { } } + private void enforceSocketOffloadSupport(int type) { + if (!(type == BluetoothSocket.TYPE_LE && mService.isLeCocSocketOffloadSupported()) + && !(type == BluetoothSocket.TYPE_RFCOMM + && mService.isRfcommSocketOffloadSupported())) { + throw new IllegalStateException("Unsupported socket type for offload " + type); + } + } + private static ParcelFileDescriptor marshalFd(int fd) { if (fd == INVALID_FD) { return null; diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java index f5e9b8cd1b..f372983d7e 100644 --- a/android/app/src/com/android/bluetooth/btservice/Config.java +++ b/android/app/src/com/android/bluetooth/btservice/Config.java @@ -30,7 +30,6 @@ import com.android.bluetooth.avrcpcontroller.AvrcpControllerService; import com.android.bluetooth.bas.BatteryService; import com.android.bluetooth.bass_client.BassClientService; import com.android.bluetooth.csip.CsipSetCoordinatorService; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.gatt.GattService; import com.android.bluetooth.hap.HapClientService; import com.android.bluetooth.hearingaid.HearingAidService; @@ -86,105 +85,45 @@ public class Config { }; /** List of profile services with the profile-supported resource flag and bit mask. */ - private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS; - - static { - if (Flags.leaudioSynchronizeStart()) { - PROFILE_SERVICES_AND_FLAGS = - new ProfileConfig[] { - // Prioritize GattService startup by making it the first Profile to - // boot. This resolves dependency issues for some Profiles. - new ProfileConfig(GattService.isEnabled(), BluetoothProfile.GATT), - new ProfileConfig(A2dpService.isEnabled(), BluetoothProfile.A2DP), - new ProfileConfig(A2dpSinkService.isEnabled(), BluetoothProfile.A2DP_SINK), - new ProfileConfig(AvrcpTargetService.isEnabled(), BluetoothProfile.AVRCP), - new ProfileConfig( - AvrcpControllerService.isEnabled(), - BluetoothProfile.AVRCP_CONTROLLER), - new ProfileConfig( - BassClientService.isEnabled(), - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT), - new ProfileConfig(BatteryService.isEnabled(), BluetoothProfile.BATTERY), - new ProfileConfig( - CsipSetCoordinatorService.isEnabled(), - BluetoothProfile.CSIP_SET_COORDINATOR), - new ProfileConfig( - HapClientService.isEnabled(), BluetoothProfile.HAP_CLIENT), - new ProfileConfig(HeadsetService.isEnabled(), BluetoothProfile.HEADSET), - new ProfileConfig( - HeadsetClientService.isEnabled(), BluetoothProfile.HEADSET_CLIENT), - new ProfileConfig( - HearingAidService.isEnabled(), BluetoothProfile.HEARING_AID), - new ProfileConfig( - HidDeviceService.isEnabled(), BluetoothProfile.HID_DEVICE), - new ProfileConfig(HidHostService.isEnabled(), BluetoothProfile.HID_HOST), - new ProfileConfig(TbsService.isEnabled(), BluetoothProfile.LE_CALL_CONTROL), - new ProfileConfig(BluetoothMapService.isEnabled(), BluetoothProfile.MAP), - new ProfileConfig( - MapClientService.isEnabled(), BluetoothProfile.MAP_CLIENT), - new ProfileConfig(McpService.isEnabled(), BluetoothProfile.MCP_SERVER), - new ProfileConfig(BluetoothOppService.isEnabled(), BluetoothProfile.OPP), - new ProfileConfig(PanService.isEnabled(), BluetoothProfile.PAN), - new ProfileConfig(BluetoothPbapService.isEnabled(), BluetoothProfile.PBAP), - new ProfileConfig( - PbapClientService.isEnabled(), BluetoothProfile.PBAP_CLIENT), - new ProfileConfig(SapService.isEnabled(), BluetoothProfile.SAP), - new ProfileConfig( - VolumeControlService.isEnabled(), BluetoothProfile.VOLUME_CONTROL), - new ProfileConfig(LeAudioService.isEnabled(), BluetoothProfile.LE_AUDIO), - new ProfileConfig( - LeAudioService.isBroadcastEnabled(), - BluetoothProfile.LE_AUDIO_BROADCAST), - }; - } else { - PROFILE_SERVICES_AND_FLAGS = - new ProfileConfig[] { - // Prioritize GattService startup by making it the first Profile to - // boot. This resolves dependency issues for some Profiles. - new ProfileConfig(GattService.isEnabled(), BluetoothProfile.GATT), - new ProfileConfig(A2dpService.isEnabled(), BluetoothProfile.A2DP), - new ProfileConfig(A2dpSinkService.isEnabled(), BluetoothProfile.A2DP_SINK), - new ProfileConfig(AvrcpTargetService.isEnabled(), BluetoothProfile.AVRCP), - new ProfileConfig( - AvrcpControllerService.isEnabled(), - BluetoothProfile.AVRCP_CONTROLLER), - new ProfileConfig( - BassClientService.isEnabled(), - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT), - new ProfileConfig(BatteryService.isEnabled(), BluetoothProfile.BATTERY), - new ProfileConfig( - CsipSetCoordinatorService.isEnabled(), - BluetoothProfile.CSIP_SET_COORDINATOR), - new ProfileConfig( - HapClientService.isEnabled(), BluetoothProfile.HAP_CLIENT), - new ProfileConfig(HeadsetService.isEnabled(), BluetoothProfile.HEADSET), - new ProfileConfig( - HeadsetClientService.isEnabled(), BluetoothProfile.HEADSET_CLIENT), - new ProfileConfig( - HearingAidService.isEnabled(), BluetoothProfile.HEARING_AID), - new ProfileConfig( - HidDeviceService.isEnabled(), BluetoothProfile.HID_DEVICE), - new ProfileConfig(HidHostService.isEnabled(), BluetoothProfile.HID_HOST), - new ProfileConfig(LeAudioService.isEnabled(), BluetoothProfile.LE_AUDIO), - new ProfileConfig( - LeAudioService.isBroadcastEnabled(), - BluetoothProfile.LE_AUDIO_BROADCAST), - new ProfileConfig(TbsService.isEnabled(), BluetoothProfile.LE_CALL_CONTROL), - new ProfileConfig(BluetoothMapService.isEnabled(), BluetoothProfile.MAP), - new ProfileConfig( - MapClientService.isEnabled(), BluetoothProfile.MAP_CLIENT), - new ProfileConfig(McpService.isEnabled(), BluetoothProfile.MCP_SERVER), - new ProfileConfig(BluetoothOppService.isEnabled(), BluetoothProfile.OPP), - new ProfileConfig(PanService.isEnabled(), BluetoothProfile.PAN), - new ProfileConfig(BluetoothPbapService.isEnabled(), BluetoothProfile.PBAP), - new ProfileConfig( - PbapClientService.isEnabled(), BluetoothProfile.PBAP_CLIENT), - new ProfileConfig(SapService.isEnabled(), BluetoothProfile.SAP), - new ProfileConfig( - VolumeControlService.isEnabled(), BluetoothProfile.VOLUME_CONTROL), - }; - } - } + private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS = + new ProfileConfig[] { + // Prioritize GattService startup by making it the first Profile to + // boot. This resolves dependency issues for some Profiles. + new ProfileConfig(GattService.isEnabled(), BluetoothProfile.GATT), + new ProfileConfig(A2dpService.isEnabled(), BluetoothProfile.A2DP), + new ProfileConfig(A2dpSinkService.isEnabled(), BluetoothProfile.A2DP_SINK), + new ProfileConfig(AvrcpTargetService.isEnabled(), BluetoothProfile.AVRCP), + new ProfileConfig( + AvrcpControllerService.isEnabled(), BluetoothProfile.AVRCP_CONTROLLER), + new ProfileConfig( + BassClientService.isEnabled(), + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT), + new ProfileConfig(BatteryService.isEnabled(), BluetoothProfile.BATTERY), + new ProfileConfig( + CsipSetCoordinatorService.isEnabled(), + BluetoothProfile.CSIP_SET_COORDINATOR), + new ProfileConfig(HapClientService.isEnabled(), BluetoothProfile.HAP_CLIENT), + new ProfileConfig(HeadsetService.isEnabled(), BluetoothProfile.HEADSET), + new ProfileConfig( + HeadsetClientService.isEnabled(), BluetoothProfile.HEADSET_CLIENT), + new ProfileConfig(HearingAidService.isEnabled(), BluetoothProfile.HEARING_AID), + new ProfileConfig(HidDeviceService.isEnabled(), BluetoothProfile.HID_DEVICE), + new ProfileConfig(HidHostService.isEnabled(), BluetoothProfile.HID_HOST), + new ProfileConfig(TbsService.isEnabled(), BluetoothProfile.LE_CALL_CONTROL), + new ProfileConfig(BluetoothMapService.isEnabled(), BluetoothProfile.MAP), + new ProfileConfig(MapClientService.isEnabled(), BluetoothProfile.MAP_CLIENT), + new ProfileConfig(McpService.isEnabled(), BluetoothProfile.MCP_SERVER), + new ProfileConfig(BluetoothOppService.isEnabled(), BluetoothProfile.OPP), + new ProfileConfig(PanService.isEnabled(), BluetoothProfile.PAN), + new ProfileConfig(BluetoothPbapService.isEnabled(), BluetoothProfile.PBAP), + new ProfileConfig(PbapClientService.isEnabled(), BluetoothProfile.PBAP_CLIENT), + new ProfileConfig(SapService.isEnabled(), BluetoothProfile.SAP), + new ProfileConfig( + VolumeControlService.isEnabled(), BluetoothProfile.VOLUME_CONTROL), + new ProfileConfig(LeAudioService.isEnabled(), BluetoothProfile.LE_AUDIO), + new ProfileConfig( + LeAudioService.isBroadcastEnabled(), BluetoothProfile.LE_AUDIO_BROADCAST), + }; /** A test function to allow for dynamic enabled */ @VisibleForTesting diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java index 376b19c321..2d87a8da2e 100644 --- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -1183,14 +1183,13 @@ public class RemoteDevices { } void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) { + DeviceProperties deviceProperties; BluetoothDevice device = getDevice(mainAddress); if (device == null) { - errorLog( - "addressConsolidateCallback: device is NULL, address=" - + Utils.getRedactedAddressStringFromByte(mainAddress) - + ", secondaryAddress=" - + Utils.getRedactedAddressStringFromByte(secondaryAddress)); - return; + deviceProperties = addDeviceProperties(mainAddress); + device = deviceProperties.getDevice(); + } else { + deviceProperties = getDeviceProperties(device); } Log.d( TAG, @@ -1199,7 +1198,6 @@ public class RemoteDevices { + ", secondaryAddress:" + Utils.getRedactedAddressStringFromByte(secondaryAddress)); - DeviceProperties deviceProperties = getDeviceProperties(device); deviceProperties.setIsConsolidated(true); deviceProperties.setDeviceType(BluetoothDevice.DEVICE_TYPE_DUAL); deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress)); @@ -1217,16 +1215,13 @@ public class RemoteDevices { */ void leAddressAssociateCallback( byte[] mainAddress, byte[] secondaryAddress, int identityAddressTypeFromNative) { + DeviceProperties deviceProperties; BluetoothDevice device = getDevice(mainAddress); if (device == null) { - errorLog( - "leAddressAssociateCallback: device is NULL, address=" - + Utils.getRedactedAddressStringFromByte(mainAddress) - + ", secondaryAddress=" - + Utils.getRedactedAddressStringFromByte(secondaryAddress) - + ", identityAddressTypeFromNative=" - + identityAddressTypeFromNative); - return; + deviceProperties = addDeviceProperties(mainAddress); + device = deviceProperties.getDevice(); + } else { + deviceProperties = getDeviceProperties(device); } Log.d( TAG, @@ -1237,7 +1232,6 @@ public class RemoteDevices { + ", identityAddressTypeFromNative=" + identityAddressTypeFromNative); - DeviceProperties deviceProperties = getDeviceProperties(device); deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress)); deviceProperties.setIdentityAddressTypeFromNative(identityAddressTypeFromNative); } diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java index 5fe00aaeb0..1247cb7330 100644 --- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java +++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java @@ -17,12 +17,13 @@ package com.android.bluetooth.csip; +import static java.util.Objects.requireNonNull; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.util.Log; import com.android.bluetooth.Utils; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.UUID; @@ -30,83 +31,29 @@ import java.util.UUID; /** CSIP Set Coordinator role native interface */ public class CsipSetCoordinatorNativeInterface { private static final String TAG = "CsipSetCoordinatorNativeInterface"; - private BluetoothAdapter mAdapter; - - @GuardedBy("INSTANCE_LOCK") - private static CsipSetCoordinatorNativeInterface sInstance; - - private static final Object INSTANCE_LOCK = new Object(); - - private CsipSetCoordinatorNativeInterface() { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - if (mAdapter == null) { - Log.wtf(TAG, "No Bluetooth Adapter Available"); - } - } - - /** Get singleton instance. */ - public static CsipSetCoordinatorNativeInterface getInstance() { - synchronized (INSTANCE_LOCK) { - if (sInstance == null) { - sInstance = new CsipSetCoordinatorNativeInterface(); - } - return sInstance; - } - } + private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); - /** Set singleton instance. */ - @VisibleForTesting - public static void setInstance(CsipSetCoordinatorNativeInterface instance) { - synchronized (INSTANCE_LOCK) { - sInstance = instance; - } + CsipSetCoordinatorNativeInterface() { + requireNonNull(mAdapter); } - /** - * Initializes the native interface. - * - * <p>priorities to configure. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void init() { + void init() { initNative(); } - /** Cleanup the native interface. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void cleanup() { + void cleanup() { cleanupNative(); } - /** - * Initiates CsipSetCoordinator connection to a remote device. - * - * @param device the remote device - * @return true on success, otherwise false. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public boolean connect(BluetoothDevice device) { + boolean connect(BluetoothDevice device) { return connectNative(getByteAddress(device)); } - /** - * Disconnects CsipSetCoordinator from a remote device. - * - * @param device the remote device - * @return true on success, otherwise false. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public boolean disconnect(BluetoothDevice device) { + boolean disconnect(BluetoothDevice device) { return disconnectNative(getByteAddress(device)); } - /** - * Get the device by the address - * - * @return the device - */ - @VisibleForTesting - public BluetoothDevice getDevice(byte[] address) { + BluetoothDevice getDevice(byte[] address) { return mAdapter.getRemoteDevice(address); } diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java index f1cdefc1b2..a206cee2a7 100644 --- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java +++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java @@ -20,6 +20,9 @@ package com.android.bluetooth.csip; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElseGet; + import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,9 +36,7 @@ import android.bluetooth.IBluetoothCsipSetCoordinator; import android.bluetooth.IBluetoothCsipSetCoordinatorCallback; import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback; import android.content.AttributionSource; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -60,7 +61,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -76,15 +76,13 @@ public class CsipSetCoordinatorService extends ProfileService { private static CsipSetCoordinatorService sCsipSetCoordinatorService; - private Handler mHandler = null; - - private AdapterService mAdapterService; - private LeAudioService mLeAudioService; - private DatabaseManager mDatabaseManager; - private HandlerThread mStateMachinesThread; - @VisibleForTesting ServiceFactory mServiceFactory = new ServiceFactory(); - - @VisibleForTesting CsipSetCoordinatorNativeInterface mCsipSetCoordinatorNativeInterface; + private final AdapterService mAdapterService; + private final DatabaseManager mDatabaseManager; + private final Handler mHandler; + private final HandlerThread mStateMachinesThread; + private final Looper mStateMachinesLooper; + private final CsipSetCoordinatorNativeInterface mNativeInterface; + private final ServiceFactory mServiceFactory; @GuardedBy("mStateMachines") private final Map<BluetoothDevice, CsipSetCoordinatorStateMachine> mStateMachines = @@ -103,66 +101,53 @@ public class CsipSetCoordinatorService extends ProfileService { private final Map<Integer, Pair<UUID, IBluetoothCsipSetCoordinatorLockCallback>> mLocks = new ConcurrentHashMap<>(); - public CsipSetCoordinatorService(Context ctx) { - super(ctx); - } - - public static boolean isEnabled() { - return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false); - } + private LeAudioService mLeAudioService; - @Override - protected IProfileServiceBinder initBinder() { - return new BluetoothCsisBinder(this); + public CsipSetCoordinatorService(AdapterService adapterService) { + this(adapterService, null, null, new ServiceFactory()); } - @Override - public void start() { - Log.d(TAG, "start()"); - if (sCsipSetCoordinatorService != null) { - throw new IllegalStateException("start() called twice"); - } - - // Get AdapterService, DatabaseManager, CsipSetCoordinatorNativeInterface. - // None of them can be null. - mAdapterService = - Objects.requireNonNull( - AdapterService.getAdapterService(), - "AdapterService cannot be null when CsipSetCoordinatorService starts"); - mDatabaseManager = - Objects.requireNonNull( - mAdapterService.getDatabase(), - "DatabaseManager cannot be null when CsipSetCoordinatorService starts"); - mCsipSetCoordinatorNativeInterface = - Objects.requireNonNull( - CsipSetCoordinatorNativeInterface.getInstance(), - "CsipSetCoordinatorNativeInterface cannot be null when" - .concat("CsipSetCoordinatorService starts")); - - // Setup Handler. - mHandler = new Handler(Looper.getMainLooper()); + @VisibleForTesting + CsipSetCoordinatorService( + AdapterService adapterService, + Looper looper, + CsipSetCoordinatorNativeInterface nativeInterface, + ServiceFactory serviceFactory) { + super(requireNonNull(adapterService)); + mAdapterService = adapterService; + mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); + mNativeInterface = + requireNonNullElseGet( + nativeInterface, () -> new CsipSetCoordinatorNativeInterface()); + mServiceFactory = requireNonNull(serviceFactory); + if (looper == null) { + mHandler = new Handler(requireNonNull(Looper.getMainLooper())); + mStateMachinesThread = new HandlerThread("CsipSetCoordinatorService.StateMachines"); + mStateMachinesThread.start(); + mStateMachinesLooper = mStateMachinesThread.getLooper(); + } else { + mHandler = new Handler(looper); + mStateMachinesThread = null; + mStateMachinesLooper = looper; + } // Get LE Audio service (can be null) mLeAudioService = mServiceFactory.getLeAudioService(); - synchronized (mStateMachines) { - mStateMachines.clear(); - } - - // Start handler thread for state machines - mStateMachinesThread = new HandlerThread("CsipSetCoordinatorService.StateMachines"); - mStateMachinesThread.start(); - - // Setup broadcast receivers - IntentFilter filter = new IntentFilter(); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); - // Mark service as started setCsipSetCoordinatorService(this); // Initialize native interface - mCsipSetCoordinatorNativeInterface.init(); + mNativeInterface.init(); + } + + public static boolean isEnabled() { + return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false); + } + + @Override + protected IProfileServiceBinder initBinder() { + return new BluetoothCsisBinder(this); } @Override @@ -174,8 +159,7 @@ public class CsipSetCoordinatorService extends ProfileService { } // Cleanup native interface - mCsipSetCoordinatorNativeInterface.cleanup(); - mCsipSetCoordinatorNativeInterface = null; + mNativeInterface.cleanup(); // Mark service as stopped setCsipSetCoordinatorService(null); @@ -193,17 +177,13 @@ public class CsipSetCoordinatorService extends ProfileService { try { mStateMachinesThread.quitSafely(); mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); - mStateMachinesThread = null; } catch (InterruptedException e) { // Do not rethrow as we are shutting down anyway } } // Unregister Handler and stop all queued messages. - if (mHandler != null) { - mHandler.removeCallbacksAndMessages(null); - mHandler = null; - } + mHandler.removeCallbacksAndMessages(null); mDeviceGroupIdRankMap.clear(); mCallbacks.clear(); @@ -213,10 +193,6 @@ public class CsipSetCoordinatorService extends ProfileService { mGroupIdToUuidMap.clear(); mLocks.clear(); - - // Clear AdapterService, CsipSetCoordinatorNativeInterface - mCsipSetCoordinatorNativeInterface = null; - mAdapterService = null; } @Override @@ -515,7 +491,7 @@ public class CsipSetCoordinatorService extends ProfileService { } Log.d(TAG, "lockGroup(): locking group: " + groupId); - mCsipSetCoordinatorNativeInterface.groupLockSet(groupId, true); + mNativeInterface.groupLockSet(groupId, true); return uuid; } @@ -536,7 +512,7 @@ public class CsipSetCoordinatorService extends ProfileService { Pair<UUID, IBluetoothCsipSetCoordinatorLockCallback> uuidCbPair = entry.getValue(); if (uuidCbPair.first.equals(lockUuid)) { Log.d(TAG, "unlockGroup(): unlocking ... " + lockUuid); - mCsipSetCoordinatorNativeInterface.groupLockSet(entry.getKey(), false); + mNativeInterface.groupLockSet(entry.getKey(), false); return; } } @@ -803,13 +779,13 @@ public class CsipSetCoordinatorService extends ProfileService { Log.d(TAG, "notifySetMemberAvailable: " + device + ", " + groupId); /* Sent intent as well */ - Intent intent = new Intent(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, groupId); - - intent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + Intent intent = + new Intent(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE) + .putExtra(BluetoothDevice.EXTRA_DEVICE, device) + .putExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, groupId) + .addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); sendOrderedBroadcast(intent, BLUETOOTH_PRIVILEGED); /* Notify registered parties */ @@ -820,18 +796,9 @@ public class CsipSetCoordinatorService extends ProfileService { BluetoothDevice device = stackEvent.device; Log.d(TAG, "Message from native: " + stackEvent); - Intent intent = null; int groupId = stackEvent.valueInt1; if (stackEvent.type == CsipSetCoordinatorStackEvent.EVENT_TYPE_DEVICE_AVAILABLE) { - Objects.requireNonNull(device); - - intent = new Intent(BluetoothCsipSetCoordinator.ACTION_CSIS_DEVICE_AVAILABLE); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, stackEvent.device); - intent.putExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, groupId); - intent.putExtra( - BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, stackEvent.valueInt2); - intent.putExtra( - BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, stackEvent.valueUuid1); + requireNonNull(device); handleDeviceAvailable( device, @@ -839,9 +806,24 @@ public class CsipSetCoordinatorService extends ProfileService { stackEvent.valueInt3, stackEvent.valueUuid1, stackEvent.valueInt2); + + Intent intent = + new Intent(BluetoothCsipSetCoordinator.ACTION_CSIS_DEVICE_AVAILABLE) + .putExtra(BluetoothDevice.EXTRA_DEVICE, stackEvent.device) + .putExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, groupId) + .putExtra( + BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, + stackEvent.valueInt2) + .putExtra( + BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, + stackEvent.valueUuid1) + .addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + sendOrderedBroadcast(intent, BLUETOOTH_PRIVILEGED); } else if (stackEvent.type == CsipSetCoordinatorStackEvent.EVENT_TYPE_SET_MEMBER_AVAILABLE) { - Objects.requireNonNull(device); + requireNonNull(device); if (!mFoundSetMemberToGroupId.containsKey(device)) { mFoundSetMemberToGroupId.put(device, groupId); } @@ -854,13 +836,6 @@ public class CsipSetCoordinatorService extends ProfileService { handleGroupLockChanged(groupId, lock_status, lock_state); } - if (intent != null) { - intent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - sendOrderedBroadcast(intent, BLUETOOTH_PRIVILEGED); - } - synchronized (mStateMachines) { CsipSetCoordinatorStateMachine sm = mStateMachines.get(device); @@ -900,10 +875,7 @@ public class CsipSetCoordinatorService extends ProfileService { Log.d(TAG, "Creating a new state machine for " + device); sm = CsipSetCoordinatorStateMachine.make( - device, - this, - mCsipSetCoordinatorNativeInterface, - mStateMachinesThread.getLooper()); + device, this, mNativeInterface, mStateMachinesLooper); mStateMachines.put(device, sm); return sm; } @@ -911,16 +883,6 @@ public class CsipSetCoordinatorService extends ProfileService { /** Process a change in the bonding state for a device */ public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) { - if (mHandler == null) { - Log.e( - TAG, - "mHandler is null, service is stopped. Ignore Bond State for " - + device - + " to state: " - + toState); - return; - } - mHandler.post(() -> bondStateChanged(device, toState)); } @@ -982,15 +944,6 @@ public class CsipSetCoordinatorService extends ProfileService { } void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) { - if (mHandler == null) { - Log.e( - TAG, - "mHandler is null, service is stopped. Ignore Connection State for " - + device - + " to state: " - + toState); - return; - } mHandler.post(() -> connectionStateChanged(device, fromState, toState)); } @@ -1074,7 +1027,7 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1087,7 +1040,7 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates( int[] states, AttributionSource source) { - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1099,8 +1052,8 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Objects.requireNonNull(device, "device cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(device); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1113,8 +1066,8 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public boolean setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source) { - Objects.requireNonNull(device, "device cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(device); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1126,8 +1079,8 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - Objects.requireNonNull(device, "device cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(device); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1142,8 +1095,8 @@ public class CsipSetCoordinatorService extends ProfileService { int groupId, @NonNull IBluetoothCsipSetCoordinatorLockCallback callback, AttributionSource source) { - Objects.requireNonNull(callback, "callback cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(callback); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1156,8 +1109,8 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public void unlockGroup(@NonNull ParcelUuid lockUuid, AttributionSource source) { - Objects.requireNonNull(lockUuid, "lockUuid cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(lockUuid); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { @@ -1169,8 +1122,8 @@ public class CsipSetCoordinatorService extends ProfileService { @Override public List<Integer> getAllGroupIds(ParcelUuid uuid, AttributionSource source) { - Objects.requireNonNull(uuid, "uuid cannot be null"); - Objects.requireNonNull(source, "source cannot be null"); + requireNonNull(uuid); + requireNonNull(source); CsipSetCoordinatorService service = getService(source); if (service == null) { diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java index 9706df37d7..65e401bd97 100644 --- a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java +++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementManager.java @@ -107,6 +107,12 @@ public class DistanceMeasurementManager { "startDistanceMeasurement:" + (" device=" + params.getDevice()) + (" method=" + params.getMethodId())); + if (!mAdapterService.isConnected(params.getDevice())) { + Log.e(TAG, "Device " + params.getDevice() + " is not connected"); + invokeStartFail( + callback, params.getDevice(), BluetoothStatusCodes.ERROR_NO_LE_CONNECTION); + return; + } String address = mAdapterService.getIdentityAddress(params.getDevice().getAddress()); if (address == null) { address = params.getDevice().getAddress(); @@ -142,12 +148,13 @@ public class DistanceMeasurementManager { BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); return; } - if (!mAdapterService.isConnected(params.getDevice())) { - Log.e(TAG, "Device " + params.getDevice() + " is not connected"); + if (mAdapterService.getBondState(params.getDevice()) + != BluetoothDevice.BOND_BONDED) { + Log.e(TAG, "StartDistanceMeasurement: the target device is not bonded."); invokeStartFail( callback, params.getDevice(), - BluetoothStatusCodes.ERROR_NO_LE_CONNECTION); + BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); return; } startCsTracker(tracker); diff --git a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java index 5adbbf2b93..af59e0c8f6 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java +++ b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java @@ -382,7 +382,7 @@ public class GattNativeInterface { byte[] val, int authReq); - private native void gattSubrateRequestNative( + private native int gattSubrateRequestNative( int clientIf, String address, int subrateMin, @@ -570,7 +570,7 @@ public class GattNativeInterface { } /** Update connection parameter. */ - public void gattSubrateRequest( + public int gattSubrateRequest( int clientIf, String address, int subrateMin, @@ -578,7 +578,7 @@ public class GattNativeInterface { int maxLatency, int contNumber, int supervisionTimeout) { - gattSubrateRequestNative( + return gattSubrateRequestNative( clientIf, address, subrateMin, diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index 2be39b6094..ef12680b72 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.PeriodicAdvertisingParameters; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; +import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -180,9 +181,11 @@ public class GattService extends ProfileService { private final AdapterService mAdapterService; private final AdvertiseManager mAdvertiseManager; private final GattNativeInterface mNativeInterface; + private final CompanionDeviceManager mCompanionDeviceManager; private final DistanceMeasurementManager mDistanceMeasurementManager; private final ActivityManager mActivityManager; private final PackageManager mPackageManager; + private final HandlerThread mScanThread; private Handler mTestModeHandler; @@ -191,6 +194,7 @@ public class GattService extends ProfileService { mAdapterService = adapterService; mActivityManager = requireNonNull(getSystemService(ActivityManager.class)); mPackageManager = requireNonNull(mAdapterService.getPackageManager()); + mCompanionDeviceManager = requireNonNull(getSystemService(CompanionDeviceManager.class)); mTransitionalScanHelper = new TransitionalScanHelper(adapterService, this::isTestModeEnabled); @@ -203,9 +207,11 @@ public class GattService extends ProfileService { mAdvertiseManager = new AdvertiseManager(this); if (!Flags.scanManagerRefactor()) { - HandlerThread thread = new HandlerThread("BluetoothScanManager"); - thread.start(); - mTransitionalScanHelper.start(thread.getLooper()); + mScanThread = new HandlerThread("BluetoothScanManager"); + mScanThread.start(); + mTransitionalScanHelper.start(mScanThread.getLooper()); + } else { + mScanThread = null; } mDistanceMeasurementManager = GattObjectsFactory.getInstance().createDistanceMeasurementManager(mAdapterService); @@ -251,6 +257,9 @@ public class GattService extends ProfileService { @Override public void cleanup() { Log.d(TAG, "cleanup()"); + if (!Flags.scanManagerRefactor()) { + mScanThread.quitSafely(); + } mNativeInterface.cleanup(); mAdvertiseManager.cleanup(); mDistanceMeasurementManager.cleanup(); @@ -802,41 +811,39 @@ public class GattService extends ProfileService { } @Override - public void subrateModeRequest( + public int subrateModeRequest( int clientIf, - String address, + BluetoothDevice device, int subrateMode, AttributionSource attributionSource) { GattService service = getService(); if (service == null) { - return; + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "subrateModeRequest")) { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED; } - service.subrateModeRequest(clientIf, address, subrateMode, attributionSource); - } - @Override - public void leSubrateRequest( - int clientIf, - String address, - int subrateMin, - int subrateMax, - int maxLatency, - int contNumber, - int supervisionTimeout, - AttributionSource attributionSource) { - GattService service = getService(); - if (service == null) { - return; + if (!Utils.checkConnectPermissionForDataDelivery( + service, attributionSource, "GattService subrateModeRequest")) { + return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; } - service.leSubrateRequest( - clientIf, - address, - subrateMin, - subrateMax, - maxLatency, - contNumber, - supervisionTimeout, - attributionSource); + + Utils.enforceCdmAssociationIfNotBluetoothPrivileged( + service, service.mCompanionDeviceManager, attributionSource, device); + + if (subrateMode < BluetoothGatt.SUBRATE_REQUEST_MODE_BALANCED + || subrateMode > BluetoothGatt.SUBRATE_REQUEST_MODE_LOW_POWER) { + throw new IllegalArgumentException("Subrate Mode not within valid range"); + } + + requireNonNull(device); + String address = device.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + throw new IllegalArgumentException("Invalid device address: " + address); + } + + return service.subrateModeRequest(clientIf, device, subrateMode); } @Override @@ -1334,6 +1341,13 @@ public class GattService extends ProfileService { if (app != null) { app.callback.onClientConnectionState( status, clientIf, (status == BluetoothGatt.GATT_SUCCESS), address); + MetricsLogger.getInstance() + .logBluetoothEvent( + getDevice(address), + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_CONNECT_JAVA, + connectionStatusToState(status), + app.appUid); } statsLogGattConnectionStateChange( BluetoothProfile.GATT, address, clientIf, connectionState, status); @@ -1379,6 +1393,13 @@ public class GattService extends ProfileService { if (app != null) { app.callback.onClientConnectionState(status, clientIf, false, address); + MetricsLogger.getInstance() + .logBluetoothEvent( + getDevice(address), + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_DISCONNECT_JAVA, + BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SUCCESS, + app.appUid); } statsLogGattConnectionStateChange( BluetoothProfile.GATT, @@ -1930,7 +1951,6 @@ public class GattService extends ProfileService { for (Map.Entry<Integer, String> entry : connMap.entrySet()) { Log.d(TAG, "disconnecting addr:" + entry.getValue()); clientDisconnect(entry.getKey(), entry.getValue(), attributionSource); - // clientDisconnect(int clientIf, String address) } } @@ -1967,7 +1987,8 @@ public class GattService extends ProfileService { return; } if (parameters.getOwnAddressType() != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT - || serverIf != 0) { + || serverIf != 0 + || parameters.isDirected()) { this.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); } mAdvertiseManager.startAdvertisingSet( @@ -2040,7 +2061,12 @@ public class GattService extends ProfileService { mAdvertiseManager.setScanResponseData(advertiserId, data); } - @RequiresPermission(BLUETOOTH_ADVERTISE) + @RequiresPermission( + allOf = { + BLUETOOTH_ADVERTISE, + BLUETOOTH_PRIVILEGED, + }, + conditional = true) void setAdvertisingParameters( int advertiserId, AdvertisingSetParameters parameters, @@ -2049,6 +2075,10 @@ public class GattService extends ProfileService { this, attributionSource, "GattService setAdvertisingParameters")) { return; } + if (parameters.getOwnAddressType() != AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT + || parameters.isDirected()) { + this.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } mAdvertiseManager.setAdvertisingParameters(advertiserId, parameters); } @@ -2156,6 +2186,15 @@ public class GattService extends ProfileService { } Log.d(TAG, "unregisterClient() - clientIf=" + clientIf); + for (ContextMap.Connection conn : mClientMap.getConnectionByApp(clientIf)) { + MetricsLogger.getInstance() + .logBluetoothEvent( + getDevice(conn.address), + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_DISCONNECT_JAVA, + BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__END, + attributionSource.getUid()); + } mClientMap.remove(clientIf); mNativeInterface.gattClientUnregisterApp(clientIf); } @@ -2198,6 +2237,18 @@ public class GattService extends ProfileService { BluetoothProtoEnums.CONNECTION_STATE_CONNECTING, -1); + MetricsLogger.getInstance() + .logBluetoothEvent( + getDevice(address), + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_CONNECT_JAVA, + isDirect + ? BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__DIRECT_CONNECT + : BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__INDIRECT_CONNECT, + attributionSource.getUid()); + int preferredMtu = 0; // Some applications expect MTU to be exchanged immediately on connections @@ -2248,6 +2299,13 @@ public class GattService extends ProfileService { clientIf, BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING, -1); + MetricsLogger.getInstance() + .logBluetoothEvent( + getDevice(address), + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__GATT_DISCONNECT_JAVA, + BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__START, + attributionSource.getUid()); mNativeInterface.gattClientDisconnect(clientIf, address, connId != null ? connId : 0); } @@ -2808,14 +2866,7 @@ public class GattService extends ProfileService { maxConnectionEventLen); } - @RequiresPermission(BLUETOOTH_CONNECT) - void subrateModeRequest( - int clientIf, String address, int subrateMode, AttributionSource attributionSource) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, attributionSource, "GattService subrateModeRequest")) { - return; - } - + private int subrateModeRequest(int clientIf, BluetoothDevice device, int subrateMode) { int subrateMin; int subrateMax; int maxLatency; @@ -2851,64 +2902,15 @@ public class GattService extends ProfileService { Log.d( TAG, - "subrateModeRequest() - " - + "address=" - + BluetoothUtils.toAnonymizedAddress(address) - + ", subrate min/max=" - + subrateMin - + "/" - + subrateMax - + ", maxLatency=" - + maxLatency - + ", continuation Number=" - + contNumber - + ", timeout=" - + supervisionTimeout); - - mNativeInterface.gattSubrateRequest( - clientIf, - address, - subrateMin, - subrateMax, - maxLatency, - contNumber, - supervisionTimeout); - } - - @RequiresPermission(BLUETOOTH_CONNECT) - void leSubrateRequest( - int clientIf, - String address, - int subrateMin, - int subrateMax, - int maxLatency, - int contNumber, - int supervisionTimeout, - AttributionSource attributionSource) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, attributionSource, "GattService leSubrateRequest")) { - return; - } - - Log.d( - TAG, - "leSubrateRequest() - " - + "address=" - + BluetoothUtils.toAnonymizedAddress(address) - + ", subrate min/max=" - + subrateMin - + "/" - + subrateMax - + ", maxLatency=" - + maxLatency - + ", continuation Number=" - + contNumber - + ", timeout=" - + supervisionTimeout); + ("subrateModeRequest(" + device + ", " + subrateMode + "): ") + + (", subrate min/max=" + subrateMin + "/" + subrateMax) + + (", maxLatency=" + maxLatency) + + (", continuationNumber=" + contNumber) + + (", timeout=" + supervisionTimeout)); - mNativeInterface.gattSubrateRequest( + return mNativeInterface.gattSubrateRequest( clientIf, - address, + device.getAddress(), subrateMin, subrateMax, maxLatency, @@ -3797,6 +3799,24 @@ public class GattService extends ProfileService { mTransitionalScanHelper.dumpProto(builder); } + private BluetoothDevice getDevice(String address) { + byte[] addressBytes = Utils.getBytesFromAddress(address); + return mAdapterService.getDeviceFromByte(addressBytes); + } + + private static int connectionStatusToState(int status) { + return switch (status) { + // GATT_SUCCESS + case 0x00 -> BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SUCCESS; + // GATT_CONNECTION_TIMEOUT + case 0x93 -> + BluetoothStatsLog + .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__CONNECTION_TIMEOUT; + // For now all other errors are bucketed together. + default -> BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__FAIL; + }; + } + /************************************************************************** * GATT Test functions *************************************************************************/ diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java index 2b0ee77e61..69feb368ef 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java @@ -1415,6 +1415,7 @@ public class HeadsetService extends ProfileService { } // Make sure the Audio Manager knows the previous active device is no longer active. + BluetoothDevice previousActiveDevice = mActiveDevice; mActiveDevice = null; mNativeInterface.setActiveDevice(null); if (Utils.isScoManagedByAudioEnabled()) { @@ -1422,7 +1423,7 @@ public class HeadsetService extends ProfileService { .getAudioManager() .handleBluetoothActiveDeviceChanged( null, - mActiveDevice, + previousActiveDevice, BluetoothProfileConnectionInfo.createHfpInfo()); } else { broadcastActiveDevice(null); diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java index fa010889ef..123cf5eb7a 100644 --- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java @@ -649,8 +649,6 @@ public class HeadsetClientStateMachine extends StateMachine { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; } else if (getCall(HfpClientCall.CALL_STATE_ACTIVE) != null) { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3; - } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { - action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } else { action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; } 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 dba7542953..4db0c1d5ac 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -26,6 +26,7 @@ import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState; import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiManagePrimaryGroup; import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; +import static com.android.bluetooth.flags.Flags.leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator; import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener; import static com.android.modules.utils.build.SdkLevel.isAtLeastU; @@ -51,7 +52,6 @@ import android.bluetooth.IBluetoothLeBroadcastCallback; import android.bluetooth.IBluetoothVolumeControl; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.IScannerCallback; -import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; @@ -222,10 +222,8 @@ public class LeAudioService extends ProfileService { new RemoteCallbackList<>(); BluetoothLeScanner mAudioServersScanner; - /* When mScanCallback is not null, it means scan is started. */ - ScanCallback mScanCallback; - private final AudioServerScanCallback2 mScanCallback2 = new AudioServerScanCallback2(); + private final AudioServerScanCallback mScanCallback = new AudioServerScanCallback(); public LeAudioService(Context ctx) { this(ctx, LeAudioNativeInterface.getInstance()); @@ -604,30 +602,6 @@ public class LeAudioService extends ProfileService { // Setup codec config mLeAudioCodecConfig = new LeAudioCodecConfig(this); - if (!Flags.leaudioSynchronizeStart()) { - // Delay the call to init by posting it. This ensures TBS and MCS are fully initialized - // before we start accepting connections - mHandler.post(this::init); - return; - } - mNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); - - if (leaudioUseAudioModeListener()) { - mAudioManager.addOnModeChangedListener(getMainExecutor(), mAudioModeChangeListener); - } - } - - // TODO: b/341385684 -- Delete the init method as it has been inlined in start - private void init() { - if (!isAvailable()) { - Log.e(TAG, " Service disabled before init"); - return; - } - - if (!mTmapStarted) { - mTmapStarted = registerTmap(); - } - mNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); if (leaudioUseAudioModeListener()) { @@ -656,9 +630,6 @@ public class LeAudioService extends ProfileService { clearCreateBroadcastTimeoutCallback(); - if (!Flags.leaudioSynchronizeStart()) { - mHandler.removeCallbacks(this::init); - } removeActiveDevice(false); if (mTmapGattServer == null) { @@ -669,7 +640,7 @@ public class LeAudioService extends ProfileService { mTmapStarted = false; } - stopAudioServersBackgroundScan(); + mScanCallback.stopBackgroundScan(); mAudioServersScanner = null; // Don't wait for async call with INACTIVE group status, clean active @@ -1600,7 +1571,13 @@ public class LeAudioService extends ProfileService { return false; } - return descriptor.mGroupId == mUnicastGroupIdDeactivatedForBroadcastTransition; + if (leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) { + return (descriptor.mGroupId == mUnicastGroupIdDeactivatedForBroadcastTransition) + || device.equals(mActiveAudioInDevice) + || device.equals(mActiveAudioOutDevice); + } else { + return descriptor.mGroupId == mUnicastGroupIdDeactivatedForBroadcastTransition; + } } /** Return true if group is primary - is active or was active before switch to broadcast */ @@ -1984,7 +1961,7 @@ public class LeAudioService extends ProfileService { return true; } - private class AudioServerScanCallback2 extends IScannerCallback.Stub { + private class AudioServerScanCallback extends IScannerCallback.Stub { // See BluetoothLeScanner.BleScanCallbackWrapper.mScannerId int mScannerId = 0; @@ -2083,44 +2060,6 @@ public class LeAudioService extends ProfileService { public void onScanManagerErrorCallback(int errorCode) {} } - private class AudioServerScanCallback extends ScanCallback { - int mMaxScanRetries = 10; - int mScanRetries = 0; - - @Override - public void onScanResult(int callbackType, ScanResult result) { - /* Filter is set in the way, that there will be no results found. - * We just need a scanner to be running for the APCF filtering defined in native - */ - } - - @Override - public void onBatchScanResults(List<ScanResult> results) { - /* Filter is set in the way, that there will be no results found. - * We just need a scanner to be running for the APCF filtering defined in native - */ - } - - @Override - public void onScanFailed(int errorCode) { - Log.w(TAG, "Scan failed err: " + errorCode + " scan retries: " + mScanRetries); - switch (errorCode) { - case SCAN_FAILED_INTERNAL_ERROR: - case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED: - if (mScanRetries < mMaxScanRetries) { - mScanRetries++; - Log.w(TAG, "Failed to start. Let's retry"); - mHandler.post(() -> startAudioServersBackgroundScan(/* retry= */ true)); - } - break; - default: - /* Indicate scan is no running */ - mScanCallback = null; - break; - } - } - } - @VisibleForTesting boolean handleAudioDeviceAdded( BluetoothDevice device, int type, boolean isSink, boolean isSource) { @@ -2968,7 +2907,7 @@ public class LeAudioService extends ProfileService { .getBroadcastItem(i) .onGroupStreamStatusChanged(groupId, groupStreamStatus); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -2984,7 +2923,7 @@ public class LeAudioService extends ProfileService { .getBroadcastItem(i) .onBroadcastToUnicastFallbackGroupChanged(groupId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -3129,87 +3068,6 @@ public class LeAudioService extends ProfileService { } } - @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/348562830 - Remove with flag - void stopAudioServersBackgroundScan() { - Log.d(TAG, "stopAudioServersBackgroundScan"); - - if (Flags.leaudioCallStartScanDirectly()) { - mScanCallback2.stopBackgroundScan(); - return; - } - - if (mAudioServersScanner == null || mScanCallback == null) { - Log.d(TAG, "stopAudioServersBackgroundScan: already stopped"); - return; - } - - try { - mAudioServersScanner.stopScan(mScanCallback); - } catch (IllegalStateException e) { - Log.e(TAG, "Fail to stop scanner, consider it stopped", e); - } - - /* Callback is the indicator for scanning being enabled */ - mScanCallback = null; - } - - @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/348562830 - Remove with flag - void startAudioServersBackgroundScan(boolean retry) { - Log.d(TAG, "startAudioServersBackgroundScan, retry: " + retry); - - if (!isScannerNeeded()) { - return; - } - - if (Flags.leaudioCallStartScanDirectly()) { - mScanCallback2.startBackgroundScan(); - return; - } - - if (mAudioServersScanner == null) { - mAudioServersScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner(); - if (mAudioServersScanner == null) { - Log.e(TAG, "startAudioServersBackgroundScan: Could not get scanner"); - return; - } - } - - if (!retry) { - if (mScanCallback != null) { - Log.d(TAG, "startAudioServersBackgroundScan: Scanning already enabled"); - return; - } - mScanCallback = new AudioServerScanCallback(); - } - - /* Filter we are building here will not match to anything. - * Eventually we should be able to start scan from native when - * b/276350722 is done - */ - byte[] serviceData = new byte[] {0x11}; - - ArrayList filterList = new ArrayList<ScanFilter>(); - ScanFilter filter = - new ScanFilter.Builder() - .setServiceData(BluetoothUuid.LE_AUDIO, serviceData) - .build(); - filterList.add(filter); - - ScanSettings settings = - new ScanSettings.Builder() - .setLegacy(false) - .setScanMode(ScanSettings.SCAN_MODE_BALANCED) - .setPhy(BluetoothDevice.PHY_LE_1M) - .build(); - - try { - mAudioServersScanner.startScan(filterList, settings, mScanCallback); - } catch (IllegalStateException e) { - Log.e(TAG, "Fail to start scanner, consider it stopped", e); - mScanCallback = null; - } - } - void transitionFromBroadcastToUnicast() { if (mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) { Log.d(TAG, "No deactivated group due for broadcast transmission"); @@ -3439,7 +3297,10 @@ public class LeAudioService extends ProfileService { case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTING: case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED: deviceDescriptor.mAclConnected = false; - startAudioServersBackgroundScan(/* retry= */ false); + + if (isScannerNeeded()) { + mScanCallback.startBackgroundScan(); + } boolean disconnectDueToUnbond = (BluetoothDevice.BOND_NONE @@ -4171,7 +4032,7 @@ public class LeAudioService extends ProfileService { } mDeviceDescriptors.remove(device); if (!isScannerNeeded()) { - stopAudioServersBackgroundScan(); + mScanCallback.stopBackgroundScan(); } } finally { mGroupWriteLock.unlock(); @@ -4213,7 +4074,7 @@ public class LeAudioService extends ProfileService { } if (!isScannerNeeded()) { - stopAudioServersBackgroundScan(); + mScanCallback.stopBackgroundScan(); } /* Set by default earliest connected device */ @@ -4249,7 +4110,7 @@ public class LeAudioService extends ProfileService { } if (!isScannerNeeded()) { - stopAudioServersBackgroundScan(); + mScanCallback.stopBackgroundScan(); } mGroupReadLock.lock(); @@ -4782,7 +4643,9 @@ public class LeAudioService extends ProfileService { mGroupReadLock.unlock(); } - startAudioServersBackgroundScan(/* retry= */ false); + if (isScannerNeeded()) { + mScanCallback.startBackgroundScan(); + } } @VisibleForTesting @@ -4894,7 +4757,9 @@ public class LeAudioService extends ProfileService { if (mBluetoothEnabled) { setAuthorizationForRelatedProfiles(device, true); - startAudioServersBackgroundScan(/* retry= */ false); + if (isScannerNeeded()) { + mScanCallback.startBackgroundScan(); + } } } @@ -4910,7 +4775,7 @@ public class LeAudioService extends ProfileService { try { mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeAdded(device, groupId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -4989,7 +4854,7 @@ public class LeAudioService extends ProfileService { try { mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeRemoved(device, groupId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -5003,7 +4868,7 @@ public class LeAudioService extends ProfileService { try { mLeAudioCallbacks.getBroadcastItem(i).onGroupStatusChanged(groupId, status); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -5017,7 +4882,7 @@ public class LeAudioService extends ProfileService { try { mLeAudioCallbacks.getBroadcastItem(i).onCodecConfigChanged(groupId, status); } catch (RemoteException e) { - continue; + // Ignore Exception } } mLeAudioCallbacks.finishBroadcast(); @@ -5031,7 +4896,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStarted(reason, broadcastId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5045,7 +4910,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStartFailed(reason); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5059,7 +4924,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopped(reason, broadcastId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5073,7 +4938,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopFailed(reason); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5087,7 +4952,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStarted(reason, broadcastId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5101,7 +4966,7 @@ public class LeAudioService extends ProfileService { try { mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStopped(reason, broadcastId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5117,7 +4982,7 @@ public class LeAudioService extends ProfileService { .getBroadcastItem(i) .onBroadcastUpdateFailed(reason, broadcastId); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); @@ -5134,7 +4999,7 @@ public class LeAudioService extends ProfileService { .getBroadcastItem(i) .onBroadcastMetadataChanged(broadcastId, metadata); } catch (RemoteException e) { - continue; + // Ignore Exception } } mBroadcastCallbacks.finishBroadcast(); diff --git a/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java b/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java index 657ea4d5a9..3223c7764e 100644 --- a/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java +++ b/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitor.java @@ -20,16 +20,18 @@ import android.bluetooth.le.ScanFilter; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Objects; import java.util.UUID; /** Helper class used to manage MSFT Advertisement Monitors. */ class MsftAdvMonitor { - /* Only pattern filtering is supported currently */ + /* Only pattern and address filtering are supported currently */ // private static final int MSFT_CONDITION_TYPE_ALL = 0x00; private static final int MSFT_CONDITION_TYPE_PATTERNS = 0x01; // private static final int MSFT_CONDITION_TYPE_UUID = 0x02; // private static final int MSFT_CONDITION_TYPE_IRK = 0x03; - // private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04; + private static final int MSFT_CONDITION_TYPE_ADDRESS = 0x04; // Hardcoded values taken from CrOS defaults private static final byte RSSI_THRESHOLD_HIGH = (byte) 0xBF; // 191 @@ -50,6 +52,25 @@ class MsftAdvMonitor { public byte ad_type; public byte start_byte; public byte[] pattern; + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Pattern other)) { + return false; + } + + return other.ad_type == this.ad_type + && other.start_byte == this.start_byte + && Arrays.equals(other.pattern, this.pattern); + } + + @Override + public int hashCode() { + return Objects.hash(ad_type, start_byte, Arrays.hashCode(pattern)); + } } static class Address { @@ -70,6 +91,13 @@ class MsftAdvMonitor { mMonitor.rssi_sampling_period = RSSI_SAMPLING_PERIOD; mMonitor.condition_type = MSFT_CONDITION_TYPE_PATTERNS; + if (filter.getDeviceAddress() != null) { + mMonitor.condition_type = MSFT_CONDITION_TYPE_ADDRESS; + mAddress.addr_type = (byte) filter.getAddressType(); + mAddress.bd_addr = filter.getDeviceAddress(); + return; + } + if (filter.getServiceDataUuid() != null && dataMaskIsEmpty(filter.getServiceDataMask())) { Pattern pattern = new Pattern(); pattern.ad_type = (byte) 0x16; // Bluetooth Core Spec Part A, Section 1 @@ -93,11 +121,6 @@ class MsftAdvMonitor { pattern.pattern = filter.getAdvertisingData(); mPatterns.add(pattern); } - - if (filter.getDeviceAddress() != null) { - mAddress.addr_type = (byte) filter.getAddressType(); - mAddress.bd_addr = filter.getDeviceAddress(); - } } Monitor getMonitor() { diff --git a/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternList.java b/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternList.java new file mode 100644 index 0000000000..922c898477 --- /dev/null +++ b/android/app/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternList.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.le_scan; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Helper class to keep track of MSFT patterns, their filter index, and number of monitors + * registered with that pattern. Some chipsets don't support multiple monitors with the same + * pattern. To solve that and to generally ease their task, we merge monitors with the same pattern, + * so those monitors will only be sent once. + */ +class MsftAdvMonitorMergedPatternList { + static class MsftAdvMonitorMergedPattern { + private final MsftAdvMonitor.Pattern[] mPatterns; + private final int mFilterIndex; + private int mCount = 0; + + MsftAdvMonitorMergedPattern(MsftAdvMonitor.Pattern[] pattern, int filterIndex) { + mPatterns = pattern; + mFilterIndex = filterIndex; + } + } + + List<MsftAdvMonitorMergedPattern> mMergedPatterns = new ArrayList<>(); + + // Two patterns are considered equal if they have the exact same pattern + // in the same order. Therefore A+B and B+A are considered different, as + // well as A and A+A. This shouldn't causes issues but could be optimized. + // Returns merged pattern or null if not found. + private MsftAdvMonitorMergedPattern getMergedPattern(MsftAdvMonitor.Pattern[] pattern) { + return mMergedPatterns.stream() + .filter(mergedPattern -> Arrays.equals(mergedPattern.mPatterns, pattern)) + .findFirst() + .orElse(null); + } + + // If pattern doesn't exist, creates new entry with given index. + // If pattern exists, increases count and returns filter index. + int add(int filterIndex, MsftAdvMonitor.Pattern[] pattern) { + MsftAdvMonitorMergedPattern mergedPattern = (getMergedPattern(pattern)); + if (mergedPattern == null) { + mergedPattern = new MsftAdvMonitorMergedPattern(pattern, filterIndex); + mMergedPatterns.add(mergedPattern); + } + + mergedPattern.mCount++; + return mergedPattern.mFilterIndex; + } + + // If pattern exists, decreases count. If count is 0, removes entry. + // Returns true if there are no more instances of the given filter index + boolean remove(int filterIndex) { + mMergedPatterns.stream() + .filter(pattern -> pattern.mFilterIndex == filterIndex) + .forEach(pattern -> pattern.mCount--); + return mMergedPatterns.removeIf(pattern -> pattern.mCount == 0); + } +} diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanController.java b/android/app/src/com/android/bluetooth/le_scan/ScanController.java index 36beda6793..d5e97a73bc 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanController.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanController.java @@ -46,6 +46,7 @@ public class ScanController { private static final String TAG = ScanController.class.getSimpleName(); public final TransitionalScanHelper mTransitionalScanHelper; + public final HandlerThread mScanThread; private final BluetoothScanBinder mBinder; @@ -72,15 +73,16 @@ public class ScanController { mMainLooper = adapterService.getMainLooper(); mBinder = new BluetoothScanBinder(this); mIsAvailable = true; - HandlerThread thread = new HandlerThread("BluetoothScanManager"); - thread.start(); - mTransitionalScanHelper.start(thread.getLooper()); + mScanThread = new HandlerThread("BluetoothScanManager"); + mScanThread.start(); + mTransitionalScanHelper.start(mScanThread.getLooper()); } public void stop() { Log.d(TAG, "stop()"); mIsAvailable = false; mBinder.clearScanController(); + mScanThread.quitSafely(); mTransitionalScanHelper.stop(); mTransitionalScanHelper.cleanup(); } diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java index 014645601a..453b5849fd 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java @@ -119,7 +119,6 @@ public class ScanManager { @GuardedBy("mCurUsedTrackableAdvertisementsLock") private int mCurUsedTrackableAdvertisements = 0; - private final Context mContext; private final TransitionalScanHelper mScanHelper; private final AdapterService mAdapterService; private final TimeProvider mTimeProvider; @@ -170,13 +169,12 @@ public class ScanManager { mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); mSuspendedScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); - mContext = adapterService; mScanHelper = scanHelper; mAdapterService = adapterService; mTimeProvider = timeProvider; mScanNative = new ScanNative(scanHelper); - mDisplayManager = mContext.getSystemService(DisplayManager.class); - mActivityManager = mContext.getSystemService(ActivityManager.class); + mDisplayManager = mAdapterService.getSystemService(DisplayManager.class); + mActivityManager = mAdapterService.getSystemService(ActivityManager.class); mLocationManager = mAdapterService.getSystemService(LocationManager.class); mBluetoothAdapterProxy = bluetoothAdapterProxy; mIsConnecting = false; @@ -203,7 +201,7 @@ public class ScanManager { } IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); locationIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mLocationReceiver, locationIntentFilter); + mAdapterService.registerReceiver(mLocationReceiver, locationIntentFilter); } public void cleanup() { @@ -226,13 +224,9 @@ public class ScanManager { // Shut down the thread mHandler.removeCallbacksAndMessages(null); - Looper looper = mHandler.getLooper(); - if (looper != null) { - looper.quitSafely(); - } try { - mContext.unregisterReceiver(mLocationReceiver); + mAdapterService.unregisterReceiver(mLocationReceiver); } catch (IllegalArgumentException e) { Log.w(TAG, "exception when invoking unregisterReceiver(mLocationReceiver)", e); } @@ -725,14 +719,9 @@ public class ScanManager { return; } int importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; - if (Flags.leScanUseUidForImportance()) { - for (String packageName : packages) { - importance = - Math.min( - importance, mActivityManager.getPackageImportance(packageName)); - } - } else { - importance = mActivityManager.getPackageImportance(packages[0]); + for (String packageName : packages) { + importance = + Math.min(importance, mActivityManager.getPackageImportance(packageName)); } boolean isForeground = importance @@ -1017,6 +1006,9 @@ public class ScanManager { private final boolean mIsMsftSupported; // Whether or not MSFT-based scanning is currently enabled in the controller private boolean scanEnabledMsft = false; + // List of merged MSFT patterns + private final MsftAdvMonitorMergedPatternList mMsftAdvMonitorMergedPatternList = + new MsftAdvMonitorMergedPatternList(); ScanNative(TransitionalScanHelper scanHelper) { mNativeInterface = ScanObjectsFactory.getInstance().getScanNativeInterface(); @@ -1024,11 +1016,11 @@ public class ScanManager { mFilterIndexStack = new ArrayDeque<Integer>(); mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>(); - mAlarmManager = mContext.getSystemService(AlarmManager.class); + mAlarmManager = mAdapterService.getSystemService(AlarmManager.class); Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null); mBatchScanIntervalIntent = PendingIntent.getBroadcast( - mContext, 0, batchIntent, PendingIntent.FLAG_IMMUTABLE); + mAdapterService, 0, batchIntent, PendingIntent.FLAG_IMMUTABLE); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(ACTION_REFRESH_BATCHED_SCAN); @@ -1050,7 +1042,7 @@ public class ScanManager { } } }); - mContext.registerReceiver(mBatchAlarmReceiver.get(), filter); + mAdapterService.registerReceiver(mBatchAlarmReceiver.get(), filter); mIsMsftSupported = Flags.leScanMsftSupport() @@ -1323,7 +1315,7 @@ public class ScanManager { // infrequently anyway. To avoid redefining parameter sets, map to the low duty cycle // parameter set as follows. private int getBatchScanWindowMillis(int scanMode) { - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = mAdapterService.getContentResolver(); switch (scanMode) { case ScanSettings.SCAN_MODE_LOW_LATENCY: return Settings.Global.getInt( @@ -1341,7 +1333,7 @@ public class ScanManager { } private int getBatchScanIntervalMillis(int scanMode) { - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = mAdapterService.getContentResolver(); switch (scanMode) { case ScanSettings.SCAN_MODE_LOW_LATENCY: return Settings.Global.getInt( @@ -1521,7 +1513,7 @@ public class ScanManager { // Protect against multiple calls of cleanup. BroadcastReceiver receiver = mBatchAlarmReceiver.getAndSet(null); if (receiver != null) { - mContext.unregisterReceiver(receiver); + mAdapterService.unregisterReceiver(receiver); } mNativeInterface.cleanup(); } @@ -1781,7 +1773,7 @@ public class ScanManager { } private int getScanWindowMillis(ScanSettings settings) { - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = mAdapterService.getContentResolver(); if (settings == null) { return Settings.Global.getInt( resolver, @@ -1819,7 +1811,7 @@ public class ScanManager { } private int getScanIntervalMillis(ScanSettings settings) { - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = mAdapterService.getContentResolver(); if (settings == null) { return Settings.Global.getInt( resolver, @@ -1987,18 +1979,48 @@ public class ScanManager { Deque<Integer> clientFilterIndices = new ArrayDeque<>(); for (ScanFilter filter : client.filters) { - int filterIndex = mFilterIndexStack.pop(); MsftAdvMonitor monitor = new MsftAdvMonitor(filter); - resetCountDownLatch(); - mNativeInterface.gattClientMsftAdvMonitorAdd( - monitor.getMonitor(), - monitor.getPatterns(), - monitor.getAddress(), - filterIndex); - waitForCallback(); + if (monitor.getAddress().bd_addr != null) { + int filterIndex = mFilterIndexStack.pop(); + + resetCountDownLatch(); + mNativeInterface.gattClientMsftAdvMonitorAdd( + monitor.getMonitor(), + monitor.getPatterns(), + monitor.getAddress(), + filterIndex); + waitForCallback(); + + clientFilterIndices.add(filterIndex); + } - clientFilterIndices.add(filterIndex); + if (monitor.getPatterns().length == 0) { + Log.d( + TAG, + "No MSFT pattern or address was translated from client filter: " + + filter); + continue; + } + + // Some chipsets don't support multiple monitors with the same pattern. Skip + // creating a new monitor if the pattern has alreaady been registered + int filterIndex = mFilterIndexStack.pop(); + int existingFilterIndex = + mMsftAdvMonitorMergedPatternList.add(filterIndex, monitor.getPatterns()); + if (filterIndex == existingFilterIndex) { + resetCountDownLatch(); + mNativeInterface.gattClientMsftAdvMonitorAdd( + monitor.getMonitor(), + monitor.getPatterns(), + monitor.getAddress(), + filterIndex); + waitForCallback(); + } else { + mFilterIndexStack.add(filterIndex); + } + + clientFilterIndices.add(existingFilterIndex); } mClientFilterIndexMap.put(client.scannerId, clientFilterIndices); @@ -2008,11 +2030,13 @@ public class ScanManager { private void removeFiltersMsft(ScanClient client) { Deque<Integer> clientFilterIndices = mClientFilterIndexMap.remove(client.scannerId); if (clientFilterIndices != null) { - mFilterIndexStack.addAll(clientFilterIndices); for (int filterIndex : clientFilterIndices) { - resetCountDownLatch(); - mNativeInterface.gattClientMsftAdvMonitorRemove(filterIndex); - waitForCallback(); + if (mMsftAdvMonitorMergedPatternList.remove(filterIndex)) { + resetCountDownLatch(); + mNativeInterface.gattClientMsftAdvMonitorRemove(filterIndex); + waitForCallback(); + mFilterIndexStack.add(filterIndex); + } } } diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java b/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java index 641c5f9bb2..f3fba49c79 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanObjectsFactory.java @@ -16,7 +16,7 @@ package com.android.bluetooth.le_scan; -import static com.android.bluetooth.Utils.sSystemClock; +import static com.android.bluetooth.Utils.getSystemClock; import android.os.Looper; import android.util.Log; @@ -79,7 +79,7 @@ public class ScanObjectsFactory { BluetoothAdapterProxy bluetoothAdapterProxy, Looper looper) { return new ScanManager( - adapterService, scanHelper, bluetoothAdapterProxy, looper, sSystemClock); + adapterService, scanHelper, bluetoothAdapterProxy, looper, getSystemClock()); } public PeriodicScanManager createPeriodicScanManager(AdapterService adapterService) { diff --git a/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java b/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java index a88630a571..00d9641032 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScannerMap.java @@ -15,7 +15,7 @@ */ package com.android.bluetooth.le_scan; -import static com.android.bluetooth.Utils.sSystemClock; +import static com.android.bluetooth.Utils.getSystemClock; import static com.android.bluetooth.util.AttributionSourceUtil.getLastAttributionTag; import android.annotation.Nullable; @@ -95,7 +95,12 @@ public class ScannerMap { if (appScanStats == null) { appScanStats = new AppScanStats( - appName, workSource, this, adapterService, scanHelper, sSystemClock); + appName, + workSource, + this, + adapterService, + scanHelper, + getSystemClock()); mAppScanStatsMap.put(appUid, appScanStats); } ScannerApp app = diff --git a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java b/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java index c68bf2d249..102d0d0649 100644 --- a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java +++ b/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java @@ -60,7 +60,6 @@ import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.BluetoothAdapterProxy; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.gatt.GattServiceConfig; import com.android.bluetooth.util.NumberUtils; import com.android.internal.annotations.VisibleForTesting; @@ -962,16 +961,10 @@ public class TransitionalScanHelper { return; } - BluetoothDevice device; - if (Flags.leScanUseAddressType()) { - device = - BluetoothAdapter.getDefaultAdapter() - .getRemoteLeDevice( - trackingInfo.getAddress(), trackingInfo.getAddressType()); - } else { - device = - BluetoothAdapter.getDefaultAdapter().getRemoteDevice(trackingInfo.getAddress()); - } + BluetoothDevice device = + BluetoothAdapter.getDefaultAdapter() + .getRemoteLeDevice( + trackingInfo.getAddress(), trackingInfo.getAddressType()); int advertiserState = trackingInfo.getAdvState(); ScanResult result = new ScanResult( diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java index b32ba797b2..2cb5b3f014 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java @@ -3483,8 +3483,6 @@ public class BluetoothMapContent { BluetoothProtoEnums.BLUETOOTH_MAP_CONTENT, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 4); - // skip this id - continue; } } diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java index e0fce72338..5facd76fcc 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java @@ -57,7 +57,6 @@ import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.map.BluetoothMapUtils.TYPE; import com.android.bluetooth.map.BluetoothMapbMessageMime.MimePart; import com.android.bluetooth.mapapi.BluetoothMapContract; @@ -1558,19 +1557,12 @@ public class BluetoothMapContentObserver { > BluetoothMapUtils.MAP_EVENT_REPORT_V10) { long timestamp = c.getLong(c.getColumnIndex(Sms.DATE)); String date = BluetoothMapUtils.getDateTimeString(timestamp); - if (Flags.mapLimitNotification()) { - if (BluetoothMapUtils.isDateTimeOlderThanDuration( - timestamp, NEW_MESSAGE_DURATION_FOR_NOTIFICATION)) { - msgListSms.remove(id); - continue; - } - } else { - if (BluetoothMapUtils.isDateTimeOlderThanOneYear(timestamp)) { - // Skip sending message events older than one year - msgListSms.remove(id); - continue; - } + if (BluetoothMapUtils.isDateTimeOlderThanDuration( + timestamp, NEW_MESSAGE_DURATION_FOR_NOTIFICATION)) { + msgListSms.remove(id); + continue; } + String subject = c.getString(c.getColumnIndex(Sms.BODY)); if (subject == null) { subject = ""; @@ -1813,18 +1805,10 @@ public class BluetoothMapContentObserver { TimeUnit.SECONDS.toMillis( c.getLong(c.getColumnIndex(Mms.DATE))); String date = BluetoothMapUtils.getDateTimeString(timestamp); - if (Flags.mapLimitNotification()) { - if (BluetoothMapUtils.isDateTimeOlderThanDuration( - timestamp, NEW_MESSAGE_DURATION_FOR_NOTIFICATION)) { - msgListMms.remove(id); - continue; - } - } else { - if (BluetoothMapUtils.isDateTimeOlderThanOneYear(timestamp)) { - // Skip sending new message events older than one year - msgListMms.remove(id); - continue; - } + if (BluetoothMapUtils.isDateTimeOlderThanDuration( + timestamp, NEW_MESSAGE_DURATION_FOR_NOTIFICATION)) { + msgListMms.remove(id); + continue; } String subject = c.getString(c.getColumnIndex(Mms.SUBJECT)); diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapConvoListingElement.java b/android/app/src/com/android/bluetooth/map/BluetoothMapConvoListingElement.java index f51a2f813e..9d29a77fc7 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapConvoListingElement.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapConvoListingElement.java @@ -323,7 +323,6 @@ public class BluetoothMapConvoListingElement } else { Log.w(TAG, "Unknown XML tag: " + name); Utils.skipCurrentTag(parser); - continue; } } // As we have extracted all attributes, we should expect an end-tag diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java index 321fbe3b4f..6f5687dc36 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java @@ -578,59 +578,54 @@ public class BluetoothMapUtils { * */ for (in = 0, out = 0; in < stopCnt; in++) { byte b0 = input[in]; - if (b0 == '=') { - byte b1 = input[++in]; - byte b2 = input[++in]; - if (b1 == '\r' && b2 == '\n') { - continue; // soft line break, remove all tree; + if (b0 != '=') { + output[out++] = b0; + continue; + } + byte b1 = input[++in]; + byte b2 = input[++in]; + if (b1 == '\r' && b2 == '\n') { + continue; // soft line break, remove all tree; + } + if (((b1 >= '0' && b1 <= '9') || (b1 >= 'A' && b1 <= 'F') || (b1 >= 'a' && b1 <= 'f')) + && ((b2 >= '0' && b2 <= '9') + || (b2 >= 'A' && b2 <= 'F') + || (b2 >= 'a' && b2 <= 'f'))) { + Log.v(TAG, "Found hex number: " + formatSimple("%c%c", b1, b2)); + if (b1 <= '9') { + b1 = (byte) (b1 - '0'); + } else if (b1 <= 'F') { + b1 = (byte) (b1 - 'A' + 10); + } else if (b1 <= 'f') { + b1 = (byte) (b1 - 'a' + 10); } - if (((b1 >= '0' && b1 <= '9') - || (b1 >= 'A' && b1 <= 'F') - || (b1 >= 'a' && b1 <= 'f')) - && ((b2 >= '0' && b2 <= '9') - || (b2 >= 'A' && b2 <= 'F') - || (b2 >= 'a' && b2 <= 'f'))) { - Log.v(TAG, "Found hex number: " + formatSimple("%c%c", b1, b2)); - if (b1 <= '9') { - b1 = (byte) (b1 - '0'); - } else if (b1 <= 'F') { - b1 = (byte) (b1 - 'A' + 10); - } else if (b1 <= 'f') { - b1 = (byte) (b1 - 'a' + 10); - } - if (b2 <= '9') { - b2 = (byte) (b2 - '0'); - } else if (b2 <= 'F') { - b2 = (byte) (b2 - 'A' + 10); - } else if (b2 <= 'f') { - b2 = (byte) (b2 - 'a' + 10); - } + if (b2 <= '9') { + b2 = (byte) (b2 - '0'); + } else if (b2 <= 'F') { + b2 = (byte) (b2 - 'A' + 10); + } else if (b2 <= 'f') { + b2 = (byte) (b2 - 'a' + 10); + } - Log.v(TAG, "Resulting nibble values: " + formatSimple("b1=%x b2=%x", b1, b2)); + Log.v(TAG, "Resulting nibble values: " + formatSimple("b1=%x b2=%x", b1, b2)); - output[out++] = (byte) (b1 << 4 | b2); // valid hex char, append - Log.v(TAG, "Resulting value: " + formatSimple("0x%2x", output[out - 1])); - continue; - } - Log.w( - TAG, - "Received wrongly quoted printable encoded text. " - + "Continuing at best effort..."); - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_UTILS, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_WARN, - 6); - /* If we get a '=' without either a hex value or CRLF following, just add it and - * rewind the in counter. */ - output[out++] = b0; - in -= 2; - continue; - } else { - output[out++] = b0; + output[out++] = (byte) (b1 << 4 | b2); // valid hex char, append + Log.v(TAG, "Resulting value: " + formatSimple("0x%2x", output[out - 1])); continue; } + Log.w( + TAG, + "Received wrongly quoted printable encoded text. Continuing at best effort..."); + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_UTILS, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_WARN, + 6); + /* If we get a '=' without either a hex value or CRLF following, just add it and + * rewind the in counter. */ + output[out++] = b0; + in -= 2; } // Just add any remaining characters. If they contain any encoding, it is invalid, @@ -755,23 +750,6 @@ public class BluetoothMapUtils { return format.format(cal.getTime()); } - static boolean isDateTimeOlderThanOneYear(long timestamp) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(timestamp); - Calendar oneYearAgo = Calendar.getInstance(); - oneYearAgo.add(Calendar.YEAR, -1); - if (cal.before(oneYearAgo)) { - Log.v( - TAG, - "isDateTimeOlderThanOneYear " - + cal.getTimeInMillis() - + " oneYearAgo: " - + oneYearAgo.getTimeInMillis()); - return true; - } - return false; - } - static boolean isDateTimeOlderThanDuration(long timestamp, Duration duration) { Instant nowMinusDuration = Instant.now().minus(duration); Instant dateTime = Instant.ofEpochMilli(timestamp); diff --git a/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java b/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java index 05ec2a39e7..87c750dfcd 100644 --- a/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java +++ b/android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java @@ -1086,15 +1086,21 @@ class MceStateMachine extends StateMachine { if (message == null) { return; } + + MessageMetadata metadata = mMessages.get(request.getHandle()); + if (metadata == null) { + Log.e(TAG, "No request record of received message, handle=" + request.getHandle()); + return; + } + mDatabase.storeMessage( - message, - request.getHandle(), - mMessages.get(request.getHandle()).getTimestamp(), - mMessages.get(request.getHandle()).getSeen()); + message, request.getHandle(), metadata.getTimestamp(), metadata.getSeen()); + if (!INBOX_PATH.equalsIgnoreCase(message.getFolder())) { Log.d(TAG, "Ignoring message received in " + message.getFolder() + "."); return; } + switch (message.getType()) { case SMS_CDMA: case SMS_GSM: @@ -1104,7 +1110,6 @@ class MceStateMachine extends StateMachine { Log.d(TAG, "Recipients" + message.getRecipients().toString()); // Grab the message metadata and update the cached read status from the bMessage - MessageMetadata metadata = mMessages.get(request.getHandle()); metadata.setRead(request.getMessage().getStatus() == Bmessage.Status.READ); Intent intent = new Intent(); diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java index 8eb316770b..c05b844634 100644 --- a/android/app/src/com/android/bluetooth/mcp/McpService.java +++ b/android/app/src/com/android/bluetooth/mcp/McpService.java @@ -24,15 +24,12 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothMcpServiceManager; import android.content.AttributionSource; import android.content.Context; -import android.os.Handler; -import android.os.Looper; import android.os.ParcelUuid; import android.sysprop.BluetoothProperties; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; @@ -48,7 +45,6 @@ public class McpService extends ProfileService { private final MediaControlProfile mGmcs; private final Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>(); - private final Handler mHandler = new Handler(Looper.getMainLooper()); public McpService(Context ctx) { this(ctx, null); @@ -102,16 +98,7 @@ public class McpService extends ProfileService { // Mark service as started setMcpService(this); - if (Flags.leaudioSynchronizeStart()) { - mGmcs.init(); - return; - } - mHandler.post( - () -> { - if (isAvailable()) { - mGmcs.init(); - } - }); + mGmcs.init(); } @Override diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java index 8cbd32cfba..6efdb17d66 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java @@ -445,9 +445,7 @@ class BluetoothOppNotification { intent.setDataAndNormalize(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id)); b.setContentIntent( PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE)); - if (Flags.oppFixMultipleNotificationsIssues()) { - b.setGroup(NOTIFICATION_GROUP_KEY_PROGRESS); - } + b.setGroup(NOTIFICATION_GROUP_KEY_PROGRESS); mNotificationMgr.notify(NOTIFICATION_ID_PROGRESS, b.build()); } } @@ -502,25 +500,14 @@ class BluetoothOppNotification { BluetoothOppUtility.formatResultText( outboundSuccNumber, outboundFailNumber, mContext); - PendingIntent pi; - if (Flags.oppStartActivityDirectlyFromNotification()) { - Intent in = new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER); - in.setClass(mContext, BluetoothOppTransferHistory.class); - in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pi = PendingIntent.getActivity(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); - } else { - Intent in = - new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER) - .setClassName(mContext, BluetoothOppReceiver.class.getName()); - pi = PendingIntent.getBroadcast(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); - } + Intent in = new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER); + in.setClass(mContext, BluetoothOppTransferHistory.class); + in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pi = + PendingIntent.getActivity(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); Intent deleteIntent = new Intent(mContext, BluetoothOppReceiver.class); - if (Flags.oppFixMultipleNotificationsIssues()) { - deleteIntent.setAction(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER); - } else { - deleteIntent.setAction(Constants.ACTION_COMPLETE_HIDE); - } + deleteIntent.setAction(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER); Notification.Builder b = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL) @@ -542,10 +529,8 @@ class BluetoothOppNotification { deleteIntent, PendingIntent.FLAG_IMMUTABLE)) .setWhen(timeStamp) - .setLocalOnly(true); - if (Flags.oppFixMultipleNotificationsIssues()) { - b.setGroup(NOTIFICATION_GROUP_KEY_TRANSFER_COMPLETE); - } + .setLocalOnly(true) + .setGroup(NOTIFICATION_GROUP_KEY_TRANSFER_COMPLETE); mNotificationMgr.notify(NOTIFICATION_ID_OUTBOUND_COMPLETE, b.build()); } else { if (mNotificationMgr != null) { @@ -591,25 +576,14 @@ class BluetoothOppNotification { BluetoothOppUtility.formatResultText( inboundSuccNumber, inboundFailNumber, mContext); - PendingIntent pi; - if (Flags.oppStartActivityDirectlyFromNotification()) { - Intent in = new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER); - in.setClass(mContext, BluetoothOppTransferHistory.class); - in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - pi = PendingIntent.getActivity(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); - } else { - Intent in = - new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER) - .setClassName(mContext, BluetoothOppReceiver.class.getName()); - pi = PendingIntent.getBroadcast(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); - } + Intent in = new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER); + in.setClass(mContext, BluetoothOppTransferHistory.class); + in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pi = + PendingIntent.getActivity(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); Intent deleteIntent = new Intent(mContext, BluetoothOppReceiver.class); - if (Flags.oppFixMultipleNotificationsIssues()) { - deleteIntent.setAction(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER); - } else { - deleteIntent.setAction(Constants.ACTION_COMPLETE_HIDE); - } + deleteIntent.setAction(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER); Notification.Builder b = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL) @@ -631,10 +605,8 @@ class BluetoothOppNotification { deleteIntent, PendingIntent.FLAG_IMMUTABLE)) .setWhen(timeStamp) - .setLocalOnly(true); - if (Flags.oppFixMultipleNotificationsIssues()) { - b.setGroup(NOTIFICATION_GROUP_KEY_TRANSFER_COMPLETE); - } + .setLocalOnly(true) + .setGroup(NOTIFICATION_GROUP_KEY_TRANSFER_COMPLETE); mNotificationMgr.notify(NOTIFICATION_ID_INBOUND_COMPLETE, b.build()); } else { if (mNotificationMgr != null) { @@ -643,7 +615,7 @@ class BluetoothOppNotification { } } - if (Flags.oppFixMultipleNotificationsIssues() && inboundNum > 0 && outboundNum > 0) { + if (inboundNum > 0 && outboundNum > 0) { Notification.Builder b = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL) .setGroup(NOTIFICATION_GROUP_KEY_TRANSFER_COMPLETE) @@ -710,30 +682,18 @@ class BluetoothOppNotification { PendingIntent.FLAG_IMMUTABLE)) .build(); - PendingIntent contentIntent; - if (Flags.oppStartActivityDirectlyFromNotification()) { - Intent intent = new Intent(mContext, BluetoothOppIncomingFileConfirmActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setDataAndNormalize(contentUri); - contentIntent = - PendingIntent.getActivity( - mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); - } else { - contentIntent = - PendingIntent.getBroadcast( - mContext, - 0, - new Intent(baseIntent) - .setAction(Constants.ACTION_INCOMING_FILE_CONFIRM), - PendingIntent.FLAG_IMMUTABLE); - } + Intent in = new Intent(mContext, BluetoothOppIncomingFileConfirmActivity.class); + in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + in.setDataAndNormalize(contentUri); + PendingIntent pi = + PendingIntent.getActivity(mContext, 0, in, PendingIntent.FLAG_IMMUTABLE); Notification.Builder publicNotificationBuilder = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL) .setOnlyAlertOnce(true) .setOngoing(true) .setWhen(info.mTimeStamp) - .setContentIntent(contentIntent) + .setContentIntent(pi) .setDeleteIntent( PendingIntent.getBroadcast( mContext, @@ -760,17 +720,15 @@ class BluetoothOppNotification { fileNameSafe))) .setSubText(Formatter.formatFileSize(mContext, info.mTotalBytes)) .setSmallIcon(R.drawable.ic_bluetooth_file_transfer_notification) - .setLocalOnly(true); - if (Flags.oppFixMultipleNotificationsIssues()) { - publicNotificationBuilder.setGroup(NOTIFICATION_GROUP_KEY_INCOMING_FILE_CONFIRM); - } + .setLocalOnly(true) + .setGroup(NOTIFICATION_GROUP_KEY_INCOMING_FILE_CONFIRM); Notification.Builder builder = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL) .setOnlyAlertOnce(true) .setOngoing(true) .setWhen(info.mTimeStamp) - .setContentIntent(contentIntent) + .setContentIntent(pi) .setDeleteIntent( PendingIntent.getBroadcast( mContext, @@ -801,10 +759,8 @@ class BluetoothOppNotification { .setVisibility(Notification.VISIBILITY_PRIVATE) .addAction(actionDecline) .addAction(actionAccept) - .setPublicVersion(publicNotificationBuilder.build()); - if (Flags.oppFixMultipleNotificationsIssues()) { - builder.setGroup(NOTIFICATION_GROUP_KEY_INCOMING_FILE_CONFIRM); - } + .setPublicVersion(publicNotificationBuilder.build()) + .setGroup(NOTIFICATION_GROUP_KEY_INCOMING_FILE_CONFIRM); mNotificationMgr.notify(NOTIFICATION_ID_PROGRESS, builder.build()); } cursor.close(); diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java index 9b7a7329a8..e0cade561d 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java @@ -100,15 +100,6 @@ public class BluetoothOppReceiver extends BroadcastReceiver { toastMsg = context.getString(R.string.bt_toast_4, deviceName); } Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show(); - } else if (action.equals(Constants.ACTION_INCOMING_FILE_CONFIRM) - && !Flags.oppStartActivityDirectlyFromNotification()) { - Log.v(TAG, "Receiver ACTION_INCOMING_FILE_CONFIRM"); - - Uri uri = intent.getData(); - Intent in = new Intent(context, BluetoothOppIncomingFileConfirmActivity.class); - in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - in.setDataAndNormalize(uri); - context.startActivity(in); } else if (action.equals(Constants.ACTION_DECLINE)) { Log.v(TAG, "Receiver ACTION_DECLINE"); @@ -164,22 +155,6 @@ public class BluetoothOppReceiver extends BroadcastReceiver { context.startActivity(in); } - } else if (action.equals(Constants.ACTION_OPEN_OUTBOUND_TRANSFER) - && !Flags.oppStartActivityDirectlyFromNotification()) { - Log.v(TAG, "Received ACTION_OPEN_OUTBOUND_TRANSFER."); - - Intent in = new Intent(context, BluetoothOppTransferHistory.class); - in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - in.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_OUTBOUND); - context.startActivity(in); - } else if (action.equals(Constants.ACTION_OPEN_INBOUND_TRANSFER) - && !Flags.oppStartActivityDirectlyFromNotification()) { - Log.v(TAG, "Received ACTION_OPEN_INBOUND_TRANSFER."); - - Intent in = new Intent(context, BluetoothOppTransferHistory.class); - in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - in.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_INBOUND); - context.startActivity(in); } else if (action.equals(Constants.ACTION_HIDE)) { Log.v(TAG, "Receiver hide for " + intent.getData()); Cursor cursor = @@ -214,20 +189,7 @@ public class BluetoothOppReceiver extends BroadcastReceiver { } cursor.close(); } - } else if (action.equals(Constants.ACTION_COMPLETE_HIDE) - && !Flags.oppFixMultipleNotificationsIssues()) { - Log.v(TAG, "Receiver ACTION_COMPLETE_HIDE"); - ContentValues updateValues = new ContentValues(); - updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN); - BluetoothMethodProxy.getInstance() - .contentResolverUpdate( - context.getContentResolver(), - BluetoothShare.CONTENT_URI, - updateValues, - BluetoothOppNotification.WHERE_COMPLETED, - null); - } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER) - && Flags.oppFixMultipleNotificationsIssues()) { + } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER)) { Log.v(TAG, "Received ACTION_HIDE_COMPLETED_INBOUND_TRANSFER"); ContentValues updateValues = new ContentValues(); updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN); @@ -238,8 +200,7 @@ public class BluetoothOppReceiver extends BroadcastReceiver { updateValues, BluetoothOppNotification.WHERE_COMPLETED_INBOUND, null); - } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER) - && Flags.oppFixMultipleNotificationsIssues()) { + } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER)) { Log.v(TAG, "Received ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER"); ContentValues updateValues = new ContentValues(); updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN); diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java index 16056cde79..2d727f9fdf 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java @@ -32,6 +32,8 @@ package com.android.bluetooth.opp; +import static android.os.UserHandle.myUserId; + import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.content.ContentResolver; @@ -41,6 +43,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.Uri; import android.provider.OpenableColumns; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -53,12 +56,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Objects; /** * This class stores information about a single sending file It will only be used for outbound * share. */ -// Next tag value for ContentProfileErrorReportUtils.report(): 15 +// Next tag value for ContentProfileErrorReportUtils.report(): 16 public class BluetoothOppSendFileInfo { private static final String TAG = "BluetoothOppSendFileInfo"; @@ -124,6 +128,16 @@ public class BluetoothOppSendFileInfo { return SEND_FILE_INFO_ERROR; } + if (isContentUriForOtherUser(uri)) { + Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId()); + ContentProfileErrorReportUtils.report( + BluetoothProfile.OPP, + BluetoothProtoEnums.BLUETOOTH_OPP_SEND_FILE_INFO, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, + 15); + return SEND_FILE_INFO_ERROR; + } + contentType = contentResolver.getType(uri); Cursor metadataCursor; try { @@ -353,6 +367,12 @@ public class BluetoothOppSendFileInfo { return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0); } + private static boolean isContentUriForOtherUser(Uri uri) { + String uriUserId = uri.getUserInfo(); + return !TextUtils.isEmpty(uriUserId) + && !Objects.equals(uriUserId, String.valueOf(myUserId())); + } + private static long getStreamSize(FileInputStream is) throws IOException { long length = 0; byte[] unused = new byte[4096]; diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java index c8ae64dd24..ae9283050e 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransferHistory.java @@ -113,18 +113,10 @@ public class BluetoothOppTransferHistory extends Activity mListView = (ListView) findViewById(R.id.list); mListView.setEmptyView(findViewById(R.id.empty)); - String direction; - - boolean isOutbound = false; - - if (Flags.oppStartActivityDirectlyFromNotification()) { - String action = getIntent().getAction(); - isOutbound = Constants.ACTION_OPEN_OUTBOUND_TRANSFER.equals(action); - } else { - int dir = getIntent().getIntExtra(Constants.EXTRA_DIRECTION, 0); - isOutbound = (dir == BluetoothShare.DIRECTION_OUTBOUND); - } + boolean isOutbound = + Constants.ACTION_OPEN_OUTBOUND_TRANSFER.equals(getIntent().getAction()); + String direction; if (isOutbound) { setTitle(getText(R.string.outbound_history_title)); direction = diff --git a/android/app/src/com/android/bluetooth/opp/Constants.java b/android/app/src/com/android/bluetooth/opp/Constants.java index e68364be9f..67e00ec343 100644 --- a/android/app/src/com/android/bluetooth/opp/Constants.java +++ b/android/app/src/com/android/bluetooth/opp/Constants.java @@ -166,14 +166,6 @@ public class Constants { /** the intent that gets sent when declining the incoming file confirmation notification */ static final String ACTION_DECLINE = "android.btopp.intent.action.DECLINE"; - /** - * The intent that gets sent when deleting the notifications of outbound and inbound completed - * transfer. - */ - // TODO(b/323096132): Remove this variable when the flag - // opp_fix_multiple_notifications_issues is ramped up. - static final String ACTION_COMPLETE_HIDE = "android.btopp.intent.action.HIDE_COMPLETE"; - /** The intent that gets sent when deleting the notifications of completed inbound transfer. */ static final String ACTION_HIDE_COMPLETED_INBOUND_TRANSFER = "android.btopp.intent.action.HIDE_COMPLETED_INBOUND_TRANSFER"; diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientContactsStorage.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientContactsStorage.java index f89fcec616..3923d4631d 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientContactsStorage.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientContactsStorage.java @@ -224,6 +224,13 @@ class PbapClientContactsStorage { * * <p>This function also associates the phonebook metadata with the contact for easy * per-phonebook cleanup operations. + * + * <p>Contacts are inserted in smaller batches so they can be loaded in chunks as opposed to + * shown all at once in the UI. This also prevents us from hitting the binder transaction limit. + * + * @param account The account to insert contacts against + * @param phonebook The phonebook these contacts belong to + * @param contacts The list of contacts to insert */ private boolean insertContacts(Account account, String phonebook, List<VCardEntry> contacts) { if (!mStorageInitialized) { @@ -374,11 +381,15 @@ class PbapClientContactsStorage { /** * Insert call history entries of a given type * + * <p>These call logs are inserted in smaller batches so they can be loaded in chunks as opposed + * to shown all at once in the UI. This also prevents us from hitting the binder transaction + * limit + * * @param account The account to insert call logs against - * @param type The type of call history provided - * @param history The call history to insert + * @param type The type of call provided + * @param history The list of calls to add + * @return True if successful, False otherwise */ - @SuppressWarnings("JavaUtilDate") // TODO: b/365629730 -- prefer Instant or LocalDate private boolean insertCallHistory(Account account, int type, List<VCardEntry> history) { if (!mStorageInitialized) { Log.w(TAG, "insertCallHistory: Failed, storage not ready"); @@ -405,61 +416,114 @@ class PbapClientContactsStorage { try { Log.i( TAG, - "insertCallHistory: Inserting call history, type=" + "insertCallHistory: inserting call history, account=" + + account + + ", type=" + type + ", count=" + history.size()); - ArrayList<ContentProviderOperation> ops = new ArrayList<>(); - for (VCardEntry vcard : history) { - ContentValues values = new ContentValues(); - values.put(CallLog.Calls.TYPE, type); - values.put(Calls.PHONE_ACCOUNT_ID, account.name); - - List<PhoneData> phones = vcard.getPhoneList(); - if (phones == null - || phones.get(0).getNumber().equals(";") - || phones.get(0).getNumber().length() == 0) { - values.put(CallLog.Calls.NUMBER, ""); - } else { - String phoneNumber = phones.get(0).getNumber(); - values.put(CallLog.Calls.NUMBER, phoneNumber); - } + ContentResolver contactsProvider = mContext.getContentResolver(); + ArrayList<ContentProviderOperation> operations = new ArrayList<>(); - List<Pair<String, String>> irmc = vcard.getUnknownXData(); - SimpleDateFormat parser = new SimpleDateFormat(TIMESTAMP_FORMAT); - if (irmc != null) { - for (Pair<String, String> pair : irmc) { - if (pair.first.startsWith(CALL_LOG_TIMESTAMP_PROPERTY)) { - try { - values.put(CallLog.Calls.DATE, parser.parse(pair.second).getTime()); - } catch (ParseException e) { - Log.d(TAG, "Failed to parse date, value=" + pair.second); - } - } - } + // Group insert operations together to minimize inter process communication and improve + // processing time. + for (VCardEntry callLog : history) { + if (Thread.currentThread().isInterrupted()) { + Log.e(TAG, "insertCallHistory: Interrupted during insert"); + break; } - ops.add( - ContentProviderOperation.newInsert(CallLog.Calls.CONTENT_URI) - .withValues(values) - .withYieldAllowed(true) - .build()); + // Append current call to list of insert operations. + int numberOfOperations = operations.size(); + constructInsertOperationsForCallLog(account, type, callLog, operations); + + if (operations.size() >= CONTACTS_INSERT_BATCH_SIZE) { + Log.i( + TAG, + "insertCallHistory: batch full, operations.size()=" + + operations.size() + + ", batch_size=" + + CONTACTS_INSERT_BATCH_SIZE); + + // If we have exceeded the limit of the insert operations, remove the latest + // call and submit. + operations.subList(numberOfOperations, operations.size()).clear(); + + contactsProvider.applyBatch(CallLog.AUTHORITY, operations); + + // Re-add the current call log operation(s) to the list + operations = constructInsertOperationsForCallLog(account, type, callLog, null); + + Log.i( + TAG, + "insertCallHistory: batch complete, operations.size()=" + + operations.size()); + } } - mContext.getContentResolver().applyBatch(CallLog.AUTHORITY, ops); - Log.d(TAG, "Inserted call logs, type=" + type); - } catch (RemoteException | OperationApplicationException e) { - Log.w(TAG, "Failed to insert call log, type=" + type, e); - return false; - } finally { - synchronized (this) { - this.notify(); + // Apply any unsubmitted calls + if (operations.size() > 0) { + contactsProvider.applyBatch(CallLog.AUTHORITY, operations); + operations.clear(); } + Log.i(TAG, "insertCallHistory: insert complete, count=" + history.size()); + } catch (OperationApplicationException | RemoteException | NumberFormatException e) { + Log.e(TAG, "insertCallHistory: Exception occurred while processing call log pull: ", e); + return false; } return true; } + // TODO: b/365629730 -- JavaUtilDate: prefer Instant or LocalDate + // NonApiType: For convenience, as the applyBatch API actually takes an ArrayList above + @SuppressWarnings({"JavaUtilDate", "NonApiType"}) + private ArrayList<ContentProviderOperation> constructInsertOperationsForCallLog( + Account account, + int type, + VCardEntry call, + ArrayList<ContentProviderOperation> operations) { + if (operations == null) { + operations = new ArrayList<ContentProviderOperation>(); + } + + ContentValues values = new ContentValues(); + values.put(Calls.PHONE_ACCOUNT_ID, account.name); + values.put(CallLog.Calls.TYPE, type); + + List<PhoneData> phones = call.getPhoneList(); + if (phones == null + || phones.get(0).getNumber().equals(";") + || phones.get(0).getNumber().length() == 0) { + values.put(CallLog.Calls.NUMBER, ""); + } else { + String phoneNumber = phones.get(0).getNumber(); + values.put(CallLog.Calls.NUMBER, phoneNumber); + } + + List<Pair<String, String>> irmc = call.getUnknownXData(); + SimpleDateFormat parser = new SimpleDateFormat(TIMESTAMP_FORMAT); + if (irmc != null) { + for (Pair<String, String> pair : irmc) { + if (pair.first.startsWith(CALL_LOG_TIMESTAMP_PROPERTY)) { + try { + values.put(CallLog.Calls.DATE, parser.parse(pair.second).getTime()); + } catch (ParseException e) { + Log.d(TAG, "Failed to parse date, value=" + pair.second); + } + } + } + } + + operations.add( + ContentProviderOperation.newInsert(CallLog.Calls.CONTENT_URI) + .withValues(values) + .withYieldAllowed(true) + .build()); + + return operations; + } + /** * Remove all call history associated with this client's account * diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java index c2aae98269..f5e4898ee1 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java @@ -478,11 +478,7 @@ public class PbapClientService extends ProfileService { Log.e(TAG, "No Statemachine found for the device=" + device.toString()); return; } - - smOld.obtainMessage( - PbapClientStateMachineOld.MSG_SDP_COMPLETE, - new PbapSdpRecord(device, pseRecord)) - .sendToTarget(); + smOld.onSdpResultReceived(status, new PbapSdpRecord(device, pseRecord)); } } } diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachineOld.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachineOld.java index 420ae07ace..c07dadc62c 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachineOld.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientStateMachineOld.java @@ -76,7 +76,6 @@ class PbapClientStateMachineOld extends StateMachine { // Messages for handling connect/disconnect requests. private static final int MSG_DISCONNECT = 2; - static final int MSG_SDP_COMPLETE = 9; // Messages for handling error conditions. private static final int MSG_CONNECT_TIMEOUT = 3; @@ -87,9 +86,20 @@ class PbapClientStateMachineOld extends StateMachine { static final int MSG_CONNECTION_FAILED = 6; static final int MSG_CONNECTION_CLOSED = 7; static final int MSG_RESUME_DOWNLOAD = 8; + static final int MSG_SDP_COMPLETE = 9; + static final int MSG_SDP_BUSY = 10; + static final int MSG_SDP_FAIL = 11; + // Constants for SDP. Note that these values come from the native stack, but no centralized + // constants exist for them as part of the various SDP APIs. + public static final int SDP_SUCCESS = 0; + public static final int SDP_FAILED = 1; + public static final int SDP_BUSY = 2; + + // All times are in milliseconds static final int CONNECT_TIMEOUT = 10000; static final int DISCONNECT_TIMEOUT = 3000; + static final int SDP_BUSY_RETRY_DELAY = 20; private static final int LOCAL_SUPPORTED_FEATURES = PbapSdpRecord.FEATURE_DEFAULT_IMAGE_FORMAT | PbapSdpRecord.FEATURE_DOWNLOADING; @@ -210,14 +220,40 @@ class PbapClientStateMachineOld extends StateMachine { break; case MSG_SDP_COMPLETE: + removeMessages(MSG_SDP_BUSY); PbapClientConnectionHandler connectionHandler = mConnectionHandler; if (connectionHandler != null) { + if (message.obj == null) { + Log.w(TAG, "Received SDP response without valid PSE record "); + } connectionHandler .obtainMessage(PbapClientConnectionHandler.MSG_CONNECT, message.obj) .sendToTarget(); + } else { + Log.w(TAG, "Received SDP complete without connection handler"); } break; + case MSG_SDP_BUSY: + removeMessages(MSG_SDP_BUSY); + Log.d(TAG, "Received SDP busy, try again"); + mCurrentDevice.sdpSearch(BluetoothUuid.PBAP_PSE); + break; + + case MSG_SDP_FAIL: + removeMessages(MSG_SDP_BUSY); + int status = message.arg1; + Log.w(TAG, "SDP failed status:" + status + ", starting disconnect"); + transitionTo(mDisconnecting); + break; + + case MSG_RESUME_DOWNLOAD: + Log.i( + TAG, + "Received request to download phonebook but still in state " + + this.getName()); + break; + default: Log.w(TAG, "Received unexpected message while Connecting"); return NOT_HANDLED; @@ -317,6 +353,24 @@ class PbapClientStateMachineOld extends StateMachine { } } + /** Notify of SDP completion. */ + public void onSdpResultReceived(int status, PbapSdpRecord record) { + Log.d(TAG, "Received SDP Result, status=" + status + ", record=" + record); + switch (status) { + case SDP_SUCCESS: + sendMessage(PbapClientStateMachineOld.MSG_SDP_COMPLETE, record); + break; + + case SDP_BUSY: + sendMessageDelayed(PbapClientStateMachineOld.MSG_SDP_BUSY, SDP_BUSY_RETRY_DELAY); + break; + + default: + sendMessage(PbapClientStateMachineOld.MSG_SDP_FAIL); + break; + } + } + /** Trigger a contacts download if the user is unlocked and our accounts are available to us */ private void downloadIfReady() { boolean userReady = mUserManager.isUserUnlocked(); diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java index 760b67d900..8099c269c8 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java +++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java @@ -17,6 +17,7 @@ package com.android.bluetooth.pbapclient; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import android.accounts.Account; import android.annotation.RequiresPermission; @@ -468,7 +469,9 @@ class PbapClientObexClient { /* Utilize SDP, if available, to create a socket connection over L2CAP, RFCOMM specified * channel, or RFCOMM default channel. */ - @RequiresPermission(BLUETOOTH_CONNECT) + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) private PbapClientSocket connectSocket(int transport, int channelOrPsm) { debug( "Connect socket, transport=" diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java index 4a17721a3e..7438a02fe9 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java +++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java @@ -17,6 +17,7 @@ package com.android.bluetooth.pbapclient; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; @@ -101,7 +102,9 @@ public class PbapClientSocket { } /** Invokes the underlying BluetoothSocket#connect(), or does nothing if a socket is injected */ - @RequiresPermission(BLUETOOTH_CONNECT) + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) public void connect() throws IOException { if (mSocket != null) { mSocket.connect(); diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java b/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java index 4cea94633c..329fa6240e 100644 --- a/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java +++ b/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java @@ -26,7 +26,6 @@ import android.telecom.InCallService; import android.telecom.PhoneAccountHandle; import com.android.bluetooth.apishim.BluetoothCallShimImpl; -import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; @@ -42,8 +41,7 @@ import java.util.UUID; * functioning of the BluetoothInCallService class, the final class must be put into a container * that can be mocked correctly. */ -@VisibleForTesting -public class BluetoothCall { +class BluetoothCall { private Call mCall; private UUID mCallId; diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java index bd0b40ec4f..2fa94bd27e 100644 --- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java +++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java @@ -624,28 +624,31 @@ public class BluetoothInCallService extends InCallService { @RequiresPermission(allOf = {BLUETOOTH_CONNECT, MODIFY_PHONE_STATE}) public void onCallAdded(BluetoothCall call) { - if (call.isExternalCall()) { - Log.d(TAG, "onCallAdded: external call"); - return; - } - if (!mBluetoothCallHashMap.containsKey(call.getId())) { - Log.i(TAG, "onCallAdded"); - CallStateCallback callback = new CallStateCallback(call.getState()); - mCallbacks.put(call.getId(), callback); - call.registerCallback(callback); - - mBluetoothCallHashMap.put(call.getId(), call); - if (!call.isConference()) { - mMaxNumberOfCalls = Integer.max(mMaxNumberOfCalls, mBluetoothCallHashMap.size()); + synchronized (LOCK) { + if (call.isExternalCall()) { + Log.d(TAG, "onCallAdded: external call"); + return; } - updateHeadsetWithCallState(false /* force */); + if (!mBluetoothCallHashMap.containsKey(call.getId())) { + Log.i(TAG, "onCallAdded"); + CallStateCallback callback = new CallStateCallback(call.getState()); + mCallbacks.put(call.getId(), callback); + call.registerCallback(callback); - BluetoothLeCall tbsCall = createTbsCall(call); - if (mBluetoothLeCallControl != null && tbsCall != null) { - mBluetoothLeCallControl.onCallAdded(tbsCall); + mBluetoothCallHashMap.put(call.getId(), call); + if (!call.isConference()) { + mMaxNumberOfCalls = + Integer.max(mMaxNumberOfCalls, mBluetoothCallHashMap.size()); + } + updateHeadsetWithCallState(false /* force */); + + BluetoothLeCall tbsCall = createTbsCall(call); + if (mBluetoothLeCallControl != null && tbsCall != null) { + mBluetoothLeCallControl.onCallAdded(tbsCall); + } + } else { + Log.i(TAG, "onCallAdded: call already exists"); } - } else { - Log.i(TAG, "onCallAdded: call already exists"); } } @@ -694,63 +697,66 @@ public class BluetoothInCallService extends InCallService { */ @RequiresPermission(allOf = {BLUETOOTH_CONNECT, MODIFY_PHONE_STATE}) public void onCallRemoved(BluetoothCall call, boolean forceRemoveCallback) { - Log.i(TAG, "onCallRemoved, forceRemoveCallback=" + forceRemoveCallback); - CallStateCallback callback = getCallback(call); - if (callback != null && (forceRemoveCallback || !call.isExternalCall())) { - call.unregisterCallback(callback); - } - - if (mBluetoothCallHashMap.containsKey(call.getId())) { - mBluetoothCallHashMap.remove(call.getId()); - - DisconnectCause cause = call.getDisconnectCause(); - if (cause != null && cause.getCode() == DisconnectCause.OTHER) { - Log.d(TAG, "add inference call with reason: " + cause.getReason()); - mBluetoothCallQueue.add(call.getId()); - mBluetoothConferenceCallInference.put(call.getId(), call); - if (Flags.maintainCallIndexAfterConference()) { - // If the disconnect is due to call merge, store the index for future use. - if (cause.getReason() != null - && cause.getReason().equals("IMS_MERGED_SUCCESSFULLY")) { - if (!mConferenceCallClccIndexMap.containsKey(getClccMapKey(call))) { - if (call.mClccIndex > -1) { - mConferenceCallClccIndexMap.put( - getClccMapKey(call), call.mClccIndex); + synchronized (LOCK) { + Log.i(TAG, "onCallRemoved, forceRemoveCallback=" + forceRemoveCallback); + CallStateCallback callback = getCallback(call); + if (callback != null && (forceRemoveCallback || !call.isExternalCall())) { + call.unregisterCallback(callback); + } + + if (mBluetoothCallHashMap.containsKey(call.getId())) { + mBluetoothCallHashMap.remove(call.getId()); + + DisconnectCause cause = call.getDisconnectCause(); + if (cause != null && cause.getCode() == DisconnectCause.OTHER) { + Log.d(TAG, "add inference call with reason: " + cause.getReason()); + mBluetoothCallQueue.add(call.getId()); + mBluetoothConferenceCallInference.put(call.getId(), call); + if (Flags.maintainCallIndexAfterConference()) { + // If the disconnect is due to call merge, store the index for future use. + if (cause.getReason() != null + && cause.getReason().equals("IMS_MERGED_SUCCESSFULLY")) { + if (!mConferenceCallClccIndexMap.containsKey(getClccMapKey(call))) { + if (call.mClccIndex > -1) { + mConferenceCallClccIndexMap.put( + getClccMapKey(call), call.mClccIndex); + } } } } - } - // queue size limited to 2 because merge operation only happens on 2 calls - // we are only interested in last 2 calls merged - if (mBluetoothCallQueue.size() > 2) { - Integer callId = mBluetoothCallQueue.peek(); - mBluetoothCallQueue.remove(); - mBluetoothConferenceCallInference.remove(callId); + // queue size limited to 2 because merge operation only happens on 2 calls + // we are only interested in last 2 calls merged + if (mBluetoothCallQueue.size() > 2) { + Integer callId = mBluetoothCallQueue.peek(); + mBluetoothCallQueue.remove(); + mBluetoothConferenceCallInference.remove(callId); + } + } + // As there is at most 1 conference call, so clear inference when parent call ends + if (call.isConference()) { + Log.d(TAG, "conference call ends, clear inference"); + mBluetoothConferenceCallInference.clear(); + mBluetoothCallQueue.clear(); } } - // As there is at most 1 conference call, so clear inference when parent call ends - if (call.isConference()) { - Log.d(TAG, "conference call ends, clear inference"); - mBluetoothConferenceCallInference.clear(); - mBluetoothCallQueue.clear(); - } - } - updateHeadsetWithCallState(false /* force */); + updateHeadsetWithCallState(false /* force */); - if (Flags.maintainCallIndexAfterConference() && mConferenceCallClccIndexMap.size() > 0) { - int anyActiveCalls = mCallInfo.isNullCall(mCallInfo.getActiveCall()) ? 0 : 1; - int numHeldCalls = mCallInfo.getNumHeldCalls(); - // If no call is active or held clear the hashmap. - if (anyActiveCalls == 0 && numHeldCalls == 0) { - mConferenceCallClccIndexMap.clear(); + if (Flags.maintainCallIndexAfterConference() + && mConferenceCallClccIndexMap.size() > 0) { + int anyActiveCalls = mCallInfo.isNullCall(mCallInfo.getActiveCall()) ? 0 : 1; + int numHeldCalls = mCallInfo.getNumHeldCalls(); + // If no call is active or held clear the hashmap. + if (anyActiveCalls == 0 && numHeldCalls == 0) { + mConferenceCallClccIndexMap.clear(); + } } - } - if (mBluetoothLeCallControl != null) { - mBluetoothLeCallControl.onCallRemoved( - call.getTbsCallId(), getTbsTerminationReason(call)); + if (mBluetoothLeCallControl != null) { + mBluetoothLeCallControl.onCallRemoved( + call.getTbsCallId(), getTbsTerminationReason(call)); + } } } diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java index bbdb35af65..e0868e416a 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java @@ -27,6 +27,9 @@ import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; +import static android.bluetooth.IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; +import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; +import static android.bluetooth.IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElseGet; @@ -41,8 +44,6 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.bluetooth.IAudioInputCallback; -import android.bluetooth.IBluetoothCsipSetCoordinator; -import android.bluetooth.IBluetoothLeAudio; import android.bluetooth.IBluetoothVolumeControl; import android.bluetooth.IBluetoothVolumeControlCallback; import android.content.AttributionSource; @@ -94,6 +95,8 @@ public class VolumeControlService extends ProfileService { * User Set Volume Setting means that remote keeps volume in its cache. */ @VisibleForTesting static final int VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK = 0x01; + private static final int GROUP_ID_INVALID = -1; + private static VolumeControlService sVolumeControlService; @VisibleForTesting @@ -268,16 +271,87 @@ public class VolumeControlService extends ProfileService { return true; } + private int getGroupId(BluetoothDevice device) { + if (device == null) { + return GROUP_ID_INVALID; + } + + CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService(); + if (csipClient != null) { + int groupId = csipClient.getGroupId(device, BluetoothUuid.CAP); + if (groupId != CSIS_GROUP_ID_INVALID) { + return groupId; + } + } else { + Log.w(TAG, "CSIP not available"); + } + + LeAudioService leAudioService = mFactory.getLeAudioService(); + if (leAudioService != null) { + int groupId = leAudioService.getGroupId(device); + if (groupId != LE_AUDIO_GROUP_ID_INVALID) { + return groupId; + } + } else { + Log.w(TAG, "leAudioService not available"); + } + + return GROUP_ID_INVALID; + } + + private List<BluetoothDevice> getGroupDevices(int groupId) { + List<BluetoothDevice> result = new ArrayList<>(); + + if (groupId == GROUP_ID_INVALID) { + return result; + } + + CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService(); + if (csipClient != null) { + result = csipClient.getGroupDevicesOrdered(groupId); + if (!result.isEmpty()) { + return result; + } + } else { + Log.w(TAG, "CSIP not available"); + } + + LeAudioService leAudioService = mFactory.getLeAudioService(); + if (leAudioService != null) { + result = leAudioService.getGroupDevices(groupId); + if (!result.isEmpty()) { + return result; + } + } else { + Log.w(TAG, "leAudioService not available"); + } + + return result; + } + public List<BluetoothDevice> getConnectedDevices() { + List<BluetoothDevice> devices = new ArrayList<>(); synchronized (mStateMachines) { - List<BluetoothDevice> devices = new ArrayList<>(); for (VolumeControlStateMachine sm : mStateMachines.values()) { if (sm.isConnected()) { devices.add(sm.getDevice()); } } - return devices; } + return devices; + } + + private List<BluetoothDevice> getConnectedDevices(int groupId) { + List<BluetoothDevice> devices = new ArrayList<>(); + synchronized (mStateMachines) { + for (BluetoothDevice dev : getGroupDevices(groupId)) { + VolumeControlStateMachine sm = mStateMachines.get(dev); + if (sm != null && sm.isConnected()) { + devices.add(dev); + } + } + } + return devices; } /** @@ -460,15 +534,14 @@ public class VolumeControlService extends ProfileService { TAG, "setDeviceVolume: " + device + ", volume: " + volume + ", isGroupOp: " + isGroupOp); - LeAudioService leAudioService = mFactory.getLeAudioService(); - if (leAudioService == null) { - Log.e(TAG, "leAudioService not available"); + if (volume < 0) { + Log.w(TAG, "Tried to set invalid volume " + volume + ". Ignored."); return; } if (isGroupOp) { - int groupId = leAudioService.getGroupId(device); - if (groupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) { + int groupId = getGroupId(device); + if (groupId == GROUP_ID_INVALID) { Log.e(TAG, "Device not a part of a group"); return; } @@ -482,6 +555,8 @@ public class VolumeControlService extends ProfileService { } public void setGroupVolume(int groupId, int volume) { + Log.d(TAG, "setGroupVolume: " + groupId + ", volume: " + volume); + if (volume < 0) { Log.w(TAG, "Tried to set invalid volume " + volume + ". Ignored."); return; @@ -491,7 +566,7 @@ public class VolumeControlService extends ProfileService { mNativeInterface.setGroupVolume(groupId, volume); // We only receive the volume change and mute state needs to be acquired manually - Boolean isGroupMute = mGroupMuteCache.getOrDefault(groupId, false); + Boolean isGroupMute = getGroupMute(groupId); Boolean isStreamMute = mAudioManager.isStreamMute(getBluetoothContextualVolumeStream()); /* Note: AudioService keeps volume levels for each stream and for each device type, @@ -523,8 +598,7 @@ public class VolumeControlService extends ProfileService { } public int getGroupVolume(int groupId) { - return mGroupVolumeCache.getOrDefault( - groupId, IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); + return mGroupVolumeCache.getOrDefault(groupId, VOLUME_CONTROL_UNKNOWN_VOLUME); } /** @@ -534,8 +608,7 @@ public class VolumeControlService extends ProfileService { * @return the cached volume */ public int getDeviceVolume(BluetoothDevice device) { - return mDeviceVolumeCache.getOrDefault( - device, IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); + return mDeviceVolumeCache.getOrDefault(device, VOLUME_CONTROL_UNKNOWN_VOLUME); } /** @@ -554,7 +627,7 @@ public class VolumeControlService extends ProfileService { int groupVolume = getGroupVolume(groupId); Boolean groupMute = getGroupMute(groupId); - if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { + if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) { /* Don't need to show volume when activating known device. */ updateGroupCacheAndAudioSystem(groupId, groupVolume, groupMute, /* showInUI*/ false); } @@ -658,37 +731,24 @@ public class VolumeControlService extends ProfileService { if (sm == null) { return; } - if (sm.getConnectionState() != STATE_CONNECTED) { + if (!sm.isConnected()) { return; } } - // Correct the volume level only if device was already reported as connected. - boolean can_change_volume = false; - synchronized (mStateMachines) { - VolumeControlStateMachine sm = mStateMachines.get(device); - if (sm != null) { - can_change_volume = (sm.getConnectionState() == STATE_CONNECTED); - } + // If group volume has already changed, the new group member should set it + Integer groupVolume = getGroupVolume(groupId); + if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) { + Log.i(TAG, "Setting value:" + groupVolume + " to " + device); + mNativeInterface.setVolume(device, groupVolume); } - // If group volume has already changed, the new group member should set it - if (can_change_volume) { - Integer groupVolume = - mGroupVolumeCache.getOrDefault( - groupId, IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); - if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - Log.i(TAG, "Setting value:" + groupVolume + " to " + device); - mNativeInterface.setVolume(device, groupVolume); - } - - Boolean isGroupMuted = mGroupMuteCache.getOrDefault(groupId, false); - Log.i(TAG, "Setting mute:" + isGroupMuted + " to " + device); - if (isGroupMuted) { - mNativeInterface.mute(device); - } else { - mNativeInterface.unmute(device); - } + Boolean isGroupMuted = getGroupMute(groupId); + Log.i(TAG, "Setting mute:" + isGroupMuted + " to " + device); + if (isGroupMuted) { + mNativeInterface.mute(device); + } else { + mNativeInterface.unmute(device); } } @@ -704,14 +764,18 @@ public class VolumeControlService extends ProfileService { + ", showInUI" + showInUI); + if (volume < 0) { + Log.w(TAG, "Tried to update invalid volume " + volume + ". Ignored."); + return; + } + mGroupVolumeCache.put(groupId, volume); mGroupMuteCache.put(groupId, mute); LeAudioService leAudioService = mFactory.getLeAudioService(); if (leAudioService != null) { int currentlyActiveGroupId = leAudioService.getActiveGroupId(); - if (currentlyActiveGroupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID - || groupId != currentlyActiveGroupId) { + if (currentlyActiveGroupId == GROUP_ID_INVALID || groupId != currentlyActiveGroupId) { if (!Flags.leaudioBroadcastVolumeControlPrimaryGroupOnly()) { Log.i( TAG, @@ -722,7 +786,7 @@ public class VolumeControlService extends ProfileService { BassClientService bassClientService = mFactory.getBassClientService(); if (bassClientService == null || bassClientService.getSyncedBroadcastSinks().stream() - .map(dev -> leAudioService.getGroupId(dev)) + .map(dev -> getGroupId(dev)) .noneMatch( id -> id == groupId && leAudioService.isPrimaryGroup(id))) { Log.i( @@ -766,16 +830,10 @@ public class VolumeControlService extends ProfileService { int flags, boolean mute, boolean isAutonomous) { - if (groupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) { - LeAudioService leAudioService = mFactory.getLeAudioService(); - if (leAudioService == null) { - Log.e(TAG, "leAudioService not available"); - return; - } - groupId = leAudioService.getGroupId(device); + if (groupId == GROUP_ID_INVALID) { + groupId = getGroupId(device); } - - if (groupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) { + if (groupId == GROUP_ID_INVALID) { Log.e(TAG, "Device not a part of the group"); return; } @@ -788,7 +846,8 @@ public class VolumeControlService extends ProfileService { TAG, ("Initial volume set after connect, volume: " + volume) + (", mute: " + mute) - + (", flags: " + flags)); + + (", flags: " + flags) + + (", device: " + device)); /* We are here, because system has just started and LeAudio device is connected. If * remote device has User Persistent flag set, Android sets the volume to local cache * and to the audio system if not already streaming to other devices. @@ -797,37 +856,36 @@ public class VolumeControlService extends ProfileService { * Note, to match BR/EDR behavior, don't show volume change in UI here */ if (((flags & VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK) == 0x01) - && (getConnectedDevices().size() == 1)) { + && (getConnectedDevices(groupId).size() == 1)) { + Log.i(TAG, "Setting device: " + device + " volume: " + volume + " to the system"); updateGroupCacheAndAudioSystem(groupId, volume, mute, false); return; } // Reset flag is used - if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - Log.i(TAG, "Setting volume: " + groupVolume + " to the group: " + groupId); + if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) { + Log.i(TAG, "Setting group volume: " + groupVolume + " to the device: " + device); setGroupVolume(groupId, groupVolume); } else { - int vol = getBleVolumeFromCurrentStream(); - Log.i(TAG, "Setting system volume: " + vol + " to the group: " + groupId); - setGroupVolume(groupId, getBleVolumeFromCurrentStream()); + int systemVolume = getBleVolumeFromCurrentStream(); + Log.i(TAG, "Setting system volume: " + systemVolume + " to the group: " + groupId); + setGroupVolume(groupId, systemVolume); } return; } - Log.i(TAG, "handleVolumeControlChanged: " + device + "; volume: " + volume); + Log.i( + TAG, + "handleVolumeControlChanged: " + + ("device: " + device) + + (", groupId: " + groupId) + + (", volume: " + volume)); if (device == null) { // notify group devices volume changed - LeAudioService leAudioService = mFactory.getLeAudioService(); - if (leAudioService != null) { - synchronized (mCallbacks) { - notifyDevicesVolumeChanged( - mCallbacks, - leAudioService.getGroupDevices(groupId), - Optional.of(volume)); - } - } else { - Log.w(TAG, "leAudioService not available"); + synchronized (mCallbacks) { + notifyDevicesVolumeChanged( + mCallbacks, getGroupDevices(groupId), Optional.of(volume)); } } else { // notify device volume changed @@ -854,7 +912,7 @@ public class VolumeControlService extends ProfileService { synchronized (mStateMachines) { VolumeControlStateMachine sm = mStateMachines.get(device); if (sm != null) { - can_change_volume = (sm.getConnectionState() == STATE_CONNECTED); + can_change_volume = sm.isConnected(); } } @@ -895,7 +953,7 @@ public class VolumeControlService extends ProfileService { volume = 0; } - if (volume == IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) return -1; + if (volume == VOLUME_CONTROL_UNKNOWN_VOLUME) return -1; return getAudioDeviceVolume(getBluetoothContextualVolumeStream(), volume); } @@ -911,7 +969,7 @@ public class VolumeControlService extends ProfileService { int getBluetoothContextualVolumeStream() { int mode = mAudioManager.getMode(); - Log.d(TAG, "Volume mode: " + mode + "0: normal, 1: ring, 2,3: call"); + Log.d(TAG, "Volume mode:" + mode + "; Description: 0:normal, 1:ring, 2,3:call"); return switch (mode) { case AudioManager.MODE_IN_CALL, AudioManager.MODE_IN_COMMUNICATION -> { @@ -981,7 +1039,7 @@ public class VolumeControlService extends ProfileService { try { mCallbacks.getBroadcastItem(i).onVolumeOffsetChanged(device, id, value); } catch (RemoteException e) { - continue; + // Ignore Exception } } mCallbacks.finishBroadcast(); @@ -1006,7 +1064,7 @@ public class VolumeControlService extends ProfileService { .getBroadcastItem(i) .onVolumeOffsetAudioLocationChanged(device, id, location); } catch (RemoteException e) { - continue; + // Ignore Exception } } mCallbacks.finishBroadcast(); @@ -1032,7 +1090,7 @@ public class VolumeControlService extends ProfileService { .getBroadcastItem(i) .onVolumeOffsetAudioDescriptionChanged(device, id, description); } catch (RemoteException e) { - continue; + // Ignore Exception } } mCallbacks.finishBroadcast(); @@ -1313,37 +1371,23 @@ public class VolumeControlService extends ProfileService { return; } - LeAudioService leAudioService = mFactory.getLeAudioService(); - if (leAudioService == null) { - Log.e(TAG, "leAudioService not available"); - return; - } - for (BluetoothDevice dev : devices) { - int cachedVolume = IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; - if (!volume.isPresent()) { - int groupId = leAudioService.getGroupId(dev); - if (groupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) { - Log.e(TAG, "Device not a part of a group"); - continue; - } - // if device volume is available, notify with device volume, otherwise group volume - cachedVolume = getDeviceVolume(dev); - if (cachedVolume == IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - cachedVolume = getGroupVolume(groupId); - } - } - int broadcastVolume = cachedVolume; + int broadcastVolume = VOLUME_CONTROL_UNKNOWN_VOLUME; if (volume.isPresent()) { broadcastVolume = volume.get(); mDeviceVolumeCache.put(dev, broadcastVolume); + } else { + broadcastVolume = getDeviceVolume(dev); + if (broadcastVolume == VOLUME_CONTROL_UNKNOWN_VOLUME) { + broadcastVolume = getGroupVolume(getGroupId(dev)); + } } int n = callbacks.beginBroadcast(); for (int i = 0; i < n; i++) { try { callbacks.getBroadcastItem(i).onDeviceVolumeChanged(dev, broadcastVolume); } catch (RemoteException e) { - continue; + // Ignore Exception } } callbacks.finishBroadcast(); @@ -1432,29 +1476,19 @@ public class VolumeControlService extends ProfileService { } } else if (toState == STATE_CONNECTED) { // Restore the group volume if it was changed while the device was not yet connected. - CsipSetCoordinatorService csipClient = mFactory.getCsipSetCoordinatorService(); - if (csipClient != null) { - Integer groupId = csipClient.getGroupId(device, BluetoothUuid.CAP); - if (groupId != IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID) { - Integer groupVolume = - mGroupVolumeCache.getOrDefault( - groupId, IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME); - if (groupVolume != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) { - mNativeInterface.setVolume(device, groupVolume); - } + Integer groupId = getGroupId(device); + if (groupId != GROUP_ID_INVALID) { + Integer groupVolume = getGroupVolume(groupId); + if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) { + mNativeInterface.setVolume(device, groupVolume); + } - Boolean groupMute = mGroupMuteCache.getOrDefault(groupId, false); - if (groupMute) { - mNativeInterface.mute(device); - } else { - mNativeInterface.unmute(device); - } + Boolean groupMute = getGroupMute(groupId); + if (groupMute) { + mNativeInterface.mute(device); + } else { + mNativeInterface.unmute(device); } - } else { - /* It could happen when Bluetooth is stopping while VC is getting - * connection event - */ - Log.w(TAG, "CSIP is not available"); } } mAdapterService.handleProfileConnectionStateChange( @@ -1982,7 +2016,6 @@ public class VolumeControlService extends ProfileService { } for (Map.Entry<Integer, Integer> entry : mGroupVolumeCache.entrySet()) { - Boolean isMute = mGroupMuteCache.getOrDefault(entry.getKey(), false); ProfileService.println( sb, " GroupId: " @@ -1990,7 +2023,7 @@ public class VolumeControlService extends ProfileService { + " volume: " + entry.getValue() + ", mute: " - + isMute); + + getGroupMute(entry.getKey())); } } } diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java b/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java index 71f520cbd9..184dbb8191 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java @@ -158,7 +158,7 @@ class VolumeControlStateMachine extends StateMachine { processConnectionEvent(event.valueInt1); } default -> { - Log.e(TAG, "Disconnected: forwarding stack event: " + event); + Log.d(TAG, "Disconnected: forwarding stack event: " + event); mService.handleStackEvent(event); } } @@ -270,7 +270,7 @@ class VolumeControlStateMachine extends StateMachine { deferMessage(message); } default -> { - Log.e(TAG, "Connecting: forwarding stack event: " + event); + Log.d(TAG, "Connecting: forwarding stack event: " + event); mService.handleStackEvent(event); } } @@ -366,7 +366,7 @@ class VolumeControlStateMachine extends StateMachine { processConnectionEvent(event.valueInt1); } default -> { - Log.e(TAG, "Disconnecting: forwarding stack event: " + event); + Log.d(TAG, "Disconnecting: forwarding stack event: " + event); mService.handleStackEvent(event); } } @@ -465,7 +465,7 @@ class VolumeControlStateMachine extends StateMachine { processConnectionEvent(event.valueInt1); } default -> { - Log.e(TAG, "Connected: forwarding stack event: " + event); + Log.d(TAG, "Connected: forwarding stack event: " + event); mService.handleStackEvent(event); } } diff --git a/android/app/tests/unit/Android.bp b/android/app/tests/unit/Android.bp index f7f7a3dfc2..346c57fffa 100644 --- a/android/app/tests/unit/Android.bp +++ b/android/app/tests/unit/Android.bp @@ -55,9 +55,10 @@ java_defaults { platform_apis: true, test_suites: [ - "automotive-tests", + "automotive-general-tests", "general-tests", "mts-bluetooth", + "mts-bt", ], instrumentation_for: "Bluetooth", diff --git a/android/app/tests/unit/src/com/android/bluetooth/FileSystemWriteTest.java b/android/app/tests/unit/src/com/android/bluetooth/FileSystemWriteTest.java index d04d2c2026..4f2b921d7c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/FileSystemWriteTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/FileSystemWriteTest.java @@ -15,10 +15,11 @@ */ package com.android.bluetooth; +import static com.google.common.truth.Truth.assertThat; + import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,35 +31,23 @@ import java.io.IOException; @RunWith(AndroidJUnit4.class) public class FileSystemWriteTest { @Test - public void testBluetoothDirWrite() { - try { + public void testBluetoothDirWrite() throws IOException { File file = new File("/data/misc/bluetooth/test.file"); - Assert.assertTrue("File not created", file.createNewFile()); + assertThat(file.createNewFile()).isTrue(); file.delete(); - } catch (IOException e) { - Assert.fail("Exception creating file /data/misc/bluetooth/test.file: " + e); - } } @Test - public void testBluedroidDirWrite() { - try { + public void testBluedroidDirWrite() throws IOException { File file = new File("/data/misc/bluedroid/test.file"); - Assert.assertTrue("File not created", file.createNewFile()); + assertThat(file.createNewFile()).isTrue(); file.delete(); - } catch (IOException e) { - Assert.fail("Exception creating file /data/misc/bluedroid/test.file: " + e); - } } @Test - public void testBluetoothLogsDirWrite() { - try { + public void testBluetoothLogsDirWrite() throws IOException { File file = new File("/data/misc/bluetooth/logs/test.file"); - Assert.assertTrue("File not created", file.createNewFile()); + assertThat(file.createNewFile()).isTrue(); file.delete(); - } catch (IOException e) { - Assert.fail("Exception creating file /data/misc/bluetooth/logs/test.file: " + e); - } } } 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 4e7d23fe22..877df75ef2 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java +++ b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java @@ -15,6 +15,7 @@ */ package com.android.bluetooth; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.eq; @@ -93,11 +94,12 @@ public class TestUtils { * mocked or spied */ public static void setAdapterService(AdapterService adapterService) { - Assert.assertNull( - "AdapterService.getAdapterService() must be null before setting another" - + " AdapterService", - AdapterService.getAdapterService()); - Assert.assertNotNull("Adapter service should not be null", adapterService); + assertWithMessage( + "AdapterService.getAdapterService() must be null before setting another" + + " AdapterService") + .that(AdapterService.getAdapterService()) + .isNull(); + assertThat(adapterService).isNotNull(); // We cannot mock AdapterService.getAdapterService() with Mockito. // Hence we need to set AdapterService.sAdapterService field. AdapterService.setAdapterService(adapterService); @@ -115,7 +117,7 @@ public class TestUtils { + " supplied adapterService in this method", adapterService, AdapterService.getAdapterService()); - Assert.assertNotNull("Adapter service should not be null", adapterService); + assertThat(adapterService).isNotNull(); AdapterService.clearAdapterService(adapterService); } @@ -142,11 +144,11 @@ public class TestUtils { * @return {@link BluetoothDevice} test device for the device ID */ public static BluetoothDevice getTestDevice(BluetoothAdapter bluetoothAdapter, int id) { - Assert.assertTrue(id <= 0xFF); - Assert.assertNotNull(bluetoothAdapter); + assertThat(id).isAtMost(0xFF); + assertThat(bluetoothAdapter).isNotNull(); BluetoothDevice testDevice = bluetoothAdapter.getRemoteDevice(String.format("00:01:02:03:04:%02X", id)); - Assert.assertNotNull(testDevice); + assertThat(testDevice).isNotNull(); return testDevice; } @@ -173,7 +175,7 @@ public class TestUtils { public static Intent waitForIntent(int timeoutMs, BlockingQueue<Intent> queue) { try { Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS); - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); return intent; } catch (InterruptedException e) { Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage()); @@ -186,17 +188,14 @@ public class TestUtils { * * @param timeoutMs the time (in milliseconds) to wait and verify no intent has been received * @param queue the queue for the intent - * @return the received intent. Should be null under normal circumstances */ - public static Intent waitForNoIntent(int timeoutMs, BlockingQueue<Intent> queue) { + public static void waitForNoIntent(int timeoutMs, BlockingQueue<Intent> queue) { try { Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS); - Assert.assertNull(intent); - return intent; + assertThat(intent).isNull(); } catch (InterruptedException e) { Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage()); } - return null; } /** @@ -279,7 +278,7 @@ public class TestUtils { * <pre>{@code * TestUtils.runOnMainSync(new Runnable() { * public void run() { - * Assert.assertTrue(mA2dpService.stop()); + * assertThat(mA2dpService.stop()).isTrue(); * } * }); * }</pre> diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java index a398b2d6df..126c1559e9 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java @@ -110,7 +110,6 @@ public class A2dpServiceTest { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { return FlagsParameterization.allCombinationsOf( - Flags.FLAG_A2DP_SERVICE_LOOPER, Flags.FLAG_A2DP_BROADCAST_CONNECTION_STATE_WHEN_TURNED_OFF); } @@ -155,19 +154,11 @@ public class A2dpServiceTest { doReturn(new ParcelUuid[] {BluetoothUuid.A2DP_SINK}) .when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); - - if (!Flags.a2dpServiceLooper()) { - mLooper.startAutoDispatch(); - } } @After public void tearDown() { - if (Flags.a2dpServiceLooper()) { - assertThat(mLooper.dispatchAll()).isEqualTo(0); - } else { - mLooper.stopAutoDispatchAndIgnoreExceptions(); - } + assertThat(mLooper.dispatchAll()).isEqualTo(0); mA2dpService.stop(); } @@ -333,7 +324,6 @@ public class A2dpServiceTest { /** Test that an outgoing connection times out */ @Test - @EnableFlags(Flags.FLAG_A2DP_SERVICE_LOOPER) public void testOutgoingConnectTimeout() { // Update the device priority so okToConnect() returns true when(mDatabaseManager.getProfileConnectionPolicy(sTestDevice, BluetoothProfile.A2DP)) @@ -654,7 +644,6 @@ public class A2dpServiceTest { * if the device is unbond. */ @Test - @EnableFlags(Flags.FLAG_A2DP_SERVICE_LOOPER) public void testDeleteStateMachineDisconnectEvents() { // Update the device priority so okToConnect() returns true when(mDatabaseManager.getProfileConnectionPolicy(sTestDevice, BluetoothProfile.A2DP)) @@ -1255,21 +1244,15 @@ public class A2dpServiceTest { // Dispatch messages for the A2dpService looper, and validate // that at least one message was handled. private void dispatchAtLeastOneMessage() { - if (Flags.a2dpServiceLooper()) { - assertThat(mLooper.dispatchAll()).isGreaterThan(0); - } + assertThat(mLooper.dispatchAll()).isGreaterThan(0); } // Validate that no messages are pending on the A2dpService looper. private void dispatchNoMessages() { - if (Flags.a2dpServiceLooper()) { - assertThat(mLooper.dispatchAll()).isEqualTo(0); - } + assertThat(mLooper.dispatchAll()).isEqualTo(0); } private void moveTimeForward(long millis) { - if (Flags.a2dpServiceLooper()) { - mLooper.moveTimeForward(millis); - } + mLooper.moveTimeForward(millis); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java index eae499a93f..09b188d55d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java @@ -27,6 +27,7 @@ import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; import static com.android.bluetooth.a2dp.A2dpStateMachine.MESSAGE_CONNECT; +import static com.android.bluetooth.a2dp.A2dpStateMachine.MESSAGE_DISCONNECT; import static com.android.bluetooth.a2dp.A2dpStateMachine.MESSAGE_STACK_EVENT; import static com.google.common.truth.Truth.assertThat; @@ -47,11 +48,14 @@ import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.os.Bundle; import android.os.test.TestLooper; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; +import com.android.bluetooth.flags.Flags; import org.hamcrest.Matcher; import org.hamcrest.core.AllOf; @@ -72,6 +76,8 @@ import java.util.Arrays; public class A2dpStateMachineTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private A2dpService mService; @Mock private A2dpNativeInterface mNativeInterface; @@ -327,6 +333,23 @@ public class A2dpStateMachineTest { mStateMachine.dump(new StringBuilder()); } + @Test + @EnableFlags(Flags.FLAG_A2DP_SM_IGNORE_CONNECT_EVENTS_IN_CONNECTING_STATE) + public void connectEventNeglectedWhileInConnectingState() { + sendAndDispatchMessage(MESSAGE_CONNECT, mDevice); + verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED); + assertThat(mStateMachine.getCurrentState()).isInstanceOf(A2dpStateMachine.Connecting.class); + + // Dispatch CONNECT event twice more + sendAndDispatchMessage(MESSAGE_CONNECT, mDevice); + sendAndDispatchMessage(MESSAGE_CONNECT, mDevice); + sendAndDispatchMessage(MESSAGE_DISCONNECT, mDevice); + verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING); + assertThat(mStateMachine.getCurrentState()) + .isInstanceOf(A2dpStateMachine.Disconnected.class); + assertThat(mLooper.dispatchAll()).isEqualTo(0); + } + private void sendAndDispatchMessage(int what, Object obj) { mStateMachine.sendMessage(what, obj); mLooper.dispatchAll(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java index e2edf0903a..4b67658962 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.audio_util; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.content.ContentResolver; @@ -393,13 +395,13 @@ public class BrowserPlayerWrapperTest { Assert.assertEquals(expected.isBrowsable(), item.isFolder); if (item.isFolder) { Folder folder = item.folder; - Assert.assertNotNull(folder); - Assert.assertFalse(folder.isPlayable); + assertThat(folder).isNotNull(); + assertThat(folder.isPlayable).isFalse(); Assert.assertEquals(expected.getDescription().getMediaId(), folder.mediaId); Assert.assertEquals(expected.getDescription().getTitle().toString(), folder.title); } else { Metadata song = item.song; - Assert.assertNotNull(song); + assertThat(song).isNotNull(); Assert.assertEquals(expected.getDescription().getMediaId(), song.mediaId); Assert.assertEquals(expected.getDescription().getTitle().toString(), song.title); Assert.assertEquals( @@ -407,11 +409,11 @@ public class BrowserPlayerWrapperTest { Assert.assertEquals( expected.getDescription().getDescription().toString(), song.album); if (expected.getDescription().getIconBitmap() != null) { - Assert.assertNotNull(song.image); + assertThat(song.image).isNotNull(); Bitmap expectedBitmap = expected.getDescription().getIconBitmap(); - Assert.assertTrue(expectedBitmap.sameAs(song.image.getImage())); + assertThat(expectedBitmap.sameAs(song.image.getImage())).isTrue(); } else if (expected.getDescription().getIconUri() != null) { - Assert.assertTrue(mTestBitmap.sameAs(song.image.getImage())); + assertThat(mTestBitmap.sameAs(song.image.getImage())).isTrue(); } else { Assert.assertEquals(null, song.image); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java index 8308d0364a..c8e6a9f200 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerListTest.java @@ -71,7 +71,6 @@ public class MediaPlayerListTest { if (Looper.myLooper() == null) { Looper.prepare(); } - Assert.assertNotNull(Looper.myLooper()); AudioManager mockAudioManager = mock(AudioManager.class); when(mMockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mockAudioManager); diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java index f1e78940c6..2ffe3589d5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java @@ -172,12 +172,10 @@ public class MediaPlayerWrapperTest { */ @Test public void testNullControllerLooper() { - MediaPlayerWrapper wrapper = - MediaPlayerWrapperFactory.wrap(mMockContext, null, mThread.getLooper()); - Assert.assertNull(wrapper); + assertThat(MediaPlayerWrapperFactory.wrap(mMockContext, null, mThread.getLooper())) + .isNull(); - wrapper = MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, null); - Assert.assertNull(wrapper); + assertThat(MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, null)).isNull(); } /* @@ -188,24 +186,24 @@ public class MediaPlayerWrapperTest { public void testIsReady() { MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); - Assert.assertTrue(wrapper.isPlaybackStateReady()); - Assert.assertTrue(wrapper.isMetadataReady()); + assertThat(wrapper.isPlaybackStateReady()).isTrue(); + assertThat(wrapper.isMetadataReady()).isTrue(); // Test isPlaybackStateReady() is false when the playback state is null doReturn(null).when(mMockController).getPlaybackState(); - Assert.assertFalse(wrapper.isPlaybackStateReady()); + assertThat(wrapper.isPlaybackStateReady()).isFalse(); // Restore the old playback state doReturn(mTestState.build()).when(mMockController).getPlaybackState(); - Assert.assertTrue(wrapper.isPlaybackStateReady()); + assertThat(wrapper.isPlaybackStateReady()).isTrue(); // Test isMetadataReady() is false when the metadata is null doReturn(null).when(mMockController).getMetadata(); - Assert.assertFalse(wrapper.isMetadataReady()); + assertThat(wrapper.isMetadataReady()).isFalse(); // Restore the old metadata doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); - Assert.assertTrue(wrapper.isMetadataReady()); + assertThat(wrapper.isMetadataReady()).isTrue(); } /* @@ -217,8 +215,8 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); - Assert.assertTrue(wrapper.isPlaybackStateReady()); - Assert.assertTrue(wrapper.isMetadataReady()); + assertThat(wrapper.isPlaybackStateReady()).isTrue(); + assertThat(wrapper.isMetadataReady()).isTrue(); wrapper.registerCallback(mTestCbs); // Create a new MediaController that has different metadata than the previous controller @@ -296,7 +294,7 @@ public class MediaPlayerWrapperTest { Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } @@ -345,7 +343,7 @@ public class MediaPlayerWrapperTest { Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } @@ -470,19 +468,16 @@ public class MediaPlayerWrapperTest { MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); doReturn(null).when(mMockController).getMetadata(); - Assert.assertFalse( - Util.toMetadata(mMockContext, mTestMetadata.build()) - .duration - .equals(wrapper.getCurrentQueue().get(0).duration)); + assertThat(Util.toMetadata(mMockContext, mTestMetadata.build()).duration) + .isNotEqualTo(wrapper.getCurrentQueue().get(0).duration); doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); Assert.assertEquals( Util.toMetadata(mMockContext, mTestMetadata.build()).duration, wrapper.getCurrentQueue().get(0).duration); // The MediaController Metadata should still not be equal to the queue // as the track count is different and should not be overridden. - Assert.assertFalse( - Util.toMetadata(mMockContext, mTestMetadata.build()) - .equals(wrapper.getCurrentQueue().get(0))); + assertThat(Util.toMetadata(mMockContext, mTestMetadata.build())) + .isNotEqualTo(wrapper.getCurrentQueue().get(0)); } /* @@ -537,7 +532,7 @@ public class MediaPlayerWrapperTest { controllerCallbacks.onPlaybackStateChanged(mTestState.build()); // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } @@ -556,7 +551,7 @@ public class MediaPlayerWrapperTest { // Ensure that everything was cleaned up verify(mMockController).unregisterCallback(any()); - Assert.assertNull(wrapper.getTimeoutHandler()); + assertThat(wrapper.getTimeoutHandler()).isNull(); } /* @@ -583,7 +578,7 @@ public class MediaPlayerWrapperTest { verify(mTestCbs, never()).mediaUpdatedCallback(any()); // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } @@ -642,7 +637,7 @@ public class MediaPlayerWrapperTest { Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue))); // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } @@ -771,7 +766,7 @@ public class MediaPlayerWrapperTest { } // Verify that there are no timeout messages pending and there were no timeouts - Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); + assertThat(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)).isFalse(); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java index 7a882a2627..5ba6e0f4e5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java @@ -42,7 +42,6 @@ import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -261,7 +260,7 @@ public class MetadataTest { assertThat(metadata.numTracks).isEqualTo(numTracks); assertThat(metadata.genre).isEqualTo(genre); assertThat(metadata.duration).isEqualTo(duration); - Assert.assertTrue(Image.sameAs(metadata.image, image)); + assertThat(Image.sameAs(metadata.image, image)).isTrue(); } /** Make sure the media ID we set is transferred to Metadata object we build */ diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java index 1713b7a29f..20bc574fd9 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java @@ -18,17 +18,41 @@ package com.android.bluetooth.avrcp; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyObject; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.media.AudioDeviceCallback; +import android.media.AudioManager; +import android.media.session.MediaSessionManager; import android.net.Uri; +import android.os.UserManager; +import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.bluetooth.TestUtils; import com.android.bluetooth.audio_util.Image; import com.android.bluetooth.audio_util.Metadata; +import com.android.bluetooth.btservice.AdapterService; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; @@ -37,8 +61,63 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class AvrcpTargetServiceTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private @Mock AdapterService mMockAdapterService; + private @Mock AudioManager mMockAudioManager; + private @Mock AvrcpNativeInterface mMockNativeInterface; + private @Mock UserManager mMockUserManager; + private @Mock Resources mMockResources; + private @Mock SharedPreferences mMockSharedPreferences; + private @Mock SharedPreferences.Editor mMockSharedPreferencesEditor; + + private @Captor ArgumentCaptor<AudioDeviceCallback> mAudioDeviceCb; + + private MediaSessionManager mMediaSessionManager; + private TestLooper mLooper; + private static final String TEST_DATA = "-1"; + @Before + public void setUp() throws Exception { + mLooper = new TestLooper(); + mLooper.startAutoDispatch(); + + doReturn(mMockAudioManager) + .when(mMockAdapterService) + .getSystemService(Context.AUDIO_SERVICE); + doReturn(Context.AUDIO_SERVICE) + .when(mMockAdapterService) + .getSystemServiceName(AudioManager.class); + + mMediaSessionManager = + InstrumentationRegistry.getInstrumentation() + .getTargetContext() + .getSystemService(MediaSessionManager.class); + TestUtils.mockGetSystemService( + mMockAdapterService, + Context.MEDIA_SESSION_SERVICE, + MediaSessionManager.class, + mMediaSessionManager); + + doReturn(mLooper.getNewExecutor()).when(mMockAdapterService).getMainExecutor(); + + doReturn(mMockAdapterService).when(mMockAdapterService).getApplicationContext(); + TestUtils.mockGetSystemService( + mMockAdapterService, Context.USER_SERVICE, UserManager.class, mMockUserManager); + doReturn(mMockResources).when(mMockAdapterService).getResources(); + + doReturn(mMockSharedPreferencesEditor).when(mMockSharedPreferences).edit(); + doReturn(mMockSharedPreferences) + .when(mMockAdapterService) + .getSharedPreferences(anyString(), anyInt()); + } + + @After + public void tearDown() throws Exception { + mLooper.stopAutoDispatchAndIgnoreExceptions(); + } + @Test public void testQueueUpdateData() { List<Metadata> firstQueue = new ArrayList<Metadata>(); @@ -75,4 +154,27 @@ public class AvrcpTargetServiceTest { Metadata.Builder builder = new Metadata.Builder(); return builder.useDefaults().build(); } + + @Test + public void testServiceInstance() { + AvrcpVolumeManager volumeManager = + new AvrcpVolumeManager( + mMockAdapterService, mMockAudioManager, mMockNativeInterface); + AvrcpTargetService service = + new AvrcpTargetService( + mMockAdapterService, + mMockAudioManager, + mMockNativeInterface, + volumeManager, + mLooper.getLooper()); + + service.start(); + verify(mMockAudioManager) + .registerAudioDeviceCallback(mAudioDeviceCb.capture(), anyObject()); + + service.stop(); + service.cleanup(); + assertThat(mAudioDeviceCb.getValue()).isNotNull(); + verify(mMockAudioManager).unregisterAudioDeviceCallback(mAudioDeviceCb.getValue()); + } } 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 dbe35e1660..03a05875b2 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 @@ -105,7 +105,6 @@ public class AvrcpControllerStateMachineTest { if (Looper.myLooper() == null) { Looper.prepare(); } - Assert.assertNotNull(Looper.myLooper()); // Set a mock Adapter Service for profile state change notifications TestUtils.setAdapterService(mAdapterService); @@ -328,7 +327,7 @@ public class AvrcpControllerStateMachineTest { // Make sure its set by re grabbing the node and checking its contents are cached nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); assertNowPlayingList(nowPlayingList); } @@ -410,7 +409,7 @@ public class AvrcpControllerStateMachineTest { int numBroadcastsSent = setUpConnectedState(true, false); MediaControllerCompat.TransportControls transportControls = BluetoothMediaBrowserService.getTransportControls(); - Assert.assertNotNull(transportControls); + assertThat(transportControls).isNotNull(); Assert.assertEquals( PlaybackStateCompat.STATE_NONE, BluetoothMediaBrowserService.getPlaybackState().getState()); @@ -487,7 +486,7 @@ public class AvrcpControllerStateMachineTest { public void testDump() { StringBuilder sb = new StringBuilder(); mAvrcpStateMachine.dump(sb); - Assert.assertNotNull(sb.toString()); + assertThat(sb.toString()).isNotNull(); } /** Test media browser play command */ @@ -755,7 +754,7 @@ public class AvrcpControllerStateMachineTest { AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); // Verify we've uncached our browse root and made the call to fetch new players - Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()); + assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isFalse(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .getPlayerList(eq(mTestAddress), eq(0), eq(19)); } @@ -784,7 +783,7 @@ public class AvrcpControllerStateMachineTest { AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); // Verify we've uncached our browse root and made the call to fetch new players - Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()); + assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isFalse(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .getPlayerList(eq(mTestAddress), eq(0), eq(19)); @@ -804,12 +803,12 @@ public class AvrcpControllerStateMachineTest { // Verify we processed the first players properly. Note the addressed player should always // be in the available player set. - Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()); + assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isTrue(); SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers(); - Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId())); + assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue(); Assert.assertEquals(testPlayers.size(), players.size()); for (AvrcpPlayer player : testPlayers) { - Assert.assertTrue(players.contains(player.getId())); + assertThat(players.contains(player.getId())).isTrue(); } // Verify we request metadata, playback state and now playing list @@ -835,7 +834,7 @@ public class AvrcpControllerStateMachineTest { AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); // Verify we've uncached our browse root and made the call to fetch new players - Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()); + assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isFalse(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .getPlayerList(eq(mTestAddress), eq(0), eq(19)); @@ -856,12 +855,12 @@ public class AvrcpControllerStateMachineTest { // Verify we processed the players properly. Note the addressed player is currently the // default player and is not in the available player set sent. This means we'll have an // extra player at ID -1. - Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()); + assertThat(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached()).isTrue(); SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers(); - Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId())); + assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue(); Assert.assertEquals(testPlayers.size() + 1, players.size()); for (AvrcpPlayer player : testPlayers) { - Assert.assertTrue(players.contains(player.getId())); + assertThat(players.contains(player.getId())).isTrue(); } // Verify we do not request metadata, playback state and now playing list because we're @@ -916,7 +915,7 @@ public class AvrcpControllerStateMachineTest { // The addressed player should always be in the available player set Assert.assertEquals(2, mAvrcpStateMachine.getAddressedPlayerId()); SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers(); - Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId())); + assertThat(players.contains(mAvrcpStateMachine.getAddressedPlayerId())).isTrue(); // Make sure the Now Playing list is now cleared assertNowPlayingList(new ArrayList<AvrcpItem>()); @@ -1194,7 +1193,7 @@ public class AvrcpControllerStateMachineTest { public void testPlaybackWhilePlayingState() { when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_GAIN); setUpConnectedState(true, true); - Assert.assertTrue(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isTrue(); mAvrcpStateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, PlaybackStateCompat.STATE_PLAYING); @@ -1210,14 +1209,14 @@ public class AvrcpControllerStateMachineTest { /** Test that isActive() reports the proper value when we're active */ @Test public void testIsActive_deviceActive() { - Assert.assertTrue(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isTrue(); } /** Test that isActive() reports the proper value when we're inactive */ @Test public void testIsActive_deviceInactive() { setActiveDevice(null); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); } /** Test becoming active from the inactive state */ @@ -1225,12 +1224,12 @@ public class AvrcpControllerStateMachineTest { public void testBecomeActive() { // Note device starts as active in setUp() and state cascades come the CONNECTED state setUpConnectedState(true, true); - Assert.assertTrue(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isTrue(); // Make the device inactive setActiveDevice(null); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); // Change device state while inactive AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null); @@ -1247,16 +1246,16 @@ public class AvrcpControllerStateMachineTest { // Make device active setActiveDevice(mTestDevice); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertTrue(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isTrue(); // See that state from BluetoothMediaBrowserService is updated MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); + assertThat(controller).isNotNull(); MediaMetadataCompat metadata = controller.getMetadata(); - Assert.assertNotNull(metadata); + assertThat(metadata).isNotNull(); Assert.assertEquals("title", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); @@ -1266,12 +1265,12 @@ public class AvrcpControllerStateMachineTest { Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)); PlaybackStateCompat playbackState = controller.getPlaybackState(); - Assert.assertNotNull(playbackState); + assertThat(playbackState).isNotNull(); Assert.assertEquals(PlaybackStateCompat.STATE_PAUSED, playbackState.getState()); Assert.assertEquals(7, playbackState.getPosition()); List<MediaSessionCompat.QueueItem> queue = controller.getQueue(); - Assert.assertNotNull(queue); + assertThat(queue).isNotNull(); Assert.assertEquals(2, queue.size()); Assert.assertEquals("title", queue.get(0).getDescription().getTitle().toString()); Assert.assertEquals("title 2", queue.get(1).getDescription().getTitle().toString()); @@ -1282,7 +1281,7 @@ public class AvrcpControllerStateMachineTest { public void testBecomeInactive() { // Note device starts as active in setUp() setUpConnectedState(true, true); - Assert.assertTrue(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isTrue(); // Set the active device to something else, verify we're inactive and send a pause upon // becoming inactive @@ -1293,7 +1292,7 @@ public class AvrcpControllerStateMachineTest { eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); } @Test @@ -1318,12 +1317,12 @@ public class AvrcpControllerStateMachineTest { // Verify track and playback state MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); + assertThat(controller).isNotNull(); MediaMetadataCompat metadata = controller.getMetadata(); - Assert.assertNotNull(metadata); + assertThat(metadata).isNotNull(); Assert.assertEquals("Song 1", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); @@ -1333,7 +1332,7 @@ public class AvrcpControllerStateMachineTest { Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)); PlaybackStateCompat playbackState = controller.getPlaybackState(); - Assert.assertNotNull(playbackState); + assertThat(playbackState).isNotNull(); Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState()); Assert.assertEquals(0, playbackState.getActiveQueueItemId()); @@ -1344,7 +1343,7 @@ public class AvrcpControllerStateMachineTest { // Assert new track metadata and active queue item metadata = controller.getMetadata(); - Assert.assertNotNull(metadata); + assertThat(metadata).isNotNull(); Assert.assertEquals("Song 2", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); @@ -1354,7 +1353,7 @@ public class AvrcpControllerStateMachineTest { Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)); playbackState = controller.getPlaybackState(); - Assert.assertNotNull(playbackState); + assertThat(playbackState).isNotNull(); Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState()); Assert.assertEquals(1, playbackState.getActiveQueueItemId()); } @@ -1368,7 +1367,7 @@ public class AvrcpControllerStateMachineTest { // becoming inactive setActiveDevice(null); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); // Change track while inactive AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null); @@ -1376,12 +1375,11 @@ public class AvrcpControllerStateMachineTest { // Since we're not active, verify BluetoothMediaBrowserService does not have these values MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); + assertThat(controller).isNotNull(); - MediaMetadataCompat metadata = controller.getMetadata(); - Assert.assertNull(metadata); // track starts as null and shouldn't change + assertThat(controller.getMetadata()).isNull(); // track starts as null and shouldn't change } /** Test receiving a playback status of playing when we're not the active device */ @@ -1392,7 +1390,7 @@ public class AvrcpControllerStateMachineTest { // Set the active device to something else, verify we're inactive setActiveDevice(null); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); clearInvocations(mAvrcpControllerService); clearInvocations(mNativeInterface); @@ -1420,7 +1418,7 @@ public class AvrcpControllerStateMachineTest { // Set the active device to something else, verify we're inactive setActiveDevice(null); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); clearInvocations(mAvrcpControllerService); clearInvocations(mNativeInterface); @@ -1429,12 +1427,12 @@ public class AvrcpControllerStateMachineTest { // Since we're not active, verify BluetoothMediaBrowserService does not have these values MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); + assertThat(controller).isNotNull(); PlaybackStateCompat playbackState = controller.getPlaybackState(); - Assert.assertNotNull(playbackState); + assertThat(playbackState).isNotNull(); Assert.assertEquals(0, playbackState.getPosition()); } @@ -1447,7 +1445,7 @@ public class AvrcpControllerStateMachineTest { // becoming inactive setActiveDevice(null); TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); - Assert.assertFalse(mAvrcpStateMachine.isActive()); + assertThat(mAvrcpStateMachine.isActive()).isFalse(); // Change queue while inactive List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>(); @@ -1461,12 +1459,11 @@ public class AvrcpControllerStateMachineTest { // Since we're not active, verify BluetoothMediaBrowserService does not have these values MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); + assertThat(controller).isNotNull(); - List<MediaSessionCompat.QueueItem> queue = controller.getQueue(); - Assert.assertNull(queue); + assertThat(controller.getQueue()).isNull(); } /** @@ -1773,7 +1770,7 @@ public class AvrcpControllerStateMachineTest { // Make sure its set by re grabbing the node and checking its contents are cached nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); assertNowPlayingList(updatedNowPlayingList); } @@ -1833,7 +1830,7 @@ public class AvrcpControllerStateMachineTest { // Make sure its set by re grabbing the node and checking its contents are cached nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); assertNowPlayingList(updatedNowPlayingList); } @@ -1901,7 +1898,7 @@ public class AvrcpControllerStateMachineTest { // Make sure its set by re grabbing the node and checking its contents are cached nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); assertNowPlayingList(updatedNowPlayingList); } @@ -1966,7 +1963,7 @@ public class AvrcpControllerStateMachineTest { // Make sure its set by re grabbing the node and checking its contents are cached nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); assertNowPlayingList(updatedNowPlayingList); } @@ -1993,7 +1990,7 @@ public class AvrcpControllerStateMachineTest { // Request for new contents should be sent verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19)); - Assert.assertFalse(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isFalse(); // Send timeout on our own instead of waiting 10 seconds mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_INTERNAL_CMD_TIMEOUT); @@ -2001,15 +1998,14 @@ public class AvrcpControllerStateMachineTest { // Node should be set to cached and notified on assertNowPlayingList(new ArrayList<AvrcpItem>()); - Assert.assertTrue(nowPlaying.isCached()); + assertThat(nowPlaying.isCached()).isTrue(); // See that state from BluetoothMediaBrowserService is updated to null (i.e. empty) MediaSessionCompat session = BluetoothMediaBrowserService.getSession(); - Assert.assertNotNull(session); + assertThat(session).isNotNull(); MediaControllerCompat controller = session.getController(); - Assert.assertNotNull(controller); - List<MediaSessionCompat.QueueItem> queue = controller.getQueue(); - Assert.assertNull(queue); + assertThat(controller).isNotNull(); + assertThat(controller.getQueue()).isNull(); } /** diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java index f11054f05c..6cf6bb466b 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtStorageTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; @@ -83,32 +85,32 @@ public final class AvrcpCoverArtStorageTest { private void assertImageSame(Bitmap expected, BluetoothDevice device, String handle) { Bitmap image = mAvrcpCoverArtStorage.getImage(device, handle); - Assert.assertTrue(expected.sameAs(image)); + assertThat(expected.sameAs(image)).isTrue(); } @Test public void addNewImage_imageExists() { Uri expectedUri = AvrcpCoverArtProvider.getImageUri(mDevice1, mHandle1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); Assert.assertEquals(expectedUri, uri); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test public void addExistingImage_imageUpdated() { Uri expectedUri = AvrcpCoverArtProvider.getImageUri(mDevice1, mHandle1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); Assert.assertEquals(expectedUri, uri); assertImageSame(mImage1, mDevice1, mHandle1); uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage2); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); Assert.assertEquals(expectedUri, uri); assertImageSame(mImage2, mDevice1, mHandle1); } @@ -117,16 +119,16 @@ public final class AvrcpCoverArtStorageTest { public void addTwoImageSameDevice_bothExist() { Uri expectedUri1 = AvrcpCoverArtProvider.getImageUri(mDevice1, mHandle1); Uri expectedUri2 = AvrcpCoverArtProvider.getImageUri(mDevice1, mHandle2); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isFalse(); Uri uri1 = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); Uri uri2 = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle2, mImage2); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); Assert.assertEquals(expectedUri1, uri1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isTrue(); Assert.assertEquals(expectedUri2, uri2); } @@ -134,51 +136,51 @@ public final class AvrcpCoverArtStorageTest { public void addTwoImageDifferentDevices_bothExist() { Uri expectedUri1 = AvrcpCoverArtProvider.getImageUri(mDevice1, mHandle1); Uri expectedUri2 = AvrcpCoverArtProvider.getImageUri(mDevice2, mHandle1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)).isFalse(); Uri uri1 = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); Uri uri2 = mAvrcpCoverArtStorage.addImage(mDevice2, mHandle1, mImage1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); Assert.assertEquals(expectedUri1, uri1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); Assert.assertEquals(expectedUri2, uri2); } @Test public void addNullImage_imageNotAdded() { Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, null); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Assert.assertEquals(null, uri); } @Test public void addImageNullDevice_imageNotAdded() { Uri uri = mAvrcpCoverArtStorage.addImage(null, mHandle1, mImage1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Assert.assertEquals(null, uri); } @Test public void addImageNullHandle_imageNotAdded() { Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, null, mImage1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Assert.assertEquals(null, uri); } @Test public void addImageEmptyHandle_imageNotAdded() { Uri uri = mAvrcpCoverArtStorage.addImage(mDevice1, "", mImage1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Assert.assertEquals(null, uri); } @Test public void getImage_canGetImageFromStorage() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); assertImageSame(mImage1, mDevice1, mHandle1); } @@ -186,36 +188,36 @@ public final class AvrcpCoverArtStorageTest { public void getImageSameHandleDifferentDevices_canGetImagesFromStorage() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.addImage(mDevice2, mHandle1, mImage2); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)).isTrue(); assertImageSame(mImage1, mDevice1, mHandle1); assertImageSame(mImage2, mDevice2, mHandle1); } @Test public void getImageThatDoesntExist_returnsNull() { - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, mHandle1); Assert.assertEquals(null, image); } @Test public void getImageNullDevice_returnsNull() { - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Bitmap image = mAvrcpCoverArtStorage.getImage(null, mHandle1); Assert.assertEquals(null, image); } @Test public void getImageNullHandle_returnsNull() { - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, null); Assert.assertEquals(null, image); } @Test public void getImageEmptyHandle_returnsNull() { - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); Bitmap image = mAvrcpCoverArtStorage.getImage(mDevice1, ""); Assert.assertEquals(null, image); } @@ -226,44 +228,44 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle2, mImage1); mAvrcpCoverArtStorage.addImage(mDevice2, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(mDevice1, mHandle1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isTrue(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)).isTrue(); } @Test public void removeNonExistentImage_nothingHappens() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(mDevice1, mHandle2); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test public void removeImageNullDevice_nothingHappens() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(null, mHandle1); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test public void removeImageNullHandle_nothingHappens() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(mDevice1, null); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test public void removeImageEmptyHandle_nothingHappens() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(mDevice1, ""); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test public void removeImageNullInputs_nothingHappens() { mAvrcpCoverArtStorage.addImage(mDevice1, mHandle1, mImage1); mAvrcpCoverArtStorage.removeImage(null, null); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); } @Test @@ -274,9 +276,9 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.removeImagesForDevice(mDevice1); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)).isTrue(); } @Test @@ -286,8 +288,8 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.removeImagesForDevice(mDevice2); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isTrue(); } @Test @@ -297,8 +299,8 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.removeImagesForDevice(null); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertTrue(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isTrue(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isTrue(); } @Test @@ -308,8 +310,8 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.clear(); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isFalse(); } @Test @@ -321,10 +323,10 @@ public final class AvrcpCoverArtStorageTest { mAvrcpCoverArtStorage.clear(); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)); - Assert.assertFalse(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle2)); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice1, mHandle2)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle1)).isFalse(); + assertThat(mAvrcpCoverArtStorage.doesImageExist(mDevice2, mHandle2)).isFalse(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java index 8df38adcde..7a296cd075 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpItemTest.java @@ -16,6 +16,9 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.net.Uri; @@ -473,7 +476,7 @@ public final class AvrcpItemTest { null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON)); Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART)); Assert.assertEquals(null, metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)); - Assert.assertFalse(metadata.containsKey(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE)); + assertThat(metadata.containsKey(MediaMetadataCompat.METADATA_KEY_BT_FOLDER_TYPE)).isFalse(); } @Test @@ -545,14 +548,14 @@ public final class AvrcpItemTest { MediaItem mediaItem = item.toMediaItem(); MediaDescriptionCompat desc = mediaItem.getDescription(); - Assert.assertTrue(mediaItem.isPlayable()); - Assert.assertFalse(mediaItem.isBrowsable()); + assertThat(mediaItem.isPlayable()).isTrue(); + assertThat(mediaItem.isBrowsable()).isFalse(); Assert.assertEquals(UUID, mediaItem.getMediaId()); Assert.assertEquals(UUID, desc.getMediaId()); Assert.assertEquals(null, desc.getMediaUri()); Assert.assertEquals(title, desc.getTitle().toString()); - Assert.assertNull(desc.getSubtitle()); + assertThat(desc.getSubtitle()).isNull(); Assert.assertEquals(uri, desc.getIconUri()); Assert.assertEquals(null, desc.getIconBitmap()); } @@ -574,14 +577,14 @@ public final class AvrcpItemTest { MediaItem mediaItem = item.toMediaItem(); MediaDescriptionCompat desc = mediaItem.getDescription(); - Assert.assertTrue(mediaItem.isPlayable()); - Assert.assertFalse(mediaItem.isBrowsable()); + assertThat(mediaItem.isPlayable()).isTrue(); + assertThat(mediaItem.isBrowsable()).isFalse(); Assert.assertEquals(UUID, mediaItem.getMediaId()); Assert.assertEquals(UUID, desc.getMediaId()); Assert.assertEquals(null, desc.getMediaUri()); Assert.assertEquals(displayName, desc.getTitle().toString()); - Assert.assertNull(desc.getSubtitle()); + assertThat(desc.getSubtitle()).isNull(); Assert.assertEquals(uri, desc.getIconUri()); Assert.assertEquals(null, desc.getIconBitmap()); } @@ -601,14 +604,14 @@ public final class AvrcpItemTest { MediaItem mediaItem = item.toMediaItem(); MediaDescriptionCompat desc = mediaItem.getDescription(); - Assert.assertFalse(mediaItem.isPlayable()); - Assert.assertTrue(mediaItem.isBrowsable()); + assertThat(mediaItem.isPlayable()).isFalse(); + assertThat(mediaItem.isBrowsable()).isTrue(); Assert.assertEquals(UUID, mediaItem.getMediaId()); Assert.assertEquals(UUID, desc.getMediaId()); Assert.assertEquals(null, desc.getMediaUri()); Assert.assertEquals(title, desc.getTitle().toString()); - Assert.assertNull(desc.getSubtitle()); + assertThat(desc.getSubtitle()).isNull(); Assert.assertEquals(uri, desc.getIconUri()); Assert.assertEquals(null, desc.getIconBitmap()); } @@ -619,17 +622,18 @@ public final class AvrcpItemTest { AvrcpItem item = builder.build(); - Assert.assertTrue(item.equals(item)); + assertThat(item).isEqualTo(item); } @Test + @SuppressLint("TruthIncompatibleType") // That the point of this test public void equals_withDifferentInstance() { AvrcpItem.Builder builder = new AvrcpItem.Builder(); String notAvrcpItem = "notAvrcpItem"; AvrcpItem item = builder.build(); - Assert.assertFalse(item.equals(notAvrcpItem)); + assertThat(item).isNotEqualTo(notAvrcpItem); } @Test @@ -640,6 +644,6 @@ public final class AvrcpItemTest { AvrcpItem item = builder.build(); AvrcpItem itemEqual = builderEqual.build(); - Assert.assertTrue(item.equals(itemEqual)); + assertThat(item).isEqualTo(itemEqual); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java index 00b56abfa4..c4ae402379 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipAttachmentFormatTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import android.annotation.SuppressLint; import androidx.test.runner.AndroidJUnit4; @@ -98,14 +100,14 @@ public class BipAttachmentFormatTest { if (created != null) { Assert.assertEquals(created, attachment.getCreatedDate().getTime()); - Assert.assertTrue(attachment.getCreatedDate().isUtc()); + assertThat(attachment.getCreatedDate().isUtc()).isTrue(); } else { Assert.assertEquals(null, attachment.getCreatedDate()); } if (modified != null) { Assert.assertEquals(modified, attachment.getModifiedDate().getTime()); - Assert.assertTrue(attachment.getModifiedDate().isUtc()); + assertThat(attachment.getModifiedDate().isUtc()).isTrue(); } else { Assert.assertEquals(null, attachment.getModifiedDate()); } @@ -424,16 +426,17 @@ public class BipAttachmentFormatTest { BipAttachmentFormat attachment = new BipAttachmentFormat("text/plain", null, "thisisatextfile.txt", -1, null, null); - Assert.assertTrue(attachment.equals(attachment)); + assertThat(attachment).isEqualTo(attachment); } @Test + @SuppressLint("TruthIncompatibleType") // That the point of this test public void testEquals_withDifferentClass() { BipAttachmentFormat attachment = new BipAttachmentFormat("text/plain", null, "thisisatextfile.txt", -1, null, null); String notAttachment = "notAttachment"; - Assert.assertFalse(attachment.equals(notAttachment)); + assertThat(attachment).isNotEqualTo(notAttachment); } @Test @@ -443,6 +446,6 @@ public class BipAttachmentFormatTest { BipAttachmentFormat attachmentEqual = new BipAttachmentFormat("text/plain", null, "thisisatextfile.txt", -1, null, null); - Assert.assertTrue(attachment.equals(attachmentEqual)); + assertThat(attachment).isEqualTo(attachmentEqual); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java index 913f77fe36..33937d8741 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipDatetimeTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import android.annotation.SuppressLint; import androidx.test.runner.AndroidJUnit4; @@ -74,7 +76,7 @@ public class BipDatetimeTest { private void testCreate(Date date, String dateStr) { BipDateTime bipDate = new BipDateTime(date); Assert.assertEquals(date, bipDate.getTime()); - Assert.assertTrue(bipDate.isUtc()); + assertThat(bipDate.isUtc()).isTrue(); Assert.assertEquals(dateStr, bipDate.toString()); } @@ -215,10 +217,11 @@ public class BipDatetimeTest { BipDateTime bipDate = new BipDateTime(makeDate(1, 1, 2000, 6, 1, 15, utc)); - Assert.assertTrue(bipDate.equals(bipDate)); + assertThat(bipDate).isEqualTo(bipDate); } @Test + @SuppressLint("TruthIncompatibleType") // That the point of this test public void testEquals_withDifferentClass() { TimeZone utc = TimeZone.getTimeZone("UTC"); utc.setRawOffset(0); @@ -226,7 +229,7 @@ public class BipDatetimeTest { BipDateTime bipDate = new BipDateTime(makeDate(1, 1, 2000, 6, 1, 15, utc)); String notBipDate = "notBipDate"; - Assert.assertFalse(bipDate.equals(notBipDate)); + assertThat(bipDate).isNotEqualTo(notBipDate); } @Test @@ -238,6 +241,6 @@ public class BipDatetimeTest { BipDateTime bipDate = new BipDateTime(date); BipDateTime bipDateEqual = new BipDateTime(date); - Assert.assertTrue(bipDate.equals(bipDateEqual)); + assertThat(bipDate).isEqualTo(bipDateEqual); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java index c0600a4c29..2ffb5a9870 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipEncodingTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; @@ -120,7 +122,7 @@ public class BipEncodingTest { Assert.assertEquals(BipEncoding.USR_XXX, encoding.getType()); Assert.assertEquals("TEST-ENCODING", encoding.getProprietaryEncodingId()); Assert.assertEquals("USR-TEST-ENCODING", encoding.toString()); - Assert.assertFalse(encoding.isAndroidSupported()); + assertThat(encoding.isAndroidSupported()).isFalse(); } @Test @@ -129,7 +131,7 @@ public class BipEncodingTest { Assert.assertEquals(BipEncoding.USR_XXX, encoding.getType()); Assert.assertEquals("", encoding.getProprietaryEncodingId()); Assert.assertEquals("USR-", encoding.toString()); - Assert.assertFalse(encoding.isAndroidSupported()); + assertThat(encoding.isAndroidSupported()).isFalse(); } @Test(expected = ParseException.class) diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java index 658dca4eef..971ec50064 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageDescriptorTest.java @@ -16,6 +16,10 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; + import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; @@ -242,17 +246,18 @@ public class BipImageDescriptorTest { BipImageDescriptor descriptor = builder.build(); - Assert.assertTrue(descriptor.equals(descriptor)); + assertThat(descriptor).isEqualTo(descriptor); } @Test + @SuppressLint("TruthIncompatibleType") // That the point of this test public void testEquals_differentClass() { BipImageDescriptor.Builder builder = new BipImageDescriptor.Builder(); BipImageDescriptor descriptor = builder.build(); String notDescriptor = "notDescriptor"; - Assert.assertFalse(descriptor.equals(notDescriptor)); + assertThat(descriptor).isNotEqualTo(notDescriptor); } @Test @@ -263,6 +268,6 @@ public class BipImageDescriptorTest { BipImageDescriptor descriptor = builder.build(); BipImageDescriptor descriptorEqual = builderEqual.build(); - Assert.assertTrue(descriptor.equals(descriptorEqual)); + assertThat(descriptor).isEqualTo(descriptorEqual); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java index 1eb5c4a5fe..9c8aec1b85 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageFormatTest.java @@ -16,6 +16,10 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; + import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; @@ -312,10 +316,11 @@ public class BipImageFormatTest { BipPixel.createFixed(1280, 1024), -1); - Assert.assertTrue(format.equals(format)); + assertThat(format).isEqualTo(format); } @Test + @SuppressLint("TruthIncompatibleType") // That the point of this test public void testEquals_withDifferentClass() { BipImageFormat format = BipImageFormat.createNative( @@ -324,7 +329,7 @@ public class BipImageFormatTest { -1); String notFormat = "notFormat"; - Assert.assertFalse(format.equals(notFormat)); + assertThat(format).isNotEqualTo(notFormat); } @Test @@ -335,6 +340,6 @@ public class BipImageFormatTest { BipImageFormat format = BipImageFormat.createNative(encoding, pixel, -1); BipImageFormat formatEqual = BipImageFormat.createNative(encoding, pixel, -1); - Assert.assertTrue(format.equals(formatEqual)); + assertThat(format).isEqualTo(formatEqual); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java index c170f69412..c238f914d9 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImagePropertiesTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; @@ -132,7 +134,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(null, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -167,7 +169,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -193,7 +195,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(null, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertFalse(properties.isValid()); + assertThat(properties.isValid()).isFalse(); Assert.assertEquals(xmlString, properties.toString()); Assert.assertEquals(null, properties.serialize()); } @@ -220,7 +222,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(null, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertFalse(properties.isValid()); + assertThat(properties.isValid()).isFalse(); Assert.assertEquals(xmlString, properties.toString()); Assert.assertEquals(null, properties.serialize()); } @@ -246,7 +248,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(null, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -269,7 +271,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -292,7 +294,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -315,7 +317,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } @@ -343,7 +345,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertFalse(properties.isValid()); + assertThat(properties.isValid()).isFalse(); Assert.assertEquals(xmlString, properties.toString()); Assert.assertEquals(null, properties.serialize()); } @@ -362,7 +364,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertFalse(properties.isValid()); + assertThat(properties.isValid()).isFalse(); Assert.assertEquals(null, properties.serialize()); } @@ -448,7 +450,7 @@ public class BipImagePropertiesTest { Assert.assertEquals(IMAGE_HANDLE, properties.getImageHandle()); Assert.assertEquals(VERSION, properties.getVersion()); Assert.assertEquals(FRIENDLY_NAME, properties.getFriendlyName()); - Assert.assertTrue(properties.isValid()); + assertThat(properties.isValid()).isTrue(); Assert.assertEquals(xmlString, properties.toString()); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java index 5f028d1ba3..87ffdc5387 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipImageTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -55,7 +57,7 @@ public class BipImageTest { Bitmap bitmap = BitmapFactory.decodeStream(expectedInputStream); Assert.assertEquals(sImageHandle, image.getImageHandle()); - Assert.assertTrue(bitmap.sameAs(image.getImage())); + assertThat(bitmap.sameAs(image.getImage())).isTrue(); } @Test @@ -69,7 +71,7 @@ public class BipImageTest { Bitmap bitmap = BitmapFactory.decodeStream(expectedInputStream); Assert.assertEquals(sImageHandle, image.getImageHandle()); - Assert.assertTrue(bitmap.sameAs(image.getImage())); + assertThat(bitmap.sameAs(image.getImage())).isTrue(); } @Test @@ -79,7 +81,7 @@ public class BipImageTest { Bitmap bitmap = BitmapFactory.decodeStream(imageInputStream); BipImage image = new BipImage(sImageHandle, bitmap); Assert.assertEquals(sImageHandle, image.getImageHandle()); - Assert.assertTrue(bitmap.sameAs(image.getImage())); + assertThat(bitmap.sameAs(image.getImage())).isTrue(); } @Test @@ -89,6 +91,6 @@ public class BipImageTest { Bitmap bitmap = BitmapFactory.decodeStream(imageInputStream); BipImage image = new BipImage(sImageHandle, bitmap); Assert.assertEquals(sImageHandle, image.getImageHandle()); - Assert.assertTrue(bitmap.sameAs(image.getImage())); + assertThat(bitmap.sameAs(image.getImage())).isTrue(); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java index f77b87c38c..3b5893c1f1 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/bip/BipTransformationTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.avrcpcontroller; +import static com.google.common.truth.Truth.assertThat; + import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; @@ -29,7 +31,7 @@ public class BipTransformationTest { @Test public void testCreateEmpty() { BipTransformation trans = new BipTransformation(); - Assert.assertFalse(trans.supportsAny()); + assertThat(trans.supportsAny()).isFalse(); Assert.assertEquals(null, trans.toString()); } @@ -37,15 +39,15 @@ public class BipTransformationTest { public void testAddTransformation() { BipTransformation trans = new BipTransformation(); trans.addTransformation(BipTransformation.CROP); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("crop", trans.toString()); trans.addTransformation(BipTransformation.STRETCH); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch crop", trans.toString()); } @@ -53,15 +55,15 @@ public class BipTransformationTest { public void testAddExistingTransformation() { BipTransformation trans = new BipTransformation(); trans.addTransformation(BipTransformation.CROP); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("crop", trans.toString()); trans.addTransformation(BipTransformation.CROP); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("crop", trans.toString()); } @@ -82,16 +84,16 @@ public class BipTransformationTest { public void testRemoveOnlyTransformation() { BipTransformation trans = new BipTransformation(); trans.addTransformation(BipTransformation.CROP); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("crop", trans.toString()); trans.removeTransformation(BipTransformation.CROP); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.supportsAny()); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.supportsAny()).isFalse(); Assert.assertEquals(null, trans.toString()); } @@ -100,15 +102,15 @@ public class BipTransformationTest { BipTransformation trans = new BipTransformation(); trans.addTransformation(BipTransformation.CROP); trans.addTransformation(BipTransformation.STRETCH); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch crop", trans.toString()); trans.removeTransformation(BipTransformation.CROP); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch", trans.toString()); } @@ -118,9 +120,9 @@ public class BipTransformationTest { trans.addTransformation(BipTransformation.CROP); trans.addTransformation(BipTransformation.STRETCH); trans.removeTransformation(BipTransformation.UNKNOWN); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch crop", trans.toString()); } @@ -130,135 +132,135 @@ public class BipTransformationTest { trans.addTransformation(BipTransformation.CROP); trans.addTransformation(BipTransformation.STRETCH); trans.removeTransformation(BipTransformation.FILL); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch crop", trans.toString()); } @Test public void testParse_Stretch() { BipTransformation trans = new BipTransformation("stretch"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("stretch", trans.toString()); } @Test public void testParse_Crop() { BipTransformation trans = new BipTransformation("crop"); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("crop", trans.toString()); } @Test public void testParse_Fill() { BipTransformation trans = new BipTransformation("Fill"); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("fill", trans.toString()); } @Test public void testParse_StretchFill() { BipTransformation trans = new BipTransformation("stretch fill"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("stretch fill", trans.toString()); } @Test public void testParse_StretchCrop() { BipTransformation trans = new BipTransformation("stretch crop"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); Assert.assertEquals("stretch crop", trans.toString()); } @Test public void testParse_FillCrop() { BipTransformation trans = new BipTransformation("fill crop"); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); Assert.assertEquals("fill crop", trans.toString()); } @Test public void testParse_StretchFillCrop() { BipTransformation trans = new BipTransformation("stretch fill crop"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @Test public void testParse_CropFill() { BipTransformation trans = new BipTransformation("crop fill"); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); Assert.assertEquals("fill crop", trans.toString()); } @Test public void testParse_CropFillStretch() { BipTransformation trans = new BipTransformation("crop fill stretch"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @Test public void testParse_CropFillStretchWithDuplicates() { BipTransformation trans = new BipTransformation("stretch crop fill fill crop stretch"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @Test public void testCreate_stretch() { BipTransformation trans = new BipTransformation(BipTransformation.STRETCH); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("stretch", trans.toString()); } @Test public void testCreate_fill() { BipTransformation trans = new BipTransformation(BipTransformation.FILL); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("fill", trans.toString()); } @Test public void testCreate_crop() { BipTransformation trans = new BipTransformation(BipTransformation.CROP); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("crop", trans.toString()); } @Test public void testCreate_cropArray() { BipTransformation trans = new BipTransformation(new int[] {BipTransformation.CROP}); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("crop", trans.toString()); } @@ -267,9 +269,9 @@ public class BipTransformationTest { BipTransformation trans = new BipTransformation( new int[] {BipTransformation.STRETCH, BipTransformation.FILL}); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("stretch fill", trans.toString()); } @@ -282,9 +284,9 @@ public class BipTransformationTest { BipTransformation.FILL, BipTransformation.CROP }); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @@ -297,9 +299,9 @@ public class BipTransformationTest { BipTransformation.FILL, BipTransformation.STRETCH }); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @@ -316,36 +318,36 @@ public class BipTransformationTest { @Test public void testParse_badTransformationOnly() { BipTransformation trans = new BipTransformation("bad"); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals(null, trans.toString()); } @Test public void testParse_badTransformationMixedIn() { BipTransformation trans = new BipTransformation("crop fill bad stretch"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("stretch fill crop", trans.toString()); } @Test public void testParse_badTransformationStart() { BipTransformation trans = new BipTransformation("bad crop fill"); - Assert.assertFalse(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertTrue(trans.isSupported(BipTransformation.FILL)); - Assert.assertTrue(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isFalse(); + assertThat(trans.isSupported(BipTransformation.FILL)).isTrue(); + assertThat(trans.isSupported(BipTransformation.CROP)).isTrue(); Assert.assertEquals("fill crop", trans.toString()); } @Test public void testParse_badTransformationEnd() { BipTransformation trans = new BipTransformation("stretch bad"); - Assert.assertTrue(trans.isSupported(BipTransformation.STRETCH)); - Assert.assertFalse(trans.isSupported(BipTransformation.FILL)); - Assert.assertFalse(trans.isSupported(BipTransformation.CROP)); + assertThat(trans.isSupported(BipTransformation.STRETCH)).isTrue(); + assertThat(trans.isSupported(BipTransformation.FILL)).isFalse(); + assertThat(trans.isSupported(BipTransformation.CROP)).isFalse(); Assert.assertEquals("stretch", trans.toString()); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java index 9b242f896d..3561f4e0e7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java @@ -140,7 +140,7 @@ public class BassClientServiceTest { // German language code in ISO 639-3 private static final String TEST_LANGUAGE = "deu"; private static final int TEST_SOURCE_ID = 10; - private static final int TEST_NUM_SOURCES = 2; + private static final int TEST_NUM_SOURCES = 1; private final HashMap<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>(); private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue; @@ -559,6 +559,9 @@ public class BassClientServiceTest { injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 1); } } + + doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice); + doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1); } private void startSearchingForSources() { @@ -1576,6 +1579,9 @@ public class BassClientServiceTest { .getCallbacks() .notifySourceAdded( sm.getDevice(), recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); + mBassClientService + .getCallbacks() + .notifyReceiveStateChanged(sm.getDevice(), recvState.getSourceId(), recvState); TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper()); return recvState; @@ -1945,7 +1951,6 @@ public class BassClientServiceTest { : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, null, (long) 0x00000001); - verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true)); } // Remove broadcast source @@ -1971,6 +1976,8 @@ public class BassClientServiceTest { for (BassClientStateMachine sm : mStateMachines.values()) { injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID); } + + verify(mLeAudioService).activeBroadcastAssistantNotification(eq(false)); } private void verifyRemoveMessageAndInjectSourceRemoval() { @@ -2176,6 +2183,29 @@ public class BassClientServiceTest { } } + @Test + public void testSecondAddSourceWithCapacityGreaterThanOne() { + prepareConnectedDeviceGroup(); + + // Set maximum source capacity to 2 + for (BassClientStateMachine sm : mStateMachines.values()) { + doReturn(2).when(sm).getMaximumSourceCapacity(); + } + + startSearchingForSources(); + onScanResult(mSourceDevice, TEST_BROADCAST_ID); + onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); + BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID); + verifyAddSourceForGroup(meta); + prepareRemoteSourceState(meta, true, true); + + // Add another new broadcast source + onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1); + onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1); + BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1); + verifyAddSourceForGroup(newMeta); + } + /** * Test that after multiple calls to service.addSource() with a group operation flag set, there * are two call to service.removeSource() needed to clear the flag @@ -6250,7 +6280,9 @@ public class BassClientServiceTest { checkAndDispatchTimeout( TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT); - verifyStopBigMonitoringWithUnsync(); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerUnregisterSync(any(), any()); verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); } @@ -6280,7 +6312,9 @@ public class BassClientServiceTest { checkAndDispatchTimeout( TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT); - verifyStopBigMonitoringWithoutUnsync(); + mInOrderMethodProxy + .verify(mMethodProxy, never()) + .periodicAdvertisingManagerUnregisterSync(any(), any()); verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); } @@ -6294,7 +6328,9 @@ public class BassClientServiceTest { sinkUnintentionalWithoutScanning(); checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT); - verifyStopBigMonitoringWithUnsync(); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerUnregisterSync(any(), any()); verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); } @@ -6308,7 +6344,9 @@ public class BassClientServiceTest { sinkUnintentionalDuringScanning(); checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT); - verifyStopBigMonitoringWithoutUnsync(); + mInOrderMethodProxy + .verify(mMethodProxy, never()) + .periodicAdvertisingManagerUnregisterSync(any(), any()); verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); } @@ -6681,7 +6719,9 @@ public class BassClientServiceTest { checkAndDispatchTimeout( TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT); - verifyStopBigMonitoringWithUnsync(); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerUnregisterSync(any(), any()); } @Test 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 b9baeb3ef4..919326767f 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 @@ -49,8 +49,6 @@ import static com.android.bluetooth.bass_client.BassConstants.CLIENT_CHARACTERIS import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -61,8 +59,10 @@ import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -92,6 +92,7 @@ import android.content.Intent; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -118,6 +119,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.hamcrest.MockitoHamcrest; @@ -278,7 +280,7 @@ public class BassClientStateMachineTest { Assert.assertThat( mBassClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class)); - assertNull(mBassClientStateMachine.mBluetoothGatt); + assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); } @Test @@ -304,7 +306,7 @@ public class BassClientStateMachineTest { mBassClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(BassClientStateMachine.Connecting.class)); - assertNotNull(mBassClientStateMachine.mGattCallback); + assertThat(mBassClientStateMachine.mGattCallback).isNotNull(); mBassClientStateMachine.notifyConnectionStateChanged( GATT_SUCCESS, BluetoothProfile.STATE_CONNECTED); @@ -552,9 +554,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void parseScanRecord_withoutBaseData_callCancelActiveSync() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); byte[] scanRecord = new byte[] { 0x02, @@ -601,9 +602,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void parseScanRecord_withBaseData_callsUpdateBase() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); byte[] scanRecordWithBaseData = new byte[] { (byte) 0x02, @@ -759,7 +759,8 @@ public class BassClientStateMachineTest { /** This also tests BassClientStateMachine#processBroadcastReceiverState. */ @Test - public void gattCallbackOnCharacteristicRead() { + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) + public void gattCallbackOnCharacteristicReadObsolete() { mBassClientStateMachine.mShouldHandleMessage = false; mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; @@ -942,7 +943,8 @@ public class BassClientStateMachineTest { } @Test - public void gattCallbackOnCharacteristicChanged() { + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) + public void gattCallbackOnCharacteristicChangedObsolete() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; @@ -974,6 +976,435 @@ public class BassClientStateMachineTest { } @Test + @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) + public void gattCallbackOnCharacteristicRead() { + mBassClientStateMachine.mShouldHandleMessage = false; + mBassClientStateMachine.connectGatt(true); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; + BluetoothGattDescriptor desc = Mockito.mock(BluetoothGattDescriptor.class); + BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); + BluetoothGattCharacteristic characteristic = + Mockito.mock(BluetoothGattCharacteristic.class); + BassClientStateMachine.BluetoothGattTestableWrapper btGatt = + Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); + when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); + when(mBassClientService.getCallbacks()).thenReturn(callbacks); + mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; + + // Characteristic read success with null value + when(characteristic.getValue()).thenReturn(null); + cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + InOrder inOrderCharacteristic = inOrder(characteristic); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic).getValue(); + InOrder inOrderCallbacks = inOrder(callbacks); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + verify(characteristic, never()).getDescriptor(any()); + + // Characteristic read failed and mBluetoothGatt is null. + mBassClientStateMachine.mBluetoothGatt = null; + cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic, never()).getUuid(); + inOrderCharacteristic.verify(characteristic, never()).getValue(); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); + assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(GATT_FAILURE); + mBassClientStateMachine.mMsgWhats.clear(); + + // Characteristic read failed and mBluetoothGatt is not null. + mBassClientStateMachine.mBluetoothGatt = btGatt; + when(characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG)).thenReturn(desc); + cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic, never()).getUuid(); + inOrderCharacteristic.verify(characteristic, never()).getValue(); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + verify(btGatt).setCharacteristicNotification(any(), anyBoolean()); + verify(btGatt).writeDescriptor(desc); + verify(desc).setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + + // Tests for processBroadcastReceiverState + + // Empty value without any previous read/change + when(characteristic.getValue()).thenReturn(new byte[] {}); + cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(4)).getValue(); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + + // Read first time first characteristic + int sourceId = 1; + int instanceId = 1234; + byte[] value = + new byte[] { + (byte) sourceId, // sourceId + (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType + Utils.getByteAddress(mSourceTestDevice)[5], + Utils.getByteAddress(mSourceTestDevice)[4], + Utils.getByteAddress(mSourceTestDevice)[3], + Utils.getByteAddress(mSourceTestDevice)[2], + Utils.getByteAddress(mSourceTestDevice)[1], + Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress + 0x00, // sourceAdvSid + 0x00, + 0x00, + 0x00, // broadcastIdBytes + (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, + (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, + // 16 bytes badBroadcastCode + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, // numSubGroups + // SubGroup #1 + 0x00, + 0x00, + 0x00, + 0x00, // audioSyncIndex + 0x02, // metaDataLength + 0x00, + 0x00, // metadata + }; + when(characteristic.getValue()).thenReturn(value); + when(characteristic.getInstanceId()).thenReturn(instanceId); + cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(4)).getValue(); + ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = + ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Read first time second (last) characteristic + int sourceId2 = 2; + int instanceId2 = 4321; + value[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX] = (byte) sourceId2; + when(characteristic.getInstanceId()).thenReturn(instanceId2); + cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(4)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId2), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + } + + /** This also tests BassClientStateMachine#processBroadcastReceiverState. */ + @Test + @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) + public void gattCallbackOnCharacteristicChanged() { + mBassClientStateMachine.connectGatt(true); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; + mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; + BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); + when(mBassClientService.getCallbacks()).thenReturn(callbacks); + + BluetoothGattCharacteristic characteristic = + Mockito.mock(BluetoothGattCharacteristic.class); + when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); + + // Null value + when(characteristic.getValue()).thenReturn(null); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + InOrder inOrderCharacteristic = inOrder(characteristic); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic).getValue(); + InOrder inOrderCallbacks = inOrder(callbacks); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + + // Empty value without any previous read/change + when(characteristic.getValue()).thenReturn(new byte[] {}); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks, never()) + .notifyReceiveStateChanged(any(), anyInt(), any()); + + // Sync value, first time + int sourceId = 1; + byte[] value = + new byte[] { + (byte) sourceId, // sourceId + (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType + Utils.getByteAddress(mSourceTestDevice)[5], + Utils.getByteAddress(mSourceTestDevice)[4], + Utils.getByteAddress(mSourceTestDevice)[3], + Utils.getByteAddress(mSourceTestDevice)[2], + Utils.getByteAddress(mSourceTestDevice)[1], + Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress + 0x00, // sourceAdvSid + 0x00, + 0x00, + 0x00, // broadcastIdBytes + (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, + (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, + // 16 bytes badBroadcastCode + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, // numSubGroups + // SubGroup #1 + 0x00, + 0x00, + 0x00, + 0x00, // audioSyncIndex + 0x02, // metaDataLength + 0x00, + 0x00, // metadata + }; + when(characteristic.getValue()).thenReturn(value); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = + ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Empty value to indicates removing source from device by remote + when(characteristic.getValue()).thenReturn(new byte[] {}); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceRemoved( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice); + + // Sync value again + mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; + when(characteristic.getValue()).thenReturn(value); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Empty value to indicates removing source from device by local app + mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; + when(characteristic.getValue()).thenReturn(new byte[] {}); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceRemoved( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice); + + // Sync value again + mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; + when(characteristic.getValue()).thenReturn(value); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Empty value to indicates removing source from device by stack (source switch) + BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); + mBassClientStateMachine.mPendingSourceToSwitch = metadata; + when(characteristic.getValue()).thenReturn(new byte[] {}); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceRemoved( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mEmptyTestDevice); + assertThat(mBassClientStateMachine.mMsgWhats).contains(ADD_BCAST_SOURCE); + assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(metadata); + + // Sync value again + mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; + when(characteristic.getValue()).thenReturn(value); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCharacteristic.verify(characteristic).getUuid(); + inOrderCharacteristic.verify(characteristic, times(2)).getValue(); + inOrderCallbacks + .verify(callbacks) + .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Update value - PA SyncInfo Request + value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = + BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST; + PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); + when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())) + .thenReturn(paResult); + int syncHandle = 100; + when(paResult.getSyncHandle()).thenReturn(syncHandle); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCallbacks + .verify(callbacks) + .notifySourceModified( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + int serviceData = 0x000000FF & sourceId; + serviceData = serviceData << 8; + // advA matches EXT_ADV_ADDRESS + // also matches source address (as we would have written) + serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); + serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); + verify(mMethodProxy) + .periodicAdvertisingManagerTransferSync( + any(), any(), eq(serviceData), eq(syncHandle)); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Update value - PA SyncInfo Request, local broadcast + mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); + when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) + .thenReturn(true); + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCallbacks + .verify(callbacks) + .notifySourceModified( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + serviceData = 0x000000FF & sourceId; + serviceData = serviceData << 8; + // Address we set in the Source Address can differ from the address in the air + serviceData = serviceData | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS; + verify(mMethodProxy) + .periodicAdvertisingManagerTransferSetInfo( + any(), any(), eq(serviceData), anyInt(), any()); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Update value - Broadcast Code + value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = + BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED; + value[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX] = + BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED; + mBassClientStateMachine.mSetBroadcastCodePending = true; + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCallbacks + .verify(callbacks) + .notifySourceModified( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + assertThat(mBassClientStateMachine.mMsgWhats).contains(SET_BCAST_CODE); + assertThat(mBassClientStateMachine.mMsgAgr1) + .isEqualTo(BassClientStateMachine.ARGTYPE_RCVSTATE); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + + // Update value - Pending Remove + value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = + BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE; + mBassClientStateMachine.mIsPendingRemove = true; + cb.onCharacteristicChanged(null, characteristic); + TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + inOrderCallbacks + .verify(callbacks) + .notifySourceModified( + any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); + assertThat(mBassClientStateMachine.mMsgWhats).contains(REMOVE_BCAST_SOURCE); + assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(sourceId); + inOrderCallbacks + .verify(callbacks) + .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); + Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice); + } + + @Test public void gattCharacteristicWrite() { mBassClientStateMachine.connectGatt(true); BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; @@ -1131,7 +1562,7 @@ public class BassClientStateMachineTest { mBassClientStateMachine.mBluetoothGatt = btGatt; sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); verify(btGatt).close(); - assertNull(mBassClientStateMachine.mBluetoothGatt); + assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); } @Test @@ -1160,7 +1591,7 @@ public class BassClientStateMachineTest { Message msg = mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, mTestDevice); sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); verify(btGatt).close(); - assertNull(mBassClientStateMachine.mBluetoothGatt); + assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); } @Test @@ -1223,7 +1654,7 @@ public class BassClientStateMachineTest { noneConnectedMsg.obj = BluetoothProfile.STATE_DISCONNECTING; sendMessageAndVerifyTransition(noneConnectedMsg, BassClientStateMachine.Disconnected.class); verify(btGatt).close(); - assertNull(mBassClientStateMachine.mBluetoothGatt); + assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); } @Test @@ -1290,9 +1721,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void sendPsyncActiveMessage_inConnectedState() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); initToConnectedState(); // need this to ensure expected mock behavior for getActiveSyncedSource when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null); @@ -1313,9 +1743,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void sendSelectBcastSourceMessage_inConnectedState() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); initToConnectedState(); byte[] scanRecord = @@ -1394,10 +1823,8 @@ public class BassClientStateMachineTest { } @Test + @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void sendAddBcastSourceMessage_inConnectedState() { - mSetFlagsRule.enableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); - initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); @@ -1430,9 +1857,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void sendReachedMaxSourceLimitMessage_inConnectedState() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); initToConnectedState(); // need this to ensure expected mock behavior for getActiveSyncedSource when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null); @@ -1794,7 +2220,7 @@ public class BassClientStateMachineTest { sendMessageAndVerifyTransition( msgToNoneConnectedState, BassClientStateMachine.Disconnected.class); verify(btGatt).close(); - assertNull(mBassClientStateMachine.mBluetoothGatt); + assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); } /** This also tests BassClientStateMachine#sendPendingCallbacks */ @@ -2030,9 +2456,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void selectBcastSource_withSameBroadcastId() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); final int testSyncHandle = 1; initToConnectedState(); @@ -2128,9 +2553,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void addBcastSource_withCachedScanResults() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); initToConnectedState(); BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); @@ -2239,9 +2663,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void periodicAdvertisingCallbackOnSyncLost_notifySourceLost() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); PeriodicAdvertisingCallback cb = mBassClientStateMachine.mLocalPeriodicAdvCallback; BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); int syncHandle = 1; @@ -2254,9 +2677,8 @@ public class BassClientStateMachineTest { } @Test + @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void periodicAdvertisingCallbackOnBigInfoAdvertisingReport_updateRssi() { - mSetFlagsRule.disableFlags( - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE); PeriodicAdvertisingCallback cb = mBassClientStateMachine.mLocalPeriodicAdvCallback; BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); int testRssi = -40; 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 b5ab6027aa..1d7e3a2c2d 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 @@ -456,6 +456,43 @@ public class ActiveDeviceManagerTest { } @Test + public void switchActiveDeviceFromLeToHfp_noFallbackToLe() { + // Turn off the dual mode audio flag + Utils.setDualModeAudioStateForTesting(false); + + // Connect A2DP + HFP device, set it not active + a2dpConnected(mA2dpHeadsetDevice, true); + headsetConnected(mA2dpHeadsetDevice, true); + a2dpActiveDeviceChanged(null); + headsetActiveDeviceChanged(null); + mTestLooper.dispatchAll(); + + Mockito.clearInvocations(mHeadsetService); + Mockito.clearInvocations(mA2dpService); + Mockito.clearInvocations(mLeAudioService); + + // Connect LE Audio device, set it to inactive + leAudioConnected(mLeAudioDevice); + leAudioActiveDeviceChanged(mLeAudioDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService).setActiveDevice(mLeAudioDevice); + assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isEqualTo(mLeAudioDevice); + + Mockito.clearInvocations(mHeadsetService); + Mockito.clearInvocations(mA2dpService); + Mockito.clearInvocations(mLeAudioService); + + // Set LE Audio device to inactive + // Set A2DP + HFP device to active + leAudioActiveDeviceChanged(null); + headsetActiveDeviceChanged(mA2dpHeadsetDevice); + mTestLooper.dispatchAll(); + // A2DP + HFP should now be active + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice); + } + + @Test public void hfpActivatedAfterTimeout_shouldActivateA2dpAgain() { a2dpConnected(mA2dpHeadsetDevice, true); headsetConnected(mA2dpHeadsetDevice, true); @@ -771,7 +808,7 @@ public class ActiveDeviceManagerTest { // Don't call mA2dpService.setActiveDevice() verify(mA2dpService, never()).setActiveDevice(mA2dpDevice); Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice()); - Assert.assertTrue(mActiveDeviceManager.getHearingAidActiveDevices().isEmpty()); + assertThat(mActiveDeviceManager.getHearingAidActiveDevices()).isEmpty(); } /** A Hearing Aid is connected. Then a Headset active device is explicitly set. */ @@ -790,7 +827,7 @@ public class ActiveDeviceManagerTest { // Don't call mHeadsetService.setActiveDevice() verify(mHeadsetService, never()).setActiveDevice(mHeadsetDevice); Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); - Assert.assertTrue(mActiveDeviceManager.getHearingAidActiveDevices().isEmpty()); + assertThat(mActiveDeviceManager.getHearingAidActiveDevices()).isEmpty(); } @Test @@ -817,6 +854,66 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); } + /** + * LE Audio is connected but is not ready for stream (no available context types). Check if it's + * not used as fallback device from A2DP + */ + @Test + public void leAudioFallbackA2dpToLeaudio_notReadyForStream() { + when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(false); + leAudioConnected(mLeAudioDevice); + a2dpConnected(mA2dpDevice, true); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + verify(mA2dpService).setActiveDevice(mA2dpDevice); + + a2dpDisconnected(mA2dpDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + } + + /** + * LE Audio is connected but is not ready for stream (no available context types). Check if it's + * not used as fallback device from A2DP + */ + @Test + public void leAudioFallbackLeaudioToLeaudio_notReadyForStream() { + /* LeAudio device from group 1 - not ready for stream */ + when(mLeAudioService.getGroupId(mLeAudioDevice)).thenReturn(1); + /* LeAudio device from group 1 - ready for stream */ + when(mLeAudioService.getGroupId(mSecondaryAudioDevice)).thenReturn(2); + when(mLeAudioService.isGroupAvailableForStream(1)).thenReturn(false); + when(mLeAudioService.isGroupAvailableForStream(2)).thenReturn(true); + leAudioConnected(mLeAudioDevice); + leAudioConnected(mSecondaryAudioDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + verify(mLeAudioService).setActiveDevice(mSecondaryAudioDevice); + + leAudioDisconnected(mSecondaryAudioDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + } + + /** + * LE Audio is connected but is not ready for stream (no available context types). Check if it's + * not used as fallback device from ASHA + */ + @Test + public void leAudioFallbackAshaToLeaudio_notReadyForStream() { + when(mLeAudioService.isGroupAvailableForStream(anyInt())).thenReturn(false); + + leAudioConnected(mLeAudioDevice); + hearingAidConnected(mHearingAidDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + verify(mHearingAidService).setActiveDevice(mHearingAidDevice); + + hearingAidDisconnected(mHearingAidDevice); + mTestLooper.dispatchAll(); + verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice); + } + /** Two LE Audio are connected. Should set the second one active. */ @Test public void secondLeAudioConnected_setSecondLeAudioActive() { @@ -1107,7 +1204,7 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService).removeActiveDevice(true); verify(mA2dpService).setActiveDevice(mA2dpDevice); Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice()); - Assert.assertNull(mActiveDeviceManager.getLeAudioActiveDevice()); + assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull(); } /** An LE Audio is connected. Then a Headset active device is explicitly set. */ @@ -1125,7 +1222,7 @@ public class ActiveDeviceManagerTest { verify(mLeAudioService).removeActiveDevice(true); verify(mHeadsetService).setActiveDevice(mHeadsetDevice); Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); - Assert.assertNull(mActiveDeviceManager.getLeAudioActiveDevice()); + assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull(); } /** @@ -1518,9 +1615,9 @@ public class ActiveDeviceManagerTest { mTestLooper.dispatchAll(); // Verify setting LEA active fails when all supported classic audio profiles are not active verify(mLeAudioService).setActiveDevice(mDualModeAudioDevice); - Assert.assertNull(mActiveDeviceManager.getLeAudioActiveDevice()); - Assert.assertNull(mActiveDeviceManager.getA2dpActiveDevice()); - Assert.assertNull(mActiveDeviceManager.getHfpActiveDevice()); + assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isNull(); + assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isNull(); + assertThat(mActiveDeviceManager.getHfpActiveDevice()).isNull(); when(mLeAudioService.setActiveDevice(any())).thenReturn(true); when(mLeAudioService.removeActiveDevice(anyBoolean())).thenReturn(true); 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 a1463b49f5..bac576e7e7 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 @@ -155,6 +155,7 @@ public class AdapterServiceTest { private @Mock AdapterNativeInterface mNativeInterface; private @Mock BluetoothKeystoreNativeInterface mKeystoreNativeInterface; private @Mock BluetoothQualityReportNativeInterface mQualityNativeInterface; + private @Mock BluetoothHciVendorSpecificNativeInterface mHciVendorSpecificNativeInterface; private @Mock SdpManagerNativeInterface mSdpNativeInterface; private @Mock AdvertiseManagerNativeInterface mAdvertiseNativeInterface; private @Mock DistanceMeasurementNativeInterface mDistanceNativeInterface; @@ -226,6 +227,7 @@ public class AdapterServiceTest { AdapterNativeInterface.setInstance(mNativeInterface); BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface); BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface); + BluetoothHciVendorSpecificNativeInterface.setInstance(mHciVendorSpecificNativeInterface); SdpManagerNativeInterface.setInstance(mSdpNativeInterface); AdvertiseManagerNativeInterface.setInstance(mAdvertiseNativeInterface); DistanceMeasurementNativeInterface.setInstance(mDistanceNativeInterface); @@ -354,6 +356,7 @@ public class AdapterServiceTest { AdapterNativeInterface.setInstance(null); BluetoothKeystoreNativeInterface.setInstance(null); BluetoothQualityReportNativeInterface.setInstance(null); + BluetoothHciVendorSpecificNativeInterface.setInstance(null); SdpManagerNativeInterface.setInstance(null); AdvertiseManagerNativeInterface.setInstance(null); DistanceMeasurementNativeInterface.setInstance(null); @@ -979,6 +982,37 @@ public class AdapterServiceTest { } @Test + @EnableFlags(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) + public void testIdentityAddressType() { + RemoteDevices remoteDevices = mAdapterService.getRemoteDevices(); + remoteDevices.addDeviceProperties(Utils.getBytesFromAddress((TEST_BT_ADDR_1))); + + int identityAddressTypePublic = 0x00; // Should map to BluetoothDevice.ADDRESS_TYPE_PUBLIC + int identityAddressTypeRandom = 0x01; // Should map to BluetoothDevice.ADDRESS_TYPE_RANDOM + + remoteDevices.leAddressAssociateCallback( + Utils.getBytesFromAddress(TEST_BT_ADDR_1), + Utils.getBytesFromAddress(TEST_BT_ADDR_2), + identityAddressTypePublic); + + BluetoothDevice.BluetoothAddress bluetoothAddress = + mAdapterService.getIdentityAddressWithType(TEST_BT_ADDR_1); + assertThat(bluetoothAddress.getAddress()).isEqualTo(TEST_BT_ADDR_2); + assertThat(bluetoothAddress.getAddressType()) + .isEqualTo(BluetoothDevice.ADDRESS_TYPE_PUBLIC); + + remoteDevices.leAddressAssociateCallback( + Utils.getBytesFromAddress(TEST_BT_ADDR_1), + Utils.getBytesFromAddress(TEST_BT_ADDR_2), + identityAddressTypeRandom); + + bluetoothAddress = mAdapterService.getIdentityAddressWithType(TEST_BT_ADDR_1); + assertThat(bluetoothAddress.getAddress()).isEqualTo(TEST_BT_ADDR_2); + assertThat(bluetoothAddress.getAddressType()) + .isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM); + } + + @Test @EnableFlags(Flags.FLAG_IDENTITY_ADDRESS_NULL_IF_NOT_KNOWN) public void testIdentityAddressNullIfUnknown() { BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0); diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterSuspendTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterSuspendTest.java index 65bf7560de..f234b25e0f 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterSuspendTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterSuspendTest.java @@ -25,7 +25,7 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import android.content.Context; -import android.hardware.display.DisplayManager; +import android.hardware.devicestate.DeviceStateManager; import android.os.test.TestLooper; import androidx.test.InstrumentationRegistry; @@ -44,7 +44,7 @@ import org.mockito.junit.MockitoRule; @RunWith(AndroidJUnit4.class) public class AdapterSuspendTest { private TestLooper mTestLooper; - private DisplayManager mDisplayManager; + private DeviceStateManager mDeviceStateManager; private AdapterSuspend mAdapterSuspend; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -54,15 +54,15 @@ public class AdapterSuspendTest { public void setUp() throws Exception { Context context = InstrumentationRegistry.getTargetContext(); mTestLooper = new TestLooper(); - mDisplayManager = context.getSystemService(DisplayManager.class); + mDeviceStateManager = context.getSystemService(DeviceStateManager.class); mAdapterSuspend = new AdapterSuspend( - mAdapterNativeInterface, mTestLooper.getLooper(), mDisplayManager); + mAdapterNativeInterface, mTestLooper.getLooper(), mDeviceStateManager); } private void triggerSuspend() throws Exception { - mAdapterSuspend.handleSuspend(); + mAdapterSuspend.handleSuspend(true); } private void triggerResume() throws Exception { diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java index e1dc5a07ce..f24c1ff2aa 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java @@ -17,6 +17,8 @@ package com.android.bluetooth.btservice; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -212,14 +214,14 @@ public class BondStateMachineTest { RemoteDevices.DeviceProperties pendingDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES_2); BluetoothDevice pendingDevice = pendingDeviceProperties.getDevice(); - Assert.assertNotNull(pendingDevice); + assertThat(pendingDevice).isNotNull(); mBondStateMachine.sendIntent(pendingDevice, BOND_BONDED, TEST_BOND_REASON, false); RemoteDevices.DeviceProperties testDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES); testDeviceProperties.mUuids = TEST_UUIDS; BluetoothDevice testDevice = testDeviceProperties.getDevice(); - Assert.assertNotNull(testDevice); + assertThat(testDevice).isNotNull(); Message bondingMsg = mBondStateMachine.obtainMessage(BondStateMachine.BONDING_STATE_CHANGE); bondingMsg.obj = testDevice; @@ -240,7 +242,7 @@ public class BondStateMachineTest { mBondStateMachine.sendMessage(bondedMsg); TestUtils.waitForLooperToFinishScheduledTask(mBondStateMachine.getHandler().getLooper()); - Assert.assertTrue(mBondStateMachine.mPendingBondedDevices.isEmpty()); + assertThat(mBondStateMachine.mPendingBondedDevices).isEmpty(); } private void resetRemoteDevice(int deviceType) { @@ -248,7 +250,7 @@ public class BondStateMachineTest { mRemoteDevices.reset(); mDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES); mDevice = mDeviceProperties.getDevice(); - Assert.assertNotNull(mDevice); + assertThat(mDevice).isNotNull(); mDeviceProperties.mDeviceType = deviceType; mBondStateMachine.mPendingBondedDevices.clear(); } @@ -606,10 +608,12 @@ public class BondStateMachineTest { } if (shouldDelayMessageExist) { - Assert.assertTrue(mBondStateMachine.hasMessage(mBondStateMachine.BONDED_INTENT_DELAY)); + assertThat(mBondStateMachine.hasMessage(mBondStateMachine.BONDED_INTENT_DELAY)) + .isTrue(); mBondStateMachine.removeMessage(mBondStateMachine.BONDED_INTENT_DELAY); } else { - Assert.assertFalse(mBondStateMachine.hasMessage(mBondStateMachine.BONDED_INTENT_DELAY)); + assertThat(mBondStateMachine.hasMessage(mBondStateMachine.BONDED_INTENT_DELAY)) + .isFalse(); } } @@ -734,7 +738,7 @@ public class BondStateMachineTest { } private void verifyBondStateChangeIntent(int oldState, int newState, Intent intent) { - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals(BluetoothDevice.ACTION_BOND_STATE_CHANGED, intent.getAction()); Assert.assertEquals(mDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(newState, intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)); diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/CompanionManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/CompanionManagerTest.java index 7cd13fe4b4..fab8ab02dc 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/CompanionManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/CompanionManagerTest.java @@ -15,6 +15,8 @@ */ package com.android.bluetooth.btservice; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothDevice; @@ -30,7 +32,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -103,13 +104,13 @@ public class CompanionManagerTest { @Test public void testIsCompanionDevice() { loadCompanionInfoHelper(TEST_DEVICE, CompanionManager.COMPANION_TYPE_NONE); - Assert.assertTrue(mCompanionManager.isCompanionDevice(TEST_DEVICE)); + assertThat(mCompanionManager.isCompanionDevice(TEST_DEVICE)).isTrue(); loadCompanionInfoHelper(TEST_DEVICE, CompanionManager.COMPANION_TYPE_PRIMARY); - Assert.assertTrue(mCompanionManager.isCompanionDevice(TEST_DEVICE)); + assertThat(mCompanionManager.isCompanionDevice(TEST_DEVICE)).isTrue(); loadCompanionInfoHelper(TEST_DEVICE, CompanionManager.COMPANION_TYPE_SECONDARY); - Assert.assertTrue(mCompanionManager.isCompanionDevice(TEST_DEVICE)); + assertThat(mCompanionManager.isCompanionDevice(TEST_DEVICE)).isTrue(); } @Test @@ -158,12 +159,12 @@ public class CompanionManagerTest { mCompanionManager.getGattConnParameters( TEST_DEVICE, CompanionManager.GATT_CONN_LATENCY, priority); - Assert.assertTrue(max >= min); - Assert.assertTrue(max >= minInterval); - Assert.assertTrue(min >= minInterval); - Assert.assertTrue(max <= maxInterval); - Assert.assertTrue(min <= maxInterval); - Assert.assertTrue(latency >= minLatency); - Assert.assertTrue(latency <= maxLatency); + assertThat(max).isAtLeast(min); + assertThat(max).isAtLeast(minInterval); + assertThat(min).isAtLeast(minInterval); + assertThat(max).isAtMost(maxInterval); + assertThat(min).isAtMost(maxInterval); + assertThat(latency).isAtLeast(minLatency); + assertThat(latency).isAtMost(maxLatency); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java index dba15c5d1a..aa49e0d0a7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java @@ -15,6 +15,7 @@ */ package com.android.bluetooth.btservice; +import static com.google.common.truth.Truth.assertThat; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -154,10 +155,10 @@ public class MetricsLoggerTest { Assert.assertEquals(2, metricsProto.getProfileConnectionStatsCount()); Map<ProfileId, ProfileConnectionStats> profileConnectionCountMap = getProfileUsageStatsMap(metricsProto.getProfileConnectionStatsList()); - Assert.assertTrue(profileConnectionCountMap.containsKey(ProfileId.AVRCP)); + assertThat(profileConnectionCountMap).containsKey(ProfileId.AVRCP); Assert.assertEquals( 2, profileConnectionCountMap.get(ProfileId.AVRCP).getNumTimesConnected()); - Assert.assertTrue(profileConnectionCountMap.containsKey(ProfileId.HEADSET)); + assertThat(profileConnectionCountMap).containsKey(ProfileId.HEADSET); Assert.assertEquals( 1, profileConnectionCountMap.get(ProfileId.HEADSET).getNumTimesConnected()); // Verify that MetricsLogger's internal state is cleared after a dump @@ -197,15 +198,15 @@ public class MetricsLoggerTest { @Test public void testAddAndSendCountersCornerCases() { - Assert.assertTrue(mTestableMetricsLogger.isInitialized()); + assertThat(mTestableMetricsLogger.isInitialized()).isTrue(); mTestableMetricsLogger.cacheCount(1, -1); mTestableMetricsLogger.cacheCount(3, 0); mTestableMetricsLogger.cacheCount(2, 10); mTestableMetricsLogger.cacheCount(2, Long.MAX_VALUE - 8L); mTestableMetricsLogger.drainBufferedCounters(); - Assert.assertFalse(mTestableMetricsLogger.mTestableCounters.containsKey(1)); - Assert.assertFalse(mTestableMetricsLogger.mTestableCounters.containsKey(3)); + assertThat(mTestableMetricsLogger.mTestableCounters).doesNotContainKey(1); + assertThat(mTestableMetricsLogger.mTestableCounters).doesNotContainKey(3); Assert.assertEquals( Long.MAX_VALUE, mTestableMetricsLogger.mTestableCounters.get(2).longValue()); } @@ -225,20 +226,20 @@ public class MetricsLoggerTest { @Test public void testMetricsLoggerNotInit() { mTestableMetricsLogger.close(); - Assert.assertFalse(mTestableMetricsLogger.cacheCount(1, 1)); + assertThat(mTestableMetricsLogger.cacheCount(1, 1)).isFalse(); mTestableMetricsLogger.drainBufferedCounters(); - Assert.assertFalse(mTestableMetricsLogger.mTestableCounters.containsKey(1)); + assertThat(mTestableMetricsLogger.mTestableCounters).doesNotContainKey(1); } @Test public void testAddAndSendCountersDoubleInit() { - Assert.assertTrue(mTestableMetricsLogger.isInitialized()); + assertThat(mTestableMetricsLogger.isInitialized()).isTrue(); // sending a null adapterService will crash in case the double init no longer works mTestableMetricsLogger.init(null, mRemoteDevices); } @Test - public void testDeviceNameToSha() { + public void testDeviceNameToSha() throws IOException { initTestingBloomfilter(); for (Map.Entry<String, String> entry : SANITIZED_DEVICE_NAME_MAP.entrySet()) { String deviceName = entry.getKey(); @@ -265,7 +266,7 @@ public class MetricsLoggerTest { Assert.assertEquals(bluetoothRemoteDeviceInformation.getOui(), oui); } catch (InvalidProtocolBufferException e) { - Assert.assertNull(e.getMessage()); // test failure here + assertThat(e.getMessage()).isNull(); // test failure here } } @@ -292,21 +293,17 @@ public class MetricsLoggerTest { } @Test - public void uploadEmptyDeviceName() { + public void uploadEmptyDeviceName() throws IOException { initTestingBloomfilter(); Assert.assertEquals("", mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, "")); } - private void initTestingBloomfilter() { + private void initTestingBloomfilter() throws IOException { byte[] bloomfilterData = DeviceBloomfilterGenerator.hexStringToByteArray( DeviceBloomfilterGenerator.BLOOM_FILTER_DEFAULT); - try { - mTestableMetricsLogger.setBloomfilter( - BloomFilter.readFrom( - new ByteArrayInputStream(bloomfilterData), Funnels.byteArrayFunnel())); - } catch (IOException e) { - Assert.assertTrue(false); - } + mTestableMetricsLogger.setBloomfilter( + BloomFilter.readFrom( + new ByteArrayInputStream(bloomfilterData), Funnels.byteArrayFunnel())); } } 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 7372288fac..2e7225e74b 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,8 @@ package com.android.bluetooth.btservice; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -32,12 +34,10 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; -import com.android.bluetooth.a2dp.A2dpNativeInterface; import com.android.bluetooth.a2dpsink.A2dpSinkNativeInterface; import com.android.bluetooth.avrcp.AvrcpNativeInterface; import com.android.bluetooth.avrcpcontroller.AvrcpControllerNativeInterface; import com.android.bluetooth.btservice.storage.DatabaseManager; -import com.android.bluetooth.csip.CsipSetCoordinatorNativeInterface; import com.android.bluetooth.hearingaid.HearingAidNativeInterface; import com.android.bluetooth.hfp.HeadsetNativeInterface; import com.android.bluetooth.hfpclient.NativeInterface; @@ -81,7 +81,6 @@ public class ProfileServiceTest { private int[] mProfiles; - @Mock private A2dpNativeInterface mA2dpNativeInterface; @Mock private A2dpSinkNativeInterface mA2dpSinkNativeInterface; @Mock private AvrcpNativeInterface mAvrcpNativeInterface; @Mock private AvrcpControllerNativeInterface mAvrcpControllerNativeInterface; @@ -91,7 +90,6 @@ public class ProfileServiceTest { @Mock private HidDeviceNativeInterface mHidDeviceNativeInterface; @Mock private HidHostNativeInterface mHidHostNativeInterface; @Mock private PanNativeInterface mPanNativeInterface; - @Mock private CsipSetCoordinatorNativeInterface mCsipSetCoordinatorInterface; @Mock private LeAudioNativeInterface mLeAudioInterface; private void setProfileState(int profile, int state) { @@ -134,7 +132,6 @@ public class ProfileServiceTest { if (Looper.myLooper() == null) { Looper.prepare(); } - Assert.assertNotNull(Looper.myLooper()); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); doNothing().when(mAdapterService).addProfile(any()); @@ -156,13 +153,14 @@ public class ProfileServiceTest { profile -> profile != BluetoothProfile.HAP_CLIENT && profile != BluetoothProfile.VOLUME_CONTROL - && profile != BluetoothProfile.GATT) + && profile != BluetoothProfile.CSIP_SET_COORDINATOR + && profile != BluetoothProfile.GATT + && profile != BluetoothProfile.A2DP) .toArray(); TestUtils.setAdapterService(mAdapterService); - Assert.assertNotNull(AdapterService.getAdapterService()); + assertThat(AdapterService.getAdapterService()).isNotNull(); - A2dpNativeInterface.setInstance(mA2dpNativeInterface); A2dpSinkNativeInterface.setInstance(mA2dpSinkNativeInterface); AvrcpNativeInterface.setInstance(mAvrcpNativeInterface); AvrcpControllerNativeInterface.setInstance(mAvrcpControllerNativeInterface); @@ -172,7 +170,6 @@ public class ProfileServiceTest { HidDeviceNativeInterface.setInstance(mHidDeviceNativeInterface); HidHostNativeInterface.setInstance(mHidHostNativeInterface); PanNativeInterface.setInstance(mPanNativeInterface); - CsipSetCoordinatorNativeInterface.setInstance(mCsipSetCoordinatorInterface); LeAudioNativeInterface.setInstance(mLeAudioInterface); } @@ -182,7 +179,6 @@ public class ProfileServiceTest { TestUtils.clearAdapterService(mAdapterService); mAdapterService = null; mProfiles = null; - A2dpNativeInterface.setInstance(null); A2dpSinkNativeInterface.setInstance(null); AvrcpNativeInterface.setInstance(null); AvrcpControllerNativeInterface.setInstance(null); @@ -192,7 +188,6 @@ public class ProfileServiceTest { HidDeviceNativeInterface.setInstance(null); HidHostNativeInterface.setInstance(null); PanNativeInterface.setInstance(null); - CsipSetCoordinatorNativeInterface.setInstance(null); LeAudioNativeInterface.setInstance(null); } @@ -243,9 +238,9 @@ public class ProfileServiceTest { List<ProfileService> stoppedArguments = stops.getAllValues(); Assert.assertEquals(startedArguments.size(), stoppedArguments.size()); for (ProfileService service : startedArguments) { - Assert.assertTrue(stoppedArguments.contains(service)); + assertThat(stoppedArguments).contains(service); stoppedArguments.remove(service); - Assert.assertFalse(stoppedArguments.contains(service)); + assertThat(stoppedArguments).doesNotContain(service); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java index 845a53b77c..28747da680 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java @@ -2,6 +2,8 @@ package com.android.bluetooth.btservice; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -99,7 +101,7 @@ public class RemoteDevicesTest { // Verify that a handler message is sent by the method call mRemoteDevices.updateUuids(mDevice1); Message msg = mTestLooperManager.next(); - Assert.assertNotNull(msg); + assertThat(msg).isNotNull(); // Verify that executing that message results in a direct call and broadcast intent mTestLooperManager.execute(msg); @@ -113,7 +115,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -124,7 +126,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -153,14 +155,14 @@ public class RemoteDevicesTest { int batteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating with invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); verify(mAdapterService, never()).sendBroadcast(any(), anyString(), any()); // Verify that device property stays null after invalid update - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); verifyNoMoreInteractions(mAdapterService); } @@ -170,14 +172,14 @@ public class RemoteDevicesTest { int batteryLevel = 101; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating invalid battery level does not trigger the intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); verify(mAdapterService, never()).sendBroadcast(any(), anyString(), any()); // Verify that device property stays null after invalid update - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); verifyNoMoreInteractions(mAdapterService); } @@ -185,11 +187,11 @@ public class RemoteDevicesTest { @Test public void testResetBatteryLevel_testResetBeforeUpdate() { // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that resetting battery level keeps device property null mRemoteDevices.resetBatteryLevel(mDevice1, /* fromBas= */ false); - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); verifyNoMoreInteractions(mAdapterService); } @@ -199,7 +201,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -210,7 +212,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -225,7 +227,7 @@ public class RemoteDevicesTest { mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), BluetoothDevice.BATTERY_LEVEL_UNKNOWN); @@ -250,7 +252,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -261,7 +263,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -279,7 +281,7 @@ public class RemoteDevicesTest { mDevice1, BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mIntentArgument); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), BluetoothDevice.BATTERY_LEVEL_UNKNOWN); @@ -300,10 +302,10 @@ public class RemoteDevicesTest { int batteryLevel = 10; BatteryService oldBatteryService = setBatteryServiceForTesting(mDevice1); - Assert.assertTrue(mRemoteDevices.hasBatteryService(mDevice1)); + assertThat(mRemoteDevices.hasBatteryService(mDevice1)).isTrue(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -314,7 +316,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -324,7 +326,7 @@ public class RemoteDevicesTest { BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED); - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); @@ -340,7 +342,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -351,7 +353,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); @@ -381,7 +383,7 @@ public class RemoteDevicesTest { BluetoothDevice.ACTION_ACL_DISCONNECTED, mIntentArgument.getValue().getAction()); Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); @@ -400,7 +402,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level mRemoteDevices.onHfIndicatorValueChanged( @@ -417,19 +419,19 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level mRemoteDevices.onHfIndicatorValueChanged(mDevice1, batteryLevel, 3); verify(mAdapterService, never()).sendBroadcast(any(), anyString()); // Verify that device property is still null after invalid update - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); } @Test public void testOnVendorSpecificHeadsetEvent_testCorrectPlantronicsXEvent() { // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level mRemoteDevices.onVendorSpecificHeadsetEvent( @@ -448,7 +450,7 @@ public class RemoteDevicesTest { @Test public void testOnVendorSpecificHeadsetEvent_testCorrectAppleBatteryVsc() { // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level mRemoteDevices.onVendorSpecificHeadsetEvent( @@ -577,7 +579,7 @@ public class RemoteDevicesTest { int batteryLevel = 10; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -588,7 +590,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -608,7 +610,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify value is reset in properties - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), BluetoothDevice.BATTERY_LEVEL_UNKNOWN); @@ -629,10 +631,10 @@ public class RemoteDevicesTest { int batteryLevel = 10; BatteryService oldBatteryService = setBatteryServiceForTesting(mDevice1); - Assert.assertTrue(mRemoteDevices.hasBatteryService(mDevice1)); + assertThat(mRemoteDevices.hasBatteryService(mDevice1)).isTrue(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -643,7 +645,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -653,7 +655,7 @@ public class RemoteDevicesTest { BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED); - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( batteryLevel, mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel()); @@ -668,10 +670,10 @@ public class RemoteDevicesTest { int batteryLevel2 = 20; BatteryService oldBatteryService = setBatteryServiceForTesting(mDevice1); - Assert.assertTrue(mRemoteDevices.hasBatteryService(mDevice1)); + assertThat(mRemoteDevices.hasBatteryService(mDevice1)).isTrue(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -682,7 +684,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -712,10 +714,10 @@ public class RemoteDevicesTest { int batteryLevel = 10; BatteryService oldBatteryService = setBatteryServiceForTesting(mDevice1); - Assert.assertTrue(mRemoteDevices.hasBatteryService(mDevice1)); + assertThat(mRemoteDevices.hasBatteryService(mDevice1)).isTrue(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that updating battery level triggers ACTION_BATTERY_LEVEL_CHANGED intent mRemoteDevices.updateBatteryLevel(mDevice1, batteryLevel, /* fromBas= */ false); @@ -726,7 +728,7 @@ public class RemoteDevicesTest { Assert.assertEquals(BLUETOOTH_CONNECT, mStringArgument.getValue()); // Verify that user can get battery level after the update - Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNotNull(); Assert.assertEquals( mRemoteDevices.getDeviceProperties(mDevice1).getBatteryLevel(), batteryLevel); @@ -750,7 +752,7 @@ public class RemoteDevicesTest { int batteryLevel = 3; // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); // Verify that ACTION_AG_EVENT intent updates battery level mRemoteDevices.onAgBatteryLevelChanged(mDevice1, batteryLevel); @@ -768,7 +770,7 @@ public class RemoteDevicesTest { @EnableFlags(Flags.FLAG_FIX_ADD_DEVICE_PROPERTIES) public void testMultipleAddDeviceProperties() { // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); DeviceProperties prop1 = mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1)); DeviceProperties prop2 = @@ -779,7 +781,7 @@ public class RemoteDevicesTest { @Test public void testSetgetHfAudioPolicyForRemoteAg() { // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1)); @@ -793,7 +795,7 @@ public class RemoteDevicesTest { .build(); deviceProp.setHfAudioPolicyForRemoteAg(policies); - // Verify that the audio policy properties are set and get propperly + // Verify that the audio policy properties are set and get properly Assert.assertEquals( policies, mRemoteDevices.getDeviceProperties(mDevice1).getHfAudioPolicyForRemoteAg()); @@ -806,13 +808,13 @@ public class RemoteDevicesTest { .getSupportedProfilesBitMask(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1)); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(mDevice1); deviceProp.setIsCoordinatedSetMember(true); - Assert.assertTrue(deviceProp.isCoordinatedSetMember()); + assertThat(deviceProp.isCoordinatedSetMember()).isTrue(); } @Test @@ -822,18 +824,18 @@ public class RemoteDevicesTest { .getSupportedProfilesBitMask(); // Verify that device property is null initially - Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1)); + assertThat(mRemoteDevices.getDeviceProperties(mDevice1)).isNull(); mRemoteDevices.addDeviceProperties(Utils.getBytesFromAddress(TEST_BT_ADDR_1)); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(mDevice1); deviceProp.setIsCoordinatedSetMember(true); - Assert.assertFalse(deviceProp.isCoordinatedSetMember()); + assertThat(deviceProp.isCoordinatedSetMember()).isFalse(); } @Test public void testIsDeviceNull() { - Assert.assertNull(mRemoteDevices.getDeviceProperties(null)); + assertThat(mRemoteDevices.getDeviceProperties(null)).isNull(); } private static void verifyBatteryLevelChangedIntent( diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java index a880b5ce13..c616c90aa7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java @@ -18,6 +18,8 @@ package com.android.bluetooth.btservice; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -109,7 +111,7 @@ public class SilenceDeviceManagerTest { // Set pre-state for mSilenceDeviceManager if (wasSilenced) { - Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, true)); + assertThat(mSilenceDeviceManager.setSilenceMode(mTestDevice, true)).isTrue(); TestUtils.waitForLooperToFinishScheduledTask(mLooper); verify(mAdapterService, times(++mVerifyCount)) .sendBroadcastAsUser( @@ -118,7 +120,7 @@ public class SilenceDeviceManagerTest { } // Set silence state and check whether state changed successfully - Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)); + assertThat(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)).isTrue(); TestUtils.waitForLooperToFinishScheduledTask(mLooper); Assert.assertEquals(enableSilence, mSilenceDeviceManager.getSilenceMode(mTestDevice)); @@ -135,7 +137,7 @@ public class SilenceDeviceManagerTest { a2dpDisconnected(mTestDevice); headsetDisconnected(mTestDevice); - Assert.assertFalse(mSilenceDeviceManager.getSilenceMode(mTestDevice)); + assertThat(mSilenceDeviceManager.getSilenceMode(mTestDevice)).isFalse(); if (enableSilence) { // If the silence mode is enabled, it should be automatically disabled // after device is disconnected. @@ -149,9 +151,9 @@ public class SilenceDeviceManagerTest { void testSetGetDeviceSilenceDisconnectedCase(boolean enableSilence) { ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); // Set silence mode and it should stay disabled - Assert.assertTrue(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)); + assertThat(mSilenceDeviceManager.setSilenceMode(mTestDevice, enableSilence)).isTrue(); TestUtils.waitForLooperToFinishScheduledTask(mLooper); - Assert.assertFalse(mSilenceDeviceManager.getSilenceMode(mTestDevice)); + assertThat(mSilenceDeviceManager.getSilenceMode(mTestDevice)).isFalse(); // Should be no intent been broadcasted verify(mAdapterService, times(mVerifyCount)) 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 276db29dda..668743ad31 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 @@ -16,12 +16,13 @@ package com.android.bluetooth.btservice.bluetoothkeystore; +import static com.google.common.truth.Truth.assertThat; + import android.os.Binder; import android.os.Process; import android.util.Log; import org.junit.After; -import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; @@ -98,7 +99,6 @@ public final class BluetoothKeystoreServiceTest { public void setUp() { Assume.assumeTrue("Ignore test when the user is not primary.", isPrimaryUser()); mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, true); - Assert.assertNotNull(mBluetoothKeystoreService); // backup origin config data. try { mConfigData = Files.readAllLines(Paths.get(CONFIG_FILE_PATH)); @@ -203,10 +203,13 @@ public final class BluetoothKeystoreServiceTest { // over write config overwriteConfigFile(mConfigTestData); // load config file. - Assert.assertTrue(parseConfigFile(CONFIG_FILE_PATH)); + assertThat(parseConfigFile(CONFIG_FILE_PATH)).isTrue(); // make sure it is same with createNameDecryptKeyResult - Assert.assertTrue( - doCompareMap(mNameDecryptKeyResult, mBluetoothKeystoreService.getNameDecryptKey())); + assertThat( + doCompareMap( + mNameDecryptKeyResult, + mBluetoothKeystoreService.getNameDecryptKey())) + .isTrue(); } @Test @@ -216,9 +219,11 @@ public final class BluetoothKeystoreServiceTest { // Wait for encryption to complete mBluetoothKeystoreService.stopThread(); - Assert.assertTrue( - doCompareKeySet( - mNameDecryptKeyResult, mBluetoothKeystoreService.getNameEncryptKey())); + assertThat( + doCompareKeySet( + mNameDecryptKeyResult, + mBluetoothKeystoreService.getNameEncryptKey())) + .isTrue(); } @Test @@ -229,24 +234,27 @@ public final class BluetoothKeystoreServiceTest { // clear up memory. mBluetoothKeystoreService.cleanupMemory(); // load encryption file and do encryption. - Assert.assertTrue(loadEncryptionFile(CONFIG_FILE_ENCRYPTION_PATH, true)); + assertThat(loadEncryptionFile(CONFIG_FILE_ENCRYPTION_PATH, true)).isTrue(); // Wait for encryption to complete mBluetoothKeystoreService.stopThread(); - Assert.assertTrue( - doCompareMap(mNameDecryptKeyResult, mBluetoothKeystoreService.getNameDecryptKey())); + assertThat( + doCompareMap( + mNameDecryptKeyResult, + mBluetoothKeystoreService.getNameDecryptKey())) + .isTrue(); } @Test public void testCompareHashFile() { // save config checksum. - Assert.assertTrue(setEncryptKeyOrRemoveKey(CONFIG_FILE_PREFIX, CONFIG_FILE_HASH)); + assertThat(setEncryptKeyOrRemoveKey(CONFIG_FILE_PREFIX, CONFIG_FILE_HASH)).isTrue(); // clean up memory mBluetoothKeystoreService.cleanupMemory(); - Assert.assertTrue(loadEncryptionFile(CONFIG_CHECKSUM_ENCRYPTION_PATH, false)); + assertThat(loadEncryptionFile(CONFIG_CHECKSUM_ENCRYPTION_PATH, false)).isTrue(); - Assert.assertTrue(compareFileHash(CONFIG_FILE_PATH)); + assertThat(compareFileHash(CONFIG_FILE_PATH)).isTrue(); } @Test @@ -255,25 +263,27 @@ public final class BluetoothKeystoreServiceTest { // need to creat encrypted file. testParserFile(); // created encrypted file - Assert.assertTrue(setEncryptKeyOrRemoveKey(CONFIG_FILE_PREFIX, CONFIG_FILE_HASH)); + assertThat(setEncryptKeyOrRemoveKey(CONFIG_FILE_PREFIX, CONFIG_FILE_HASH)).isTrue(); // clean up memory and stop thread. mBluetoothKeystoreService.cleanupForCommonCriteriaModeEnable(); // new mBluetoothKeystoreService and the Common Criteria mode is false. mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, false); - Assert.assertNotNull(mBluetoothKeystoreService); mBluetoothKeystoreService.loadConfigData(); // check encryption file clean up. - Assert.assertFalse(Files.exists(Paths.get(CONFIG_CHECKSUM_ENCRYPTION_PATH))); - Assert.assertFalse(Files.exists(Paths.get(CONFIG_FILE_ENCRYPTION_PATH))); + assertThat(Files.exists(Paths.get(CONFIG_CHECKSUM_ENCRYPTION_PATH))).isFalse(); + assertThat(Files.exists(Paths.get(CONFIG_FILE_ENCRYPTION_PATH))).isFalse(); // remove hash data avoid interfering result. mBluetoothKeystoreService.getNameDecryptKey().remove(CONFIG_FILE_PREFIX); - Assert.assertTrue( - doCompareMap(mNameDecryptKeyResult, mBluetoothKeystoreService.getNameDecryptKey())); + assertThat( + doCompareMap( + mNameDecryptKeyResult, + mBluetoothKeystoreService.getNameDecryptKey())) + .isTrue(); } @Test @@ -285,6 +295,6 @@ public final class BluetoothKeystoreServiceTest { mBluetoothKeystoreService = new BluetoothKeystoreService(mMockNativeInterface, true); mBluetoothKeystoreService.loadConfigData(); - Assert.assertTrue(mBluetoothKeystoreService.getCompareResult() == 0); + assertThat(mBluetoothKeystoreService.getCompareResult()).isEqualTo(0); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java index 5f1b8e0f97..bcaccfd94f 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/storage/DatabaseManagerTest.java @@ -16,7 +16,8 @@ package com.android.bluetooth.btservice.storage; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; @@ -50,8 +51,6 @@ import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.flags.Flags; -import com.google.common.truth.Truth; - import org.hamcrest.CoreMatchers; import org.junit.After; import org.junit.Assert; @@ -167,7 +166,7 @@ public final class DatabaseManagerTest { mDatabaseManager.getA2dpOptionalCodecsEnabled(mTestDevice)); for (int id = 0; id < MAX_META_ID; id++) { - Assert.assertNull(mDatabaseManager.getCustomMeta(mTestDevice, id)); + assertThat(mDatabaseManager.getCustomMeta(mTestDevice, id)).isNull(); } mDatabaseManager.factoryReset(); @@ -524,16 +523,16 @@ public final class DatabaseManagerTest { mSetFlagsRule.disableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE); // Verify pre-conditions to ensure a fresh test Assert.assertEquals(0, mDatabaseManager.mMetadataCache.size()); - Assert.assertNotNull(mTestDevice); - Assert.assertNotNull(mTestDevice2); - Assert.assertNull(mDatabaseManager.getMostRecentlyActiveHfpDevice()); + assertThat(mTestDevice).isNotNull(); + assertThat(mTestDevice2).isNotNull(); + assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isNull(); // Set the first device's connection mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.HEADSET); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice); + assertThat(mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice) + .isTrue(); List<BluetoothDevice> mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyActiveHfpDevice()); @@ -544,10 +543,10 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice2, BluetoothProfile.HEADSET); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()).isActiveHfpDevice); + assertThat(mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice) + .isFalse(); + assertThat(mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()).isActiveHfpDevice) + .isTrue(); Assert.assertEquals(mTestDevice2, mDatabaseManager.getMostRecentlyActiveHfpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size()); @@ -558,9 +557,9 @@ public final class DatabaseManagerTest { mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.HEADSET); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice); - Assert.assertNotNull(mDatabaseManager.getMostRecentlyActiveHfpDevice()); + assertThat(mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()).isActiveHfpDevice) + .isFalse(); + assertThat(mDatabaseManager.getMostRecentlyActiveHfpDevice()).isNotNull(); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size()); Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(1)); @@ -577,17 +576,18 @@ public final class DatabaseManagerTest { mSetFlagsRule.disableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE); // Verify pre-conditions to ensure a fresh test Assert.assertEquals(0, mDatabaseManager.mMetadataCache.size()); - Assert.assertNotNull(mTestDevice); - Assert.assertNotNull(mTestDevice2); - Assert.assertNull(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); + assertThat(mTestDevice).isNotNull(); + assertThat(mTestDevice2).isNotNull(); + assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull(); // Set the first device's connection mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isTrue(); List<BluetoothDevice> mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); @@ -598,12 +598,14 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice2, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isTrue(); Assert.assertEquals(mTestDevice2, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size()); @@ -614,12 +616,14 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isTrue(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size()); @@ -630,13 +634,15 @@ public final class DatabaseManagerTest { mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertNull(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull(); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(2, mostRecentlyConnectedDevicesOrdered.size()); Assert.assertEquals(mTestDevice, mostRecentlyConnectedDevicesOrdered.get(0)); @@ -646,16 +652,19 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice3, BluetoothProfile.HEADSET); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); - Assert.assertNull(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull(); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0)); @@ -666,15 +675,18 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isTrue(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); @@ -686,15 +698,18 @@ public final class DatabaseManagerTest { mDatabaseManager.setConnection(mTestDevice3, BluetoothProfile.HEADSET); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isTrue(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); @@ -706,15 +721,18 @@ public final class DatabaseManagerTest { mDatabaseManager.setDisconnection(mTestDevice2, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertTrue( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isTrue(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); Assert.assertEquals(mTestDevice, mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); @@ -726,16 +744,19 @@ public final class DatabaseManagerTest { mDatabaseManager.setDisconnection(mTestDevice, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); - Assert.assertNull(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull(); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0)); @@ -746,16 +767,19 @@ public final class DatabaseManagerTest { mDatabaseManager.setDisconnection(mTestDevice3, BluetoothProfile.A2DP); // Wait for database update TestUtils.waitForLooperToFinishScheduledTask(mDatabaseManager.getHandlerLooper()); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) - .is_active_a2dp_device); - Assert.assertFalse( - mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) - .is_active_a2dp_device); - Assert.assertNull(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice2.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat( + mDatabaseManager.mMetadataCache.get(mTestDevice3.getAddress()) + .is_active_a2dp_device) + .isFalse(); + assertThat(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).isNull(); mostRecentlyConnectedDevicesOrdered = mDatabaseManager.getMostRecentlyConnectedDevices(); Assert.assertEquals(3, mostRecentlyConnectedDevicesOrdered.size()); Assert.assertEquals(mTestDevice3, mostRecentlyConnectedDevicesOrdered.get(0)); @@ -859,7 +883,7 @@ public final class DatabaseManagerTest { device.put("unthethered_left_charging", testString); device.put("unthethered_right_charging", testString); device.put("unthethered_case_charging", testString); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -986,7 +1010,7 @@ public final class DatabaseManagerTest { device.put("untethered_left_charging", testString); device.put("untethered_right_charging", testString); device.put("untethered_case_charging", testString); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1132,7 +1156,7 @@ public final class DatabaseManagerTest { device.put("untethered_left_charging", testString); device.put("untethered_right_charging", testString); device.put("untethered_case_charging", testString); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1201,7 +1225,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1229,7 +1253,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1256,7 +1280,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 107 to 108 @@ -1280,7 +1304,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 108 to 109 @@ -1304,7 +1328,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 109 to 110 @@ -1328,7 +1352,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 111 to 112 @@ -1352,7 +1376,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 112 to 113 @@ -1378,7 +1402,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 113 to 114 @@ -1402,7 +1426,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1432,7 +1456,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1459,7 +1483,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 116 to 117 @@ -1485,7 +1509,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 117 to 118 @@ -1509,7 +1533,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); @@ -1534,7 +1558,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 119 to 120 @@ -1558,7 +1582,7 @@ public final class DatabaseManagerTest { ContentValues device = new ContentValues(); device.put("address", TEST_BT_ADDR); device.put("migrated", false); - assertThat( + Assert.assertThat( db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device), CoreMatchers.not(-1)); // Migrate database from 120 to 121 @@ -1577,27 +1601,27 @@ public final class DatabaseManagerTest { /** Helper function to check whether the database has the expected column */ void assertHasColumn(Cursor cursor, String columnName, boolean hasColumn) { if (hasColumn) { - assertThat(cursor.getColumnIndex(columnName), CoreMatchers.not(-1)); + Assert.assertThat(cursor.getColumnIndex(columnName), CoreMatchers.not(-1)); } else { - assertThat(cursor.getColumnIndex(columnName), CoreMatchers.is(-1)); + Assert.assertThat(cursor.getColumnIndex(columnName), CoreMatchers.is(-1)); } } /** Helper function to check whether the database has the expected value */ void assertColumnIntData(Cursor cursor, String columnName, int value) { - assertThat(cursor.getInt(cursor.getColumnIndex(columnName)), CoreMatchers.is(value)); + Assert.assertThat(cursor.getInt(cursor.getColumnIndex(columnName)), CoreMatchers.is(value)); } /** Helper function to check whether the column data type is BLOB */ void assertColumnBlob(Cursor cursor, String columnName) { - assertThat( + Assert.assertThat( cursor.getType(cursor.getColumnIndex(columnName)), CoreMatchers.is(Cursor.FIELD_TYPE_BLOB)); } /** Helper function to check the BLOB data in a column is expected */ void assertColumnBlobData(Cursor cursor, String columnName, byte[] data) { - assertThat(cursor.getBlob(cursor.getColumnIndex(columnName)), CoreMatchers.is(data)); + Assert.assertThat(cursor.getBlob(cursor.getColumnIndex(columnName)), CoreMatchers.is(data)); } void restartDatabaseManagerHelper() { @@ -1725,7 +1749,7 @@ public final class DatabaseManagerTest { verify(mAdapterService, times(verifyTime)).onMetadataChanged(mTestDevice, key, value); Assert.assertEquals(value, mDatabaseManager.getCustomMeta(mTestDevice, key)); } else { - Assert.assertNull(mDatabaseManager.getCustomMeta(mTestDevice, key)); + assertThat(mDatabaseManager.getCustomMeta(mTestDevice, key)).isNull(); return; } // Wait for database update @@ -1758,7 +1782,7 @@ public final class DatabaseManagerTest { // Check for callback and get value Assert.assertEquals(policy, mDatabaseManager.getAudioPolicyMetadata(mTestDevice)); } else { - Assert.assertNull(mDatabaseManager.getAudioPolicyMetadata(mTestDevice)); + assertThat(mDatabaseManager.getAudioPolicyMetadata(mTestDevice)).isNull(); return; } // Wait for database update @@ -1796,8 +1820,8 @@ public final class DatabaseManagerTest { mDatabaseManager.setPreferredAudioProfiles(groupDevices, preferencesToSet)); Bundle testDevicePreferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice); Bundle testDevice2Preferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice2); - Assert.assertNotNull(testDevicePreferences); - Assert.assertNotNull(testDevice2Preferences); + assertThat(testDevicePreferences).isNotNull(); + assertThat(testDevice2Preferences).isNotNull(); Assert.assertEquals( expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY), @@ -1825,8 +1849,8 @@ public final class DatabaseManagerTest { restartDatabaseManagerHelper(); testDevicePreferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice); testDevice2Preferences = mDatabaseManager.getPreferredAudioProfiles(mTestDevice2); - Assert.assertNotNull(testDevicePreferences); - Assert.assertNotNull(testDevice2Preferences); + assertThat(testDevicePreferences).isNotNull(); + assertThat(testDevice2Preferences).isNotNull(); Assert.assertEquals( expectedPreferences.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY), @@ -1868,6 +1892,6 @@ public final class DatabaseManagerTest { mDatabaseManager.setCustomMeta(mTestDevice, key, newValue); - Truth.assertThat(future.get()).isEqualTo(newValue); + assertThat(future.get()).isEqualTo(newValue); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java index 77bb8b522c..f9e5ab429c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java @@ -17,20 +17,46 @@ package com.android.bluetooth.csip; -import static org.mockito.Mockito.*; - -import android.bluetooth.*; +import static android.bluetooth.BluetoothCsipSetCoordinator.ACTION_CSIS_DEVICE_AVAILABLE; +import static android.bluetooth.BluetoothDevice.BOND_BONDED; +import static android.bluetooth.BluetoothDevice.BOND_BONDING; +import static android.bluetooth.BluetoothDevice.BOND_NONE; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE; +import static android.bluetooth.BluetoothProfile.EXTRA_STATE; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothCsipSetCoordinator; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothUuid; -import android.content.BroadcastReceiver; -import android.content.Context; +import android.bluetooth.IBluetoothCsipSetCoordinator; +import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback; import android.content.Intent; -import android.content.IntentFilter; -import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.test.TestLooper; import androidx.test.filters.MediumTest; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; @@ -40,521 +66,289 @@ import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.le_audio.LeAudioService; +import org.hamcrest.Matcher; +import org.hamcrest.core.AllOf; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Spy; +import org.mockito.hamcrest.MockitoHamcrest; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.HashMap; import java.util.List; import java.util.UUID; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeoutException; @MediumTest @RunWith(AndroidJUnit4.class) public class CsipSetCoordinatorServiceTest { - private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false"); - - private Context mTargetContext; - private BluetoothAdapter mAdapter; - private BluetoothDevice mTestDevice; - private BluetoothDevice mTestDevice2; - private BluetoothDevice mTestDevice3; - private CsipSetCoordinatorService mService; - private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue; - private BroadcastReceiver mCsipSetCoordinatorIntentReceiver; - private static final int TIMEOUT_MS = 1000; - @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Spy private ServiceFactory mServiceFactory = new ServiceFactory(); @Mock private AdapterService mAdapterService; @Mock private LeAudioService mLeAudioService; - @Spy private ServiceFactory mServiceFactory = new ServiceFactory(); @Mock private DatabaseManager mDatabaseManager; - @Mock private CsipSetCoordinatorNativeInterface mCsipSetCoordinatorNativeInterface; + @Mock private CsipSetCoordinatorNativeInterface mNativeInterface; @Mock private IBluetoothCsipSetCoordinatorLockCallback mCsipSetCoordinatorLockCallback; - @Before - public void setUp() throws Exception { - if (!mFlagDexmarker.equals("true")) { - System.setProperty("dexmaker.share_classloader", "true"); - } + // private final Context mTargetContext = + // InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); + private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 0); + private final BluetoothDevice mDevice2 = TestUtils.getTestDevice(mAdapter, 1); + private final BluetoothDevice mDevice3 = TestUtils.getTestDevice(mAdapter, 2); - mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - if (Looper.myLooper() == null) { - Looper.prepare(); - } - Assert.assertNotNull(Looper.myLooper()); + private CsipSetCoordinatorService mService; + private InOrder mInOrder; + private TestLooper mLooper; + private final CsipSetCoordinatorNativeInterface mNativeCallback = + new CsipSetCoordinatorNativeInterface(); - TestUtils.setAdapterService(mAdapterService); + @Before + public void setUp() throws Exception { doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); - - mAdapter = BluetoothAdapter.getDefaultAdapter(); - - CsipSetCoordinatorNativeInterface.setInstance(mCsipSetCoordinatorNativeInterface); - startService(); - mService.mServiceFactory = mServiceFactory; - when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService); - - // Override the timeout value to speed up the test - CsipSetCoordinatorStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s - - IntentFilter filter = new IntentFilter(); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - filter.addAction(BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothCsipSetCoordinator.ACTION_CSIS_DEVICE_AVAILABLE); - filter.addAction(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE); - - mCsipSetCoordinatorIntentReceiver = new CsipSetCoordinatorIntentReceiver(); - mTargetContext.registerReceiver(mCsipSetCoordinatorIntentReceiver, filter); - - mTestDevice = TestUtils.getTestDevice(mAdapter, 0); - when(mCsipSetCoordinatorNativeInterface.getDevice(getByteAddress(mTestDevice))) - .thenReturn(mTestDevice); - mTestDevice2 = TestUtils.getTestDevice(mAdapter, 1); - when(mCsipSetCoordinatorNativeInterface.getDevice(getByteAddress(mTestDevice2))) - .thenReturn(mTestDevice2); - mTestDevice3 = TestUtils.getTestDevice(mAdapter, 2); - when(mCsipSetCoordinatorNativeInterface.getDevice(getByteAddress(mTestDevice3))) - .thenReturn(mTestDevice3); - - doReturn(BluetoothDevice.BOND_BONDED) - .when(mAdapterService) - .getBondState(any(BluetoothDevice.class)); + doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService).getBondState(any()); doReturn(new ParcelUuid[] {BluetoothUuid.COORDINATED_SET}) .when(mAdapterService) - .getRemoteUuids(any(BluetoothDevice.class)); - - mIntentQueue = new HashMap<>(); - mIntentQueue.put(mTestDevice, new LinkedBlockingQueue<>()); - mIntentQueue.put(mTestDevice2, new LinkedBlockingQueue<>()); - mIntentQueue.put(mTestDevice3, new LinkedBlockingQueue<>()); - } - - @After - public void tearDown() throws Exception { - if (!mFlagDexmarker.equals("true")) { - System.setProperty("dexmaker.share_classloader", mFlagDexmarker); - } + .getRemoteUuids(any()); - if (Looper.myLooper() == null) { - return; - } + doReturn(CONNECTION_POLICY_ALLOWED) + .when(mDatabaseManager) + .getProfileConnectionPolicy(any(), anyInt()); + doReturn(true).when(mNativeInterface).connect(any()); + doReturn(true).when(mNativeInterface).disconnect(any()); + doReturn(new ParcelUuid[] {BluetoothUuid.COORDINATED_SET}) + .when(mAdapterService) + .getRemoteUuids(any()); - if (mService == null) { - return; - } + doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService(); - stopService(); - CsipSetCoordinatorNativeInterface.setInstance(null); - mTargetContext.unregisterReceiver(mCsipSetCoordinatorIntentReceiver); - TestUtils.clearAdapterService(mAdapterService); - mIntentQueue.clear(); - } + mInOrder = inOrder(mAdapterService); + mLooper = new TestLooper(); - private void startService() throws TimeoutException { - mService = new CsipSetCoordinatorService(mTargetContext); - mService.start(); + mService = + new CsipSetCoordinatorService( + mAdapterService, mLooper.getLooper(), mNativeInterface, mServiceFactory); mService.setAvailable(true); - } - private void stopService() throws TimeoutException { - mService.stop(); - mService = CsipSetCoordinatorService.getCsipSetCoordinatorService(); - Assert.assertNull(mService); } - /** Test getting CsipSetCoordinator Service */ - @Test - public void testGetService() { - Assert.assertEquals(mService, CsipSetCoordinatorService.getCsipSetCoordinatorService()); + @After + public void tearDown() throws Exception { + mService.stop(); + assertThat(CsipSetCoordinatorService.getCsipSetCoordinatorService()).isNull(); } - /** Test stop CsipSetCoordinator Service */ @Test - public void testStopService() { - Assert.assertEquals(mService, CsipSetCoordinatorService.getCsipSetCoordinatorService()); - - InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::stop); - InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::start); + public void getService() { + assertThat(CsipSetCoordinatorService.getCsipSetCoordinatorService()).isEqualTo(mService); } - /** Test get/set policy for BluetoothDevice */ @Test - public void testGetSetPolicy() { - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); - Assert.assertEquals( - "Initial device policy", - BluetoothProfile.CONNECTION_POLICY_UNKNOWN, - mService.getConnectionPolicy(mTestDevice)); - - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); - Assert.assertEquals( - "Setting device policy to POLICY_FORBIDDEN", - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, - mService.getConnectionPolicy(mTestDevice)); - - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - Assert.assertEquals( - "Setting device policy to POLICY_ALLOWED", - BluetoothProfile.CONNECTION_POLICY_ALLOWED, - mService.getConnectionPolicy(mTestDevice)); + public void getConnectionPolicy() { + for (int policy : + List.of( + CONNECTION_POLICY_UNKNOWN, + CONNECTION_POLICY_FORBIDDEN, + CONNECTION_POLICY_ALLOWED)) { + doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt()); + assertThat(mService.getConnectionPolicy(mDevice)).isEqualTo(policy); + } } - /** Test if getProfileConnectionPolicy works after the service is stopped. */ @Test - public void testGetPolicyAfterStopped() { - mService.stop(); - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); - Assert.assertEquals( - "Initial device policy", - BluetoothProfile.CONNECTION_POLICY_UNKNOWN, - mService.getConnectionPolicy(mTestDevice)); + public void canConnect_whenNotBonded_returnFalse() { + int badPolicyValue = 1024; + int badBondState = 42; + for (int bondState : List.of(BOND_NONE, BOND_BONDING, badBondState)) { + for (int policy : + List.of( + CONNECTION_POLICY_UNKNOWN, + CONNECTION_POLICY_FORBIDDEN, + CONNECTION_POLICY_ALLOWED, + badPolicyValue)) { + doReturn(bondState).when(mAdapterService).getBondState(any()); + doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt()); + assertThat(mService.okToConnect(mDevice)).isEqualTo(false); + } + } } - /** Test okToConnect method using various test cases */ @Test - public void testOkToConnect() { + public void canConnect_whenBonded() { int badPolicyValue = 1024; - int badBondState = 42; - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_NONE, - BluetoothProfile.CONNECTION_POLICY_UNKNOWN, - false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_NONE, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, - false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_NONE, - BluetoothProfile.CONNECTION_POLICY_ALLOWED, - false); - testOkToConnectCase(mTestDevice, BluetoothDevice.BOND_NONE, badPolicyValue, false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDING, - BluetoothProfile.CONNECTION_POLICY_UNKNOWN, - false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDING, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, - false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDING, - BluetoothProfile.CONNECTION_POLICY_ALLOWED, - false); - testOkToConnectCase(mTestDevice, BluetoothDevice.BOND_BONDING, badPolicyValue, false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDED, - BluetoothProfile.CONNECTION_POLICY_UNKNOWN, - true); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDED, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, - false); - testOkToConnectCase( - mTestDevice, - BluetoothDevice.BOND_BONDED, - BluetoothProfile.CONNECTION_POLICY_ALLOWED, - true); - testOkToConnectCase(mTestDevice, BluetoothDevice.BOND_BONDED, badPolicyValue, false); - testOkToConnectCase( - mTestDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); - testOkToConnectCase( - mTestDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); - testOkToConnectCase( - mTestDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); - testOkToConnectCase(mTestDevice, badBondState, badPolicyValue, false); + doReturn(BOND_BONDED).when(mAdapterService).getBondState(any()); + + for (int policy : List.of(CONNECTION_POLICY_FORBIDDEN, badPolicyValue)) { + doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt()); + assertThat(mService.okToConnect(mDevice)).isEqualTo(false); + } + for (int policy : List.of(CONNECTION_POLICY_UNKNOWN, CONNECTION_POLICY_ALLOWED)) { + doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt()); + assertThat(mService.okToConnect(mDevice)).isEqualTo(true); + } } /** Test that call to groupLockSet method calls corresponding native interface method */ @Test - public void testGroupLockSetNative() { + public void testGroupLockSetNative() throws RemoteException { int group_id = 0x01; int group_size = 0x01; long uuidLsb = 0x01; long uuidMsb = 0x01; - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 1, uuidLsb, uuidMsb); - Assert.assertFalse(mService.isGroupLocked(group_id)); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 1, uuidLsb, uuidMsb); + assertThat(mService.isGroupLocked(group_id)).isFalse(); UUID lock_uuid = mService.lockGroup(group_id, mCsipSetCoordinatorLockCallback); - Assert.assertNotNull(lock_uuid); - verify(mCsipSetCoordinatorNativeInterface).groupLockSet(eq(group_id), eq(true)); - Assert.assertTrue(mService.isGroupLocked(group_id)); - - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onGroupLockChanged(anyInt(), anyBoolean(), anyInt()); - mCsipSetCoordinatorNativeInterface.onGroupLockChanged( + assertThat(lock_uuid).isNotNull(); + verify(mNativeInterface).groupLockSet(eq(group_id), eq(true)); + assertThat(mService.isGroupLocked(group_id)).isTrue(); + + mNativeCallback.onGroupLockChanged( group_id, true, IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_SUCCESS); - try { - verify(mCsipSetCoordinatorLockCallback) - .onGroupLockSet(group_id, BluetoothStatusCodes.SUCCESS, true); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + verify(mCsipSetCoordinatorLockCallback) + .onGroupLockSet(group_id, BluetoothStatusCodes.SUCCESS, true); mService.unlockGroup(lock_uuid); - verify(mCsipSetCoordinatorNativeInterface).groupLockSet(eq(group_id), eq(false)); + verify(mNativeInterface).groupLockSet(eq(group_id), eq(false)); - mCsipSetCoordinatorNativeInterface.onGroupLockChanged( + mNativeCallback.onGroupLockChanged( group_id, false, IBluetoothCsipSetCoordinator.CSIS_GROUP_LOCK_SUCCESS); - Assert.assertFalse(mService.isGroupLocked(group_id)); + assertThat(mService.isGroupLocked(group_id)).isFalse(); - try { - verify(mCsipSetCoordinatorLockCallback) - .onGroupLockSet(group_id, BluetoothStatusCodes.SUCCESS, false); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + verify(mCsipSetCoordinatorLockCallback) + .onGroupLockSet(group_id, BluetoothStatusCodes.SUCCESS, false); } /** Test that call to groupLockSet method calls corresponding native interface method */ @Test - public void testGroupExclusiveLockSet() { + public void testGroupExclusiveLockSet() throws RemoteException { int group_id = 0x01; int group_size = 0x01; long uuidLsb = 0x01; long uuidMsb = 0x01; - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 1, uuidLsb, uuidMsb); - Assert.assertFalse(mService.isGroupLocked(group_id)); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 1, uuidLsb, uuidMsb); + assertThat(mService.isGroupLocked(group_id)).isFalse(); UUID lock_uuid = mService.lockGroup(group_id, mCsipSetCoordinatorLockCallback); - verify(mCsipSetCoordinatorNativeInterface).groupLockSet(eq(group_id), eq(true)); - Assert.assertNotNull(lock_uuid); - Assert.assertTrue(mService.isGroupLocked(group_id)); + verify(mNativeInterface).groupLockSet(eq(group_id), eq(true)); + assertThat(lock_uuid).isNotNull(); + assertThat(mService.isGroupLocked(group_id)).isTrue(); lock_uuid = mService.lockGroup(group_id, mCsipSetCoordinatorLockCallback); - verify(mCsipSetCoordinatorNativeInterface).groupLockSet(eq(group_id), eq(true)); - - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onGroupLockChanged(anyInt(), anyBoolean(), anyInt()); - - try { - verify(mCsipSetCoordinatorLockCallback) - .onGroupLockSet( - group_id, BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER, true); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - Assert.assertNull(lock_uuid); + verify(mNativeInterface).groupLockSet(eq(group_id), eq(true)); + + verify(mCsipSetCoordinatorLockCallback) + .onGroupLockSet( + group_id, BluetoothStatusCodes.ERROR_CSIP_GROUP_LOCKED_BY_OTHER, true); + assertThat(lock_uuid).isNull(); } - /** Test that an outgoing connection to device that does not have MICS UUID is rejected */ @Test - public void testOutgoingConnectMissingUuid() { - // Update the device policy so okToConnect() returns true - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - - // Return No UUID + public void connectToDevice_whenUuidIsMissing_returnFalse() { doReturn(new ParcelUuid[] {}) .when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); - // Send a connect request - Assert.assertFalse("Connect expected to fail", mService.connect(mTestDevice)); + assertThat(mService.connect(mDevice)).isFalse(); } - /** Test that an outgoing connection to device that have MICS UUID is successful */ @Test - public void testOutgoingConnectExistingUuid() { - // Update the device policy so okToConnect() returns true - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - doReturn(true) - .when(mCsipSetCoordinatorNativeInterface) - .disconnect(any(BluetoothDevice.class)); - - doReturn(new ParcelUuid[] {BluetoothUuid.COORDINATED_SET}) - .when(mAdapterService) - .getRemoteUuids(any(BluetoothDevice.class)); + public void connectToDevice_whenPolicyForbid_returnFalse() { + doReturn(CONNECTION_POLICY_FORBIDDEN) + .when(mDatabaseManager) + .getProfileConnectionPolicy(any(), anyInt()); - // Send a connect request - Assert.assertTrue("Connect expected to succeed", mService.connect(mTestDevice)); - - TestUtils.waitForIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice)); + assertThat(mService.connect(mDevice)).isFalse(); } - /** Test that an outgoing connection to device with POLICY_FORBIDDEN is rejected */ @Test - public void testOutgoingConnectPolicyForbidden() { - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - doReturn(true) - .when(mCsipSetCoordinatorNativeInterface) - .disconnect(any(BluetoothDevice.class)); - - // Set the device policy to POLICY_FORBIDDEN so connect() should fail - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); - - // Send a connect request - Assert.assertFalse("Connect expected to fail", mService.connect(mTestDevice)); - } + public void outgoingConnect_whenTimeOut_isDisconnected() { + assertThat(mService.connect(mDevice)).isTrue(); + mLooper.dispatchAll(); - /** Test that an outgoing connection times out */ - @Test - public void testOutgoingConnectTimeout() { - // Update the device policy so okToConnect() returns true - when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - doReturn(true) - .when(mCsipSetCoordinatorNativeInterface) - .disconnect(any(BluetoothDevice.class)); - - // Send a connect request - Assert.assertTrue("Connect failed", mService.connect(mTestDevice)); - - // Verify the connection state broadcast, and that we are in Connecting state - verifyConnectionStateIntent( - TIMEOUT_MS, - mTestDevice, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTED); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mTestDevice)); - - // Verify the connection state broadcast, and that we are in Disconnected state - verifyConnectionStateIntent( - CsipSetCoordinatorStateMachine.sConnectTimeoutMs * 2, - mTestDevice, - BluetoothProfile.STATE_DISCONNECTED, - BluetoothProfile.STATE_CONNECTING); - Assert.assertEquals( - BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mTestDevice)); + verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED); + + mLooper.moveTimeForward(CsipSetCoordinatorStateMachine.sConnectTimeoutMs); + mLooper.dispatchAll(); + + verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING); } - /** Test that native callback generates proper intent. */ @Test - public void testStackEventDeviceAvailable() { + public void deviceAvailable_withDifferentRank_areOrdered() { int group_id = 0x01; int group_size = 0x03; long uuidLsb = 0x01; long uuidMsb = 0x01; UUID uuid = new UUID(uuidMsb, uuidLsb); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); - - Intent intent = TestUtils.waitForIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice)); - Assert.assertNotNull(intent); - Assert.assertEquals( - BluetoothCsipSetCoordinator.ACTION_CSIS_DEVICE_AVAILABLE, intent.getAction()); - Assert.assertEquals(mTestDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); - Assert.assertEquals( - group_id, intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, -1)); - Assert.assertEquals( - group_size, - intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, -1)); - Assert.assertEquals( - uuid, - intent.getSerializableExtra( - BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID)); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); + + verifyOrderedIntentSent( + hasAction(ACTION_CSIS_DEVICE_AVAILABLE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, group_id), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, group_size), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, uuid)); // Another device with the highest rank - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice2), group_id, group_size, 0x01, uuidLsb, uuidMsb); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice2), group_id, group_size, 0x01, uuidLsb, uuidMsb); + verifyOrderedIntentSent( + hasAction(ACTION_CSIS_DEVICE_AVAILABLE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice2), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, group_id), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, group_size), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, uuid)); // Yet another device with the lowest rank - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice3), group_id, group_size, 0x03, uuidLsb, uuidMsb); - - // Verify if the list of devices is sorted, with the lowest rank value devices first - List<BluetoothDevice> devices = mService.getGroupDevicesOrdered(group_id); - Assert.assertEquals(0, devices.indexOf(mTestDevice2)); - Assert.assertEquals(1, devices.indexOf(mTestDevice)); - Assert.assertEquals(2, devices.indexOf(mTestDevice3)); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice3), group_id, group_size, 0x03, uuidLsb, uuidMsb); + verifyOrderedIntentSent( + hasAction(ACTION_CSIS_DEVICE_AVAILABLE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice3), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, group_id), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_SIZE, group_size), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_TYPE_UUID, uuid)); + + assertThat(mService.getGroupDevicesOrdered(group_id)) + .containsExactly(mDevice2, mDevice, mDevice3) + .inOrder(); } /** Test that native callback generates proper intent after group connected. */ @Test - public void testStackEventSetMemberAvailableAfterGroupConnected() { + public void nativeCallback_afterGroupConnected_generateIntent() { int group_id = 0x01; int group_size = 0x02; long uuidLsb = BluetoothUuid.CAP.getUuid().getLeastSignificantBits(); long uuidMsb = BluetoothUuid.CAP.getUuid().getMostSignificantBits(); - // Make sure to use real methods when needed below - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onConnectionStateChanged(any(byte[].class), anyInt()); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onSetMemberAvailable(any(byte[].class), anyInt()); - - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); - mCsipSetCoordinatorNativeInterface.onConnectionStateChanged( - getByteAddress(mTestDevice), BluetoothProfile.STATE_CONNECTED); + mNativeCallback.onConnectionStateChanged(getByteAddress(mDevice), STATE_CONNECTED); // Comes from state machine - mService.connectionStateChanged( - mTestDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); - - mCsipSetCoordinatorNativeInterface.onSetMemberAvailable( - getByteAddress(mTestDevice2), group_id); - - Intent intent = TestUtils.waitForIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice2)); - Assert.assertNotNull(intent); - Assert.assertEquals( - BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE, intent.getAction()); - Assert.assertEquals(mTestDevice2, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); - Assert.assertEquals( - group_id, intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, -1)); + mService.connectionStateChanged(mDevice, STATE_CONNECTING, STATE_CONNECTED); + + mNativeCallback.onSetMemberAvailable(getByteAddress(mDevice2), group_id); + + verifyOrderedIntentSent( + hasAction(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice2), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, group_id)); } /** Test that native callback generates proper intent before group connected. */ @@ -565,42 +359,24 @@ public class CsipSetCoordinatorServiceTest { long uuidLsb = BluetoothUuid.CAP.getUuid().getLeastSignificantBits(); long uuidMsb = BluetoothUuid.CAP.getUuid().getMostSignificantBits(); - // Make sure to use real methods when needed below - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onSetMemberAvailable(any(byte[].class), anyInt()); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onConnectionStateChanged(any(byte[].class), anyInt()); - - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); + verifyOrderedIntentSent(hasAction(ACTION_CSIS_DEVICE_AVAILABLE)); - mCsipSetCoordinatorNativeInterface.onConnectionStateChanged( - getByteAddress(mTestDevice), BluetoothProfile.STATE_CONNECTED); + mNativeCallback.onConnectionStateChanged(getByteAddress(mDevice), STATE_CONNECTED); + // verifyConnectionStateIntent(STATE_CONNECTED, STATE_DISCONNECTED); - mCsipSetCoordinatorNativeInterface.onSetMemberAvailable( - getByteAddress(mTestDevice2), group_id); + mNativeCallback.onSetMemberAvailable(getByteAddress(mDevice2), group_id); - Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice2)); - Assert.assertNull(intent); + mInOrder.verify(mAdapterService, never()).sendOrderedBroadcast(any(), any()); // Comes from state machine - mService.connectionStateChanged( - mTestDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); + mService.connectionStateChanged(mDevice, STATE_CONNECTING, STATE_CONNECTED); - intent = TestUtils.waitForIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice2)); - Assert.assertNotNull(intent); - - Assert.assertEquals( - BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE, intent.getAction()); - Assert.assertEquals(mTestDevice2, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); - Assert.assertEquals( - group_id, intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, -1)); + verifyOrderedIntentSent( + hasAction(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice2), + hasExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, group_id)); } /** @@ -614,125 +390,76 @@ public class CsipSetCoordinatorServiceTest { long uuidLsb = BluetoothUuid.CAP.getUuid().getLeastSignificantBits(); long uuidMsb = BluetoothUuid.CAP.getUuid().getMostSignificantBits(); - doCallRealMethod() - .when(mCsipSetCoordinatorNativeInterface) - .onDeviceAvailable( - any(byte[].class), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()); - when(mLeAudioService.getConnectionPolicy(any())) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + doReturn(CONNECTION_POLICY_FORBIDDEN).when(mLeAudioService).getConnectionPolicy(any()); // Make first set device available and connected - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); - mService.connectionStateChanged( - mTestDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice), group_id, group_size, 0x02, uuidLsb, uuidMsb); + mService.connectionStateChanged(mDevice, STATE_CONNECTING, STATE_CONNECTED); // Another device with the highest rank - mCsipSetCoordinatorNativeInterface.onDeviceAvailable( - getByteAddress(mTestDevice2), group_id, group_size, 0x01, uuidLsb, uuidMsb); + mNativeCallback.onDeviceAvailable( + getByteAddress(mDevice2), group_id, group_size, 0x01, uuidLsb, uuidMsb); // When LEA is FORBIDDEN, verify we don't disable CSIP until all set devices are available verify(mDatabaseManager, never()) .setProfileConnectionPolicy( - mTestDevice, + mDevice, BluetoothProfile.CSIP_SET_COORDINATOR, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager, never()) .setProfileConnectionPolicy( - mTestDevice2, + mDevice2, BluetoothProfile.CSIP_SET_COORDINATOR, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + CONNECTION_POLICY_FORBIDDEN); // Mark the second device as connected - mService.connectionStateChanged( - mTestDevice2, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); + mService.connectionStateChanged(mDevice2, STATE_CONNECTING, STATE_CONNECTED); // When LEA is FORBIDDEN, verify we disable CSIP once all set devices are available verify(mDatabaseManager) .setProfileConnectionPolicy( - mTestDevice, + mDevice, BluetoothProfile.CSIP_SET_COORDINATOR, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + CONNECTION_POLICY_FORBIDDEN); verify(mDatabaseManager) .setProfileConnectionPolicy( - mTestDevice2, + mDevice2, BluetoothProfile.CSIP_SET_COORDINATOR, - BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + CONNECTION_POLICY_FORBIDDEN); } @Test public void testDump_doesNotCrash() { - // Update the device policy so okToConnect() returns true - when(mDatabaseManager.getProfileConnectionPolicy( - mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class)); - doReturn(true) - .when(mCsipSetCoordinatorNativeInterface) - .disconnect(any(BluetoothDevice.class)); - doReturn(new ParcelUuid[] {BluetoothUuid.COORDINATED_SET}) - .when(mAdapterService) - .getRemoteUuids(any(BluetoothDevice.class)); // add state machines for testing dump() - mService.connect(mTestDevice); - - TestUtils.waitForIntent(TIMEOUT_MS, mIntentQueue.get(mTestDevice)); + mService.connect(mDevice); mService.dump(new StringBuilder()); } - /** Helper function to test ConnectionStateIntent() method */ - private void verifyConnectionStateIntent( - int timeoutMs, BluetoothDevice device, int newState, int prevState) { - Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue.get(device)); - Assert.assertNotNull(intent); - Assert.assertEquals( - BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED, - intent.getAction()); - Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); - Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - Assert.assertEquals( - prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)); - } - - /** Helper function to test okToConnect() method */ - private void testOkToConnectCase( - BluetoothDevice device, int bondState, int policy, boolean expected) { - doReturn(bondState).when(mAdapterService).getBondState(device); - when(mDatabaseManager.getProfileConnectionPolicy( - device, BluetoothProfile.CSIP_SET_COORDINATOR)) - .thenReturn(policy); - Assert.assertEquals(expected, mService.okToConnect(device)); + private void verifyConnectionStateIntent(int newState, int prevState) { + verifyIntentSent( + hasAction(BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice), + hasExtra(EXTRA_STATE, newState), + hasExtra(EXTRA_PREVIOUS_STATE, prevState)); + assertThat(mService.getConnectionState(mDevice)).isEqualTo(newState); } /** Helper function to get byte array for a device address */ private byte[] getByteAddress(BluetoothDevice device) { - if (device == null) { - return Utils.getBytesFromAddress("00:00:00:00:00:00"); - } return Utils.getBytesFromAddress(device.getAddress()); } - private class CsipSetCoordinatorIntentReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - try { - /* Ignore intent when service is inactive */ - if (mService == null) { - return; - } - - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - // Use first device's queue in case of no device in the intent - if (device == null) { - device = mTestDevice; - } - LinkedBlockingQueue<Intent> queue = mIntentQueue.get(device); - Assert.assertNotNull(queue); - queue.put(intent); - } catch (InterruptedException e) { - Assert.fail("Cannot add Intent to the queue: " + e.getMessage()); - } - } + @SafeVarargs + private void verifyIntentSent(Matcher<Intent>... matchers) { + mInOrder.verify(mAdapterService) + .sendBroadcast(MockitoHamcrest.argThat(AllOf.allOf(matchers)), any()); + } + + @SafeVarargs + private void verifyOrderedIntentSent(Matcher<Intent>... matchers) { + mInOrder.verify(mAdapterService) + .sendOrderedBroadcast(MockitoHamcrest.argThat(AllOf.allOf(matchers)), any()); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java index 3890c8d15b..8502c240d1 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java @@ -22,6 +22,8 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -277,10 +279,10 @@ public class CsipSetCoordinatorStateMachineTest { @Test public void testIsConnected() { - Assert.assertFalse(mStateMachine.isConnected()); + assertThat(mStateMachine.isConnected()).isFalse(); initToConnectedState(); - Assert.assertTrue(mStateMachine.isConnected()); + assertThat(mStateMachine.isConnected()).isTrue(); } @Test @@ -396,9 +398,10 @@ public class CsipSetCoordinatorStateMachineTest { initToConnectingState(); mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT); TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertTrue( - mStateMachine.doesSuperHaveDeferredMessages( - CsipSetCoordinatorStateMachine.CONNECT)); + assertThat( + mStateMachine.doesSuperHaveDeferredMessages( + CsipSetCoordinatorStateMachine.CONNECT)) + .isTrue(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java index 0d9f7bee9e..2b34af7690 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.after; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothAdapter; @@ -67,12 +68,15 @@ public class DistanceMeasurementManagerTest { private static final String IDENTITY_ADDRESS = "00:01:02:03:04:05"; private static final int RSSI_FREQUENCY_LOW = 3000; + private static final int CS_FREQUENCY_LOW = 5000; @Before public void setUp() throws Exception { doReturn(mPackageManager).when(mAdapterService).getPackageManager(); doReturn(true).when(mPackageManager).hasSystemFeature(any()); + doReturn(true).when(mAdapterService).isLeChannelSoundingSupported(); doReturn(IDENTITY_ADDRESS).when(mAdapterService).getIdentityAddress(IDENTITY_ADDRESS); + doReturn(true).when(mAdapterService).isConnected(any()); DistanceMeasurementNativeInterface.setInstance(mDistanceMeasurementNativeInterface); mDistanceMeasurementManager = new DistanceMeasurementManager(mAdapterService); mUuid = UUID.randomUUID(); @@ -160,6 +164,67 @@ public class DistanceMeasurementManagerTest { } @Test + public void testCsStartFailForNoBondedBLE() throws RemoteException { + doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService).getBondState(any()); + DistanceMeasurementParams params = + new DistanceMeasurementParams.Builder(mDevice) + .setDurationSeconds(1000) + .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW) + .setMethodId( + DistanceMeasurementMethod + .DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING) + .build(); + mDistanceMeasurementManager.startDistanceMeasurement(mUuid, params, mCallback); + + verify(mDistanceMeasurementNativeInterface, never()) + .startDistanceMeasurement( + IDENTITY_ADDRESS, + CS_FREQUENCY_LOW, + DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING); + verify(mCallback).onStartFail(mDevice, BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED); + } + + @Test + public void testCsStartSuccessForBondedBLE() throws RemoteException { + doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService).getBondState(any()); + DistanceMeasurementParams params = + new DistanceMeasurementParams.Builder(mDevice) + .setDurationSeconds(1000) + .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW) + .setMethodId( + DistanceMeasurementMethod + .DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING) + .build(); + mDistanceMeasurementManager.startDistanceMeasurement(mUuid, params, mCallback); + + verify(mDistanceMeasurementNativeInterface) + .startDistanceMeasurement( + IDENTITY_ADDRESS, + CS_FREQUENCY_LOW, + DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING); + + mDistanceMeasurementManager.onDistanceMeasurementStarted( + IDENTITY_ADDRESS, + DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING); + mDistanceMeasurementManager.onDistanceMeasurementResult( + IDENTITY_ADDRESS, + 100, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_CHANNEL_SOUNDING); + ArgumentCaptor<DistanceMeasurementResult> result = + ArgumentCaptor.forClass(DistanceMeasurementResult.class); + + verify(mCallback).onResult(eq(mDevice), result.capture()); + assertThat(result.getValue().getResultMeters()).isEqualTo(1.00); + } + + @Test public void testHandleRssiStopped() throws RemoteException { DistanceMeasurementParams params = new DistanceMeasurementParams.Builder(mDevice) 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 c652787209..8750c283ac 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 @@ -36,6 +36,7 @@ import android.bluetooth.le.DistanceMeasurementMethod; import android.bluetooth.le.DistanceMeasurementParams; import android.bluetooth.le.IDistanceMeasurementCallback; import android.bluetooth.le.PeriodicAdvertisingParameters; +import android.companion.CompanionDeviceManager; import android.content.AttributionSource; import android.content.Context; import android.content.res.Resources; @@ -102,6 +103,8 @@ public class GattServiceTest { private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); private final AttributionSource mAttributionSource = mAdapter.getAttributionSource(); private final Context mContext = InstrumentationRegistry.getTargetContext(); + private final CompanionDeviceManager mCompanionDeviceManager = + mContext.getSystemService(CompanionDeviceManager.class); private MockContentResolver mMockContentResolver; @@ -141,6 +144,11 @@ public class GattServiceTest { mAdapterService, Context.LOCATION_SERVICE, LocationManager.class); TestUtils.mockGetSystemService( mAdapterService, Context.ACTIVITY_SERVICE, ActivityManager.class); + TestUtils.mockGetSystemService( + mAdapterService, + Context.COMPANION_DEVICE_SERVICE, + CompanionDeviceManager.class, + mCompanionDeviceManager); mBtCompanionManager = new CompanionManager(mAdapterService, null); doReturn(mBtCompanionManager).when(mAdapterService).getCompanionManager(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java index 27ed8219f5..a1095a6c59 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.hfp; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; @@ -153,7 +155,7 @@ public class HeadsetServiceTest { // Mock methods in HeadsetObjectsFactory doAnswer( invocation -> { - Assert.assertNotNull(mCurrentDevice); + assertThat(mCurrentDevice).isNotNull(); final HeadsetStateMachine stateMachine = mock(HeadsetStateMachine.class); doReturn(BluetoothProfile.STATE_DISCONNECTED) @@ -181,7 +183,7 @@ public class HeadsetServiceTest { mHeadsetService.stop(); HeadsetNativeInterface.setInstance(null); mHeadsetService = HeadsetService.getHeadsetService(); - Assert.assertNull(mHeadsetService); + assertThat(mHeadsetService).isNull(); mStateMachines.clear(); mCurrentDevice = null; HeadsetObjectsFactory.setInstanceForTesting(null); @@ -284,7 +286,7 @@ public class HeadsetServiceTest { any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -309,7 +311,7 @@ public class HeadsetServiceTest { Assert.assertEquals( Collections.singletonList(mCurrentDevice), mHeadsetService.getConnectedDevices()); // 2nd connection attempt will fail - Assert.assertFalse(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isFalse(); // Verify makeStateMachine is only called once verify(mObjectsFactory).makeStateMachine(any(), any(), any(), any(), any(), any()); // Verify CONNECT is only sent once @@ -424,7 +426,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -465,7 +467,7 @@ public class HeadsetServiceTest { } // Connect the next device will fail mCurrentDevice = TestUtils.getTestDevice(mAdapter, MAX_HEADSET_CONNECTIONS); - Assert.assertFalse(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isFalse(); // Though connection failed, a new state machine is still lazily created for the device verify(mObjectsFactory, times(MAX_HEADSET_CONNECTIONS + 1)) .makeStateMachine( @@ -494,7 +496,7 @@ public class HeadsetServiceTest { any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -520,7 +522,7 @@ public class HeadsetServiceTest { BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); // Test connect audio - set the device first as the active device - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); Assert.assertEquals( BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice)); verify(mStateMachines.get(mCurrentDevice)) @@ -560,7 +562,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -614,7 +616,7 @@ public class HeadsetServiceTest { BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE, mHeadsetService.connectAudio(mCurrentDevice)); // Should succeed after setActiveDevice() - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); Assert.assertEquals( BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(mCurrentDevice)); verify(mStateMachines.get(mCurrentDevice)) @@ -661,7 +663,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -715,7 +717,7 @@ public class HeadsetServiceTest { BluetoothDevice firstDevice = connectedDevices.get(0); BluetoothDevice secondDevice = connectedDevices.get(1); // Set the first device as the active device - Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); + assertThat(mHeadsetService.setActiveDevice(firstDevice)).isTrue(); Assert.assertEquals( BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio(firstDevice)); verify(mStateMachines.get(firstDevice)) @@ -764,7 +766,7 @@ public class HeadsetServiceTest { .getBondedDevices(); for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -817,7 +819,7 @@ public class HeadsetServiceTest { } // Try to connect audio BluetoothDevice firstDevice = connectedDevices.get(0); - Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); + assertThat(mHeadsetService.setActiveDevice(firstDevice)).isTrue(); Assert.assertEquals(BluetoothStatusCodes.SUCCESS, mHeadsetService.connectAudio()); verify(mStateMachines.get(firstDevice)) .sendMessage(HeadsetStateMachine.CONNECT_AUDIO, firstDevice); @@ -845,7 +847,7 @@ public class HeadsetServiceTest { any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -920,7 +922,7 @@ public class HeadsetServiceTest { mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); final ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); // Connect one device - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -984,7 +986,7 @@ public class HeadsetServiceTest { mPhoneState, headsetCallState, ASYNC_CALL_TIMEOUT_MILLIS); // Set the device first as the active device - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); // Change phone state headsetCallState.mCallState = HeadsetHalConstants.CALL_STATE_ALERTING; mHeadsetService.phoneStateChanged( @@ -1023,7 +1025,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -1073,7 +1075,7 @@ public class HeadsetServiceTest { mCurrentDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); } // Change phone state mHeadsetService.phoneStateChanged( @@ -1104,7 +1106,7 @@ public class HeadsetServiceTest { any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -1139,7 +1141,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 2; i >= 0; i--) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); verify(mObjectsFactory) .makeStateMachine( mCurrentDevice, @@ -1171,7 +1173,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); for (int i = 0; i < 2; i++) { mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); when(mStateMachines.get(mCurrentDevice).getConnectionState()) .thenReturn(BluetoothProfile.STATE_CONNECTED); @@ -1182,23 +1184,23 @@ public class HeadsetServiceTest { BluetoothDevice otherDevice = TestUtils.getTestDevice(mAdapter, 1); // Test whether active device been removed after enable silence mode. - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); - Assert.assertTrue(mHeadsetService.setSilenceMode(mCurrentDevice, true)); - Assert.assertNull(mHeadsetService.getActiveDevice()); + assertThat(mHeadsetService.setSilenceMode(mCurrentDevice, true)).isTrue(); + assertThat(mHeadsetService.getActiveDevice()).isNull(); // Test whether active device been resumed after disable silence mode. - Assert.assertTrue(mHeadsetService.setSilenceMode(mCurrentDevice, false)); + assertThat(mHeadsetService.setSilenceMode(mCurrentDevice, false)).isTrue(); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); // Test that active device should not be changed when silence a non-active device - Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isTrue(); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); - Assert.assertTrue(mHeadsetService.setSilenceMode(otherDevice, true)); + assertThat(mHeadsetService.setSilenceMode(otherDevice, true)).isTrue(); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); // Test that active device should not be changed when another device exits silence mode - Assert.assertTrue(mHeadsetService.setSilenceMode(otherDevice, false)); + assertThat(mHeadsetService.setSilenceMode(otherDevice, false)).isTrue(); Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); } @@ -1211,17 +1213,17 @@ public class HeadsetServiceTest { mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); mHeadsetService.setForceScoAudio(false); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); when(mStateMachines.get(mCurrentDevice).getConnectionState()) .thenReturn(BluetoothProfile.STATE_CONNECTED); - Assert.assertTrue(mHeadsetService.setActiveDevice(null)); + assertThat(mHeadsetService.setActiveDevice(null)).isTrue(); when(mSystemInterface.isInCall()).thenReturn(true); mHeadsetService.setAudioRouteAllowed(false); // Test that active device should not be changed if audio is not allowed - Assert.assertFalse(mHeadsetService.setActiveDevice(mCurrentDevice)); + assertThat(mHeadsetService.setActiveDevice(mCurrentDevice)).isFalse(); Assert.assertEquals(null, mHeadsetService.getActiveDevice()); } @@ -1240,19 +1242,17 @@ public class HeadsetServiceTest { .thenReturn(null); // No connected device - Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty()); + assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)).isEmpty(); // One connected device addConnectedDeviceHelper(deviceA); - Assert.assertTrue( - mHeadsetService.getFallbackCandidates(mDatabaseManager).contains(deviceA)); + assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)) + .containsExactly(deviceA); // Two connected devices addConnectedDeviceHelper(deviceB); - Assert.assertTrue( - mHeadsetService.getFallbackCandidates(mDatabaseManager).contains(deviceA)); - Assert.assertTrue( - mHeadsetService.getFallbackCandidates(mDatabaseManager).contains(deviceB)); + assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)) + .containsExactly(deviceA, deviceB); } @Test @@ -1268,14 +1268,12 @@ public class HeadsetServiceTest { // Has a connected watch device addConnectedDeviceHelper(deviceWatch); - Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty()); + assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)).isEmpty(); // Two connected devices with one watch addConnectedDeviceHelper(deviceRegular); - Assert.assertFalse( - mHeadsetService.getFallbackCandidates(mDatabaseManager).contains(deviceWatch)); - Assert.assertTrue( - mHeadsetService.getFallbackCandidates(mDatabaseManager).contains(deviceRegular)); + assertThat(mHeadsetService.getFallbackCandidates(mDatabaseManager)) + .containsExactly(deviceRegular); } @Test @@ -1284,7 +1282,7 @@ public class HeadsetServiceTest { any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); - Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); + assertThat(mHeadsetService.connect(mCurrentDevice)).isTrue(); when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); when(mStateMachines.get(mCurrentDevice).getConnectionState()) .thenReturn(BluetoothProfile.STATE_CONNECTED); @@ -1325,7 +1323,7 @@ public class HeadsetServiceTest { when(mDatabaseManager.getProfileConnectionPolicy( any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); - Assert.assertTrue(mHeadsetService.connect(device)); + assertThat(mHeadsetService.connect(device)).isTrue(); when(mStateMachines.get(device).getDevice()).thenReturn(device); when(mStateMachines.get(device).getConnectionState()) .thenReturn(BluetoothProfile.STATE_CONNECTING); @@ -1335,7 +1333,7 @@ public class HeadsetServiceTest { .thenReturn(BluetoothProfile.STATE_CONNECTED); Assert.assertEquals( BluetoothProfile.STATE_CONNECTED, mHeadsetService.getConnectionState(device)); - Assert.assertTrue(mHeadsetService.getConnectedDevices().contains(device)); + assertThat(mHeadsetService.getConnectedDevices()).contains(device); } /* diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java index 41c5ab614b..75dfc971a1 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java @@ -19,6 +19,8 @@ package com.android.bluetooth.hfp; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -1888,56 +1890,66 @@ public class HeadsetStateMachineTest { // Commands that will be handled int counter_ok = 0; int counter_error = 0; - Assert.assertTrue(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=?", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=?", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_ok)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID=SINKAUDIOPOLICY,1,1,1", mTestDevice)); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID=SINKAUDIOPOLICY,1,1,1", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_ok)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID=SINKAUDIOPOLICY,100,100,100", mTestDevice)); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID=SINKAUDIOPOLICY,100,100,100", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_ok)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_OK, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID=SINKAUDIOPOLICY,1,2,3,4,5", mTestDevice)); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID=SINKAUDIOPOLICY,1,2,3,4,5", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_error)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); - Assert.assertTrue(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_error)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1,2", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1,2", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_error)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1,2,3", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=1,2,3", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_error)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); - Assert.assertTrue( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID=1,2,3,4,5,6,7", mTestDevice)); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID=1,2,3,4,5,6,7", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(++counter_error)) .atResponseCode(mTestDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0); // Commands with correct format but will not be handled - Assert.assertFalse(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=", mTestDevice)); - Assert.assertFalse( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID: PROBE,1,\"`AB\"", mTestDevice)); - Assert.assertFalse( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "+ANDROID= PROBE,1,\"`AB\"", mTestDevice)); - Assert.assertFalse( - mHeadsetStateMachine.checkAndProcessAndroidAt( - "AT+ANDROID=PROBE,1,1,\"PQGHRSBCTU__\"", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=", mTestDevice)) + .isFalse(); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID: PROBE,1,\"`AB\"", mTestDevice)) + .isFalse(); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "+ANDROID= PROBE,1,\"`AB\"", mTestDevice)) + .isFalse(); + assertThat( + mHeadsetStateMachine.checkAndProcessAndroidAt( + "AT+ANDROID=PROBE,1,1,\"PQGHRSBCTU__\"", mTestDevice)) + .isFalse(); // Incorrect format AT command - Assert.assertFalse( - mHeadsetStateMachine.checkAndProcessAndroidAt("RANDOM FORMAT", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("RANDOM FORMAT", mTestDevice)) + .isFalse(); // Check no any AT result was sent for the failed ones verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(counter_ok)) @@ -1957,7 +1969,8 @@ public class HeadsetStateMachineTest { @Test public void testCheckAndProcessAndroidAt_replyAndroidAtFeatureRequest() { // Commands that will be handled - Assert.assertTrue(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=?", mTestDevice)); + assertThat(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=?", mTestDevice)) + .isTrue(); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) .atResponseString(mTestDevice, "+ANDROID: (SINKAUDIOPOLICY)"); verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) @@ -1966,7 +1979,7 @@ public class HeadsetStateMachineTest { /** A end to end test to validate received Android AT commands and processing */ @Test - public void testCehckAndProcessAndroidAtFromStateMachine() { + public void testCheckAndProcessAndroidAtFromStateMachine() { // setAudioPolicyMetadata is invoked in HeadsetStateMachine.init() so start from 1 int expectCallTimes = 1; @@ -2000,22 +2013,22 @@ public class HeadsetStateMachineTest { @Test public void testProcessAndroidAtSinkAudioPolicy() { // expected format - Assert.assertTrue(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0", mTestDevice)); - Assert.assertTrue(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,1", mTestDevice)); - Assert.assertTrue(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,1,0", mTestDevice)); - Assert.assertTrue(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,1,0,0", mTestDevice)); - Assert.assertTrue(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,1,1,1", mTestDevice)); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0", mTestDevice)).isTrue(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,1", mTestDevice)).isTrue(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,1,0", mTestDevice)).isTrue(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,1,0,0", mTestDevice)).isTrue(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,1,1,1", mTestDevice)).isTrue(); // invalid format - Assert.assertFalse(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0", mTestDevice)); - Assert.assertFalse(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0", mTestDevice)); - Assert.assertFalse(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0,0", mTestDevice)); - Assert.assertFalse(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,NOT,INT,TYPE", mTestDevice)); - Assert.assertFalse(setSinkAudioPolicyArgs("RANDOM,VALUE-#$%,*(&^", mTestDevice)); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0", mTestDevice)).isFalse(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0", mTestDevice)).isFalse(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0,0", mTestDevice)).isFalse(); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,NOT,INT,TYPE", mTestDevice)).isFalse(); + assertThat(setSinkAudioPolicyArgs("RANDOM,VALUE-#$%,*(&^", mTestDevice)).isFalse(); // wrong device BluetoothDevice device = mAdapter.getRemoteDevice("01:01:01:01:01:01"); - Assert.assertFalse(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0", device)); + assertThat(setSinkAudioPolicyArgs("SINKAUDIOPOLICY,0,0,0", device)).isFalse(); } /** Test setting audio parameters according to received SWB event. SWB AptX is enabled. */ diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java index 9093b8b5e0..e80048dafa 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetTestUtils.java @@ -16,6 +16,8 @@ package com.android.bluetooth.hfp; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothDevice; @@ -38,7 +40,7 @@ public class HeadsetTestUtils { */ public static void verifyAudioStateBroadcast( BluetoothDevice device, int toState, int fromState, Intent intent) { - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED, intent.getAction()); Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(toState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); @@ -58,7 +60,7 @@ public class HeadsetTestUtils { */ public static void verifyConnectionStateBroadcast( BluetoothDevice device, int toState, int fromState, Intent intent, boolean checkFlag) { - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, intent.getAction()); if (checkFlag) { Assert.assertEquals(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, intent.getFlags()); @@ -94,7 +96,7 @@ public class HeadsetTestUtils { */ public static void verifyActiveDeviceChangedBroadcast( BluetoothDevice device, Intent intent, boolean checkFlag) { - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, intent.getAction()); Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); if (checkFlag) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java index 945a99cd6b..3541670116 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java @@ -21,6 +21,8 @@ import static android.content.pm.PackageManager.FEATURE_WATCH; import static com.android.bluetooth.hfpclient.HeadsetClientService.MAX_HFP_SCO_VOICE_CALL_VOLUME; import static com.android.bluetooth.hfpclient.HeadsetClientService.MIN_HFP_SCO_VOICE_CALL_VOLUME; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; @@ -107,7 +109,7 @@ public class HeadsetClientServiceTest { @Test public void testInitialize() throws Exception { startService(); - Assert.assertNotNull(HeadsetClientService.getHeadsetClientService()); + assertThat(HeadsetClientService.getHeadsetClientService()).isNotNull(); } @Ignore("b/260202548") @@ -215,7 +217,7 @@ public class HeadsetClientServiceTest { int amMax = 10; Map<Integer, Integer> amToHfMap = new HashMap<>(); - Assert.assertTrue(amMax < MAX_HFP_SCO_VOICE_CALL_VOLUME); + assertThat(amMax).isLessThan(MAX_HFP_SCO_VOICE_CALL_VOLUME); doReturn(amMax).when(mMockAudioManager).getStreamMaxVolume(anyInt()); doReturn(amMin).when(mMockAudioManager).getStreamMinVolume(anyInt()); @@ -245,7 +247,7 @@ public class HeadsetClientServiceTest { int amMax = 20; Map<Integer, Integer> hfToAmMap = new HashMap<>(); - Assert.assertTrue(amMax > MAX_HFP_SCO_VOICE_CALL_VOLUME); + assertThat(amMax).isGreaterThan(MAX_HFP_SCO_VOICE_CALL_VOLUME); doReturn(amMax).when(mMockAudioManager).getStreamMaxVolume(anyInt()); doReturn(amMin).when(mMockAudioManager).getStreamMinVolume(anyInt()); @@ -272,7 +274,7 @@ public class HeadsetClientServiceTest { private void stopServiceIfStarted() throws Exception { if (mIsHeadsetClientServiceStarted) { mService.stop(); - Assert.assertNull(HeadsetClientService.getHeadsetClientService()); + assertThat(HeadsetClientService.getHeadsetClientService()).isNull(); } } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java index a509da0730..fc33b519d7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.hid; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -102,7 +104,6 @@ public class HidDeviceTest { if (Looper.myLooper() == null) { Looper.prepare(); } - Assert.assertNotNull(Looper.myLooper()); TestUtils.setAdapterService(mAdapterService); doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); @@ -147,7 +148,7 @@ public class HidDeviceTest { public void tearDown() throws Exception { mHidDeviceService.stop(); mHidDeviceService = HidDeviceService.getHidDeviceService(); - Assert.assertNull(mHidDeviceService); + assertThat(mHidDeviceService).isNull(); mTargetContext.unregisterReceiver(mConnectionStateChangedReceiver); mConnectionStateChangedQueue.clear(); mCallbackQueue.clear(); @@ -172,7 +173,7 @@ public class HidDeviceTest { private Intent waitForIntent(int timeoutMs, BlockingQueue<Intent> queue) { try { Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS); - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); return intent; } catch (InterruptedException e) { throw new AssertionError("Cannot obtain an Intent from the queue", e); @@ -182,7 +183,7 @@ public class HidDeviceTest { private void verifyConnectionStateIntent( int timeoutMs, BluetoothDevice device, int newState, int prevState) { Intent intent = waitForIntent(timeoutMs, mConnectionStateChangedQueue); - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED, intent.getAction()); Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); @@ -193,7 +194,7 @@ public class HidDeviceTest { private void verifyCallback(int timeoutMs, int callbackType, BlockingQueue<Integer> queue) { try { Integer lastCallback = queue.poll(timeoutMs, TimeUnit.MILLISECONDS); - Assert.assertNotNull(lastCallback); + assertThat(lastCallback).isNotNull(); int lastCallbackType = lastCallback; Assert.assertEquals(callbackType, lastCallbackType); } catch (InterruptedException e) { @@ -292,7 +293,7 @@ public class HidDeviceTest { // Register app BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper(); - Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper)); + assertThat(mHidDeviceService.registerApp(mSettings, null, null, helper)).isTrue(); verify(mHidDeviceNativeInterface) .registerApp( @@ -339,7 +340,7 @@ public class HidDeviceTest { isNull(), isNull()); BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper(); - Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper)); + assertThat(mHidDeviceService.registerApp(mSettings, null, null, helper)).isTrue(); // App registered mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true); @@ -384,7 +385,7 @@ public class HidDeviceTest { isNull(), isNull()); BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper(); - Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper)); + assertThat(mHidDeviceService.registerApp(mSettings, null, null, helper)).isTrue(); // App registered mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true); @@ -425,7 +426,7 @@ public class HidDeviceTest { isNull(), isNull()); BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper(); - Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper)); + assertThat(mHidDeviceService.registerApp(mSettings, null, null, helper)).isTrue(); // App registered mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true); @@ -466,7 +467,7 @@ public class HidDeviceTest { mHidDeviceService.onApplicationStateChangedFromNative(mTestDevice, true); // Send a connect request - Assert.assertTrue("Connect failed", mHidDeviceService.connect(mTestDevice)); + assertThat(mHidDeviceService.connect(mTestDevice)).isTrue(); mHidDeviceService.onConnectStateChangedFromNative( mTestDevice, HidDeviceService.HAL_CONN_STATE_CONNECTING); @@ -493,14 +494,13 @@ public class HidDeviceTest { mHidDeviceService.getConnectionState(mTestDevice)); // Verify the list of connected devices - Assert.assertTrue( - mHidDeviceService - .getDevicesMatchingConnectionStates( - new int[] {BluetoothProfile.STATE_CONNECTED}) - .contains(mTestDevice)); + assertThat( + mHidDeviceService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .contains(mTestDevice); // Send a disconnect request - Assert.assertTrue("Disconnect failed", mHidDeviceService.disconnect(mTestDevice)); + assertThat(mHidDeviceService.disconnect(mTestDevice)).isTrue(); mHidDeviceService.onConnectStateChangedFromNative( mTestDevice, HidDeviceService.HAL_CONN_STATE_DISCONNECTING); @@ -527,11 +527,10 @@ public class HidDeviceTest { mHidDeviceService.getConnectionState(mTestDevice)); // Verify the list of connected devices - Assert.assertFalse( - mHidDeviceService - .getDevicesMatchingConnectionStates( - new int[] {BluetoothProfile.STATE_CONNECTED}) - .contains(mTestDevice)); + assertThat( + mHidDeviceService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .doesNotContain(mTestDevice); // Unregister app doReturn(true).when(mHidDeviceNativeInterface).unregisterApp(); @@ -568,7 +567,7 @@ public class HidDeviceTest { // Register app BluetoothHidDeviceCallbackTestHelper helper = new BluetoothHidDeviceCallbackTestHelper(); - Assert.assertTrue(mHidDeviceService.registerApp(mSettings, null, null, helper)); + assertThat(mHidDeviceService.registerApp(mSettings, null, null, helper)).isTrue(); verify(mHidDeviceNativeInterface) .registerApp( diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java index a13f676bf2..7cf714c142 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java @@ -15,6 +15,8 @@ */ package com.android.bluetooth.hid; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -77,12 +79,12 @@ public class HidHostServiceTest { mService.cleanup(); HidHostNativeInterface.setInstance(null); mService = HidHostService.getHidHostService(); - Assert.assertNull(mService); + assertThat(mService).isNull(); } @Test public void testInitialize() { - Assert.assertNotNull(HidHostService.getHidHostService()); + assertThat(HidHostService.getHidHostService()).isNotNull(); } /** Test okToConnect method using various test cases */ diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java index 572eb95e51..fdd2aacad0 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/ContentControlIdKeeperTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.le_audio; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothLeAudio; @@ -68,7 +70,7 @@ public class ContentControlIdKeeperTest { Map<ParcelUuid, Pair<Integer, Integer>> uuidToCcidContextPair = ContentControlIdKeeper.getUuidToCcidContextPairMap(); Assert.assertEquals(expectedListSize, uuidToCcidContextPair.size()); - Assert.assertTrue(uuidToCcidContextPair.containsKey(uuid)); + assertThat(uuidToCcidContextPair).containsKey(uuid); Assert.assertEquals(ccid, (long) uuidToCcidContextPair.get(uuid).first); Assert.assertEquals(context, (long) uuidToCcidContextPair.get(uuid).second); @@ -78,11 +80,11 @@ public class ContentControlIdKeeperTest { public void testCcidRelease(ParcelUuid uuid, int ccid, int expectedListSize) { Map<ParcelUuid, Pair<Integer, Integer>> uuidToCcidContextPair = ContentControlIdKeeper.getUuidToCcidContextPairMap(); - Assert.assertTrue(uuidToCcidContextPair.containsKey(uuid)); + assertThat(uuidToCcidContextPair).containsKey(uuid); ContentControlIdKeeper.releaseCcid(ccid); uuidToCcidContextPair = ContentControlIdKeeper.getUuidToCcidContextPairMap(); - Assert.assertFalse(uuidToCcidContextPair.containsKey(uuid)); + assertThat(uuidToCcidContextPair).doesNotContainKey(uuid); verify(mLeAudioServiceMock).setCcidInformation(eq(uuid), eq(ccid), eq(0)); diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java index e60ddac592..182569dda7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java @@ -21,6 +21,8 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static com.android.bluetooth.bass_client.BassConstants.INVALID_BROADCAST_ID; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.annotation.Nullable; @@ -229,7 +231,7 @@ public class LeAudioBroadcastServiceTest { private void stopService() throws TimeoutException { mService.stop(); mService = LeAudioService.getLeAudioService(); - Assert.assertNull(mService); + assertThat(mService).isNull(); } /** Test getting LeAudio Service */ @@ -749,7 +751,7 @@ public class LeAudioBroadcastServiceTest { TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); List<BluetoothLeBroadcastMetadata> meta_list = mService.getAllBroadcastMetadata(); - Assert.assertNotNull(meta_list); + assertThat(meta_list).isNotNull(); Assert.assertNotEquals(meta_list.size(), 0); Assert.assertEquals(meta_list.get(0), state_event.broadcastMetadata); } @@ -780,7 +782,7 @@ public class LeAudioBroadcastServiceTest { mService.messageFromNative(state_event); // Verify if broadcast is active - Assert.assertTrue(mService.isBroadcastActive()); + assertThat(mService.isBroadcastActive()).isTrue(); mService.stopBroadcast(broadcastId); verify(mLeAudioBroadcasterNativeInterface).stopBroadcast(eq(broadcastId)); @@ -797,13 +799,13 @@ public class LeAudioBroadcastServiceTest { mService.messageFromNative(state_event); // Verify if broadcast is not active - Assert.assertFalse(mService.isBroadcastActive()); + assertThat(mService.isBroadcastActive()).isFalse(); } private void verifyConnectionStateIntent( int timeoutMs, BluetoothDevice device, int newState, int prevState) { Intent intent = TestUtils.waitForIntent(timeoutMs, mIntentQueue); - Assert.assertNotNull(intent); + assertThat(intent).isNotNull(); Assert.assertEquals( BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED, intent.getAction()); Assert.assertEquals( @@ -825,7 +827,7 @@ public class LeAudioBroadcastServiceTest { LeAudioStackEvent stackEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED); mService.messageFromNative(stackEvent); - Assert.assertTrue(mService.mLeAudioNativeIsInitialized); + assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); } private void prepareConnectedUnicastDevice(int groupId, BluetoothDevice device) { @@ -844,7 +846,7 @@ public class LeAudioBroadcastServiceTest { doReturn(new ParcelUuid[] {BluetoothUuid.LE_AUDIO}) .when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); - Assert.assertTrue(mService.connect(device)); + assertThat(mService.connect(device)).isTrue(); // Verify the connection state broadcast, and that we are in Connected state verifyConnectionStateIntent( @@ -1108,7 +1110,7 @@ public class LeAudioBroadcastServiceTest { /* Imitate setting device in call */ mService.setInCall(true); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Check if broadcast is paused by InCall handling */ verify(mLeAudioBroadcasterNativeInterface).pauseBroadcast(eq(broadcastId)); @@ -1158,7 +1160,7 @@ public class LeAudioBroadcastServiceTest { eq(mBroadcastDevice), eq(null), any(BluetoothProfileConnectionInfo.class)); /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } @Test @@ -1174,7 +1176,7 @@ public class LeAudioBroadcastServiceTest { /* Imitate setting device in call */ mService.handleAudioModeChange(AudioManager.MODE_IN_CALL); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Check if broadcast is paused by AudioMode handling */ verify(mLeAudioBroadcasterNativeInterface).pauseBroadcast(eq(broadcastId)); @@ -1233,7 +1235,7 @@ public class LeAudioBroadcastServiceTest { if (Flags.leaudioBigDependsOnAudioState()) { /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } /* Verify if broadcast is auto-started on start */ @@ -1257,7 +1259,7 @@ public class LeAudioBroadcastServiceTest { create_event.valueInt2 = LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED; mService.messageFromNative(create_event); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Check if broadcast is paused triggered by group change request */ verify(mLeAudioBroadcasterNativeInterface).pauseBroadcast(eq(broadcastId)); @@ -1309,7 +1311,7 @@ public class LeAudioBroadcastServiceTest { if (Flags.leaudioBigDependsOnAudioState()) { /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } verify(mLeAudioBroadcasterNativeInterface, times(2)).startBroadcast(eq(broadcastId)); @@ -1335,7 +1337,7 @@ public class LeAudioBroadcastServiceTest { /* Imitate setting device in call */ mService.setInCall(true); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Broadcast already paused, not call pause again by InCall handling */ verify(mLeAudioBroadcasterNativeInterface, never()).pauseBroadcast(eq(broadcastId)); @@ -1379,7 +1381,7 @@ public class LeAudioBroadcastServiceTest { eq(mBroadcastDevice), eq(null), any(BluetoothProfileConnectionInfo.class)); /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } @Test @@ -1402,7 +1404,7 @@ public class LeAudioBroadcastServiceTest { /* Imitate setting device in call */ mService.handleAudioModeChange(AudioManager.MODE_IN_CALL); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Broadcast already paused, not call pause again by AudioMode handling */ verify(mLeAudioBroadcasterNativeInterface, never()).pauseBroadcast(eq(broadcastId)); @@ -1454,7 +1456,7 @@ public class LeAudioBroadcastServiceTest { eq(mBroadcastDevice), eq(null), any(BluetoothProfileConnectionInfo.class)); /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } @Test @@ -1480,7 +1482,7 @@ public class LeAudioBroadcastServiceTest { create_event.valueInt2 = LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED; mService.messageFromNative(create_event); - Assert.assertTrue(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isTrue(); /* Broadcast already paused, not call pause again by group change request */ verify(mLeAudioBroadcasterNativeInterface, never()).pauseBroadcast(eq(broadcastId)); @@ -1524,7 +1526,7 @@ public class LeAudioBroadcastServiceTest { .handleBluetoothActiveDeviceChanged( eq(mBroadcastDevice), eq(null), any(BluetoothProfileConnectionInfo.class)); /* Verify if broadcast triggers transition */ - Assert.assertFalse(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()); + assertThat(mService.mBroadcastIdDeactivatedForUnicastTransition.isPresent()).isFalse(); } @Test @@ -1577,7 +1579,7 @@ public class LeAudioBroadcastServiceTest { ArgumentCaptor<BluetoothProfileConnectionInfo> connectionInfoArgumentCaptor = ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); - Assert.assertTrue(mService.setActiveDevice(mDevice2)); + assertThat(mService.setActiveDevice(mDevice2)).isTrue(); verify(mAudioManager) .handleBluetoothActiveDeviceChanged( @@ -1585,7 +1587,7 @@ public class LeAudioBroadcastServiceTest { List<BluetoothProfileConnectionInfo> connInfos = connectionInfoArgumentCaptor.getAllValues(); Assert.assertEquals(connInfos.size(), 1); - Assert.assertFalse(connInfos.get(0).isLeOutput()); + assertThat(connInfos.get(0).isLeOutput()).isFalse(); Assert.assertEquals(mService.mUnicastGroupIdDeactivatedForBroadcastTransition, groupId2); } @@ -1633,7 +1635,7 @@ public class LeAudioBroadcastServiceTest { TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); Assert.assertEquals(groupId1, mService.mUnicastGroupIdDeactivatedForBroadcastTransition); - Assert.assertTrue(onBroadcastToUnicastFallbackGroupChangedCallbackCalled); + assertThat(onBroadcastToUnicastFallbackGroupChangedCallbackCalled).isTrue(); onBroadcastToUnicastFallbackGroupChangedCallbackCalled = false; synchronized (mService.mLeAudioCallbacks) { @@ -1691,7 +1693,7 @@ public class LeAudioBroadcastServiceTest { List<BluetoothProfileConnectionInfo> connInfos = connectionInfoArgumentCaptor.getAllValues(); Assert.assertEquals(connInfos.size(), 1); - Assert.assertFalse(connInfos.get(0).isLeOutput()); + assertThat(connInfos.get(0).isLeOutput()).isFalse(); Assert.assertEquals(mService.getBroadcastToUnicastFallbackGroup(), groupId2); } 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 1ffcef09f0..988042721b 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 @@ -607,8 +607,7 @@ public class LeAudioServiceTest { if (expectedIntent) { verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, device); } else { - Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(device)); - assertThat(intent).isNull(); + TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(device)); } } @@ -622,8 +621,7 @@ public class LeAudioServiceTest { if (expectedIntent) { verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, null); } else { - Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(device)); - assertThat(intent).isNull(); + TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(device)); } } @@ -1073,8 +1071,7 @@ public class LeAudioServiceTest { } private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) { - Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device)); - assertThat(intent).isNull(); + TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device)); } /** Test setting connection policy */ @@ -1936,12 +1933,12 @@ public class LeAudioServiceTest { 3); injectGroupStatusChange(testGroupId, BluetoothLeAudio.GROUP_STATUS_ACTIVE); - /* Expect 2 calles to Audio Manager - one for output and second for input as this is + /* Expect 2 calls to Audio Manager - one for output and second for input as this is * Conversational use case */ verify(mAudioManager, times(2)) .handleBluetoothActiveDeviceChanged( any(), any(), any(BluetoothProfileConnectionInfo.class)); - /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback + /* Since LeAudioService called AudioManager - assume Audio manager calls properly callback * mAudioManager.onAudioDeviceAdded */ injectAudioDeviceAdded(mSingleDevice, AudioDeviceInfo.TYPE_BLE_HEADSET, true, false, true); @@ -1957,8 +1954,7 @@ public class LeAudioServiceTest { BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL; injectAudioConfChanged(testGroupId, contexts, 3); - Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); - assertThat(intent).isNull(); + TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); } /** Test native interface audio configuration changed message handling */ @@ -1968,8 +1964,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); injectAudioConfChanged(testGroupId, 0, 3); - Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); - assertThat(intent).isNull(); + TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); } /** diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java index de23afb6fb..24d8e2c0b5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java @@ -16,7 +16,7 @@ package com.android.bluetooth.le_scan; -import static com.android.bluetooth.Utils.sSystemClock; +import static com.android.bluetooth.Utils.getSystemClock; import static com.google.common.truth.Truth.assertThat; @@ -78,7 +78,8 @@ public class AppScanStatsTest { WorkSource source = null; AppScanStats appScanStats = - new AppScanStats(name, source, map, mAdapterService, mMockScanHelper, sSystemClock); + new AppScanStats( + name, source, map, mAdapterService, mMockScanHelper, getSystemClock()); assertThat(appScanStats.mScannerMap).isEqualTo(map); assertThat(appScanStats.mScanHelper).isEqualTo(mMockScanHelper); @@ -92,7 +93,8 @@ public class AppScanStatsTest { WorkSource source = null; AppScanStats appScanStats = - new AppScanStats(name, source, map, mAdapterService, mMockScanHelper, sSystemClock); + new AppScanStats( + name, source, map, mAdapterService, mMockScanHelper, getSystemClock()); ScanSettings settings = new ScanSettings.Builder().build(); List<ScanFilter> filters = new ArrayList<>(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternListTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternListTest.java new file mode 100644 index 0000000000..62c8545fa2 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/MsftAdvMonitorMergedPatternListTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.le_scan; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.le.ScanFilter; +import android.os.ParcelUuid; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.UUID; + +/** Test cases for {@link MsftAdvMonitorMergedPatternList}. */ +@RunWith(JUnit4.class) +public final class MsftAdvMonitorMergedPatternListTest { + private static final ParcelUuid SERVICE_DATA_UUID = + new ParcelUuid(UUID.fromString("01234567-890A-BCDE-F123-4567890ABCDE")); + private static final byte[] SERVICE_DATA = new byte[] {0x01, 0x02, 0x03}; + + private static final ParcelUuid ANOTHER_SERVICE_DATA_UUID = + new ParcelUuid(UUID.fromString("12345678-90AB-CDEF-1234-567890ABCDEF")); + + @Test + public void testAddandRemove() { + MsftAdvMonitorMergedPatternList patternList = new MsftAdvMonitorMergedPatternList(); + int filterIndex = 0; + int addedFilterIndex = filterIndex; + + // Ensure returned filter index is the same as passed filter index + MsftAdvMonitor monitor = + new MsftAdvMonitor( + new ScanFilter.Builder() + .setServiceData(SERVICE_DATA_UUID, SERVICE_DATA) + .build()); + assertThat(patternList.add(filterIndex, monitor.getPatterns())).isEqualTo(filterIndex); + + // Add a different pattern and ensure returned filter index is the same as passed filter + // index + filterIndex++; + MsftAdvMonitor anotherMonitor = + new MsftAdvMonitor( + new ScanFilter.Builder() + .setServiceData(ANOTHER_SERVICE_DATA_UUID, SERVICE_DATA) + .build()); + assertThat(patternList.add(filterIndex, anotherMonitor.getPatterns())) + .isEqualTo(filterIndex); + + // Add the same first pattern with different filter index and confirm previous filter index + // was returned + filterIndex++; + assertThat(patternList.add(filterIndex, monitor.getPatterns())).isEqualTo(addedFilterIndex); + + // Only removing the last filter index should result in successful removal + assertThat(patternList.remove(addedFilterIndex)).isEqualTo(false); + assertThat(patternList.remove(addedFilterIndex)).isEqualTo(true); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java index 041aea999b..e0f908d70b 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java @@ -19,6 +19,8 @@ package com.android.bluetooth.le_scan; import static android.bluetooth.BluetoothDevice.PHY_LE_1M_MASK; import static android.bluetooth.BluetoothDevice.PHY_LE_CODED; import static android.bluetooth.BluetoothDevice.PHY_LE_CODED_MASK; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH; import static android.bluetooth.le.ScanSettings.PHY_LE_ALL_SUPPORTED; import static android.bluetooth.le.ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY; @@ -65,6 +67,7 @@ import android.os.BatteryStatsManager; import android.os.Binder; import android.os.Bundle; import android.os.Message; +import android.os.ParcelUuid; import android.os.SystemProperties; import android.os.WorkSource; import android.os.test.TestLooper; @@ -106,7 +109,9 @@ import org.mockito.junit.MockitoRule; import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.UUID; /** Test cases for {@link ScanManager}. */ @SmallTest @@ -138,6 +143,13 @@ public class ScanManagerTest { // MSFT-based hardware scan offload sysprop private static final String MSFT_HCI_EXT_ENABLED = "bluetooth.core.le.use_msft_hci_ext"; + private static final Map<Integer, Integer> defaultScanMode = + Map.of( + SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER, + SCAN_MODE_BALANCED, SCAN_MODE_BALANCED, + SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY, + SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); + private final Context mTargetContext = InstrumentationRegistry.getTargetContext(); // BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the // underlying binder calls. @@ -152,6 +164,7 @@ public class ScanManagerTest { private long mScanReportDelay; private FakeTimeProvider mTimeProvider; private InOrder mInOrder; + private int mClientId; @Before public void setUp() throws Exception { @@ -219,6 +232,7 @@ public class ScanManagerTest { doReturn(mTargetContext.getUser()).when(mAdapterService).getUser(); doReturn(mTargetContext.getPackageName()).when(mAdapterService).getPackageName(); + mClientId = 0; mTimeProvider = new FakeTimeProvider(); mLooper = new TestLooper(); mScanManager = @@ -270,26 +284,38 @@ public class ScanManagerTest { } private ScanClient createScanClient( - int id, boolean isFiltered, - boolean isEmptyFilter, int scanMode, boolean isBatch, boolean isAutoBatch, int appUid, - AppScanStats appScanStats) { - List<ScanFilter> scanFilterList = createScanFilterList(isFiltered, isEmptyFilter); + AppScanStats appScanStats, + List<ScanFilter> scanFilterList) { ScanSettings scanSettings = createScanSettings(scanMode, isBatch, isAutoBatch); - ScanClient client = new ScanClient(id, scanSettings, scanFilterList, appUid); + mClientId = mClientId + 1; + ScanClient client = new ScanClient(mClientId, scanSettings, scanFilterList, appUid); client.stats = appScanStats; - client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id, null); + client.stats.recordScanStart( + scanSettings, scanFilterList, isFiltered, false, mClientId, null); return client; } - private ScanClient createScanClient(int id, boolean isFiltered, int scanMode) { + private ScanClient createScanClient( + boolean isFiltered, + boolean isEmptyFilter, + int scanMode, + boolean isBatch, + boolean isAutoBatch, + int appUid, + AppScanStats appScanStats) { + List<ScanFilter> scanFilterList = createScanFilterList(isFiltered, isEmptyFilter); + return createScanClient( + isFiltered, scanMode, isBatch, isAutoBatch, appUid, appScanStats, scanFilterList); + } + + private ScanClient createScanClient(boolean isFiltered, int scanMode) { return createScanClient( - id, isFiltered, false, scanMode, @@ -300,15 +326,13 @@ public class ScanManagerTest { } private ScanClient createScanClient( - int id, boolean isFiltered, int scanMode, int appUid, AppScanStats appScanStats) { - return createScanClient( - id, isFiltered, false, scanMode, false, false, appUid, appScanStats); + boolean isFiltered, int scanMode, int appUid, AppScanStats appScanStats) { + return createScanClient(isFiltered, false, scanMode, false, false, appUid, appScanStats); } private ScanClient createScanClient( - int id, boolean isFiltered, int scanMode, boolean isBatch, boolean isAutoBatch) { + boolean isFiltered, int scanMode, boolean isBatch, boolean isAutoBatch) { return createScanClient( - id, isFiltered, false, scanMode, @@ -318,10 +342,8 @@ public class ScanManagerTest { mMockAppScanStats); } - private ScanClient createScanClient( - int id, boolean isFiltered, boolean isEmptyFilter, int scanMode) { + private ScanClient createScanClient(boolean isFiltered, boolean isEmptyFilter, int scanMode) { return createScanClient( - id, isFiltered, isEmptyFilter, scanMode, @@ -433,33 +455,22 @@ public class ScanManagerTest { public void testScreenOffStartUnfilteredScan() { // Set filtered scan flag final boolean isFiltered = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).contains(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).contains(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test @@ -476,17 +487,12 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -500,99 +506,66 @@ public class ScanManagerTest { // Set filtered scan flag final boolean isFiltered = true; final boolean isEmptyFilter = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, isEmptyFilter, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).contains(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + // Create scan client + ScanClient client = createScanClient(isFiltered, isEmptyFilter, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).contains(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test public void testScreenOnStartUnfilteredScan() { // Set filtered scan flag final boolean isFiltered = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test public void testScreenOnStartFilteredScan() { // Set filtered scan flag final boolean isFiltered = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test @@ -609,17 +582,12 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); @@ -647,17 +615,12 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -675,99 +638,80 @@ public class ScanManagerTest { public void testUnfilteredScanTimeout() { // Set filtered scan flag final boolean isFiltered = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_OPPORTUNISTIC); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_OPPORTUNISTIC); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_OPPORTUNISTIC); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_OPPORTUNISTIC); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - // Wait for scan timeout - advanceTime(DEFAULT_SCAN_TIMEOUT_MILLIS); - mLooper.dispatchAll(); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - assertThat(client.stats.isScanTimeout(client.scannerId)).isTrue(); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Set as background app - sendMessageWaitForProcessed(createImportanceMessage(false)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_OPPORTUNISTIC; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + // Wait for scan timeout + advanceTime(DEFAULT_SCAN_TIMEOUT_MILLIS); + mLooper.dispatchAll(); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + assertThat(client.stats.isScanTimeout(client.scannerId)).isTrue(); + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Set as background app + sendMessageWaitForProcessed(createImportanceMessage(false)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test public void testFilteredScanTimeout() { // Set filtered scan flag final boolean isFiltered = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_LOW_POWER); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan, this sends scan timeout message with delay - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - // Move time forward so scan timeout message can be dispatched - advanceTime(DEFAULT_SCAN_TIMEOUT_MILLIS); - // Since we are using a TestLooper, need to mock AppScanStats.isScanningTooLong to - // return true because no real time is elapsed - doReturn(true).when(mMockAppScanStats).isScanningTooLong(); - syncHandler(ScanManager.MSG_SCAN_TIMEOUT); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - assertThat(client.stats.isScanTimeout(client.scannerId)).isTrue(); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF); - // Set as background app - sendMessageWaitForProcessed(createImportanceMessage(false)); - assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_LOW_POWER; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan, this sends scan timeout message with delay + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + // Move time forward so scan timeout message can be dispatched + advanceTime(DEFAULT_SCAN_TIMEOUT_MILLIS); + // Since we are using a TestLooper, need to mock AppScanStats.isScanningTooLong + // to + // return true because no real time is elapsed + doReturn(true).when(mMockAppScanStats).isScanningTooLong(); + syncHandler(ScanManager.MSG_SCAN_TIMEOUT); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + assertThat(client.stats.isScanTimeout(client.scannerId)).isTrue(); + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF); + // Set as background app + sendMessageWaitForProcessed(createImportanceMessage(false)); + assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF); + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test @@ -777,7 +721,7 @@ public class ScanManagerTest { // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); // Create scan client - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_POWER); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_POWER); // Put a timeout message in the queue to emulate the scan being started already Message timeoutMessage = @@ -802,86 +746,66 @@ public class ScanManagerTest { public void testSwitchForeBackgroundUnfilteredScan() { // Set filtered scan flag final boolean isFiltered = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_LOW_POWER); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - // Set as background app - sendMessageWaitForProcessed(createImportanceMessage(false)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_LOW_POWER; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + // Set as background app + sendMessageWaitForProcessed(createImportanceMessage(false)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + }); } @Test public void testSwitchForeBackgroundFilteredScan() { // Set filtered scan flag final boolean isFiltered = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_LOW_POWER); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - // Set as background app - sendMessageWaitForProcessed(createImportanceMessage(false)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_LOW_POWER; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + // Set as background app + sendMessageWaitForProcessed(createImportanceMessage(false)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + }); } @Test @@ -901,19 +825,14 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); // Set as foreground app sendMessageWaitForProcessed(createImportanceMessage(true)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -928,14 +847,6 @@ public class ScanManagerTest { @Test public void testUpDowngradeStartScanForConcurrency() { - // Set filtered scan flag - final boolean isFiltered = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_BALANCED); doReturn(DEFAULT_SCAN_UPGRADE_DURATION_MILLIS) .when(mAdapterService) .getScanUpgradeDurationMillis(); @@ -943,39 +854,38 @@ public class ScanManagerTest { .when(mAdapterService) .getScanDowngradeDurationMillis(); - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + // Set filtered scan flag + final boolean isFiltered = true; - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - // Set connecting state - sendMessageWaitForProcessed(createConnectingMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - // Wait for upgrade and downgrade duration - int max_duration = - DEFAULT_SCAN_UPGRADE_DURATION_MILLIS - > DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS - ? DEFAULT_SCAN_UPGRADE_DURATION_MILLIS - : DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS; - advanceTime(max_duration); - mLooper.dispatchAll(); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_BALANCED; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + // Set connecting state + sendMessageWaitForProcessed(createConnectingMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + // Wait for upgrade and downgrade duration + int max_duration = + DEFAULT_SCAN_UPGRADE_DURATION_MILLIS + > DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS + ? DEFAULT_SCAN_UPGRADE_DURATION_MILLIS + : DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS; + advanceTime(max_duration); + mLooper.dispatchAll(); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + }); } @Test @@ -996,19 +906,14 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); // Set as foreground app sendMessageWaitForProcessed(createImportanceMessage(true)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -1042,19 +947,14 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); // Set as foreground app sendMessageWaitForProcessed(createImportanceMessage(true)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -1076,51 +976,41 @@ public class ScanManagerTest { @Test public void testDowngradeDuringScanForConcurrencyBackground() { - // Set filtered scan flag - final boolean isFiltered = true; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_LOW_POWER); - doReturn(DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS) .when(mAdapterService) .getScanDowngradeDurationMillis(); - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + // Set filtered scan flag + final boolean isFiltered = true; - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Set as foreground app - sendMessageWaitForProcessed(createImportanceMessage(true)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - // Set connecting state - sendMessageWaitForProcessed(createConnectingMessage(true)); - // Set as background app - sendMessageWaitForProcessed(createImportanceMessage(false)); - // Wait for downgrade duration - advanceTime(DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS); - mLooper.dispatchAll(); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_LOW_POWER; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Set as foreground app + sendMessageWaitForProcessed(createImportanceMessage(true)); + // Create scan client + ScanClient client = createScanClient(isFiltered, scanMode); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + // Set connecting state + sendMessageWaitForProcessed(createConnectingMessage(true)); + // Set as background app + sendMessageWaitForProcessed(createImportanceMessage(false)); + // Wait for downgrade duration + advanceTime(DEFAULT_SCAN_DOWNGRADE_DURATION_BT_CONNECTING_MILLIS); + mLooper.dispatchAll(); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode); + }); } @Test @@ -1139,17 +1029,12 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode, isBatch, isAutoBatch); + ScanClient client = createScanClient(isFiltered, scanMode, isBatch, isAutoBatch); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); @@ -1180,17 +1065,12 @@ public class ScanManagerTest { for (int i = 0; i < scanModeMap.size(); i++) { int scanMode = scanModeMap.keyAt(i); int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode, isBatch, isAutoBatch); + ScanClient client = createScanClient(isFiltered, scanMode, isBatch, isAutoBatch); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); @@ -1213,47 +1093,38 @@ public class ScanManagerTest { final boolean isAutoBatch = true; // Set report delay for auto batch scan callback type mScanReportDelay = ScanSettings.AUTO_BATCH_MIN_REPORT_DELAY_MILLIS; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_SCREEN_OFF); - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode, isBatch, isAutoBatch); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanParams()).isNull(); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanParams()).isNull(); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanParams()).isNull(); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_SCREEN_OFF; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + // Create scan client + ScanClient client = + createScanClient(isFiltered, scanMode, isBatch, isAutoBatch); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).contains(client); + assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanParams()).isNull(); + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanParams()).isNull(); + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).contains(client); + assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanParams()).isNull(); + }); } @Test @@ -1264,47 +1135,40 @@ public class ScanManagerTest { final boolean isAutoBatch = true; // Set report delay for auto batch scan callback type mScanReportDelay = ScanSettings.AUTO_BATCH_MIN_REPORT_DELAY_MILLIS; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_SCREEN_OFF); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_SCREEN_OFF); - - for (int i = 0; i < scanModeMap.size(); i++) { - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode, isBatch, isAutoBatch); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode); - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - assertThat(mScanManager.getRegularScanQueue()).contains(client); - assertThat(client.settings.getScanMode()).isEqualTo(scanMode); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanParams()).isNull(); - // Turn off screen - sendMessageWaitForProcessed(createScreenOnOffMessage(false)); - assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); - assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + expectedScanMode = SCAN_MODE_SCREEN_OFF; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + // Create scan client + ScanClient client = + createScanClient(isFiltered, scanMode, isBatch, isAutoBatch); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanQueue()).contains(client); + assertThat(mScanManager.getBatchScanParams().scanMode) + .isEqualTo(expectedScanMode); + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + assertThat(mScanManager.getRegularScanQueue()).contains(client); + assertThat(client.settings.getScanMode()).isEqualTo(scanMode); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanParams()).isNull(); + // Turn off screen + sendMessageWaitForProcessed(createScreenOnOffMessage(false)); + assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); + assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); + assertThat(mScanManager.getBatchScanQueue()).contains(client); + assertThat(mScanManager.getBatchScanParams().scanMode) + .isEqualTo(expectedScanMode); + }); } @Test @@ -1321,11 +1185,11 @@ public class ScanManagerTest { for (int i = 0; i < scanModeArr.length; i++) { int scanMode = scanModeArr[i]; - Log.d(TAG, "ScanMode: " + String.valueOf(scanMode)); + Log.d(TAG, "ScanMode: " + scanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -1396,7 +1260,7 @@ public class ScanManagerTest { mScanHelper, mTimeProvider)); // Create scan client for the app, which also records scan start - ScanClient client = createScanClient(i, isFiltered, scanMode, UID, appScanStats); + ScanClient client = createScanClient(isFiltered, scanMode, UID, appScanStats); // Verify that the app scan start is logged mInOrder.verify(mMetricsLogger) .logAppScanStateChanged( @@ -1418,7 +1282,7 @@ public class ScanManagerTest { advanceTime(scanTestDuration); // Record scan stop - client.stats.recordScanStop(i); + client.stats.recordScanStop(mClientId); // Verify that the app scan stop is logged mInOrder.verify(mMetricsLogger) .logAppScanStateChanged( @@ -1468,7 +1332,7 @@ public class ScanManagerTest { mTimeProvider)); // Create scan client for the first app ScanClient client1 = - createScanClient(0, isFiltered, SCAN_MODE_LOW_POWER, UID_1, appScanStats1); + createScanClient(isFiltered, SCAN_MODE_LOW_POWER, UID_1, appScanStats1); // Start scan with lower duty cycle for the first app sendMessageWaitForProcessed(createStartStopScanMessage(true, client1)); advanceTime(scanTestDuration); @@ -1489,8 +1353,7 @@ public class ScanManagerTest { mScanHelper, mTimeProvider)); // Create scan client for the second app - ScanClient client2 = - createScanClient(1, isFiltered, SCAN_MODE_BALANCED, UID_2, appScanStats2); + ScanClient client2 = createScanClient(isFiltered, SCAN_MODE_BALANCED, UID_2, appScanStats2); // Start scan with higher duty cycle for the second app sendMessageWaitForProcessed(createStartStopScanMessage(true, client2)); // Verify radio scan stop is logged with the first app @@ -1527,7 +1390,7 @@ public class ScanManagerTest { mTimeProvider)); // Create scan client for the third app ScanClient client3 = - createScanClient(2, isFiltered, SCAN_MODE_LOW_LATENCY, UID_3, appScanStats3); + createScanClient(isFiltered, SCAN_MODE_LOW_LATENCY, UID_3, appScanStats3); // Start scan with highest duty cycle for the third app sendMessageWaitForProcessed(createStartStopScanMessage(true, client3)); // Verify radio scan stop is logged with the second app @@ -1564,7 +1427,7 @@ public class ScanManagerTest { mTimeProvider)); // Create scan client for the fourth app ScanClient client4 = - createScanClient(3, isFiltered, SCAN_MODE_AMBIENT_DISCOVERY, UID_4, appScanStats4); + createScanClient(isFiltered, SCAN_MODE_AMBIENT_DISCOVERY, UID_4, appScanStats4); // Start scan with lower duty cycle for the fourth app sendMessageWaitForProcessed(createStartStopScanMessage(true, client4)); // Verify radio scan stop is not logged with the third app since there is no change in radio @@ -1714,7 +1577,7 @@ public class ScanManagerTest { sendMessageWaitForProcessed(createScreenOnOffMessage(true)); Mockito.clearInvocations(mMetricsLogger); // Create scan client - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_POWER); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_POWER); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); mInOrder.verify(mMetricsLogger, never()) @@ -1750,7 +1613,7 @@ public class ScanManagerTest { sendMessageWaitForProcessed(createScreenOnOffMessage(true)); Mockito.clearInvocations(mMetricsLogger); // Create scan client - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_POWER); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_POWER); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); mInOrder.verify(mMetricsLogger, never()) @@ -1812,8 +1675,8 @@ public class ScanManagerTest { sendMessageWaitForProcessed(createScreenOnOffMessage(true)); Mockito.clearInvocations(mMetricsLogger); // Create scan clients with different duty cycles - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_POWER); - ScanClient client2 = createScanClient(1, isFiltered, SCAN_MODE_BALANCED); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_POWER); + ScanClient client2 = createScanClient(isFiltered, SCAN_MODE_BALANCED); // Start scan with lower duty cycle sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); mInOrder.verify(mMetricsLogger, never()) @@ -1876,15 +1739,10 @@ public class ScanManagerTest { int scanMode = scanModeMap.keyAt(i); long weightedScanDuration = (long) (scanTestDuration * scanModeMap.get(scanMode) * 0.01); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " weightedScanDuration: " - + String.valueOf(weightedScanDuration)); + Log.d(TAG, "ScanMode: " + scanMode + " weightedScanDuration: " + weightedScanDuration); // Create scan client - ScanClient client = createScanClient(i, isFiltered, scanMode); + ScanClient client = createScanClient(isFiltered, scanMode); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); // Wait for scan test duration @@ -1929,7 +1787,7 @@ public class ScanManagerTest { // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_LATENCY); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_LATENCY); // Start Scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -1953,7 +1811,7 @@ public class ScanManagerTest { // Turn off screen sendMessageWaitForProcessed(createScreenOnOffMessage(false)); // Create scan client - ScanClient client = createScanClient(0, isFiltered, SCAN_MODE_LOW_LATENCY); + ScanClient client = createScanClient(isFiltered, SCAN_MODE_LOW_LATENCY); // Start Scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).contains(client); @@ -1975,9 +1833,7 @@ public class ScanManagerTest { assertThat(mScanManager.mIsConnecting).isFalse(); mScanManager.handleBluetoothProfileConnectionStateChanged( - BluetoothProfile.A2DP, - BluetoothProfile.STATE_DISCONNECTED, - BluetoothProfile.STATE_CONNECTING); + BluetoothProfile.A2DP, STATE_DISCONNECTED, STATE_CONNECTING); mLooper.dispatchAll(); assertThat(mScanManager.mIsConnecting).isTrue(); @@ -1991,88 +1847,47 @@ public class ScanManagerTest { assertThat(mScanManager.mIsConnecting).isFalse(); mScanManager.handleBluetoothProfileConnectionStateChanged( - BluetoothProfile.HEADSET, - BluetoothProfile.STATE_DISCONNECTED, - BluetoothProfile.STATE_CONNECTING); + BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTING); mScanManager.handleBluetoothProfileConnectionStateChanged( - BluetoothProfile.A2DP, - BluetoothProfile.STATE_DISCONNECTED, - BluetoothProfile.STATE_CONNECTING); + BluetoothProfile.A2DP, STATE_DISCONNECTED, STATE_CONNECTING); mScanManager.handleBluetoothProfileConnectionStateChanged( - BluetoothProfile.HID_HOST, - BluetoothProfile.STATE_DISCONNECTED, - BluetoothProfile.STATE_CONNECTING); + BluetoothProfile.HID_HOST, STATE_DISCONNECTED, STATE_CONNECTING); mLooper.dispatchAll(); assertThat(mScanManager.mProfilesConnecting).isEqualTo(3); } - @Test - public void testSetScanPhy() { + private void phytest(int phy, int expectedPhy) { final boolean isFiltered = false; final boolean isEmptyFilter = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - - for (int i = 0; i < scanModeMap.size(); i++) { - int phy = PHY_LE_CODED; - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - Log.d( - TAG, - "ScanMode: " - + String.valueOf(scanMode) - + " expectedScanMode: " - + String.valueOf(expectedScanMode)); - - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = - createScanClientWithPhy(i, isFiltered, isEmptyFilter, scanMode, phy); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - assertThat(client.settings.getPhy()).isEqualTo(phy); - verify(mScanNativeInterface, atLeastOnce()) - .gattSetScanParameters(anyInt(), anyInt(), anyInt(), eq(PHY_LE_CODED_MASK)); - } + defaultScanMode.forEach( + (scanMode, expectedScanMode) -> { + mClientId = mClientId + 1; + Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); + + // Turn on screen + sendMessageWaitForProcessed(createScreenOnOffMessage(true)); + // Create scan client + ScanClient client = + createScanClientWithPhy( + mClientId, isFiltered, isEmptyFilter, scanMode, phy); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + + assertThat(client.settings.getPhy()).isEqualTo(phy); + verify(mScanNativeInterface, atLeastOnce()) + .gattSetScanParameters(anyInt(), anyInt(), anyInt(), eq(expectedPhy)); + }); } @Test - public void testSetScanPhyAllSupported() { - final boolean isFiltered = false; - final boolean isEmptyFilter = false; - // Set scan mode map {original scan mode (ScanMode) : expected scan mode (expectedScanMode)} - SparseIntArray scanModeMap = new SparseIntArray(); - scanModeMap.put(SCAN_MODE_LOW_POWER, SCAN_MODE_LOW_POWER); - scanModeMap.put(SCAN_MODE_BALANCED, SCAN_MODE_BALANCED); - scanModeMap.put(SCAN_MODE_LOW_LATENCY, SCAN_MODE_LOW_LATENCY); - scanModeMap.put(SCAN_MODE_AMBIENT_DISCOVERY, SCAN_MODE_AMBIENT_DISCOVERY); - - for (int i = 0; i < scanModeMap.size(); i++) { - int phy = PHY_LE_ALL_SUPPORTED; - int scanMode = scanModeMap.keyAt(i); - int expectedScanMode = scanModeMap.get(scanMode); - int expectedPhy = PHY_LE_1M_MASK; - - Log.d(TAG, "ScanMode: " + scanMode + " expectedScanMode: " + expectedScanMode); - - // Turn on screen - sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = - createScanClientWithPhy(i, isFiltered, isEmptyFilter, scanMode, phy); - // Start scan - sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); + public void startScan_whenPhyCoded_isStarted() { + phytest(PHY_LE_CODED, PHY_LE_CODED_MASK); + } - assertThat(client.settings.getPhy()).isEqualTo(phy); - verify(mScanNativeInterface, atLeastOnce()) - .gattSetScanParameters(anyInt(), anyInt(), anyInt(), eq(expectedPhy)); - } + @Test + public void startScan_whenPhyAllSupported_isStarted() { + phytest(PHY_LE_ALL_SUPPORTED, PHY_LE_1M_MASK); } @Test @@ -2082,7 +1897,9 @@ public class ScanManagerTest { doReturn(false).when(mBluetoothAdapterProxy).isOffloadedScanFilteringSupported(); final boolean isFiltered = true; - final boolean isEmptyFilter = false; + final ParcelUuid serviceUuid = + new ParcelUuid(UUID.fromString("12345678-90AB-CDEF-1234-567890ABCDEF")); + final byte[] serviceData = new byte[] {0x01, 0x02, 0x03}; boolean isMsftEnabled = SystemProperties.getBoolean(MSFT_HCI_EXT_ENABLED, false); SystemProperties.set(MSFT_HCI_EXT_ENABLED, Boolean.toString(true)); @@ -2099,19 +1916,45 @@ public class ScanManagerTest { // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); - // Create scan client - ScanClient client = createScanClient(0, isFiltered, isEmptyFilter, SCAN_MODE_LOW_POWER); + // Create scan client with service data + List<ScanFilter> scanFilterList = + List.of( + new ScanFilter.Builder() + .setServiceData(serviceUuid, serviceData) + .build()); + ScanClient client = + createScanClient( + isFiltered, + SCAN_MODE_LOW_POWER, + false, + false, + Binder.getCallingUid(), + mMockAppScanStats, + scanFilterList); // Start scan sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); - // Verify MSFT APIs - verify(mScanNativeInterface, atLeastOnce()) + // Create another scan client with the same service data + ScanClient anotherClient = + createScanClient( + isFiltered, + SCAN_MODE_LOW_POWER, + false, + false, + Binder.getCallingUid(), + mMockAppScanStats, + scanFilterList); + // Start scan + sendMessageWaitForProcessed(createStartStopScanMessage(true, anotherClient)); + + // Verify MSFT APIs are only called once + verify(mScanNativeInterface) .gattClientMsftAdvMonitorAdd( any(MsftAdvMonitor.Monitor.class), any(MsftAdvMonitor.Pattern[].class), any(MsftAdvMonitor.Address.class), anyInt()); - verify(mScanNativeInterface, atLeastOnce()).gattClientMsftAdvMonitorEnable(eq(true)); + verify(mScanNativeInterface).gattClientMsftAdvMonitorEnable(eq(true)); } finally { SystemProperties.set(MSFT_HCI_EXT_ENABLED, Boolean.toString(isMsftEnabled)); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java index f032abae08..e3db44b86b 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java @@ -393,7 +393,6 @@ public class TransitionalScanHelperTest { } @Test - @EnableFlags(Flags.FLAG_LE_SCAN_USE_ADDRESS_TYPE) public void onTrackAdvFoundLost() throws Exception { int scannerId = 1; int advPktLen = 1; diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java index 042666d321..316dd5f621 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.map; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; @@ -45,7 +47,6 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMethodProxy; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.map.BluetoothMapUtils.TYPE; import com.android.bluetooth.mapapi.BluetoothMapContract; import com.android.bluetooth.mapapi.BluetoothMapContract.MessageColumns; @@ -233,8 +234,8 @@ public class BluetoothMapContentObserverTest { // Validate that 3 addresses were inserted into the database with 2 being the recipients Assert.assertEquals(3, mProvider.mContents.size()); - Assert.assertTrue(mProvider.mContents.contains(TEST_NUMBER_ONE)); - Assert.assertTrue(mProvider.mContents.contains(TEST_NUMBER_TWO)); + assertThat(mProvider.mContents.contains(TEST_NUMBER_ONE)).isTrue(); + assertThat(mProvider.mContents.contains(TEST_NUMBER_TWO)).isTrue(); } @Test @@ -386,9 +387,10 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setMessageStatusRead( - TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)); + assertThat( + mObserver.setMessageStatusRead( + TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)) + .isTrue(); Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE); } @@ -404,9 +406,10 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setMessageStatusRead( - TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)); + assertThat( + mObserver.setMessageStatusRead( + TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)) + .isTrue(); Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE); } @@ -421,9 +424,10 @@ public class BluetoothMapContentObserverTest { mObserver.mProviderClient = mProviderClient; when(mProviderClient.update(any(), any(), any(), any())).thenReturn(TEST_PLACEHOLDER_INT); - Assert.assertTrue( - mObserver.setMessageStatusRead( - TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)); + assertThat( + mObserver.setMessageStatusRead( + TEST_HANDLE_ONE, type, TEST_URI_STR, TEST_STATUS_VALUE)) + .isTrue(); Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE); } @@ -446,7 +450,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE_ONE)); + assertThat(mObserver.deleteMessageMms(TEST_HANDLE_ONE)).isTrue(); Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID); } @@ -458,7 +462,7 @@ public class BluetoothMapContentObserverTest { createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL, TEST_THREAD_ID); map.put(TEST_HANDLE_ONE, msg); mObserver.setMsgListMms(map, true); - Assert.assertNotNull(mObserver.getMsgListMms().get(TEST_HANDLE_ONE)); + assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE)).isNotNull(); MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID}); cursor.addRow(new Object[] {BluetoothMapContentObserver.DELETED_THREAD_ID}); @@ -469,9 +473,9 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverDelete(any(), any(), any(), any()); - Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE_ONE)); + assertThat(mObserver.deleteMessageMms(TEST_HANDLE_ONE)).isTrue(); - Assert.assertNull(mObserver.getMsgListMms().get(TEST_HANDLE_ONE)); + assertThat(mObserver.getMsgListMms().get(TEST_HANDLE_ONE)).isNull(); } @Test @@ -492,7 +496,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE_ONE)); + assertThat(mObserver.deleteMessageSms(TEST_HANDLE_ONE)).isTrue(); Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID); } @@ -504,7 +508,7 @@ public class BluetoothMapContentObserverTest { createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL, TEST_THREAD_ID); map.put(TEST_HANDLE_ONE, msg); mObserver.setMsgListSms(map, true); - Assert.assertNotNull(mObserver.getMsgListSms().get(TEST_HANDLE_ONE)); + assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE)).isNotNull(); MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID}); cursor.addRow(new Object[] {BluetoothMapContentObserver.DELETED_THREAD_ID}); @@ -515,9 +519,9 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverDelete(any(), any(), any(), any()); - Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE_ONE)); + assertThat(mObserver.deleteMessageSms(TEST_HANDLE_ONE)).isTrue(); - Assert.assertNull(mObserver.getMsgListSms().get(TEST_HANDLE_ONE)); + assertThat(mObserver.getMsgListSms().get(TEST_HANDLE_ONE)).isNull(); } @Test @@ -550,7 +554,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .telephonyGetOrCreateThreadId(any(), any()); - Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)); + assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue(); Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID); Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX); @@ -586,7 +590,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .telephonyGetOrCreateThreadId(any(), any()); - Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)); + assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue(); Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID); Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX); @@ -615,7 +619,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .telephonyGetOrCreateThreadId(any(), any()); - Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)); + assertThat(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE)).isTrue(); // Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID Assert.assertEquals(msg.threadId, TEST_THREAD_ID); @@ -644,7 +648,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .telephonyGetOrCreateThreadId(any(), any()); - Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)); + assertThat(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)).isTrue(); Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID); Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_INBOX); @@ -669,7 +673,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .telephonyGetOrCreateThreadId(any(), any()); - Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)); + assertThat(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE)).isTrue(); // Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID Assert.assertEquals(msg.threadId, TEST_THREAD_ID); @@ -708,12 +712,13 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setEmailMessageStatusDelete( - mCurrentFolder, - TEST_URI_STR, - TEST_HANDLE_ONE, - BluetoothMapAppParams.STATUS_VALUE_YES)); + assertThat( + mObserver.setEmailMessageStatusDelete( + mCurrentFolder, + TEST_URI_STR, + TEST_HANDLE_ONE, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isTrue(); Assert.assertEquals(msg.folderId, TEST_DELETE_FOLDER_ID); } @@ -729,12 +734,13 @@ public class BluetoothMapContentObserverTest { mObserver.setMsgListMsg(map, true); doReturn(0).when(mMapMethodProxy).contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertFalse( - mObserver.setEmailMessageStatusDelete( - mCurrentFolder, - TEST_URI_STR, - TEST_HANDLE_ONE, - BluetoothMapAppParams.STATUS_VALUE_YES)); + assertThat( + mObserver.setEmailMessageStatusDelete( + mCurrentFolder, + TEST_URI_STR, + TEST_HANDLE_ONE, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isFalse(); } @Test @@ -755,12 +761,13 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setEmailMessageStatusDelete( - mCurrentFolder, - TEST_URI_STR, - TEST_HANDLE_ONE, - BluetoothMapAppParams.STATUS_VALUE_NO)); + assertThat( + mObserver.setEmailMessageStatusDelete( + mCurrentFolder, + TEST_URI_STR, + TEST_HANDLE_ONE, + BluetoothMapAppParams.STATUS_VALUE_NO)) + .isTrue(); Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID); } @@ -783,12 +790,13 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setEmailMessageStatusDelete( - mCurrentFolder, - TEST_URI_STR, - TEST_HANDLE_ONE, - BluetoothMapAppParams.STATUS_VALUE_NO)); + assertThat( + mObserver.setEmailMessageStatusDelete( + mCurrentFolder, + TEST_URI_STR, + TEST_HANDLE_ONE, + BluetoothMapAppParams.STATUS_VALUE_NO)) + .isTrue(); Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID); } @@ -813,12 +821,13 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setEmailMessageStatusDelete( - mCurrentFolder, - TEST_URI_STR, - TEST_HANDLE_ONE, - BluetoothMapAppParams.STATUS_VALUE_NO)); + assertThat( + mObserver.setEmailMessageStatusDelete( + mCurrentFolder, + TEST_URI_STR, + TEST_HANDLE_ONE, + BluetoothMapAppParams.STATUS_VALUE_NO)) + .isTrue(); Assert.assertEquals(msg.folderId, TEST_OLD_FOLDER_ID); } @@ -836,24 +845,26 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverUpdate(any(), any(), any(), any(), any()); - Assert.assertTrue( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.EMAIL, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_YES)); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.EMAIL, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isTrue(); } @Test public void setMessageStatusDeleted_withTypeIm() { - Assert.assertFalse( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.IM, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_YES)); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.IM, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isFalse(); } @Test @@ -867,20 +878,22 @@ public class BluetoothMapContentObserverTest { // setMessageStatusDeleted with type Gsm or Mms calls either deleteMessage() or // unDeleteMessage(), which returns false when no cursor is set with BluetoothMethodProxy. - Assert.assertFalse( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.MMS, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_NO)); - Assert.assertFalse( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.SMS_GSM, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_NO)); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.MMS, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_NO)) + .isFalse(); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.SMS_GSM, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_NO)) + .isFalse(); } @Test @@ -894,20 +907,22 @@ public class BluetoothMapContentObserverTest { // setMessageStatusDeleted with type Gsm or Mms calls either deleteMessage() or // unDeleteMessage(), which returns false when no cursor is set with BluetoothMethodProxy. - Assert.assertFalse( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.MMS, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_YES)); - Assert.assertFalse( - mObserver.setMessageStatusDeleted( - TEST_HANDLE_ONE, - TYPE.SMS_GSM, - mCurrentFolder, - TEST_URI_STR, - BluetoothMapAppParams.STATUS_VALUE_YES)); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.MMS, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isFalse(); + assertThat( + mObserver.setMessageStatusDeleted( + TEST_HANDLE_ONE, + TYPE.SMS_GSM, + mCurrentFolder, + TEST_URI_STR, + BluetoothMapAppParams.STATUS_VALUE_YES)) + .isFalse(); } @Test @@ -1459,60 +1474,7 @@ public class BluetoothMapContentObserverTest { } @Test - public void handleMsgListChangesMms_withNonExistingOldMessage_andVersion12() { - mSetFlagsRule.disableFlags(Flags.FLAG_MAP_LIMIT_NOTIFICATION); - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.YEAR, -1); - cal.add(Calendar.DATE, -1); - long timestampSec = TimeUnit.MILLISECONDS.toSeconds(cal.getTimeInMillis()); - - MatrixCursor cursor = - new MatrixCursor( - new String[] { - Mms._ID, - Mms.MESSAGE_BOX, - Mms.MESSAGE_TYPE, - Mms.THREAD_ID, - Mms.READ, - Mms.DATE, - Mms.SUBJECT, - Mms.PRIORITY, - Mms.Addr.ADDRESS - }); - cursor.addRow( - new Object[] { - TEST_HANDLE_ONE, - TEST_MMS_TYPE_ALL, - TEST_MMS_MTYPE, - TEST_THREAD_ID, - TEST_READ_FLAG_ONE, - timestampSec, - TEST_SUBJECT, - PduHeaders.PRIORITY_HIGH, - null - }); - doReturn(cursor) - .when(mMapMethodProxy) - .contentResolverQuery(any(), any(), any(), any(), any(), any()); - - Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>(); - // Giving a different handle for msg below and cursor above makes handleMsgListChangesMms() - // function for a non-existing message - BluetoothMapContentObserver.Msg msg = - new BluetoothMapContentObserver.Msg( - TEST_HANDLE_TWO, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE); - map.put(TEST_HANDLE_TWO, msg); - mObserver.setMsgListMms(map, true); - mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12; - - mObserver.handleMsgListChangesMms(); - - Assert.assertEquals(null, mObserver.getMsgListMms().get(TEST_HANDLE_ONE)); - } - - @Test public void handleMsgListChangesMms_withNonExistingOldMessage_andVersion12_andOneWeekLimit() { - mSetFlagsRule.enableFlags(Flags.FLAG_MAP_LIMIT_NOTIFICATION); Instant oldInstant = Instant.now() .minus(BluetoothMapContentObserver.NEW_MESSAGE_DURATION_FOR_NOTIFICATION); @@ -1861,55 +1823,7 @@ public class BluetoothMapContentObserverTest { } @Test - public void handleMsgListChangesSms_withNonExistingOldMessage_andVersion12() { - mSetFlagsRule.disableFlags(Flags.FLAG_MAP_LIMIT_NOTIFICATION); - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.YEAR, -1); - cal.add(Calendar.DATE, -1); - - MatrixCursor cursor = - new MatrixCursor( - new String[] { - Sms._ID, - Sms.TYPE, - Sms.THREAD_ID, - Sms.READ, - Sms.DATE, - Sms.BODY, - Sms.ADDRESS - }); - cursor.addRow( - new Object[] { - TEST_HANDLE_ONE, - TEST_SMS_TYPE_ALL, - TEST_THREAD_ID, - TEST_READ_FLAG_ONE, - cal.getTimeInMillis(), - "", - null - }); - doReturn(cursor) - .when(mMapMethodProxy) - .contentResolverQuery(any(), any(), any(), any(), any(), any()); - - Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>(); - // Giving a different handle for msg below and cursor above makes handleMsgListChangesMms() - // function for a non-existing message - BluetoothMapContentObserver.Msg msg = - new BluetoothMapContentObserver.Msg( - TEST_HANDLE_TWO, TEST_SMS_TYPE_INBOX, TEST_READ_FLAG_ONE); - map.put(TEST_HANDLE_TWO, msg); - mObserver.setMsgListSms(map, true); - mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12; - - mObserver.handleMsgListChangesSms(); - - Assert.assertEquals(null, mObserver.getMsgListSms().get(TEST_HANDLE_ONE)); - } - - @Test public void handleMsgListChangesSms_withNonExistingOldMessage_andVersion12_andOneWeekLimit() { - mSetFlagsRule.enableFlags(Flags.FLAG_MAP_LIMIT_NOTIFICATION); Instant oldInstant = Instant.now() .minus(BluetoothMapContentObserver.NEW_MESSAGE_DURATION_FOR_NOTIFICATION); @@ -2091,7 +2005,7 @@ public class BluetoothMapContentObserverTest { public void handleMmsSendIntent_withMnsClientNotConnected() { when(mClient.isConnected()).thenReturn(false); - Assert.assertFalse(mObserver.handleMmsSendIntent(mContext, mIntent)); + assertThat(mObserver.handleMmsSendIntent(mContext, mIntent)).isFalse(); } @Test @@ -2101,7 +2015,7 @@ public class BluetoothMapContentObserverTest { .when(mIntent) .getLongExtra(BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1); - Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent)); + assertThat(mObserver.handleMmsSendIntent(mContext, mIntent)).isTrue(); } @Test @@ -2120,7 +2034,7 @@ public class BluetoothMapContentObserverTest { .getIntExtra(BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0); mObserver.mObserverRegistered = true; - Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent)); + assertThat(mObserver.handleMmsSendIntent(mContext, mIntent)).isTrue(); } @Test @@ -2139,7 +2053,7 @@ public class BluetoothMapContentObserverTest { .when(mMapMethodProxy) .contentResolverDelete(any(), any(), any(), any()); - Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent)); + assertThat(mObserver.handleMmsSendIntent(mContext, mIntent)).isTrue(); } @Test @@ -2160,7 +2074,7 @@ public class BluetoothMapContentObserverTest { mObserver.actionMessageSentDisconnected(mContext, mIntent, 1); - Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE)); + assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue(); } @Test @@ -2197,7 +2111,7 @@ public class BluetoothMapContentObserverTest { mObserver.actionMmsSent(mContext, mIntent, 1, mmsMsgList); - Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE)); + assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue(); } @Test @@ -2218,7 +2132,7 @@ public class BluetoothMapContentObserverTest { mObserver.actionMmsSent(mContext, mIntent, 1, mmsMsgList); - Assert.assertFalse(mmsMsgList.containsKey(TEST_HANDLE_ONE)); + assertThat(mmsMsgList).doesNotContainKey(TEST_HANDLE_ONE); } @Test @@ -2244,7 +2158,7 @@ public class BluetoothMapContentObserverTest { mObserver.actionMmsSent(mContext, mIntent, Activity.RESULT_OK, mmsMsgList); - Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE)); + assertThat(mmsMsgList.containsKey(TEST_HANDLE_ONE)).isTrue(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java index 2da0f14a2d..e6716b487c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/BmessageTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.mapclient; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import androidx.test.filters.MediumTest; @@ -59,39 +61,33 @@ public class BmessageTest { @Test public void testNormalMessages() { - Bmessage message = BmessageParser.createBmessage(SIMPLE_MMS_MESSAGE); - Assert.assertNotNull(message); + assertThat(BmessageParser.createBmessage(SIMPLE_MMS_MESSAGE)).isNotNull(); } @Test public void testParseWrongLengthMessage() { - Bmessage message = BmessageParser.createBmessage(WRONG_LENGTH_MESSAGE); - Assert.assertNull(message); + assertThat(BmessageParser.createBmessage(WRONG_LENGTH_MESSAGE)).isNull(); } @Test public void testParseNoEndMessage() { - Bmessage message = BmessageParser.createBmessage(NO_END_MESSAGE); - Assert.assertNull(message); + assertThat(BmessageParser.createBmessage(NO_END_MESSAGE)).isNull(); } @Test public void testParseReallyLongMessage() { String testMessage = new String(new char[68048]).replace('\0', 'A'); - Bmessage message = BmessageParser.createBmessage(testMessage); - Assert.assertNull(message); + assertThat(BmessageParser.createBmessage(testMessage)).isNull(); } @Test public void testNoBodyMessage() { - Bmessage message = BmessageParser.createBmessage(NO_BODY_MESSAGE); - Assert.assertNull(message); + assertThat(BmessageParser.createBmessage(NO_BODY_MESSAGE)).isNull(); } @Test public void testNegativeLengthMessage() { - Bmessage message = BmessageParser.createBmessage(NEGATIVE_LENGTH_MESSAGE); - Assert.assertNull(message); + assertThat(BmessageParser.createBmessage(NEGATIVE_LENGTH_MESSAGE)).isNull(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java index a6dadddff2..5d4207faa5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java @@ -108,6 +108,7 @@ public class MapClientStateMachineTest { private Bmessage mTestIncomingMmsBmessage; private String mTestMessageSmsHandle = "0001"; private String mTestMessageMmsHandle = "0002"; + private String mTestMessageUnknownHandle = "0003"; boolean mIsAdapterServiceSet; boolean mIsMapClientServiceStarted; @@ -367,8 +368,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTED); - Assert.assertTrue( - mMceStateMachine.setMessageStatus("123456789AB", BluetoothMapClient.READ)); + assertThat(mMceStateMachine.setMessageStatus("123456789AB", BluetoothMapClient.READ)) + .isTrue(); } /** Test MceStateMachine#disconnect */ @@ -721,6 +722,48 @@ public class MapClientStateMachineTest { eq(MESSAGE_NOT_SEEN)); } + @Test + public void testReceiveNewMessage_handleNotRecognized_messageDropped() { + setupSdpRecordReceipt(); + Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED); + mMceStateMachine.sendMessage(msg); + + // verifying that state machine is in the Connected state + assertCurrentStateAfterScheduledTask(BluetoothProfile.STATE_CONNECTED); + + // Send new message event with handle A + String dateTime = new ObexTime(Instant.now()).toString(); + EventReport event = + createNewEventReport( + "NewMessage", + dateTime, + mTestMessageMmsHandle, + "telecom/msg/inbox", + null, + "MMS"); + + // Prepare to send back message content, but use handle B + when(mMockRequestGetMessage.getHandle()).thenReturn(mTestMessageUnknownHandle); + when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage); + + mMceStateMachine.receiveEvent(event); + + TestUtils.waitForLooperToBeIdle(mMceStateMachine.getHandler().getLooper()); + verify(mMockMasClient).makeRequest(any(RequestGetMessage.class)); + + msg = + Message.obtain( + mHandler, + MceStateMachine.MSG_MAS_REQUEST_COMPLETED, + mMockRequestGetMessage); + mMceStateMachine.sendMessage(msg); + + // We should drop the message and not store it, as it's not one we requested + TestUtils.waitForLooperToBeIdle(mMceStateMachine.getHandler().getLooper()); + verify(mMockDatabase, never()) + .storeMessage(any(Bmessage.class), anyString(), anyLong(), anyBoolean()); + } + /** Test seen status set in database on initial download */ @Test public void testDownloadExistingSms_messageStoredAsSeen() { @@ -930,7 +973,7 @@ public class MapClientStateMachineTest { verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .sendBroadcast( mIntentArgument.capture(), eq(android.Manifest.permission.RECEIVE_SMS)); - Assert.assertNull(mIntentArgument.getValue().getPackage()); + assertThat(mIntentArgument.getValue().getPackage()).isNull(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java index 04ed3e5d40..4086981761 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.mapclient; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -84,7 +86,7 @@ public class MapClientTest { mService.stop(); mService.cleanup(); mService = MapClientService.getMapClientService(); - Assert.assertNull(mService); + assertThat(mService).isNull(); } if (mIsAdapterServiceSet) { TestUtils.clearAdapterService(mAdapterService); @@ -104,7 +106,7 @@ public class MapClientTest { @Test public void testInitialize() { - Assert.assertNotNull(MapClientService.getMapClientService()); + assertThat(MapClientService.getMapClientService()).isNotNull(); } /** Test connection of one device. */ @@ -112,23 +114,23 @@ public class MapClientTest { public void testConnect() { // make sure there is no statemachine already defined for this device BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11"); - Assert.assertNull(mService.getInstanceMap().get(device)); + assertThat(mService.getInstanceMap()).doesNotContainKey(device); // connect a bluetooth device mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - Assert.assertTrue(mService.connect(device)); + assertThat(mService.connect(device)).isTrue(); // is the statemachine created Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap(); Assert.assertEquals(1, map.size()); MceStateMachine sm = map.get(device); - Assert.assertNotNull(sm); + assertThat(sm).isNotNull(); TestUtils.waitForLooperToFinishScheduledTask(sm.getHandler().getLooper()); Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, sm.getState()); mService.cleanupDevice(device, sm); - Assert.assertNull(mService.getInstanceMap().get(device)); + assertThat(mService.getInstanceMap()).doesNotContainKey(device); } /** Test that a PRIORITY_OFF device is not connected to */ @@ -136,16 +138,14 @@ public class MapClientTest { public void testConnectPriorityOffDevice() { // make sure there is no statemachine already defined for this device BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11"); - Assert.assertNull(mService.getInstanceMap().get(device)); + assertThat(mService.getInstanceMap()).doesNotContainKey(device); // connect a bluetooth device mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); - Assert.assertFalse(mService.connect(device)); + assertThat(mService.connect(device)).isFalse(); // is the statemachine created - Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap(); - Assert.assertEquals(0, map.size()); - Assert.assertNull(map.get(device)); + assertThat(mService.getInstanceMap()).isEmpty(); } /** Test connecting MAXIMUM_CONNECTED_DEVICES devices. */ @@ -160,25 +160,25 @@ public class MapClientTest { // make sure there is no statemachine already defined for the devices defined above for (BluetoothDevice d : list) { - Assert.assertNull(mService.getInstanceMap().get(d)); + assertThat(mService.getInstanceMap().get(d)).isNull(); } // run the test - connect all devices, set their priorities to on for (BluetoothDevice d : list) { mockDevicePriority(d, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - Assert.assertTrue(mService.connect(d)); + assertThat(mService.connect(d)).isTrue(); } // verify Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap(); Assert.assertEquals(MapClientService.MAXIMUM_CONNECTED_DEVICES, map.size()); for (BluetoothDevice d : list) { - Assert.assertNotNull(map.get(d)); + assertThat(map).containsKey(d); } // Try to connect one more device. Should fail. BluetoothDevice last = makeBluetoothDevice("11:22:33:44:55:66"); - Assert.assertFalse(mService.connect(last)); + assertThat(mService.connect(last)).isFalse(); } /** Test calling connect via Binder */ @@ -186,12 +186,8 @@ public class MapClientTest { public void testConnectViaBinder() { BluetoothDevice device = makeBluetoothDevice("11:11:11:11:11:11"); mockDevicePriority(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - try { - Utils.setForegroundUserId(UserHandle.getCallingUserId()); - Assert.assertTrue(mService.connect(device)); - } catch (Exception e) { - Assert.fail(e.toString()); - } + Utils.setForegroundUserId(UserHandle.getCallingUserId()); + assertThat(mService.connect(device)).isTrue(); } private BluetoothDevice makeBluetoothDevice(String address) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java index 2f2bf7f064..dd9e61ae7f 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java @@ -17,6 +17,8 @@ package com.android.bluetooth.mcp; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -77,7 +79,7 @@ public class McpServiceTest { mMcpService.stop(); mMcpService = McpService.getMcpService(); - Assert.assertNull(mMcpService); + assertThat(mMcpService).isNull(); reset(mMediaControlProfile); TestUtils.clearAdapterService(mAdapterService); } @@ -85,7 +87,7 @@ public class McpServiceTest { @Test public void testGetService() { McpService mMcpServiceDuplicate = McpService.getMcpService(); - Assert.assertNotNull(mMcpServiceDuplicate); + assertThat(mMcpServiceDuplicate).isNotNull(); Assert.assertSame(mMcpServiceDuplicate, mMcpService); } @@ -110,7 +112,7 @@ public class McpServiceTest { @Test public void testStopMcpService() { InstrumentationRegistry.getInstrumentation().runOnMainSync(mMcpService::stop); - Assert.assertNull(McpService.getMcpService()); + assertThat(McpService.getMcpService()).isNull(); // Try to restart the service. Note: must be done on the main thread InstrumentationRegistry.getInstrumentation().runOnMainSync(mMcpService::start); diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java index 03fbe7f71a..3da40b9bbd 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlGattServiceTest.java @@ -17,6 +17,8 @@ package com.android.bluetooth.mcp; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -132,7 +134,7 @@ public class MediaControlGattServiceTest { long mMandatoryFeatures = ServiceFeature.ALL_MANDATORY_SERVICE_FEATURES; doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags(); - Assert.assertTrue(mMcpService.init(UUID_GMCS)); + assertThat(mMcpService.init(UUID_GMCS)).isTrue(); Assert.assertEquals(mMcpService.getServiceUuid(), UUID_GMCS); Assert.assertEquals(mMcpService.getContentControlId(), TEST_CCID); @@ -146,7 +148,7 @@ public class MediaControlGattServiceTest { long mMandatoryFeatures = 0; doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags(); - Assert.assertFalse(mMcpService.init(UUID_GMCS)); + assertThat(mMcpService.init(UUID_GMCS)).isFalse(); } private BluetoothGattService initAllFeaturesGattService() { @@ -182,13 +184,13 @@ public class MediaControlGattServiceTest { | ServiceFeature.MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_NOTIFY; doReturn(features).when(mMockMcsCallbacks).onGetFeatureFlags(); - Assert.assertTrue(mMcpService.init(UUID_GMCS)); + assertThat(mMcpService.init(UUID_GMCS)).isTrue(); verify(mMockGattServer).addService(mGattServiceCaptor.capture()); // Capture GATT Service definition for verification BluetoothGattService service = mGattServiceCaptor.getValue(); - Assert.assertNotNull(service); + assertThat(service).isNotNull(); // Call back the low level GATT callback and expect proper higher level callback to be // called @@ -207,7 +209,7 @@ public class MediaControlGattServiceTest { // Check initial state of all mandatory characteristics BluetoothGattCharacteristic characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -215,7 +217,7 @@ public class MediaControlGattServiceTest { Assert.assertEquals("", characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -223,7 +225,7 @@ public class MediaControlGattServiceTest { Assert.assertEquals("", characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -235,7 +237,7 @@ public class MediaControlGattServiceTest { .intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -249,7 +251,7 @@ public class MediaControlGattServiceTest { .intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -259,7 +261,7 @@ public class MediaControlGattServiceTest { characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_CONTENT_CONTROL_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ); Assert.assertEquals( @@ -268,24 +270,24 @@ public class MediaControlGattServiceTest { // Check initial state of all optional characteristics characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ); Assert.assertEquals("", characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_CHANGED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_NOTIFY); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -297,7 +299,7 @@ public class MediaControlGattServiceTest { characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0).intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -309,54 +311,54 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic( MediaControlGattService.UUID_CURRENT_TRACK_SEGMENT_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PARENT_GROUP_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -369,7 +371,7 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ); Assert.assertEquals( @@ -380,7 +382,7 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_CONTROL_POINT); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_NOTIFY @@ -390,7 +392,7 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic( MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ @@ -403,16 +405,16 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEARCH_RESULT_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY); - Assert.assertTrue(characteristic.getValue().length == 0); + assertThat(characteristic.getValue().length).isEqualTo(0); characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEARCH_CONTROL_POINT); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_NOTIFY @@ -485,18 +487,18 @@ public class MediaControlGattServiceTest { BluetoothGattCharacteristic characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( playback_speed, mMcpService.getPlaybackSpeedChar().floatValue(), 0.001f); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( playing_order.getValue(), characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); // Set value as ms, kept in characteristic as 0.01s Assert.assertEquals( track_position / 10, @@ -505,21 +507,21 @@ public class MediaControlGattServiceTest { .intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals(player_name, characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals(icon_url, characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( icon_obj_id.longValue(), mMcpService.byteArray2ObjId(characteristic.getValue())); characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( playing_order_supported.intValue(), characteristic @@ -529,7 +531,7 @@ public class MediaControlGattServiceTest { characteristic = service.getCharacteristic( MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( opcodes_supported.intValue(), characteristic @@ -537,11 +539,11 @@ public class MediaControlGattServiceTest { .intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals(track_title, characteristic.getStringValue(0)); characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); // Set value as ms, kept in characteristic as 0.01s Assert.assertEquals( track_duration / 10, @@ -550,13 +552,13 @@ public class MediaControlGattServiceTest { .intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals( playback_state.getValue(), characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue()); characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); Assert.assertEquals(seeking_speed, mMcpService.getSeekingSpeedChar().floatValue(), 0.001f); } @@ -1031,7 +1033,7 @@ public class MediaControlGattServiceTest { @Test public void testMediaControlPointRequestInvalid() { - Assert.assertFalse(mMcpService.isOpcodeSupported(Request.Opcodes.PLAY)); + assertThat(mMcpService.isOpcodeSupported(Request.Opcodes.PLAY)).isFalse(); } @Test @@ -1311,7 +1313,7 @@ public class MediaControlGattServiceTest { BluetoothGattDescriptor descriptor = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION) .getDescriptor(UUID_CCCD); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); prepareConnectedDevice(); doReturn(BluetoothDevice.ACCESS_REJECTED) @@ -1336,7 +1338,7 @@ public class MediaControlGattServiceTest { BluetoothGattDescriptor descriptor = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION) .getDescriptor(UUID_CCCD); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); prepareConnectedDevice(); doReturn(BluetoothDevice.ACCESS_UNKNOWN) @@ -1361,7 +1363,7 @@ public class MediaControlGattServiceTest { BluetoothGattDescriptor descriptor = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION) .getDescriptor(UUID_CCCD); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); prepareConnectedDevice(); doReturn(BluetoothDevice.ACCESS_REJECTED) @@ -1391,7 +1393,7 @@ public class MediaControlGattServiceTest { BluetoothGattDescriptor descriptor = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION) .getDescriptor(UUID_CCCD); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); prepareConnectedDevice(); doReturn(BluetoothDevice.ACCESS_UNKNOWN) @@ -1420,7 +1422,7 @@ public class MediaControlGattServiceTest { BluetoothGattCharacteristic characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); byte[] nullname = null; characteristic.setValue(nullname); @@ -1441,7 +1443,7 @@ public class MediaControlGattServiceTest { BluetoothGattCharacteristic characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); byte[] nullBytes = null; characteristic.setValue(nullBytes); diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java index a59b94ce4a..1da98c68af 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java @@ -18,6 +18,8 @@ package com.android.bluetooth.mcp; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.*; import android.bluetooth.BluetoothAdapter; @@ -251,18 +253,18 @@ public class MediaControlProfileTest { verify(mMockGMcsService, timeout(100).times(2)).updatePlayerState(stateMapCaptor.capture()); stateMap = stateMapCaptor.getValue(); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYER_NAME)); + assertThat(stateMap).containsKey(PlayerStateField.PLAYER_NAME); // state changed - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_STATE)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.OPCODES_SUPPORTED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.SEEKING_SPEED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_SPEED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_POSITION)); + assertThat(stateMap).containsKey(PlayerStateField.PLAYBACK_STATE); + assertThat(stateMap).containsKey(PlayerStateField.OPCODES_SUPPORTED); + assertThat(stateMap).containsKey(PlayerStateField.SEEKING_SPEED); + assertThat(stateMap).containsKey(PlayerStateField.PLAYBACK_SPEED); + assertThat(stateMap).containsKey(PlayerStateField.TRACK_POSITION); // metadata changed - Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_DURATION)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_TITLE)); + assertThat(stateMap).containsKey(PlayerStateField.TRACK_DURATION); + assertThat(stateMap).containsKey(PlayerStateField.TRACK_TITLE); } private void testHandleTrackPositionSetRequest(long position, long duration, int times) { @@ -464,15 +466,15 @@ public class MediaControlProfileTest { verify(mMockGMcsService, timeout(100).times(2)).updatePlayerState(stateMapCaptor.capture()); stateMap = stateMapCaptor.getValue(); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_STATE)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_DURATION)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_SPEED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.SEEKING_SPEED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYING_ORDER)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_POSITION)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYER_NAME)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYING_ORDER_SUPPORTED)); - Assert.assertTrue(stateMap.containsKey(PlayerStateField.OPCODES_SUPPORTED)); + assertThat(stateMap).containsKey(PlayerStateField.PLAYBACK_STATE); + assertThat(stateMap).containsKey(PlayerStateField.TRACK_DURATION); + assertThat(stateMap).containsKey(PlayerStateField.PLAYBACK_SPEED); + assertThat(stateMap).containsKey(PlayerStateField.SEEKING_SPEED); + assertThat(stateMap).containsKey(PlayerStateField.PLAYING_ORDER); + assertThat(stateMap).containsKey(PlayerStateField.TRACK_POSITION); + assertThat(stateMap).containsKey(PlayerStateField.PLAYER_NAME); + assertThat(stateMap).containsKey(PlayerStateField.PLAYING_ORDER_SUPPORTED); + assertThat(stateMap).containsKey(PlayerStateField.OPCODES_SUPPORTED); } private void testGetCurrentPlayerPlayingOrder( @@ -526,8 +528,8 @@ public class MediaControlProfileTest { // BluetoothDevice class is not mockable BluetoothDevice bluetoothDevice = TestUtils.getTestDevice(mAdapter, 0); mMediaControlProfile.setNotificationSubscription(ccid1, bluetoothDevice, charUuid1, true); - Assert.assertNotNull( - mMediaControlProfile.getNotificationSubscriptions(ccid1, bluetoothDevice)); + assertThat(mMediaControlProfile.getNotificationSubscriptions(ccid1, bluetoothDevice)) + .isNotNull(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java index 7b6c5f7344..9683a02fac 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java @@ -42,7 +42,6 @@ import android.content.ContextWrapper; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.platform.test.flag.junit.SetFlagsRule; import android.sysprop.BluetoothProperties; import androidx.test.espresso.intent.Intents; @@ -51,7 +50,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.TestUtils; -import com.android.bluetooth.flags.Flags; import com.google.common.base.Objects; @@ -71,8 +69,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class BluetoothOppReceiverTest { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - Context mContext; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -132,24 +128,6 @@ public class BluetoothOppReceiverTest { } @Test - public void onReceive_withActionIncomingFileConfirm_startsIncomingFileConfirmActivity() { - mSetFlagsRule.disableFlags(Flags.FLAG_OPP_START_ACTIVITY_DIRECTLY_FROM_NOTIFICATION); - try { - BluetoothOppTestUtils.enableActivity( - BluetoothOppIncomingFileConfirmActivity.class, true, mContext); - - Intent intent = new Intent(); - intent.setAction(Constants.ACTION_INCOMING_FILE_CONFIRM); - intent.setData(Uri.parse("content:///not/important")); - mReceiver.onReceive(mContext, intent); - intended(hasComponent(BluetoothOppIncomingFileConfirmActivity.class.getName())); - } finally { - BluetoothOppTestUtils.enableActivity( - BluetoothOppIncomingFileConfirmActivity.class, false, mContext); - } - } - - @Test public void onReceive_withActionAccept_updatesContents() { Uri uri = Uri.parse("content:///important"); Intent intent = new Intent(); @@ -190,49 +168,6 @@ public class BluetoothOppReceiverTest { } @Test - public void onReceive_withActionOutboundTransfer_startsTransferHistoryActivity() { - mSetFlagsRule.disableFlags(Flags.FLAG_OPP_START_ACTIVITY_DIRECTLY_FROM_NOTIFICATION); - try { - BluetoothOppTestUtils.enableActivity(BluetoothOppTransferHistory.class, true, mContext); - - Intent intent = new Intent(); - intent.setAction(Constants.ACTION_OPEN_OUTBOUND_TRANSFER); - intent.setData(Uri.parse("content:///not/important")); - intending(anyIntent()) - .respondWith( - new Instrumentation.ActivityResult(Activity.RESULT_OK, new Intent())); - - mReceiver.onReceive(mContext, intent); - intended(hasComponent(BluetoothOppTransferHistory.class.getName())); - intended(hasExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_OUTBOUND)); - } finally { - BluetoothOppTestUtils.enableActivity( - BluetoothOppTransferHistory.class, false, mContext); - } - } - - @Test - public void onReceive_withActionInboundTransfer_startsTransferHistoryActivity() { - mSetFlagsRule.disableFlags(Flags.FLAG_OPP_START_ACTIVITY_DIRECTLY_FROM_NOTIFICATION); - try { - BluetoothOppTestUtils.enableActivity(BluetoothOppTransferHistory.class, true, mContext); - - Intent intent = new Intent(); - intent.setAction(Constants.ACTION_OPEN_INBOUND_TRANSFER); - intent.setData(Uri.parse("content:///not/important")); - intending(anyIntent()) - .respondWith( - new Instrumentation.ActivityResult(Activity.RESULT_OK, new Intent())); - mReceiver.onReceive(mContext, intent); - intended(hasComponent(BluetoothOppTransferHistory.class.getName())); - intended(hasExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_INBOUND)); - } finally { - BluetoothOppTestUtils.enableActivity( - BluetoothOppTransferHistory.class, false, mContext); - } - } - - @Test public void onReceive_withActionHide_contentUpdate() { List<BluetoothOppTestUtils.CursorMockData> cursorMockDataList; Cursor cursor = mock(Cursor.class); @@ -273,27 +208,7 @@ public class BluetoothOppReceiverTest { } @Test - public void onReceive_withActionCompleteHide_makeAllVisibilityHidden() { - mSetFlagsRule.disableFlags(Flags.FLAG_OPP_FIX_MULTIPLE_NOTIFICATIONS_ISSUES); - Intent intent = new Intent(); - intent.setAction(Constants.ACTION_COMPLETE_HIDE); - mReceiver.onReceive(mContext, intent); - verify(mBluetoothMethodProxy) - .contentResolverUpdate( - any(), - eq(BluetoothShare.CONTENT_URI), - argThat( - arg -> - Objects.equal( - BluetoothShare.VISIBILITY_HIDDEN, - arg.get(BluetoothShare.VISIBILITY))), - any(), - any()); - } - - @Test public void onReceive_withActionHideCompletedInboundTransfer_makesInboundVisibilityHidden() { - mSetFlagsRule.enableFlags(Flags.FLAG_OPP_FIX_MULTIPLE_NOTIFICATIONS_ISSUES); Intent intent = new Intent(); intent.setAction(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER); mReceiver.onReceive(mContext, intent); @@ -312,7 +227,6 @@ public class BluetoothOppReceiverTest { @Test public void onReceive_withActionHideCompletedOutboundTransfer_makesOutboundVisibilityHidden() { - mSetFlagsRule.enableFlags(Flags.FLAG_OPP_FIX_MULTIPLE_NOTIFICATIONS_ISSUES); Intent intent = new Intent(); intent.setAction(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER); mReceiver.onReceive(mContext, intent); diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java index 273f6fd8d5..aad8a31170 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.opp; +import static android.os.UserHandle.myUserId; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -120,6 +122,110 @@ public class BluetoothOppSendFileInfoTest { } @Test + public void generateFileInfo_withContentUriForOtherUser_returnsSendFileInfoError() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority((myUserId() + 1) + "@media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR); + } + + @Test + public void generateFileInfo_withContentUriForImplicitUser_returnsInfoWithCorrectLength() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority("media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info.mInputStream).isEqualTo(fs); + assertThat(info.mFileName).isEqualTo(fileName); + assertThat(info.mLength).isEqualTo(fileLength); + assertThat(info.mStatus).isEqualTo(0); + } + + @Test + public void generateFileInfo_withContentUriForSameUser_returnsInfoWithCorrectLength() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority(myUserId() + "@media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info.mInputStream).isEqualTo(fs); + assertThat(info.mFileName).isEqualTo(fileName); + assertThat(info.mLength).isEqualTo(fileLength); + assertThat(info.mStatus).isEqualTo(0); + } + + private static Uri buildContentUriWithEncodedAuthority(String authority) { + return new Uri.Builder() + .scheme("content") + .encodedAuthority(authority) + .path("external/images/media/1") + .build(); + } + + @Test public void generateFileInfo_withoutPermissionForAccessingUri_returnsSendFileInfoError() { String type = "text/plain"; Uri uri = Uri.parse("content:///hello/world"); 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 4b5bd23465..7e54701383 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 @@ -42,7 +42,6 @@ import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.btservice.AdapterService; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -121,7 +120,7 @@ public class BluetoothOppServiceTest { @Test public void testInitialize() { - Assert.assertNotNull(BluetoothOppService.getBluetoothOppService()); + assertThat(BluetoothOppService.getBluetoothOppService()).isNotNull(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java index e336cd354b..69d01b2853 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java @@ -44,7 +44,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; -import com.android.bluetooth.flags.Flags; import com.google.common.base.Objects; @@ -155,11 +154,7 @@ public class BluetoothOppTransferHistoryTest { mTargetContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); BluetoothOppTestUtils.setUpMockCursor(mCursor, mCursorMockDataList); - if (Flags.oppStartActivityDirectlyFromNotification()) { - mIntent.setAction(Constants.ACTION_OPEN_INBOUND_TRANSFER); - } else { - mIntent.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_INBOUND); - } + mIntent.setAction(Constants.ACTION_OPEN_INBOUND_TRANSFER); ActivityScenario.launch(mIntent); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -178,11 +173,7 @@ public class BluetoothOppTransferHistoryTest { 1, new BluetoothOppTestUtils.CursorMockData( BluetoothShare.DIRECTION, 2, BluetoothShare.DIRECTION_OUTBOUND)); - if (Flags.oppStartActivityDirectlyFromNotification()) { - mIntent.setAction(Constants.ACTION_OPEN_OUTBOUND_TRANSFER); - } else { - mIntent.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_OUTBOUND); - } + mIntent.setAction(Constants.ACTION_OPEN_OUTBOUND_TRANSFER); ActivityScenario.launch(mIntent); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java index c0f52ee602..41dac6b274 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java @@ -59,7 +59,6 @@ import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.flags.Flags; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -170,7 +169,7 @@ public class PbapClientServiceTest { .getStorageAccountForDevice(any(BluetoothDevice.class)); mAdapter = BluetoothAdapter.getDefaultAdapter(); - Assert.assertNotNull(mAdapter); + assertThat(mAdapter).isNotNull(); mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); if (Looper.myLooper() == null) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java index 2e5e12ffd5..798984b273 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java @@ -19,6 +19,9 @@ package com.android.bluetooth.tbs; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.mockito.AdditionalMatchers.*; import static org.mockito.Mockito.*; @@ -125,28 +128,27 @@ public class TbsGattTest { mCurrentProviderName = "unknown"; mCurrentTechnology = 0x00; - Assert.assertTrue( - mTbsGatt.init( - mCurrentCcid, - mCurrentUci, - mCurrentUriSchemes, - true, - true, - mCurrentProviderName, - mCurrentTechnology, - mMockTbsGattCallback)); - Assert.assertNotNull(mMockGattServer); + assertThat( + mTbsGatt.init( + mCurrentCcid, + mCurrentUci, + mCurrentUriSchemes, + true, + true, + mCurrentProviderName, + mCurrentTechnology, + mMockTbsGattCallback)) + .isTrue(); verify(mAdapterService).registerBluetoothStateCallback(any(), any()); verify(mMockGattServer).addService(mGattServiceCaptor.capture()); doReturn(mGattServiceCaptor.getValue()).when(mMockGattServer).getService(any(UUID.class)); - Assert.assertNotNull(mMockGattServer); } private BluetoothGattCharacteristic getCharacteristic(UUID uuid) { BluetoothGattService service = mGattServiceCaptor.getValue(); BluetoothGattCharacteristic characteristic = service.getCharacteristic(uuid); - Assert.assertNotNull(characteristic); + assertThat(characteristic).isNotNull(); return characteristic; } @@ -155,7 +157,7 @@ public class TbsGattTest { BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean enable) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor(TbsGatt.UUID_CLIENT_CHARACTERISTIC_CONFIGURATION); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); mTbsGatt.mGattServerCallback.onDescriptorWriteRequest( device, @@ -183,14 +185,14 @@ public class TbsGattTest { if (characteristic.getUuid().equals(TbsGatt.UUID_BEARER_PROVIDER_NAME)) { boolean valueChanged = !characteristic.getStringValue(0).equals((String) value); if (valueChanged) { - Assert.assertTrue(mTbsGatt.setBearerProviderName((String) value)); + assertThat(mTbsGatt.setBearerProviderName((String) value)).isTrue(); } else { - Assert.assertFalse(mTbsGatt.setBearerProviderName((String) value)); + assertThat(mTbsGatt.setBearerProviderName((String) value)).isFalse(); } Assert.assertEquals((String) value, characteristic.getStringValue(0)); } else if (characteristic.getUuid().equals(TbsGatt.UUID_BEARER_TECHNOLOGY)) { - Assert.assertTrue(mTbsGatt.setBearerTechnology((Integer) value)); + assertThat(mTbsGatt.setBearerTechnology((Integer) value)).isTrue(); Assert.assertEquals( (Integer) value, characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)); @@ -201,9 +203,11 @@ public class TbsGattTest { String valueString = String.join(",", (List<String>) value); boolean valueChanged = !characteristic.getStringValue(0).equals(valueString); if (valueChanged) { - Assert.assertTrue(mTbsGatt.setBearerUriSchemesSupportedList((List<String>) value)); + assertThat(mTbsGatt.setBearerUriSchemesSupportedList((List<String>) value)) + .isTrue(); } else { - Assert.assertFalse(mTbsGatt.setBearerUriSchemesSupportedList((List<String>) value)); + assertThat(mTbsGatt.setBearerUriSchemesSupportedList((List<String>) value)) + .isFalse(); } Assert.assertEquals(valueString, characteristic.getStringValue(0)); @@ -213,80 +217,73 @@ public class TbsGattTest { switch (flagStatePair.first) { case TbsGatt.STATUS_FLAG_INBAND_RINGTONE_ENABLED: if (flagStatePair.second) { - Assert.assertTrue(mTbsGatt.setInbandRingtoneFlag(device)); + assertThat(mTbsGatt.setInbandRingtoneFlag(device)).isTrue(); } else { - Assert.assertTrue(mTbsGatt.clearInbandRingtoneFlag(device)); + assertThat(mTbsGatt.clearInbandRingtoneFlag(device)).isTrue(); } break; case TbsGatt.STATUS_FLAG_SILENT_MODE_ENABLED: if (flagStatePair.second) { - Assert.assertTrue(mTbsGatt.setSilentModeFlag()); + assertThat(mTbsGatt.setSilentModeFlag()).isTrue(); } else { - Assert.assertTrue(mTbsGatt.clearSilentModeFlag()); + assertThat(mTbsGatt.clearSilentModeFlag()).isTrue(); } break; default: - Assert.assertTrue(false); + assertWithMessage("Unexpected flag: " + flagStatePair.first).fail(); } } else if (characteristic.getUuid().equals(TbsGatt.UUID_CALL_STATE)) { Pair<Map<Integer, TbsCall>, byte[]> callsExpectedPacketPair = (Pair<Map<Integer, TbsCall>, byte[]>) value; - Assert.assertTrue(mTbsGatt.setCallState(callsExpectedPacketPair.first)); - Assert.assertTrue( - Arrays.equals(callsExpectedPacketPair.second, characteristic.getValue())); + assertThat(mTbsGatt.setCallState(callsExpectedPacketPair.first)).isTrue(); + assertThat(characteristic.getValue()).isEqualTo(callsExpectedPacketPair.second); } else if (characteristic.getUuid().equals(TbsGatt.UUID_BEARER_LIST_CURRENT_CALLS)) { Pair<Map<Integer, TbsCall>, byte[]> callsExpectedPacketPair = (Pair<Map<Integer, TbsCall>, byte[]>) value; - Assert.assertTrue(mTbsGatt.setBearerListCurrentCalls(callsExpectedPacketPair.first)); - Assert.assertTrue( - Arrays.equals(callsExpectedPacketPair.second, characteristic.getValue())); + assertThat(mTbsGatt.setBearerListCurrentCalls(callsExpectedPacketPair.first)).isTrue(); + assertThat(characteristic.getValue()).isEqualTo(callsExpectedPacketPair.second); } else if (characteristic.getUuid().equals(TbsGatt.UUID_TERMINATION_REASON)) { Pair<Integer, Integer> indexReasonPair = (Pair<Integer, Integer>) value; - Assert.assertTrue( - mTbsGatt.setTerminationReason(indexReasonPair.first, indexReasonPair.second)); - Assert.assertTrue( - Arrays.equals( - new byte[] { - (byte) indexReasonPair.first.byteValue(), - indexReasonPair.second.byteValue() - }, - characteristic.getValue())); - + assertThat(mTbsGatt.setTerminationReason(indexReasonPair.first, indexReasonPair.second)) + .isTrue(); + assertThat(characteristic.getValue()) + .asList() + .containsExactly( + indexReasonPair.first.byteValue(), indexReasonPair.second.byteValue()) + .inOrder(); } else if (characteristic.getUuid().equals(TbsGatt.UUID_INCOMING_CALL)) { if (value == null) { - Assert.assertTrue(mTbsGatt.clearIncomingCall()); + assertThat(mTbsGatt.clearIncomingCall()).isTrue(); Assert.assertEquals(0, characteristic.getValue().length); } else { Pair<Integer, String> indexStrPair = (Pair<Integer, String>) value; - Assert.assertTrue( - mTbsGatt.setIncomingCall(indexStrPair.first, indexStrPair.second)); - Assert.assertTrue( - Arrays.equals( + assertThat(mTbsGatt.setIncomingCall(indexStrPair.first, indexStrPair.second)) + .isTrue(); + assertThat(characteristic.getValue()) + .isEqualTo( Bytes.concat( new byte[] {(byte) indexStrPair.first.byteValue()}, - indexStrPair.second.getBytes(StandardCharsets.UTF_8)), - characteristic.getValue())); + indexStrPair.second.getBytes(StandardCharsets.UTF_8))); } } else if (characteristic.getUuid().equals(TbsGatt.UUID_CALL_FRIENDLY_NAME)) { if (value == null) { - Assert.assertTrue(mTbsGatt.clearFriendlyName()); + assertThat(mTbsGatt.clearFriendlyName()).isTrue(); Assert.assertEquals(0, characteristic.getValue().length); } else { Pair<Integer, String> indexNamePair = (Pair<Integer, String>) value; - Assert.assertTrue( - mTbsGatt.setCallFriendlyName(indexNamePair.first, indexNamePair.second)); - Assert.assertTrue( - Arrays.equals( + assertThat(mTbsGatt.setCallFriendlyName(indexNamePair.first, indexNamePair.second)) + .isTrue(); + assertThat(characteristic.getValue()) + .isEqualTo( Bytes.concat( new byte[] {(byte) indexNamePair.first.byteValue()}, - indexNamePair.second.getBytes(StandardCharsets.UTF_8)), - characteristic.getValue())); + indexNamePair.second.getBytes(StandardCharsets.UTF_8))); } } @@ -568,21 +565,17 @@ public class TbsGattTest { BluetoothGattCharacteristic characteristic = getCharacteristic(TbsGatt.UUID_CALL_CONTROL_POINT); - int requestedOpcode = TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT; - int callIndex = 0x01; - int result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; + byte requestedOpcode = TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT; + byte callIndex = 0x01; + byte result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS; // Check with notifications enabled configureNotifications(mFirstDevice, characteristic, true); mTbsGatt.setCallControlPointResult(mFirstDevice, requestedOpcode, callIndex, result); - Assert.assertTrue( - Arrays.equals( - characteristic.getValue(), - new byte[] { - (byte) (requestedOpcode & 0xff), - (byte) (callIndex & 0xff), - (byte) (result & 0xff) - })); + assertThat(characteristic.getValue()) + .asList() + .containsExactly(requestedOpcode, callIndex, result) + .inOrder(); verify(mMockGattServer, after(2000)) .notifyCharacteristicChanged(eq(mFirstDevice), eq(characteristic), eq(false)); reset(mMockGattServer); @@ -592,14 +585,10 @@ public class TbsGattTest { // Check with notifications disabled configureNotifications(mFirstDevice, characteristic, false); mTbsGatt.setCallControlPointResult(mFirstDevice, requestedOpcode, callIndex, result); - Assert.assertTrue( - Arrays.equals( - characteristic.getValue(), - new byte[] { - (byte) (requestedOpcode & 0xff), - (byte) (callIndex & 0xff), - (byte) (result & 0xff) - })); + assertThat(characteristic.getValue()) + .asList() + .containsExactly(requestedOpcode, callIndex, result) + .inOrder(); verify(mMockGattServer, after(2000).times(0)) .notifyCharacteristicChanged(any(), any(), anyBoolean()); } @@ -1053,14 +1042,14 @@ public class TbsGattTest { @Test public void testCharacteristicNotifyOnAuthorization() { prepareDefaultService(); - Assert.assertNotNull(mGattServiceCaptor.getValue()); + assertThat(mGattServiceCaptor.getValue()).isNotNull(); BluetoothGattCharacteristic characteristic = getCharacteristic(TbsGatt.UUID_STATUS_FLAGS); configureNotifications(mFirstDevice, characteristic, true); configureNotifications(mSecondDevice, characteristic, true); doReturn(mGattServiceCaptor.getValue()).when(mMockGattServer).getService(any(UUID.class)); - Assert.assertNotNull(mGattServiceCaptor.getValue()); + assertThat(mGattServiceCaptor.getValue()).isNotNull(); // Leave it as unauthorized yet doReturn(BluetoothDevice.ACCESS_REJECTED) @@ -1073,7 +1062,7 @@ public class TbsGattTest { BluetoothGattCharacteristic characteristic2 = getCharacteristic(TbsGatt.UUID_CALL_STATE); characteristic2.setValue((byte[]) null); - Assert.assertNotNull(mGattServiceCaptor.getValue()); + assertThat(mGattServiceCaptor.getValue()).isNotNull(); // Call it once but expect no notification for the unauthorized device byte[] valueBytes = new byte[2]; @@ -1087,7 +1076,7 @@ public class TbsGattTest { doReturn(BluetoothDevice.ACCESS_ALLOWED) .when(mMockTbsService) .getDeviceAuthorization(any(BluetoothDevice.class)); - Assert.assertNotNull(mGattServiceCaptor.getValue()); + assertThat(mGattServiceCaptor.getValue()).isNotNull(); mTbsGatt.onDeviceAuthorizationSet(mFirstDevice); verify(mMockGattServer, times(0)) .notifyCharacteristicChanged(any(), eq(characteristic2), eq(false)); @@ -1169,7 +1158,7 @@ public class TbsGattTest { BluetoothGattDescriptor descriptor = getCharacteristic(TbsGatt.UUID_BEARER_TECHNOLOGY) .getDescriptor(TbsGatt.UUID_CLIENT_CHARACTERISTIC_CONFIGURATION); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); doReturn(BluetoothDevice.ACCESS_REJECTED) .when(mMockTbsService) @@ -1193,7 +1182,7 @@ public class TbsGattTest { BluetoothGattDescriptor descriptor = getCharacteristic(TbsGatt.UUID_BEARER_TECHNOLOGY) .getDescriptor(TbsGatt.UUID_CLIENT_CHARACTERISTIC_CONFIGURATION); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); doReturn(BluetoothDevice.ACCESS_UNKNOWN) .when(mMockTbsService) @@ -1211,7 +1200,7 @@ public class TbsGattTest { BluetoothGattDescriptor descriptor = getCharacteristic(TbsGatt.UUID_CALL_CONTROL_POINT) .getDescriptor(TbsGatt.UUID_CLIENT_CHARACTERISTIC_CONFIGURATION); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); doReturn(BluetoothDevice.ACCESS_REJECTED) .when(mMockTbsService) @@ -1241,7 +1230,7 @@ public class TbsGattTest { BluetoothGattDescriptor descriptor = getCharacteristic(TbsGatt.UUID_CALL_CONTROL_POINT) .getDescriptor(TbsGatt.UUID_CLIENT_CHARACTERISTIC_CONFIGURATION); - Assert.assertNotNull(descriptor); + assertThat(descriptor).isNotNull(); doReturn(BluetoothDevice.ACCESS_UNKNOWN) .when(mMockTbsService) diff --git a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java index e568fc7bac..c0f275b149 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.telephony; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -125,13 +127,12 @@ public class BluetoothInCallServiceTest { boolean callAnswered = mBluetoothInCallService.answerCall(); verify(mockCall).answer(any(int.class)); - Assert.assertTrue(callAnswered); + assertThat(callAnswered).isTrue(); } @Test public void headsetAnswerCallNull() { - boolean callAnswered = mBluetoothInCallService.answerCall(); - Assert.assertFalse(callAnswered); + assertThat(mBluetoothInCallService.answerCall()).isFalse(); } @Test @@ -141,13 +142,12 @@ public class BluetoothInCallServiceTest { boolean callHungup = mBluetoothInCallService.hangupCall(); verify(mockCall).disconnect(); - Assert.assertTrue(callHungup); + assertThat(callHungup).isTrue(); } @Test public void headsetHangupCallNull() { - boolean callHungup = mBluetoothInCallService.hangupCall(); - Assert.assertFalse(callHungup); + assertThat(mBluetoothInCallService.hangupCall()).isFalse(); } @Test @@ -158,13 +158,12 @@ public class BluetoothInCallServiceTest { verify(mockCall).playDtmfTone(eq((char) TEST_DTMF_TONE)); verify(mockCall).stopDtmfTone(); - Assert.assertTrue(sentDtmf); + assertThat(sentDtmf).isTrue(); } @Test public void headsetSendDTMFNull() { - boolean sentDtmf = mBluetoothInCallService.sendDtmf(TEST_DTMF_TONE); - Assert.assertFalse(sentDtmf); + assertThat(mBluetoothInCallService.sendDtmf(TEST_DTMF_TONE)).isFalse(); } @Test @@ -1144,7 +1143,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_RELEASEHELD); verify(ringingCall).reject(eq(false), nullable(String.class)); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1153,7 +1152,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_RELEASEHELD); verify(onHoldCall).disconnect(); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1166,7 +1165,7 @@ public class BluetoothInCallServiceTest { verify(activeCall).disconnect(); verify(ringingCall).answer(any(int.class)); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1179,7 +1178,7 @@ public class BluetoothInCallServiceTest { verify(activeCall).disconnect(); // BluetoothCall unhold will occur as part of CallsManager auto-unholding // the background BluetoothCall on its own. - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1189,7 +1188,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_HOLDACTIVE_ACCEPTHELD); verify(ringingCall).answer(any(int.class)); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1199,7 +1198,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_HOLDACTIVE_ACCEPTHELD); verify(heldCall).unhold(); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1210,7 +1209,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_HOLDACTIVE_ACCEPTHELD); verify(activeCall).hold(); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1221,7 +1220,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_ADDHELDTOCONF); verify(activeCall).mergeConference(); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1237,7 +1236,7 @@ public class BluetoothInCallServiceTest { boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_ADDHELDTOCONF); verify(activeCall).conference(conferenceableCall); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } @Test @@ -1262,7 +1261,7 @@ public class BluetoothInCallServiceTest { verify(mMockBluetoothHeadset) .phoneStateChanged( eq(1), eq(1), eq(CALL_STATE_IDLE), eq(""), eq(128), nullable(String.class)); - Assert.assertTrue(didProcess); + assertThat(didProcess).isTrue(); } // Testing the CallsManager Listener Functionality on Bluetooth @@ -1681,8 +1680,8 @@ public class BluetoothInCallServiceTest { public void clear() { mBluetoothInCallService.clear(); - Assert.assertNull(mBluetoothInCallService.mBluetoothAdapterReceiver); - Assert.assertNull(mBluetoothInCallService.mBluetoothHeadset); + assertThat(mBluetoothInCallService.mBluetoothAdapterReceiver).isNull(); + assertThat(mBluetoothInCallService.mBluetoothHeadset).isNull(); } @Test @@ -1814,11 +1813,11 @@ public class BluetoothInCallServiceTest { @Test public void onDestroy() { - Assert.assertTrue(mBluetoothInCallService.mOnCreateCalled); + assertThat(mBluetoothInCallService.mOnCreateCalled).isTrue(); mBluetoothInCallService.onDestroy(); - Assert.assertFalse(mBluetoothInCallService.mOnCreateCalled); + assertThat(mBluetoothInCallService.mOnCreateCalled).isFalse(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java index 18388e1223..3e912701f6 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java @@ -27,7 +27,9 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; +import static android.bluetooth.IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; +import static android.bluetooth.IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; @@ -40,7 +42,6 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -465,10 +466,9 @@ public class VolumeControlServiceTest { int volume = 6; assertThat(mService.getGroupVolume(groupId)).isEqualTo(-1); - mBinder.setGroupVolume(groupId, volume, mAttributionSource); + mService.setGroupVolume(groupId, volume); - int groupVolume = mBinder.getGroupVolume(groupId, mAttributionSource); - assertThat(groupVolume).isEqualTo(volume); + assertThat(mService.getGroupVolume(groupId)).isEqualTo(volume); volume = 10; // Send autonomous volume change. @@ -485,15 +485,14 @@ public class VolumeControlServiceTest { int groupId_2 = 2; int volume_groupId_2 = 20; - assertThat(mService.getGroupVolume(groupId_1)).isEqualTo(-1); - assertThat(mService.getGroupVolume(groupId_2)).isEqualTo(-1); - mBinder.setGroupVolume(groupId_1, volume_groupId_1, mAttributionSource); - - mBinder.setGroupVolume(groupId_2, volume_groupId_2, mAttributionSource); + assertThat(mService.getGroupVolume(groupId_1)).isEqualTo(VOLUME_CONTROL_UNKNOWN_VOLUME); + assertThat(mService.getGroupVolume(groupId_2)).isEqualTo(VOLUME_CONTROL_UNKNOWN_VOLUME); + mService.setGroupVolume(groupId_1, volume_groupId_1); + mService.setGroupVolume(groupId_2, volume_groupId_2); // Make device Active now. This will trigger setting volume to AF when(mLeAudioService.getActiveGroupId()).thenReturn(groupId_1); - mBinder.setGroupActive(groupId_1, true, mAttributionSource); + mService.setGroupActive(groupId_1, true); // Expected index for STREAM_MUSIC int expectedVol = @@ -502,7 +501,7 @@ public class VolumeControlServiceTest { // Make device Active now. This will trigger setting volume to AF when(mLeAudioService.getActiveGroupId()).thenReturn(groupId_2); - mBinder.setGroupActive(groupId_2, true, mAttributionSource); + mService.setGroupActive(groupId_2, true); expectedVol = (int) Math.round((double) (volume_groupId_2 * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL); @@ -520,7 +519,7 @@ public class VolumeControlServiceTest { generateVolumeStateChanged(null, groupId, volume, 0, false, true); // Mute - mBinder.muteGroup(groupId, mAttributionSource); + mService.muteGroup(groupId); assertThat(mService.getGroupMute(groupId)).isTrue(); // Make sure the volume is kept even when muted @@ -545,7 +544,8 @@ public class VolumeControlServiceTest { // Mute mService.muteGroup(groupId); assertThat(mService.getGroupMute(groupId)).isTrue(); - verify(mNativeInterface).muteGroup(eq(groupId)); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface).muteGroup(eq(groupId)); // Make sure the volume is kept even when muted doReturn(true).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); @@ -554,27 +554,28 @@ public class VolumeControlServiceTest { // Lower the volume and keep it mute mService.setGroupVolume(groupId, --volume); assertThat(mService.getGroupMute(groupId)).isTrue(); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); - verify(mNativeInterface, never()).unmuteGroup(eq(groupId)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface, never()).unmuteGroup(anyInt()); // Don't unmute on consecutive calls either mService.setGroupVolume(groupId, --volume); assertThat(mService.getGroupMute(groupId)).isTrue(); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); - verify(mNativeInterface, never()).unmuteGroup(eq(groupId)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface, never()).unmuteGroup(anyInt()); // Raise the volume and unmute volume += 10; // avoid previous volume levels and simplify mock verification doReturn(false).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); mService.setGroupVolume(groupId, ++volume); assertThat(mService.getGroupMute(groupId)).isFalse(); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface).unmuteGroup(eq(groupId)); // Verify the number of unmute calls after the second volume change mService.setGroupVolume(groupId, ++volume); assertThat(mService.getGroupMute(groupId)).isFalse(); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); // Make sure we unmuted only once - verify(mNativeInterface).unmuteGroup(eq(groupId)); + inOrderNative.verify(mNativeInterface, never()).unmuteGroup(anyInt()); } /** Test if phone will set volume which is read from the buds */ @@ -591,6 +592,8 @@ public class VolumeControlServiceTest { // Both devices are in the same group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupDevicesOrdered(groupId)) + .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); generateDeviceAvailableMessageFromNative(mDevice, 1); generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); @@ -600,15 +603,21 @@ public class VolumeControlServiceTest { when(mBassClientService.getSyncedBroadcastSinks()).thenReturn(new ArrayList<>()); // Group is not active unicast and not active primary broadcast, AF will not be notified generateVolumeStateChanged( - mDevice, groupId, volumeDevice, flags, initialMuteState, initialAutonomousFlag); - verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + mDevice, + LE_AUDIO_GROUP_ID_INVALID, + volumeDevice, + flags, + initialMuteState, + initialAutonomousFlag); + InOrder inOrderAudio = inOrder(mAudioManager); + inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); // Make device Active now. This will trigger setting volume to AF when(mLeAudioService.getActiveGroupId()).thenReturn(groupId); - mBinder.setGroupActive(groupId, true, mAttributionSource); + mService.setGroupActive(groupId, true); int expectedAfVol = (int) Math.round((double) (volumeDevice * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL); - verify(mAudioManager).setStreamVolume(anyInt(), eq(expectedAfVol), anyInt()); + inOrderAudio.verify(mAudioManager).setStreamVolume(anyInt(), eq(expectedAfVol), anyInt()); // Connect second device and read different volume. Expect it will NOT be set to AF // and to another set member, but the existing volume gets applied to it @@ -616,21 +625,16 @@ public class VolumeControlServiceTest { generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - - // Group is now active, AF will be notified. Native will take care to sync the volume generateVolumeStateChanged( mDeviceTwo, - groupId, + LE_AUDIO_GROUP_ID_INVALID, volumeDeviceTwo, flags, initialMuteState, initialAutonomousFlag); - expectedAfVol = volumeDevice; - int unexpectedAfVol = - (int) Math.round((double) (volumeDeviceTwo * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL); - verify(mAudioManager, times(0)).setStreamVolume(anyInt(), eq(unexpectedAfVol), anyInt()); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol)); + inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volumeDevice)); } private void testConnectedDeviceWithResetFlag( @@ -646,6 +650,8 @@ public class VolumeControlServiceTest { // Both devices are in the same group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupDevicesOrdered(groupId)) + .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); when(mAudioManager.getStreamVolume(anyInt())).thenReturn(streamVolume); when(mAudioManager.getStreamMaxVolume(anyInt())).thenReturn(streamMaxVolume); @@ -658,41 +664,41 @@ public class VolumeControlServiceTest { int expectedAfVol = (int) Math.round((double) streamVolume * BT_LE_AUDIO_MAX_VOL / streamMaxVolume); - // Group is not active, AF will not be notified + // Group is not active, AF will not be notified. Device volume updated to system volume. generateVolumeStateChanged( mDevice, - groupId, + LE_AUDIO_GROUP_ID_INVALID, resetVolumeDeviceOne, resetFlag, initialMuteState, initialAutonomousFlag); - verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + + InOrder inOrderAudio = inOrder(mAudioManager); + inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol)); // Make device Active now. This will trigger setting volume to AF when(mLeAudioService.getActiveGroupId()).thenReturn(groupId); - mBinder.setGroupActive(groupId, true, mAttributionSource); - - verify(mAudioManager).setStreamVolume(anyInt(), eq(streamVolume), anyInt()); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol)); + mService.setGroupActive(groupId, true); + inOrderAudio.verify(mAudioManager).setStreamVolume(anyInt(), eq(streamVolume), anyInt()); - // Connect second device and read different volume. Expect it will be set to AF and to - // another set member + // Connect second device and read different volume. Expect it will NOT be set to AF + // and to another set member, but the existing volume gets applied to it generateDeviceAvailableMessageFromNative(mDeviceTwo, 1); generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - - // Group is now active, AF will be notified. Native will take care to sync the volume generateVolumeStateChanged( mDeviceTwo, - groupId, + LE_AUDIO_GROUP_ID_INVALID, resetVolumeDeviceTwo, resetFlag, initialMuteState, initialAutonomousFlag); - verify(mAudioManager).setStreamVolume(anyInt(), anyInt(), anyInt()); - verify(mNativeInterface, times(2)).setGroupVolume(eq(groupId), eq(expectedAfVol)); + inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(expectedAfVol)); } /** Test if phone will set volume which is read from the buds */ @@ -715,6 +721,7 @@ public class VolumeControlServiceTest { public void lateConnectingDevice() { int groupId = 1; int groupVolume = 56; + int volume_2 = 20; // Both devices are in the same group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); @@ -725,14 +732,18 @@ public class VolumeControlServiceTest { assertThat(mService.getDevices()).contains(mDevice); mService.setGroupVolume(groupId, groupVolume); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); - verify(mNativeInterface, never()).setVolume(eq(mDeviceTwo), eq(groupVolume)); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); // Verify that second device gets the proper group volume level when connected generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); + generateVolumeStateChanged(mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, volume_2, 0, false, true); + + inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); } /** @@ -746,7 +757,8 @@ public class VolumeControlServiceTest { // For now only one device is in the group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); - when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)) + .thenReturn(CSIS_GROUP_ID_INVALID); generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDevice)).isEqualTo(STATE_CONNECTED); @@ -759,12 +771,13 @@ public class VolumeControlServiceTest { generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - verify(mNativeInterface, never()).setVolume(eq(mDeviceTwo), eq(groupVolume)); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); // But gets the volume when it becomes the group member when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); mService.handleGroupNodeAdded(groupId, mDeviceTwo); - verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); + inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); } /** @@ -776,6 +789,7 @@ public class VolumeControlServiceTest { public void muteLateConnectingDevice() { int groupId = 1; int volume = 100; + int volume_2 = 20; // Both devices are in the same group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); @@ -789,20 +803,23 @@ public class VolumeControlServiceTest { doReturn(true).when(mAudioManager).isStreamMute(anyInt()); mService.setGroupVolume(groupId, volume); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); - verify(mNativeInterface, never()).setVolume(eq(mDeviceTwo), eq(volume)); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); // Check if it was muted - verify(mNativeInterface).muteGroup(eq(groupId)); - + inOrderNative.verify(mNativeInterface).muteGroup(eq(groupId)); assertThat(mService.getGroupMute(groupId)).isTrue(); // Verify that second device gets the proper group volume level when connected generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume)); + generateVolumeStateChanged(mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, volume_2, 0, false, true); + // Check if new device was muted - verify(mNativeInterface).mute(eq(mDeviceTwo)); + inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume)); + inOrderNative.verify(mNativeInterface).mute(eq(mDeviceTwo)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(volume)); } /** @@ -817,7 +834,8 @@ public class VolumeControlServiceTest { // For now only one device is in the group when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); - when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)) + .thenReturn(CSIS_GROUP_ID_INVALID); generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDevice)).isEqualTo(STATE_CONNECTED); @@ -831,15 +849,18 @@ public class VolumeControlServiceTest { generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - verify(mNativeInterface, never()).setVolume(eq(mDeviceTwo), eq(volume)); + generateVolumeStateChanged(mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, volume, 0, false, true); + + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); // Check if it was not muted - verify(mNativeInterface, never()).mute(eq(mDeviceTwo)); + inOrderNative.verify(mNativeInterface, never()).mute(any()); // But gets the volume when it becomes the group member when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); mService.handleGroupNodeAdded(groupId, mDeviceTwo); - verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume)); - verify(mNativeInterface).mute(eq(mDeviceTwo)); + inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(volume)); + inOrderNative.verify(mNativeInterface).mute(eq(mDeviceTwo)); } @Test @@ -876,6 +897,72 @@ public class VolumeControlServiceTest { } @Test + public void getGroupId() { + int groupId_1 = 1; + int groupId_2 = 2; + int groupVolume = 56; + + generateDeviceAvailableMessageFromNative(mDevice, 1); + generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); + assertThat(mService.getConnectionState(mDevice)).isEqualTo(STATE_CONNECTED); + assertThat(mService.getDevices()).contains(mDevice); + + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)) + .thenReturn(LE_AUDIO_GROUP_ID_INVALID); + when(mLeAudioService.getGroupId(mDevice)).thenReturn(LE_AUDIO_GROUP_ID_INVALID); + mService.setDeviceVolume(mDevice, groupVolume, true); + verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt()); + + when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId_1); + mService.setDeviceVolume(mDevice, groupVolume, true); + verify(mNativeInterface).setGroupVolume(groupId_1, groupVolume); + + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId_2); + mService.setDeviceVolume(mDevice, groupVolume, true); + verify(mNativeInterface).setGroupVolume(groupId_2, groupVolume); + } + + @Test + public void getGroupDevices() throws Exception { + int groupId = 1; + int groupVolume = 56; + + // Send a message to trigger connection completed + generateDeviceAvailableMessageFromNative(mDevice, 2); + + // Register callback and verify it is called with known devices + IBluetoothVolumeControlCallback callback = + Mockito.mock(IBluetoothVolumeControlCallback.class); + Binder binder = Mockito.mock(Binder.class); + when(callback.asBinder()).thenReturn(binder); + + synchronized (mService.mCallbacks) { + int size = mService.mCallbacks.getRegisteredCallbackCount(); + mService.registerCallback(callback); + assertThat(mService.mCallbacks.getRegisteredCallbackCount()).isEqualTo(size + 1); + } + + InOrder inOrderCallback = inOrder(callback); + + generateVolumeStateChanged(null, groupId, groupVolume, 0, false, false); + inOrderCallback.verify(callback, never()).onDeviceVolumeChanged(any(), anyInt()); + + when(mLeAudioService.getGroupDevices(groupId)).thenReturn(Arrays.asList(mDevice)); + generateVolumeStateChanged(null, groupId, groupVolume, 0, false, false); + inOrderCallback.verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(groupVolume)); + + when(mCsipService.getGroupDevicesOrdered(groupId)).thenReturn(Arrays.asList(mDeviceTwo)); + generateVolumeStateChanged(null, groupId, groupVolume, 0, false, false); + inOrderCallback.verify(callback).onDeviceVolumeChanged(eq(mDeviceTwo), eq(groupVolume)); + + when(mCsipService.getGroupDevicesOrdered(groupId)) + .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); + generateVolumeStateChanged(null, groupId, groupVolume, 0, false, false); + inOrderCallback.verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(groupVolume)); + inOrderCallback.verify(callback).onDeviceVolumeChanged(eq(mDeviceTwo), eq(groupVolume)); + } + + @Test public void serviceBinderSetDeviceVolumeMethods() { int groupId = 1; int groupVolume = 56; @@ -883,43 +970,54 @@ public class VolumeControlServiceTest { int deviceTwoVolume = 36; // Both devices are in the same group - when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); - when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); generateDeviceAvailableMessageFromNative(mDevice, 1); generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDevice)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDevice); + InOrder inOrderNative = inOrder(mNativeInterface); + mBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource); - verify(mNativeInterface).setGroupVolume(groupId, groupVolume); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); + inOrderNative.verify(mNativeInterface).setGroupVolume(groupId, groupVolume); assertThat(mService.getGroupVolume(groupId)).isEqualTo(groupVolume); mBinder.setDeviceVolume(mDevice, deviceOneVolume, false, mAttributionSource); - verify(mNativeInterface).setVolume(mDevice, deviceOneVolume); + inOrderNative.verify(mNativeInterface).setVolume(mDevice, deviceOneVolume); assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(deviceOneVolume); Assert.assertNotEquals(deviceOneVolume, mService.getDeviceVolume(mDeviceTwo)); + inOrderNative.verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt()); mBinder.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false, mAttributionSource); - verify(mNativeInterface).setVolume(mDeviceTwo, deviceTwoVolume); + inOrderNative.verify(mNativeInterface).setVolume(mDeviceTwo, deviceTwoVolume); assertThat(mService.getDeviceVolume(mDeviceTwo)).isEqualTo(deviceTwoVolume); Assert.assertNotEquals(deviceTwoVolume, mService.getDeviceVolume(mDevice)); + inOrderNative.verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt()); } @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES) public void testServiceBinderSetDeviceVolumeNoGroupId() throws Exception { int deviceVolume = 42; + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)) + .thenReturn(LE_AUDIO_GROUP_ID_INVALID); when(mLeAudioService.getGroupId(mDevice)).thenReturn(LE_AUDIO_GROUP_ID_INVALID); generateDeviceAvailableMessageFromNative(mDevice, 1); - generateConnectionMessageFromNative( - mDevice, STATE_CONNECTED, STATE_DISCONNECTED); + generateConnectionMessageFromNative(mDevice, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getDevices()).contains(mDevice); + mBinder.setDeviceVolume(mDevice, deviceVolume, true, mAttributionSource); + verify(mNativeInterface, never()).setVolume(any(), anyInt()); + verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt()); + mBinder.setDeviceVolume(mDevice, deviceVolume, false, mAttributionSource); verify(mNativeInterface).setVolume(mDevice, deviceVolume); assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(deviceVolume); + verify(mNativeInterface, never()).setGroupVolume(anyInt(), anyInt()); } @Test @@ -954,15 +1052,20 @@ public class VolumeControlServiceTest { assertThat(mService.getDevices()).contains(mDevice); mService.setGroupVolume(groupId, groupVolume); - verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); - verify(mNativeInterface, never()).setVolume(eq(mDeviceTwo), eq(groupVolume)); + InOrder inOrderNative = inOrder(mNativeInterface); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); + inOrderNative.verify(mNativeInterface, never()).setVolume(any(), anyInt()); // Verify that second device gets the proper group volume level when connected generateDeviceAvailableMessageFromNative(mDeviceTwo, 1); generateConnectionMessageFromNative(mDeviceTwo, STATE_CONNECTED, STATE_DISCONNECTED); assertThat(mService.getConnectionState(mDeviceTwo)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDeviceTwo); - verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); + generateVolumeStateChanged( + mDeviceTwo, LE_AUDIO_GROUP_ID_INVALID, groupVolume, 0, false, true); + + inOrderNative.verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(groupVolume)); + inOrderNative.verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); // Generate events for both devices generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 100); @@ -1042,8 +1145,8 @@ public class VolumeControlServiceTest { verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume)); // Both devices are in the same group - when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); - when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); // Register callback and verify it is called with known devices IBluetoothVolumeControlCallback callback = @@ -1083,8 +1186,8 @@ public class VolumeControlServiceTest { verify(mNativeInterface).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume)); // Both devices are in the same group - when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); - when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); // Register callback and verify it is called with known devices IBluetoothVolumeControlCallback callback = @@ -1150,13 +1253,13 @@ public class VolumeControlServiceTest { int deviceOneVolume = 46; // Both devices are in the same group - when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); - when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); + when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); + when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); // Send a message to trigger connection completed generateDeviceAvailableMessageFromNative(mDevice, 2); - mBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource); + mService.setDeviceVolume(mDevice, groupVolume, true); verify(mNativeInterface).setGroupVolume(eq(groupId), eq(groupVolume)); // Register callback and verify it is called with known devices @@ -1171,7 +1274,7 @@ public class VolumeControlServiceTest { assertThat(mService.mCallbacks.getRegisteredCallbackCount()).isEqualTo(size + 1); } - when(mLeAudioService.getGroupDevices(groupId)) + when(mCsipService.getGroupDevicesOrdered(groupId)) .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); // Send group volume change. @@ -1181,7 +1284,8 @@ public class VolumeControlServiceTest { verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(groupVolume)); // Send device volume change only for one device - generateVolumeStateChanged(mDevice, -1, deviceOneVolume, 0, false, false); + generateVolumeStateChanged( + mDevice, LE_AUDIO_GROUP_ID_INVALID, deviceOneVolume, 0, false, false); verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume)); verify(callback, never()).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceOneVolume)); @@ -1205,25 +1309,23 @@ public class VolumeControlServiceTest { assertThat(mService.getConnectionState(mDevice)).isEqualTo(STATE_CONNECTED); assertThat(mService.getDevices()).contains(mDevice); - // Make active group as null and broadcast active + // Make active group as null and broadcast not active when(mLeAudioService.getActiveGroupId()).thenReturn(LE_AUDIO_GROUP_ID_INVALID); when(mBassClientService.getSyncedBroadcastSinks()).thenReturn(new ArrayList<>()); - // Group is broadcast primary group, AF will not be notified + // Group is not broadcast primary group, AF will not be notified generateVolumeStateChanged(null, groupId, groupVolume, 0, false, true); - verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + InOrder inOrderAudio = inOrder(mAudioManager); + inOrderAudio.verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); // Make active group as null and broadcast active when(mLeAudioService.getActiveGroupId()).thenReturn(LE_AUDIO_GROUP_ID_INVALID); when(mBassClientService.getSyncedBroadcastSinks()) .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); - when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); - when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); when(mLeAudioService.isPrimaryGroup(groupId)).thenReturn(true); - // Group is not broadcast primary group, AF will not be notified + // Group is broadcast primary group, AF will be notified generateVolumeStateChanged(null, groupId, groupVolume, 0, false, true); - - verify(mAudioManager).setStreamVolume(anyInt(), anyInt(), anyInt()); + inOrderAudio.verify(mAudioManager).setStreamVolume(anyInt(), anyInt(), anyInt()); } private void generateConnectionMessageFromNative( diff --git a/android/pandora/gen_cov.py b/android/pandora/gen_cov.py index 85ed6c6a6d..b3d85b32d6 100755 --- a/android/pandora/gen_cov.py +++ b/android/pandora/gen_cov.py @@ -9,8 +9,8 @@ import subprocess import sys import xml.etree.ElementTree as ET -JAVA_UNIT_TESTS = 'test/mts/tools/mts-tradefed/res/config/mts-bluetooth-tests-list-shard-01.xml' -NATIVE_UNIT_TESTS = 'test/mts/tools/mts-tradefed/res/config/mts-bluetooth-tests-list-shard-02.xml' +JAVA_UNIT_TESTS = 'test/mts/tools/mts-tradefed/res/config/mts-bt-tests-list-shard-01.xml' +NATIVE_UNIT_TESTS = 'test/mts/tools/mts-tradefed/res/config/mts-bt-tests-list-shard-02.xml' DO_NOT_RETRY_TESTS = { 'CtsBluetoothTestCases', 'GoogleBluetoothInstrumentationTests', diff --git a/android/pandora/server/Android.bp b/android/pandora/server/Android.bp index 52e9e405a9..83cb971cd3 100644 --- a/android/pandora/server/Android.bp +++ b/android/pandora/server/Android.bp @@ -77,6 +77,9 @@ android_test { "configs/pts_bot_tests_config.json", "configs/pts_bot_tests_config_auto.json", ], - test_suites: ["mts-bluetooth"], + test_suites: [ + "mts-bluetooth", + "mts-bt", + ], min_sdk_version: "Tiramisu", } diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json index 6ebae93244..758d21f07c 100644 --- a/android/pandora/server/configs/pts_bot_tests_config.json +++ b/android/pandora/server/configs/pts_bot_tests_config.json @@ -3216,15 +3216,6 @@ "flags": [ { "flags": [ - "avdtp_error_codes" - ], - "tests": [ - "A2DP/SNK/AVP", - "A2DP/SRC/AVP" - ] - }, - { - "flags": [ "leaudio_allow_leaudio_only_devices", "enable_hap_by_default" ], diff --git a/android/pandora/server/src/Pbap.kt b/android/pandora/server/src/Pbap.kt index 5b7cef45ad..e8eab8bf1c 100644 --- a/android/pandora/server/src/Pbap.kt +++ b/android/pandora/server/src/Pbap.kt @@ -159,8 +159,6 @@ class Pbap(val context: Context) : PBAPImplBase(), Closeable { """ %d Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus condimentum rhoncus est volutpat venenatis. - Fusce semper, sapien ut venenatis pellentesque, - lorem dui aliquam sapien, non pharetra diam neque id mi. """ } } diff --git a/android/pandora/test/a2dp/__init__.py b/android/pandora/test/a2dp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/android/pandora/test/a2dp/__init__.py diff --git a/android/pandora/test/a2dp/packets/__init__.py b/android/pandora/test/a2dp/packets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/android/pandora/test/a2dp/packets/__init__.py diff --git a/android/pandora/test/a2dp/packets/avdtp.pdl b/android/pandora/test/a2dp/packets/avdtp.pdl new file mode 100644 index 0000000000..ff9fc849dc --- /dev/null +++ b/android/pandora/test/a2dp/packets/avdtp.pdl @@ -0,0 +1,343 @@ +little_endian_packets + +enum PacketType : 2 { + SINGLE_PACKET = 0, + START_PACKET = 1, + CONTINUE_PACKET = 2, + END_PACKET = 3, +} + +enum MessageType : 2 { + COMMAND = 0, + GENERAL_REJECT = 1, + RESPONSE_ACCEPT = 2, + RESPONSE_REJECT = 3, +} + +enum SignalIdentifier : 6 { + AVDTP_DISCOVER = 0x01, + AVDTP_GET_CAPABILITIES = 0x02, + AVDTP_SET_CONFIGURATION = 0x03, + AVDTP_GET_CONFIGURATION = 0x04, + AVDTP_RECONFIGURE = 0x05, + AVDTP_OPEN = 0x06, + AVDTP_START = 0x07, + AVDTP_CLOSE = 0x08, + AVDTP_SUSPEND = 0x09, + AVDTP_ABORT = 0x0A, + AVDTP_SECURITY_CONTROL = 0x0B, + AVDTP_GET_ALL_CAPABILITIES = 0x0C, + AVDTP_DELAYREPORT = 0x0D, +} + +enum ErrorCode : 8 { + SUCCESS = 0, + + // [AVDTP_1.3] 8.20.6.2 ERROR_CODE tables. + AVDTP_BAD_HEADER_FORMAT = 0x01, + AVDTP_BAD_LENGTH = 0x11, + AVDTP_BAD_ACP_SEID = 0x12, + AVDTP_SEP_IN_USE = 0x13, + AVDTP_SEP_NOT_IN_USE = 0x14, + AVDTP_BAD_SERV_CATEGORY = 0x17, + AVDTP_BAD_PAYLOAD_FORMAT = 0x18, + AVDTP_NOT_SUPPORTED_COMMAND = 0x19, + AVDTP_INVALID_CAPABILITIES = 0x1A, + AVDTP_BAD_RECOVERY_TYPE = 0x22, + AVDTP_BAD_MEDIA_TRANSPORT_FORMAT = 0x23, + AVDTP_BAD_RECOVERY_FORMAT = 0x25, + AVDTP_BAD_ROHC_FORMAT = 0x26, + AVDTP_BAD_CP_FORMAT = 0x27, + AVDTP_BAD_MULTIPLEXING_FORMAT = 0x28, + AVDTP_UNSUPPORTED_CONFIGURATION = 0x29, + AVDTP_BAD_STATE = 0x31, + + // [GAVDTP_1.3] 3.3 Error codes. + GAVDTP_BAD_SERVICE = 0x80, + GAVDTP_INSUFFICIENT_RESOURCES = 0x81, + + // [A2DP_1.3.2] 5.1.3 Error Codes. + A2DP_INVALID_CODEC_TYPE = 0xC1, + A2DP_NOT_SUPPORTED_CODEC_TYPE = 0xC2, + A2DP_INVALID_SAMPLING_FREQUENCY = 0xC3, + A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xC4, + A2DP_INVALID_CHANNEL_MODE = 0xC5, + A2DP_NOT_SUPPORTED_CHANNEL_MODE = 0xC6, + A2DP_INVALID_SUBBANDS = 0xC7, + A2DP_NOT_SUPPORTED_SUBBANDS = 0xC8, + A2DP_INVALID_ALLOCATION_METHOD = 0xC9, + A2DP_NOT_SUPPORTED_ALLOCATION_METHOD = 0xCA, + A2DP_INVALID_MINIMUM_BITPOOL_VALUE = 0xCB, + A2DP_NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xCC, + A2DP_INVALID_MAXIMUM_BITPOOL_VALUE = 0xCD, + A2DP_NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xCE, + A2DP_INVALID_LAYER = 0xCF, + A2DP_NOT_SUPPORTED_LAYER = 0xD0, + A2DP_NOT_SUPPORTED_CRC = 0xD1, + A2DP_NOT_SUPPORTED_MPF = 0xD2, + A2DP_NOT_SUPPORTED_VBR = 0xD3, + A2DP_INVALID_BIT_RATE = 0xD4, + A2DP_NOT_SUPPORTED_BIT_RATE = 0xD5, + A2DP_INVALID_OBJECT_TYPE = 0xD6, + A2DP_NOT_SUPPORTED_OBJECT_TYPE = 0xD7, + A2DP_INVALID_CHANNELS = 0xD8, + A2DP_NOT_SUPPORTED_CHANNELS = 0xD9, + A2DP_INVALID_BLOCK_LENGTH = 0xDD, + A2DP_INVALID_CP_TYPE = 0xE0, + A2DP_INVALID_CP_FORMAT = 0xE1, + A2DP_INVALID_CODEC_PARAMETER = 0xE2, + A2DP_NOT_SUPPORTED_CODEC_PARAMETER = 0xE3, +} + +enum Tsep : 1 { + SOURCE = 0, + SINK = 1, +} + +enum ServiceCategory : 8 { + MEDIA_TRANSPORT = 0x1, + REPORTING = 0x2, + RECOVERY = 0x3, + CONTENT_PROTECTION = 0x4, + HEADER_COMPRESSION = 0x5, + MULTIPLEXING = 0x6, + MEDIA_CODEC = 0x7, + DELAY_REPORTING = 0x8, +} + +struct SeidInformation { + _reserved_ : 1, + in_use : 1, + acp_seid : 6, + _reserved_ : 3, + tsep : Tsep, + media_type : 4, +} + +struct ServiceCapability { + service_category : ServiceCategory, + _size_ (_payload_) : 8, + _payload_, +} + +struct MediaTransportCapability : ServiceCapability (service_category = MEDIA_TRANSPORT) { +} + +struct ReportingCapability : ServiceCapability (service_category = REPORTING) { +} + +struct RecoveryCapability : ServiceCapability (service_category = RECOVERY) { + recovery_type : 8, + maximum_recovery_window_size : 8, + maximum_number_of_media_packets_in_parity_code : 8, +} + +struct ContentProtectionCapability : ServiceCapability (service_category = CONTENT_PROTECTION) { + cp_type : 16, // Little endian + _payload_, +} + +struct HeaderCompressionCapability : ServiceCapability (service_category = HEADER_COMPRESSION) { + _reserved_ : 5, + recovery : 1, + media : 1, + back_ch : 1, +} + +struct MultiplexingCapability : ServiceCapability (service_category = MULTIPLEXING) { + _reserved_ : 7, + frag : 1, + _payload_, +} + +struct MediaCodecCapability : ServiceCapability (service_category = MEDIA_CODEC) { + _reserved_ : 4, + media_type : 4, + media_codec_type : 8, + media_codec_specific_information_elements : 8[], +} + +struct DelayReportingCapability : ServiceCapability (service_category = DELAY_REPORTING) { +} + +packet SignalingPacket { + message_type : MessageType, + packet_type : PacketType, + transaction_label : 4, + signal_identifier : SignalIdentifier, + _reserved_ : 2, + _payload_, +} + +packet DiscoverCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_DISCOVER) { +} + +packet DiscoverResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_DISCOVER) { + seid_information : SeidInformation[] +} + +packet DiscoverReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_DISCOVER) { + error_code : ErrorCode, +} + +packet GetCapabilitiesCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_GET_CAPABILITIES) { + _reserved_ : 2, + acp_seid : 6, +} + +packet GetCapabilitiesResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_GET_CAPABILITIES) { + service_capabilities: ServiceCapability[], +} + +packet GetCapabilitiesReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_GET_CAPABILITIES) { + error_code: ErrorCode, +} + +packet GetAllCapabilitiesCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_GET_ALL_CAPABILITIES) { + _reserved_ : 2, + acp_seid : 6, +} + +packet GetAllCapabilitiesResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_GET_ALL_CAPABILITIES) { + service_capabilities: ServiceCapability[], +} + +packet GetAllCapabilitiesReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_GET_ALL_CAPABILITIES) { + error_code: ErrorCode, +} + +packet SetConfigurationCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_SET_CONFIGURATION) { + _reserved_ : 2, + acp_seid : 6, + _reserved_ : 2, + int_seid : 6, + service_capabilities: ServiceCapability[], +} + +packet SetConfigurationResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_SET_CONFIGURATION) { +} + +packet SetConfigurationReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_SET_CONFIGURATION) { + service_category: 8, + error_code: ErrorCode, +} + +packet GetConfigurationCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_GET_CONFIGURATION) { + _reserved_ : 2, + acp_seid : 6, +} + +packet GetConfigurationResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_GET_CONFIGURATION) { + service_capabilities: ServiceCapability[], +} + +packet GetConfigurationReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_GET_CONFIGURATION) { + error_code: ErrorCode, +} + +packet ReconfigureCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_RECONFIGURE) { + _reserved_ : 2, + acp_seid : 6, + service_capabilities: ServiceCapability[], +} + +packet ReconfigureResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_RECONFIGURE) { +} + +packet ReconfigureReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_RECONFIGURE) { + service_category: 8, + error_code: ErrorCode, +} + +packet OpenCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_OPEN) { + _reserved_ : 2, + acp_seid : 6, +} + +packet OpenResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_OPEN) { +} + +packet OpenReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_OPEN) { + error_code: ErrorCode, +} + +packet StartCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_START) { + _reserved_ : 2, + acp_seid : 6, + // acp_seids: 8[], +} + +packet StartResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_START) { +} + +packet StartReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_START) { + _reserved_ : 2, + acp_seid : 6, + error_code: ErrorCode, +} + +packet CloseCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_CLOSE) { + _reserved_ : 2, + acp_seid: 6, +} + +packet CloseResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_CLOSE) { +} + +packet CloseReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_CLOSE) { + error_code: ErrorCode, +} + +packet SuspendCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_SUSPEND) { + _reserved_ : 2, + acp_seid : 6, + // acp_seids: 8[], +} + +packet SuspendResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_SUSPEND) { +} + +packet SuspendReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_SUSPEND) { + _reserved_ : 2, + acp_seid : 6, + error_code: ErrorCode, +} + +packet AbortCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_ABORT) { + _reserved_ : 2, + acp_seid : 6, +} + +packet AbortResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_ABORT) { +} + +packet SecurityControlCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_SECURITY_CONTROL) { + _reserved_ : 2, + acp_seid : 6, + content_protection_data: 8[], +} + +packet SecurityControlResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_SECURITY_CONTROL) { + content_protection_data: 8[], +} + +packet SecurityControlReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_SECURITY_CONTROL) { + error_code: ErrorCode, +} + +packet GeneralReject : SignalingPacket (message_type = GENERAL_REJECT) { +} + +packet DelayReportCommand : SignalingPacket (message_type = COMMAND, signal_identifier = AVDTP_DELAYREPORT) { + _reserved_ : 2, + acp_seid : 6, + delay_msb: 8, + delay_lsb: 8, +} + +packet DelayReportResponse : SignalingPacket (message_type = RESPONSE_ACCEPT, signal_identifier = AVDTP_DELAYREPORT) { +} + +packet DelayReportReject : SignalingPacket (message_type = RESPONSE_REJECT, signal_identifier = AVDTP_DELAYREPORT) { + error_code: ErrorCode, +} diff --git a/android/pandora/test/a2dp/packets/avdtp.py b/android/pandora/test/a2dp/packets/avdtp.py new file mode 100644 index 0000000000..15f9e29d27 --- /dev/null +++ b/android/pandora/test/a2dp/packets/avdtp.py @@ -0,0 +1,2109 @@ +# File generated from avdtp_packets.pdl with the command: +# pdlc avdtp_packets.pdl | pdl-compiler/scripts/generate_python_backend.py +# /!\ Do not edit by hand. +from dataclasses import dataclass, field, fields +from typing import Optional, List, Tuple, Union +import enum +import inspect +import math + + +@dataclass +class Packet: + payload: Optional[bytes] = field(repr=False, default_factory=bytes, compare=False) + + @classmethod + def parse_all(cls, span: bytes) -> "Packet": + packet, remain = getattr(cls, "parse")(span) + if len(remain) > 0: + raise Exception("Unexpected parsing remainder") + return packet + + @property + def size(self) -> int: + pass + + def show(self, prefix: str = ""): + print(f"{self.__class__.__name__}") + + def print_val(p: str, pp: str, name: str, align: int, typ, val): + if name == "payload": + pass + + # Scalar fields. + elif typ is int: + print(f"{p}{name:{align}} = {val} (0x{val:x})") + + # Byte fields. + elif typ is bytes: + print(f"{p}{name:{align}} = [", end="") + line = "" + n_pp = "" + for idx, b in enumerate(val): + if idx > 0 and idx % 8 == 0: + print(f"{n_pp}{line}") + line = "" + n_pp = pp + (" " * (align + 4)) + line += f" {b:02x}" + print(f"{n_pp}{line} ]") + + # Enum fields. + elif inspect.isclass(typ) and issubclass(typ, enum.IntEnum): + print(f"{p}{name:{align}} = {typ.__name__}::{val.name} (0x{val:x})") + + # Struct fields. + elif inspect.isclass(typ) and issubclass(typ, globals().get("Packet")): + print(f"{p}{name:{align}} = ", end="") + val.show(prefix=pp) + + # Array fields. + elif getattr(typ, "__origin__", None) == list: + print(f"{p}{name:{align}}") + last = len(val) - 1 + align = 5 + for idx, elt in enumerate(val): + n_p = pp + ("├── " if idx != last else "└── ") + n_pp = pp + ("│ " if idx != last else " ") + print_val(n_p, n_pp, f"[{idx}]", align, typ.__args__[0], val[idx]) + + # Custom fields. + elif inspect.isclass(typ): + print(f"{p}{name:{align}} = {repr(val)}") + + else: + print(f"{p}{name:{align}} = ##{typ}##") + + last = len(fields(self)) - 1 + align = max(len(f.name) for f in fields(self) if f.name != "payload") + + for idx, f in enumerate(fields(self)): + p = prefix + ("├── " if idx != last else "└── ") + pp = prefix + ("│ " if idx != last else " ") + val = getattr(self, f.name) + + print_val(p, pp, f.name, align, f.type, val) + + +class PacketType(enum.IntEnum): + SINGLE_PACKET = 0x0 + START_PACKET = 0x1 + CONTINUE_PACKET = 0x2 + END_PACKET = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, "PacketType"]: + try: + return PacketType(v) + except ValueError as exn: + raise exn + + +class MessageType(enum.IntEnum): + COMMAND = 0x0 + GENERAL_REJECT = 0x1 + RESPONSE_ACCEPT = 0x2 + RESPONSE_REJECT = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, "MessageType"]: + try: + return MessageType(v) + except ValueError as exn: + raise exn + + +class SignalIdentifier(enum.IntEnum): + AVDTP_DISCOVER = 0x1 + AVDTP_GET_CAPABILITIES = 0x2 + AVDTP_SET_CONFIGURATION = 0x3 + AVDTP_GET_CONFIGURATION = 0x4 + AVDTP_RECONFIGURE = 0x5 + AVDTP_OPEN = 0x6 + AVDTP_START = 0x7 + AVDTP_CLOSE = 0x8 + AVDTP_SUSPEND = 0x9 + AVDTP_ABORT = 0xA + AVDTP_SECURITY_CONTROL = 0xB + AVDTP_GET_ALL_CAPABILITIES = 0xC + AVDTP_DELAYREPORT = 0xD + + @staticmethod + def from_int(v: int) -> Union[int, "SignalIdentifier"]: + try: + return SignalIdentifier(v) + except ValueError as exn: + raise exn + + +class ErrorCode(enum.IntEnum): + SUCCESS = 0x0 + AVDTP_BAD_HEADER_FORMAT = 0x1 + AVDTP_BAD_LENGTH = 0x11 + AVDTP_BAD_ACP_SEID = 0x12 + AVDTP_SEP_IN_USE = 0x13 + AVDTP_SEP_NOT_IN_USE = 0x14 + AVDTP_BAD_SERV_CATEGORY = 0x17 + AVDTP_BAD_PAYLOAD_FORMAT = 0x18 + AVDTP_NOT_SUPPORTED_COMMAND = 0x19 + AVDTP_INVALID_CAPABILITIES = 0x1A + AVDTP_BAD_RECOVERY_TYPE = 0x22 + AVDTP_BAD_MEDIA_TRANSPORT_FORMAT = 0x23 + AVDTP_BAD_RECOVERY_FORMAT = 0x25 + AVDTP_BAD_ROHC_FORMAT = 0x26 + AVDTP_BAD_CP_FORMAT = 0x27 + AVDTP_BAD_MULTIPLEXING_FORMAT = 0x28 + AVDTP_UNSUPPORTED_CONFIGURATION = 0x29 + AVDTP_BAD_STATE = 0x31 + GAVDTP_BAD_SERVICE = 0x80 + GAVDTP_INSUFFICIENT_RESOURCES = 0x81 + A2DP_INVALID_CODEC_TYPE = 0xC1 + A2DP_NOT_SUPPORTED_CODEC_TYPE = 0xC2 + A2DP_INVALID_SAMPLING_FREQUENCY = 0xC3 + A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xC4 + A2DP_INVALID_CHANNEL_MODE = 0xC5 + A2DP_NOT_SUPPORTED_CHANNEL_MODE = 0xC6 + A2DP_INVALID_SUBBANDS = 0xC7 + A2DP_NOT_SUPPORTED_SUBBANDS = 0xC8 + A2DP_INVALID_ALLOCATION_METHOD = 0xC9 + A2DP_NOT_SUPPORTED_ALLOCATION_METHOD = 0xCA + A2DP_INVALID_MINIMUM_BITPOOL_VALUE = 0xCB + A2DP_NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xCC + A2DP_INVALID_MAXIMUM_BITPOOL_VALUE = 0xCD + A2DP_NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xCE + A2DP_INVALID_LAYER = 0xCF + A2DP_NOT_SUPPORTED_LAYER = 0xD0 + A2DP_NOT_SUPPORTED_CRC = 0xD1 + A2DP_NOT_SUPPORTED_MPF = 0xD2 + A2DP_NOT_SUPPORTED_VBR = 0xD3 + A2DP_INVALID_BIT_RATE = 0xD4 + A2DP_NOT_SUPPORTED_BIT_RATE = 0xD5 + A2DP_INVALID_OBJECT_TYPE = 0xD6 + A2DP_NOT_SUPPORTED_OBJECT_TYPE = 0xD7 + A2DP_INVALID_CHANNELS = 0xD8 + A2DP_NOT_SUPPORTED_CHANNELS = 0xD9 + A2DP_INVALID_BLOCK_LENGTH = 0xDD + A2DP_INVALID_CP_TYPE = 0xE0 + A2DP_INVALID_CP_FORMAT = 0xE1 + A2DP_INVALID_CODEC_PARAMETER = 0xE2 + A2DP_NOT_SUPPORTED_CODEC_PARAMETER = 0xE3 + + @staticmethod + def from_int(v: int) -> Union[int, "ErrorCode"]: + try: + return ErrorCode(v) + except ValueError as exn: + raise exn + + +class Tsep(enum.IntEnum): + SOURCE = 0x0 + SINK = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, "Tsep"]: + try: + return Tsep(v) + except ValueError as exn: + raise exn + + +class ServiceCategory(enum.IntEnum): + MEDIA_TRANSPORT = 0x1 + REPORTING = 0x2 + RECOVERY = 0x3 + CONTENT_PROTECTION = 0x4 + HEADER_COMPRESSION = 0x5 + MULTIPLEXING = 0x6 + MEDIA_CODEC = 0x7 + DELAY_REPORTING = 0x8 + + @staticmethod + def from_int(v: int) -> Union[int, "ServiceCategory"]: + try: + return ServiceCategory(v) + except ValueError as exn: + raise exn + + +@dataclass +class SeidInformation(Packet): + in_use: int = field(kw_only=True, default=0) + acp_seid: int = field(kw_only=True, default=0) + tsep: Tsep = field(kw_only=True, default=Tsep.SOURCE) + media_type: int = field(kw_only=True, default=0) + + def __post_init__(self): + pass + + @staticmethod + def parse(span: bytes) -> Tuple["SeidInformation", bytes]: + fields = {"payload": None} + if len(span) < 2: + raise Exception("Invalid packet size") + fields["in_use"] = (span[0] >> 1) & 0x1 + fields["acp_seid"] = (span[0] >> 2) & 0x3F + fields["tsep"] = Tsep.from_int((span[1] >> 3) & 0x1) + fields["media_type"] = (span[1] >> 4) & 0xF + span = span[2:] + return SeidInformation(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.in_use > 1: + print(f"Invalid value for field SeidInformation::in_use: {self.in_use} > 1; the value will be truncated") + self.in_use &= 1 + if self.acp_seid > 63: + print( + f"Invalid value for field SeidInformation::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _value = (self.in_use << 1) | (self.acp_seid << 2) + _span.append(_value) + if self.media_type > 15: + print( + f"Invalid value for field SeidInformation::media_type: {self.media_type} > 15; the value will be truncated" + ) + self.media_type &= 15 + _value = (self.tsep << 3) | (self.media_type << 4) + _span.append(_value) + return bytes(_span) + + @property + def size(self) -> int: + return 2 + + +@dataclass +class ServiceCapability(Packet): + service_category: ServiceCategory = field(kw_only=True, default=ServiceCategory.MEDIA_TRANSPORT) + + def __post_init__(self): + pass + + @staticmethod + def parse(span: bytes) -> Tuple["ServiceCapability", bytes]: + fields = {"payload": None} + if len(span) < 2: + raise Exception("Invalid packet size") + fields["service_category"] = ServiceCategory.from_int(span[0]) + _payload__size = span[1] + span = span[2:] + if len(span) < _payload__size: + raise Exception("Invalid packet size") + payload = span[:_payload__size] + span = span[_payload__size:] + fields["payload"] = payload + try: + child, remain = MediaTransportCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = ReportingCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = RecoveryCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = ContentProtectionCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = HeaderCompressionCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = MultiplexingCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = MediaCodecCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DelayReportingCapability.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + return ServiceCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.service_category << 0)) + _payload_size = len(payload or self.payload or []) + if _payload_size > 255: + print(f"Invalid length for payload field: {_payload_size} > 255; the packet cannot be generated") + raise Exception("Invalid payload length") + _span.append((_payload_size << 0)) + _span.extend(payload or self.payload or []) + return bytes(_span) + + @property + def size(self) -> int: + return len(self.payload) + 2 + + +@dataclass +class MediaTransportCapability(ServiceCapability): + + def __post_init__(self): + self.service_category = ServiceCategory.MEDIA_TRANSPORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["MediaTransportCapability", bytes]: + if fields["service_category"] != ServiceCategory.MEDIA_TRANSPORT: + raise Exception("Invalid constraint field values") + return MediaTransportCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class ReportingCapability(ServiceCapability): + + def __post_init__(self): + self.service_category = ServiceCategory.REPORTING + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["ReportingCapability", bytes]: + if fields["service_category"] != ServiceCategory.REPORTING: + raise Exception("Invalid constraint field values") + return ReportingCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class RecoveryCapability(ServiceCapability): + recovery_type: int = field(kw_only=True, default=0) + maximum_recovery_window_size: int = field(kw_only=True, default=0) + maximum_number_of_media_packets_in_parity_code: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.service_category = ServiceCategory.RECOVERY + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["RecoveryCapability", bytes]: + if fields["service_category"] != ServiceCategory.RECOVERY: + raise Exception("Invalid constraint field values") + if len(span) < 3: + raise Exception("Invalid packet size") + fields["recovery_type"] = span[0] + fields["maximum_recovery_window_size"] = span[1] + fields["maximum_number_of_media_packets_in_parity_code"] = span[2] + span = span[3:] + return RecoveryCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.recovery_type > 255: + print( + f"Invalid value for field RecoveryCapability::recovery_type: {self.recovery_type} > 255; the value will be truncated" + ) + self.recovery_type &= 255 + _span.append((self.recovery_type << 0)) + if self.maximum_recovery_window_size > 255: + print( + f"Invalid value for field RecoveryCapability::maximum_recovery_window_size: {self.maximum_recovery_window_size} > 255; the value will be truncated" + ) + self.maximum_recovery_window_size &= 255 + _span.append((self.maximum_recovery_window_size << 0)) + if self.maximum_number_of_media_packets_in_parity_code > 255: + print( + f"Invalid value for field RecoveryCapability::maximum_number_of_media_packets_in_parity_code: {self.maximum_number_of_media_packets_in_parity_code} > 255; the value will be truncated" + ) + self.maximum_number_of_media_packets_in_parity_code &= 255 + _span.append((self.maximum_number_of_media_packets_in_parity_code << 0)) + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 3 + + +@dataclass +class ContentProtectionCapability(ServiceCapability): + cp_type: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.service_category = ServiceCategory.CONTENT_PROTECTION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["ContentProtectionCapability", bytes]: + if fields["service_category"] != ServiceCategory.CONTENT_PROTECTION: + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + value_ = int.from_bytes(span[0:2], byteorder="little") + fields["cp_type"] = value_ + span = span[2:] + payload = span + span = bytes([]) + fields["payload"] = payload + return ContentProtectionCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.cp_type > 65535: + print( + f"Invalid value for field ContentProtectionCapability::cp_type: {self.cp_type} > 65535; the value will be truncated" + ) + self.cp_type &= 65535 + _span.extend(int.to_bytes((self.cp_type << 0), length=2, byteorder="little")) + _span.extend(payload or self.payload or []) + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return len(self.payload) + 2 + + +@dataclass +class HeaderCompressionCapability(ServiceCapability): + recovery: int = field(kw_only=True, default=0) + media: int = field(kw_only=True, default=0) + back_ch: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.service_category = ServiceCategory.HEADER_COMPRESSION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["HeaderCompressionCapability", bytes]: + if fields["service_category"] != ServiceCategory.HEADER_COMPRESSION: + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["recovery"] = (span[0] >> 5) & 0x1 + fields["media"] = (span[0] >> 6) & 0x1 + fields["back_ch"] = (span[0] >> 7) & 0x1 + span = span[1:] + return HeaderCompressionCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.recovery > 1: + print( + f"Invalid value for field HeaderCompressionCapability::recovery: {self.recovery} > 1; the value will be truncated" + ) + self.recovery &= 1 + if self.media > 1: + print( + f"Invalid value for field HeaderCompressionCapability::media: {self.media} > 1; the value will be truncated" + ) + self.media &= 1 + if self.back_ch > 1: + print( + f"Invalid value for field HeaderCompressionCapability::back_ch: {self.back_ch} > 1; the value will be truncated" + ) + self.back_ch &= 1 + _value = (self.recovery << 5) | (self.media << 6) | (self.back_ch << 7) + _span.append(_value) + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class MultiplexingCapability(ServiceCapability): + frag: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.service_category = ServiceCategory.MULTIPLEXING + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["MultiplexingCapability", bytes]: + if fields["service_category"] != ServiceCategory.MULTIPLEXING: + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["frag"] = (span[0] >> 7) & 0x1 + span = span[1:] + payload = span + span = bytes([]) + fields["payload"] = payload + return MultiplexingCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.frag > 1: + print(f"Invalid value for field MultiplexingCapability::frag: {self.frag} > 1; the value will be truncated") + self.frag &= 1 + _span.append((self.frag << 7)) + _span.extend(payload or self.payload or []) + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return len(self.payload) + 1 + + +@dataclass +class MediaCodecCapability(ServiceCapability): + media_type: int = field(kw_only=True, default=0) + media_codec_type: int = field(kw_only=True, default=0) + media_codec_specific_information_elements: bytearray = field(kw_only=True, default_factory=bytearray) + + def __post_init__(self): + self.service_category = ServiceCategory.MEDIA_CODEC + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["MediaCodecCapability", bytes]: + if fields["service_category"] != ServiceCategory.MEDIA_CODEC: + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["media_type"] = (span[0] >> 4) & 0xF + fields["media_codec_type"] = span[1] + span = span[2:] + fields["media_codec_specific_information_elements"] = list(span) + span = bytes() + return MediaCodecCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.media_type > 15: + print( + f"Invalid value for field MediaCodecCapability::media_type: {self.media_type} > 15; the value will be truncated" + ) + self.media_type &= 15 + _span.append((self.media_type << 4)) + if self.media_codec_type > 255: + print( + f"Invalid value for field MediaCodecCapability::media_codec_type: {self.media_codec_type} > 255; the value will be truncated" + ) + self.media_codec_type &= 255 + _span.append((self.media_codec_type << 0)) + _span.extend(self.media_codec_specific_information_elements) + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return len(self.media_codec_specific_information_elements) * 1 + 2 + + +@dataclass +class DelayReportingCapability(ServiceCapability): + + def __post_init__(self): + self.service_category = ServiceCategory.DELAY_REPORTING + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DelayReportingCapability", bytes]: + if fields["service_category"] != ServiceCategory.DELAY_REPORTING: + raise Exception("Invalid constraint field values") + return DelayReportingCapability(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return ServiceCapability.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class SignalingPacket(Packet): + message_type: MessageType = field(kw_only=True, default=MessageType.COMMAND) + packet_type: PacketType = field(kw_only=True, default=PacketType.SINGLE_PACKET) + transaction_label: int = field(kw_only=True, default=0) + signal_identifier: SignalIdentifier = field(kw_only=True, default=SignalIdentifier.AVDTP_DISCOVER) + + def __post_init__(self): + pass + + @staticmethod + def parse(span: bytes) -> Tuple["SignalingPacket", bytes]: + fields = {"payload": None} + if len(span) < 2: + raise Exception("Invalid packet size") + fields["message_type"] = MessageType.from_int((span[0] >> 0) & 0x3) + fields["packet_type"] = PacketType.from_int((span[0] >> 2) & 0x3) + fields["transaction_label"] = (span[0] >> 4) & 0xF + fields["signal_identifier"] = SignalIdentifier.from_int((span[1] >> 0) & 0x3F) + span = span[2:] + payload = span + span = bytes([]) + fields["payload"] = payload + try: + child, remain = DiscoverCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DiscoverResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DiscoverReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetCapabilitiesCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetCapabilitiesResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetCapabilitiesReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetAllCapabilitiesCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetAllCapabilitiesResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetAllCapabilitiesReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SetConfigurationCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SetConfigurationResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SetConfigurationReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetConfigurationCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetConfigurationResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GetConfigurationReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = ReconfigureCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = ReconfigureResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = ReconfigureReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = OpenCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = OpenResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = OpenReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = StartCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = StartResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = StartReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = CloseCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = CloseResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = CloseReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SuspendCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SuspendResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SuspendReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = AbortCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = AbortResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SecurityControlCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SecurityControlResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = SecurityControlReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = GeneralReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DelayReportCommand.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DelayReportResponse.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + try: + child, remain = DelayReportReject.parse(fields.copy(), payload) + assert len(remain) == 0 + return child, span + except Exception as exn: + pass + return SignalingPacket(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.transaction_label > 15: + print( + f"Invalid value for field SignalingPacket::transaction_label: {self.transaction_label} > 15; the value will be truncated" + ) + self.transaction_label &= 15 + _value = ((self.message_type << 0) | (self.packet_type << 2) | (self.transaction_label << 4)) + _span.append(_value) + _span.append((self.signal_identifier << 0)) + _span.extend(payload or self.payload or []) + return bytes(_span) + + @property + def size(self) -> int: + return len(self.payload) + 2 + + +@dataclass +class DiscoverCommand(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_DISCOVER + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DiscoverCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DISCOVER): + raise Exception("Invalid constraint field values") + return DiscoverCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class DiscoverResponse(SignalingPacket): + seid_information: List[SeidInformation] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_DISCOVER + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DiscoverResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DISCOVER): + raise Exception("Invalid constraint field values") + if len(span) % 2 != 0: + raise Exception("Array size is not a multiple of the element size") + seid_information_count = int(len(span) / 2) + seid_information = [] + for n in range(seid_information_count): + seid_information.append(SeidInformation.parse_all(span[n * 2:(n + 1) * 2])) + fields["seid_information"] = seid_information + span = bytes() + return DiscoverResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + for _elt in self.seid_information: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.seid_information]) + + +@dataclass +class DiscoverReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_DISCOVER + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DiscoverReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DISCOVER): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return DiscoverReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GetCapabilitiesCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_GET_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetCapabilitiesCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CAPABILITIES): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return GetCapabilitiesCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field GetCapabilitiesCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GetCapabilitiesResponse(SignalingPacket): + service_capabilities: List[ServiceCapability] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_GET_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetCapabilitiesResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CAPABILITIES): + raise Exception("Invalid constraint field values") + service_capabilities = [] + while len(span) > 0: + element, span = ServiceCapability.parse(span) + service_capabilities.append(element) + fields["service_capabilities"] = service_capabilities + return GetCapabilitiesResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + for _elt in self.service_capabilities: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.service_capabilities]) + + +@dataclass +class GetCapabilitiesReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_GET_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetCapabilitiesReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CAPABILITIES): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return GetCapabilitiesReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GetAllCapabilitiesCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetAllCapabilitiesCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return GetAllCapabilitiesCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field GetAllCapabilitiesCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GetAllCapabilitiesResponse(SignalingPacket): + service_capabilities: List[ServiceCapability] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetAllCapabilitiesResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES): + raise Exception("Invalid constraint field values") + service_capabilities = [] + while len(span) > 0: + element, span = ServiceCapability.parse(span) + service_capabilities.append(element) + fields["service_capabilities"] = service_capabilities + return GetAllCapabilitiesResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + for _elt in self.service_capabilities: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.service_capabilities]) + + +@dataclass +class GetAllCapabilitiesReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetAllCapabilitiesReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_ALL_CAPABILITIES): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return GetAllCapabilitiesReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class SetConfigurationCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + int_seid: int = field(kw_only=True, default=0) + service_capabilities: List[ServiceCapability] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_SET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SetConfigurationCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SET_CONFIGURATION): + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + fields["int_seid"] = (span[1] >> 2) & 0x3F + span = span[2:] + service_capabilities = [] + while len(span) > 0: + element, span = ServiceCapability.parse(span) + service_capabilities.append(element) + fields["service_capabilities"] = service_capabilities + return SetConfigurationCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field SetConfigurationCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + if self.int_seid > 63: + print( + f"Invalid value for field SetConfigurationCommand::int_seid: {self.int_seid} > 63; the value will be truncated" + ) + self.int_seid &= 63 + _span.append((self.int_seid << 2)) + for _elt in self.service_capabilities: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.service_capabilities]) + 2 + + +@dataclass +class SetConfigurationResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_SET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SetConfigurationResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SET_CONFIGURATION): + raise Exception("Invalid constraint field values") + return SetConfigurationResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class SetConfigurationReject(SignalingPacket): + service_category: int = field(kw_only=True, default=0) + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_SET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SetConfigurationReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SET_CONFIGURATION): + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["service_category"] = span[0] + fields["error_code"] = ErrorCode.from_int(span[1]) + span = span[2:] + return SetConfigurationReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.service_category > 255: + print( + f"Invalid value for field SetConfigurationReject::service_category: {self.service_category} > 255; the value will be truncated" + ) + self.service_category &= 255 + _span.append((self.service_category << 0)) + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 2 + + +@dataclass +class GetConfigurationCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_GET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetConfigurationCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CONFIGURATION): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return GetConfigurationCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field GetConfigurationCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GetConfigurationResponse(SignalingPacket): + service_capabilities: List[ServiceCapability] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_GET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetConfigurationResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CONFIGURATION): + raise Exception("Invalid constraint field values") + service_capabilities = [] + while len(span) > 0: + element, span = ServiceCapability.parse(span) + service_capabilities.append(element) + fields["service_capabilities"] = service_capabilities + return GetConfigurationResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + for _elt in self.service_capabilities: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.service_capabilities]) + + +@dataclass +class GetConfigurationReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_GET_CONFIGURATION + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GetConfigurationReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_GET_CONFIGURATION): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return GetConfigurationReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class ReconfigureCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + service_capabilities: List[ServiceCapability] = field(kw_only=True, default_factory=list) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_RECONFIGURE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["ReconfigureCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_RECONFIGURE): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + service_capabilities = [] + while len(span) > 0: + element, span = ServiceCapability.parse(span) + service_capabilities.append(element) + fields["service_capabilities"] = service_capabilities + return ReconfigureCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field ReconfigureCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + for _elt in self.service_capabilities: + _span.extend(_elt.serialize()) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return sum([elt.size for elt in self.service_capabilities]) + 1 + + +@dataclass +class ReconfigureResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_RECONFIGURE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["ReconfigureResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_RECONFIGURE): + raise Exception("Invalid constraint field values") + return ReconfigureResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class ReconfigureReject(SignalingPacket): + service_category: int = field(kw_only=True, default=0) + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_RECONFIGURE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["ReconfigureReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_RECONFIGURE): + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["service_category"] = span[0] + fields["error_code"] = ErrorCode.from_int(span[1]) + span = span[2:] + return ReconfigureReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.service_category > 255: + print( + f"Invalid value for field ReconfigureReject::service_category: {self.service_category} > 255; the value will be truncated" + ) + self.service_category &= 255 + _span.append((self.service_category << 0)) + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 2 + + +@dataclass +class OpenCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_OPEN + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["OpenCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_OPEN): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return OpenCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print(f"Invalid value for field OpenCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class OpenResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_OPEN + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["OpenResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_OPEN): + raise Exception("Invalid constraint field values") + return OpenResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class OpenReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_OPEN + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["OpenReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_OPEN): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return OpenReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class StartCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_START + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["StartCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_START): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return StartCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print(f"Invalid value for field StartCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class StartResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_START + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["StartResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_START): + raise Exception("Invalid constraint field values") + return StartResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class StartReject(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_START + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["StartReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_START): + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + fields["error_code"] = ErrorCode.from_int(span[1]) + span = span[2:] + return StartReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print(f"Invalid value for field StartReject::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 2 + + +@dataclass +class CloseCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_CLOSE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["CloseCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_CLOSE): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return CloseCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 255: + print(f"Invalid value for field CloseCommand::acp_seid: {self.acp_seid} > 255; the value will be truncated") + self.acp_seid &= 255 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class CloseResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_CLOSE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["CloseResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_CLOSE): + raise Exception("Invalid constraint field values") + return CloseResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class CloseReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_CLOSE + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["CloseReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_CLOSE): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return CloseReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class SuspendCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_SUSPEND + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SuspendCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SUSPEND): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return SuspendCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field SuspendCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class SuspendResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_SUSPEND + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SuspendResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SUSPEND): + raise Exception("Invalid constraint field values") + return SuspendResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class SuspendReject(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_SUSPEND + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SuspendReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SUSPEND): + raise Exception("Invalid constraint field values") + if len(span) < 2: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + fields["error_code"] = ErrorCode.from_int(span[1]) + span = span[2:] + return SuspendReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print(f"Invalid value for field SuspendReject::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 2 + + +@dataclass +class AbortCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_ABORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["AbortCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_ABORT): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + return AbortCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print(f"Invalid value for field AbortCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated") + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class AbortResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_ABORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["AbortResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_ABORT): + raise Exception("Invalid constraint field values") + return AbortResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class SecurityControlCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + content_protection_data: bytearray = field(kw_only=True, default_factory=bytearray) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_SECURITY_CONTROL + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SecurityControlCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SECURITY_CONTROL): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + span = span[1:] + fields["content_protection_data"] = list(span) + span = bytes() + return SecurityControlCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field SecurityControlCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + _span.extend(self.content_protection_data) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return len(self.content_protection_data) * 1 + 1 + + +@dataclass +class SecurityControlResponse(SignalingPacket): + content_protection_data: bytearray = field(kw_only=True, default_factory=bytearray) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_SECURITY_CONTROL + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SecurityControlResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SECURITY_CONTROL): + raise Exception("Invalid constraint field values") + fields["content_protection_data"] = list(span) + span = bytes() + return SecurityControlResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.extend(self.content_protection_data) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return len(self.content_protection_data) * 1 + + +@dataclass +class SecurityControlReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_SECURITY_CONTROL + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["SecurityControlReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_SECURITY_CONTROL): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return SecurityControlReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 + + +@dataclass +class GeneralReject(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.GENERAL_REJECT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["GeneralReject", bytes]: + if fields["message_type"] != MessageType.GENERAL_REJECT: + raise Exception("Invalid constraint field values") + return GeneralReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class DelayReportCommand(SignalingPacket): + acp_seid: int = field(kw_only=True, default=0) + delay_msb: int = field(kw_only=True, default=0) + delay_lsb: int = field(kw_only=True, default=0) + + def __post_init__(self): + self.message_type = MessageType.COMMAND + self.signal_identifier = SignalIdentifier.AVDTP_DELAYREPORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DelayReportCommand", bytes]: + if (fields["message_type"] != MessageType.COMMAND or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DELAYREPORT): + raise Exception("Invalid constraint field values") + if len(span) < 3: + raise Exception("Invalid packet size") + fields["acp_seid"] = (span[0] >> 2) & 0x3F + fields["delay_msb"] = span[1] + fields["delay_lsb"] = span[2] + span = span[3:] + return DelayReportCommand(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.acp_seid > 63: + print( + f"Invalid value for field DelayReportCommand::acp_seid: {self.acp_seid} > 63; the value will be truncated" + ) + self.acp_seid &= 63 + _span.append((self.acp_seid << 2)) + if self.delay_msb > 255: + print( + f"Invalid value for field DelayReportCommand::delay_msb: {self.delay_msb} > 255; the value will be truncated" + ) + self.delay_msb &= 255 + _span.append((self.delay_msb << 0)) + if self.delay_lsb > 255: + print( + f"Invalid value for field DelayReportCommand::delay_lsb: {self.delay_lsb} > 255; the value will be truncated" + ) + self.delay_lsb &= 255 + _span.append((self.delay_lsb << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 3 + + +@dataclass +class DelayReportResponse(SignalingPacket): + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_ACCEPT + self.signal_identifier = SignalIdentifier.AVDTP_DELAYREPORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DelayReportResponse", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_ACCEPT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DELAYREPORT): + raise Exception("Invalid constraint field values") + return DelayReportResponse(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 0 + + +@dataclass +class DelayReportReject(SignalingPacket): + error_code: ErrorCode = field(kw_only=True, default=ErrorCode.SUCCESS) + + def __post_init__(self): + self.message_type = MessageType.RESPONSE_REJECT + self.signal_identifier = SignalIdentifier.AVDTP_DELAYREPORT + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple["DelayReportReject", bytes]: + if (fields["message_type"] != MessageType.RESPONSE_REJECT or + fields["signal_identifier"] != SignalIdentifier.AVDTP_DELAYREPORT): + raise Exception("Invalid constraint field values") + if len(span) < 1: + raise Exception("Invalid packet size") + fields["error_code"] = ErrorCode.from_int(span[0]) + span = span[1:] + return DelayReportReject(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + _span.append((self.error_code << 0)) + return SignalingPacket.serialize(self, payload=bytes(_span)) + + @property + def size(self) -> int: + return 1 diff --git a/android/pandora/test/a2dp_test.py b/android/pandora/test/a2dp_test.py index 05c5bedd7f..92df133d62 100644 --- a/android/pandora/test/a2dp_test.py +++ b/android/pandora/test/a2dp_test.py @@ -14,19 +14,19 @@ import asyncio import avatar +import bumble import dataclasses import itertools import logging import numpy as np +from a2dp.packets import avdtp from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices, pandora from avatar.pandora_server import AndroidPandoraServer -import bumble -from bumble.avctp import AVCTP_PSM from bumble.a2dp import ( A2DP_MPEG_2_4_AAC_CODEC_TYPE, - MPEG_2_AAC_LC_OBJECT_TYPE, A2DP_SBC_CODEC_TYPE, + MPEG_2_AAC_LC_OBJECT_TYPE, SBC_DUAL_CHANNEL_MODE, SBC_JOINT_STEREO_CHANNEL_MODE, SBC_LOUDNESS_ALLOCATION_METHOD, @@ -37,11 +37,29 @@ from bumble.a2dp import ( SbcMediaCodecInformation, make_audio_sink_service_sdp_records, ) -from bumble.avdtp import (AVDTP_AUDIO_MEDIA_TYPE, AVDTP_OPEN_STATE, AVDTP_PSM, AVDTP_STREAMING_STATE, AVDTP_IDLE_STATE, - AVDTP_CLOSING_STATE, Listener, MediaCodecCapabilities, Protocol, AVDTP_BAD_STATE_ERROR, - Suspend_Reject) -from bumble.l2cap import (ChannelManager, ClassicChannel, ClassicChannelSpec, L2CAP_Configure_Request, - L2CAP_Connection_Response, L2CAP_SIGNALING_CID) +from bumble.avctp import AVCTP_PSM +from bumble.avdtp import ( + AVDTP_AUDIO_MEDIA_TYPE, + AVDTP_BAD_STATE_ERROR, + AVDTP_CLOSING_STATE, + AVDTP_IDLE_STATE, + AVDTP_OPEN_STATE, + AVDTP_PSM, + AVDTP_STREAMING_STATE, + AVDTP_TSEP_SRC, + Listener, + MediaCodecCapabilities, + Protocol, + Suspend_Reject, +) +from bumble.l2cap import ( + L2CAP_SIGNALING_CID, + ChannelManager, + ClassicChannel, + ClassicChannelSpec, + L2CAP_Configure_Request, + L2CAP_Connection_Response, +) from bumble.pairing import PairingDelegate from mobly import base_test, test_runner from mobly.asserts import assert_equal # type: ignore @@ -49,15 +67,15 @@ from mobly.asserts import assert_in # type: ignore from mobly.asserts import assert_is_not_none # type: ignore from mobly.asserts import fail # type: ignore from pandora.a2dp_grpc_aio import A2DP -from pandora.a2dp_pb2 import PlaybackAudioRequest, Source, Configuration, STEREO +from pandora.a2dp_pb2 import STEREO, Configuration, PlaybackAudioRequest, Source from pandora.host_pb2 import Connection from pandora.security_pb2 import LEVEL2 from typing import Optional, Tuple logger = logging.getLogger(__name__) -AVRCP_CONNECT_A2DP_WITH_DELAY = 'com.android.bluetooth.flags.avrcp_connect_a2dp_with_delay' AVDTP_HANDLE_SUSPEND_CFM_BAD_STATE = 'com.android.bluetooth.flags.avdt_handle_suspend_cfm_bad_state' +AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE = 'com.android.bluetooth.flags.avdt_handle_signaling_on_peer_failure' async def initiate_pairing(device, address) -> Connection: @@ -280,13 +298,6 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] 3. Check AVDTP status on RD1 """ - # Enable AVRCP connect A2DP delayed feature - for server in self.devices._servers: - if isinstance(server, AndroidPandoraServer): - server.device.adb.shell(['device_config override bluetooth', AVRCP_CONNECT_A2DP_WITH_DELAY, - 'true']) # type: ignore - break - # Connect and pair RD1. ref1_dut, dut_ref1 = await asyncio.gather( initiate_pairing(self.ref1, self.dut.address), @@ -353,17 +364,19 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] def on_l2cap_connection_request(self, connection: Connection, cid: int, request) -> None: global pending_configuration_request - if (request.psm == AVDTP_PSM and pending_configuration_request is not None): + if request.psm == AVDTP_PSM and pending_configuration_request is not None: logger.info("<< 4. RD1 rejects AVDTP connection request from DUT >>") self.send_control_frame( - connection, cid, + connection, + cid, L2CAP_Connection_Response( identifier=request.identifier, destination_cid=0, source_cid=request.source_cid, result=L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE, status=0x0000, - )) + ), + ) logger.info("<< 5. RD1 proceeds with first AVDTP channel configuration >>") chan_connection = pending_configuration_request.connection chan_cid = pending_configuration_request.cid @@ -377,14 +390,15 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] def on_connection_response(self, response): assert self.state == self.State.WAIT_CONNECT_RSP - assert response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL, f"Connection response: {response}" + assert (response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL + ), f"Connection response: {response}" self.destination_cid = response.destination_cid self._change_state(self.State.WAIT_CONFIG) logger.info("<< 2. RD1 connected DUT, configuration postponed >>") def on_configure_request(self, request) -> None: global pending_configuration_request - if (pending_configuration_request is not None): + if pending_configuration_request is not None: logger.info("<< 3. Block RD1 until DUT tries AVDTP channel connection >>") pending_configuration_request.connection = self.connection pending_configuration_request.cid = self.source_cid @@ -648,6 +662,76 @@ class A2dpTest(base_test.BaseTestClass): # type: ignore[misc] # Wait for AVDTP Close await asyncio.wait_for(avdtp_future, timeout=10.0) + @avatar.asynchronous + async def test_avdt_open_after_timeout(self) -> None: + """Test AVDTP automatically opens stream after timeout if peer device only configures codec. + + 1. Pair and Connect RD1 -> DUT + 2. Connect AVDTP RD1 -> DUT but do not send AVDT Open Command + 3. Check that the DUT will abort and reopen the AVDTP as initiator + """ + + class TestAvdtProtocol(Protocol): + + def on_open_command(self, command): + nonlocal avdtp_future + logger.info("<< AVDTP Open received >>") + avdtp_future.set_result(None) + return super().on_open_command(command) + + # Enable BAD_STATE handling + for server in self.devices._servers: + if isinstance(server, AndroidPandoraServer): + server.device.adb.shell( + ['device_config override bluetooth', AVDTP_HANDLE_SIGNALING_ON_PEER_FAILURE, + 'true']) # type: ignore + break + + # Connect and pair RD1. + ref1_dut, dut_ref1 = await asyncio.gather( + initiate_pairing(self.ref1, self.dut.address), + accept_pairing(self.dut, self.ref1.address), + ) + + # Create a listener to wait for AVDTP open + avdtp_future = asyncio.get_running_loop().create_future() + + # Retrieve Bumble connection object from Pandora connection token + connection = pandora.get_raw_connection(device=self.ref1, connection=ref1_dut) + assert connection is not None + + channel = await connection.create_l2cap_channel(spec=ClassicChannelSpec(psm=AVDTP_PSM)) + client = TestAvdtProtocol(channel) + sink = client.add_sink(sbc_codec_capabilities()) + endpoints = await client.discover_remote_endpoints() + logger.info(f"endpoints: {endpoints}") + assert len(endpoints) >= 1 + remote_source = list(endpoints)[0] + assert remote_source.in_use == 0 + assert remote_source.media_type == AVDTP_AUDIO_MEDIA_TYPE + assert remote_source.tsep == AVDTP_TSEP_SRC + logger.info(f"remote_source: {remote_source}") + + configuration = MediaCodecCapabilities( + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=SbcMediaCodecInformation.from_lists( + sampling_frequencies=[44100], + channel_modes=[SBC_JOINT_STEREO_CHANNEL_MODE], + block_lengths=[16], + subbands=[8], + allocation_methods=[SBC_LOUDNESS_ALLOCATION_METHOD], + minimum_bitpool_value=2, + maximum_bitpool_value=53, + ), + ) + + response = await remote_source.set_configuration(sink.seid, [configuration]) + logger.info(f"response: {response}") + + # Wait for AVDTP Open from DUT + await asyncio.wait_for(avdtp_future, timeout=10.0) + if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) diff --git a/apex/Android.bp b/apex/Android.bp index 52b9631fb4..4eccda2a11 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -1,10 +1,19 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "system_bt_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["system_bt_license"], + default_applicable_licenses: ["Android-Apache-2.0"], } linker_config { @@ -13,7 +22,8 @@ linker_config { installable: false, } -// Mainline bluetooth apex module. +// Legacy Bluetooth apex prior to Baklava +// TODO b/383863941 delete apex { name: "com.android.btservices", defaults: ["t-launched-apex-module"], @@ -121,7 +131,6 @@ bootclasspath_fragment { "com.android.bluetooth", ], }, - } systemserverclasspath_fragment { @@ -129,3 +138,110 @@ systemserverclasspath_fragment { standalone_contents: ["service-bluetooth"], apex_available: ["com.android.btservices"], } + +// Mainline bluetooth apex module. +apex { + name: "com.android.bt", + defaults: ["b-launched-apex-module"], + // TODO b/356727632: remove min_sdk_version as this is set from b-launched-apex-module + min_sdk_version: "35", + manifest: "manifest.json", + bootclasspath_fragments: ["com.android.bt-bootclasspath-fragment"], + systemserverclasspath_fragments: ["com.android.bt-systemserverclasspath-fragment"], + file_contexts: ":com.android.bt-file_contexts", + compat_configs: [ + "bluetooth-compat-config", + "bluetoothapk-platform-compat-config", + "framework-bluetooth-compat-config", + ], + apps: ["Bluetooth"], + + multilib: { + first: { + // Extractor process runs only with the primary ABI. + jni_libs: [ + "libbluetooth_jni", + ], + }, + }, + + prebuilts: [ + "audio_set_configurations_bfbs", + "audio_set_configurations_json", + "audio_set_scenarios_bfbs", + "audio_set_scenarios_json", + "bt_did.conf", + "bt_stack.conf", + "interop_database.conf", + ], + key: "com.android.bt.key", + certificate: ":com.android.bt.certificate", + updatable: true, + compressible: false, + visibility: ["//packages/modules/common/build"], +} + +sdk { + name: "bt-module-sdk", + apexes: [ + // Adds exportable dependencies of the APEX to the sdk, + // e.g. *classpath_fragments. + "com.android.bt", + ], +} + +// Encapsulate the contributions made by the com.android.bluetooth to the bootclasspath. +bootclasspath_fragment { + name: "com.android.bt-bootclasspath-fragment", + contents: ["framework-bluetooth"], + apex_available: ["com.android.bt"], + + // The bootclasspath_fragments that provide APIs on which this depends. + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + + // Additional stubs libraries that this fragment's contents use which are + // not provided by another bootclasspath_fragment. + additional_stubs: [ + "android-non-updatable", + ], + + // Additional hidden API flag files to override the defaults. This must only be + // modified by the Soong or platform compat team. + hidden_api: { + max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"], + max_target_r_low_priority: ["hiddenapi/hiddenapi-max-target-r-low-priority.txt"], + unsupported: ["hiddenapi/hiddenapi-unsupported.txt"], + + // The following packages contain classes from other modules on the + // bootclasspath. That means that the hidden API flags for this module + // has to explicitly list every single class this module provides in + // that package to differentiate them from the classes provided by other + // modules. That can include private classes that are not part of the + // API. + split_packages: [ + "android.bluetooth", + ], + + // The following packages and all their subpackages currently only + // contain classes from this bootclasspath_fragment. Listing a package + // here won't prevent other bootclasspath modules from adding classes in + // any of those packages but it will prevent them from adding those + // classes into an API surface, e.g. public, system, etc.. Doing so will + // result in a build failure due to inconsistent flags. + package_prefixes: [ + "android.bluetooth.le", + "com.android.bluetooth", + ], + }, +} + +systemserverclasspath_fragment { + name: "com.android.bt-systemserverclasspath-fragment", + standalone_contents: ["service-bluetooth-new"], + apex_available: ["com.android.bt"], +} diff --git a/apex/hiddenapi/OWNERS b/apex/hiddenapi/OWNERS index 82b599dbf1..ac8a2b627c 100644 --- a/apex/hiddenapi/OWNERS +++ b/apex/hiddenapi/OWNERS @@ -1,7 +1,3 @@ -# These files are only intended to be changed by platform-compat and -# the soong teams. -set noparent - # soong-team@ as the hiddenapi files are tightly coupled with Soong file:platform/build/soong:/OWNERS diff --git a/apex/key.pem b/apex/key.pem deleted file mode 100644 index 527a032e89..0000000000 --- a/apex/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCzjMPKWIQ0dP75 -H8VFPNKgK/rXTdoAeZ7+lKtfSpbHwDvkgES168JphH8OoIf9CUBk6d1/cw/N/V54 -ZhIn5hwEZcE1DaeKRSull3prRwFo5enhJBXerNNqw8IHNw4RIRqeKNxJh0HfVLmc -C0UpDVC7dW2CbDI7DcLb0DQN5I3W4NChlcv7XVysn5mJXK9EDk502goumlxfjl8w -UR/RV6XcKn5dt0QG7enUOQQ3Q/EqY87jeFLb/aGloSbLcwzYny/VLNF3JOPYW1y/ -8fYlTUP1FQ8o47yImRNYuAJ7ATxVV0KFlHj+Hq9goFPi/98TjNMdLSwuUmGsIDPT -wFufE0hQAH9bNzAczL4Q7QBqpng8vCFZ8HZQFfj+au7/YGygvjv9p5ex9CLn4zw2 -t+dRH4oNQG71IKpj2dKm8NVYsMkoxWE5CbZAFgKI8egNEEKWSRDG0GRAncJ4fk96 -5+hqat32gFykmLxkkIO0xaiLQh7h0lFz1vgD/bkaTE7CFLI14gaZ2ereixIrp/WC -sN5+LoabG9NyTK8ExDQ5LgGN9ozCqnAakd7o0VmcMHep9A+rAY5ZzAKFMVqhqOQ7 -8ROC/2bpFB6U4E1BsHWUrc+ZwhvUgLf3GrkGndtxvQN1MD9wq6cTiSMM0T7MVJpM -Mqh7EHpO0qNNYwwHqFCDHorBk1ZxzwIDAQABAoICAGXfODdWgaxBtWkj3YmrONYo -HeqLAWXDm7JWJ+WpLGOpblH3dQTC/0tfIbfq3T10QlT/W+00G4OEflVRlHuO09aq -5TR0ytpo5JrPl2XGo8YgrNJQ5xewd0PcTfpKAnE1lySlilctpvJP3//pZRQlueii -d7/II7fd1vFg8CfxxYhlhfooQ9Qa5LoM09BcBhVXCKzECYLnkgGlhdykKqlcUX7Z -nVkO4koMMt92ei3hHrQubPQjEOBAx7Zx+ND2NhOyPjF1fGNdSuk27Sj63+3KKQSl -LjTFAfN+5rsvrDieOt0w5U1lMED+qALq9K2W/7rX6/Bh8O9XTpOasc0zFsjq59uI -b+uBsZIPaGDpTjkYBKK3xrhNqIuq2ylN+0D14uAK4/z4sXsvP1XymjWkVNR9ii45 -doiDxANvZzqYoXcR5aEkJcO126RbDIw/SIglWTag6mRvNYCnLVcTzVf7uZXFyLXc -Yk0NpTGAphVl4uVjXFgr8ZXfQIoM51zFvVVh3+dhosNGms1lgcvGHzJRKsOCzaA8 -EdWm3mGZrM8kvviqFoS4PndIIz6hB71MGhISEWbTUssF/AJSDWKdvJEWpTSLUjyY -OoWaTABjZEjyCXpaIvssQilyOb+UVP6ayMAyfDjz5lDKB2216PhoQ9sOb9fIDrdK -24XwqdMUqDZJV6GY8nQBAoIBAQDnS1DTUYeQwO1C831d1m8P8PhEDpwioBPv53y/ -owbmbwADTMxWvRfGnmXM5BYAeiDFfECp2HPnB5SGpkXOJOQ8H8WStgkhRglNHBbK -yDaoLYTZqkk49enYEO6MNuFgTqiOWwgO+MVXPKmjreenhsoAQAKFFBPqyNU6v9a2 -WEHuIMc6Gv+2GkT7wkVwgmTZA1L58Y2EWX0wdDAYLS+0+6u+xJ5lPQI014Y7YNsa -qvu7PG5gQ8fdU1fPmMRk9OOxiHilLc0GAQPtnF4fQYcNdcZ66FARMjHjZnFqHCPI -irddmFReMkKj+HHufO6EVf8KkWgyIr9CzS9wh43PWW/CILwZAoIBAQDGuoM4DEV5 -tr+FmF3Rri1P0G+iC3Bm9oTa1S1AC4r8yPiY9Q8/lV009am2gbz6R5TFma+oFU1y -R3pm6AaijeKmusmi2NGtdbqa9cUL98HtVAT7ZQmzyz9UepyZQmDy/0B4q2fGOUX1 -QnxKIOU2j8CFVUkbijUooRypZG7mIs/PsnTtmDb0JdOr54E26BLaBXbFcrxLuJxX -STpQVbU3U/Y2jaLnRcPF4e6bCrQbrsH37wMLM3JTes8PRr0IcPDs4sp30Gj55rcL -vSYZqfeVKadA312In5p4OPJV7/HSzRl4JE4JSWlCRLZepmAy36kFpQzI6N3LUVMp -RuZtf+UXIVonAoIBAQCVLEIoyICCn6tmbtwAVXiz81prqnCQ9GVnaQlQH7knjZeY -iUOQ/cwD0c0eZEy8aggQ1p5t7Khz7LOiVELZPXnmPSeUA8vHpgABt4gLqS13MkRo -jidDkXcMX38693pMPu7/QT7lSRUduoY+hr7NkWVe2+nEIrrlxjmf9nJokGuVRZa4 -pwkdTbwIE7ftZGveewJdKal4Hq9bPNR4A0ytkVOnafAuozZ1FjwAt+sYPAa3L7aQ -Z6bT3BjaT1D7O+ZObhJBllSQ9r6t1RfvWLfduoQUaRiavqDmZP/oy9VYVf8FYmWn -iwrn7iitA+5hc4VWL4ngLADm+KcMEKEphilKLwqhAoIBAAC7FNpy6Rp+eovSOJ4Z -xt7hRFfTNPGb/HcVi5oNsNAnKQre89RnBzW3pY0fQwOkmb50RzoWAffmnWOdfNDC -NtAoJa+snnDF2w7Q0o2tto/Z3D7Ua0m2+J+l58eEf/jEyYboEnSfJ0u0l+Jp5o3h -z3JuEtvAEVv547IXxXShMiRwYo+xHJqfPP0H1+jMx6z3ki09s8WPgzuq8ET1W7o9 -W81tjejNz999ajQ1wN3NMbdosJks8kGuVO6Ycv+B8tDcMKRqJsiseYXYhzhW8Ksi -wROy/pQPCjFg/Dsmq7v7txlDAOp106ZDvGvyrq3hNqzno3llqdMilGy1bwl+C+ie -3ccCggEBAISSVWD3owRf5n8unlVTmg64miLv3rM3s67F+Hn62qeyYmW3AhMSPyt2 -RIkEr2YrOKQ5D8Ijk3Gad4ae5UdyeCOPg495qk7bD5thcYG4Mo/MVvCQXxgdkeaK -TFHpzzUttgKUe2EBfCbkxPjzWqf3ba0mvcnW621vUVA+VDqIUlBU9aosRMgDOFUi -N+667Kj5P382oZHHxFnUZnCa3PczyGG3WLYBLRFAHl3n8kJ9mOUAAlVC8sRgcYmZ -z2ZYfc22sUREah+kdxvDvR2ayopl86XoyrdTnK0XKEo+lU8Ghovc5Kbopf15KdWz -/cAk+1ZkOAzn5RIZNaFGw2FKHds1ODQ= ------END PRIVATE KEY----- diff --git a/apex/key/Android.bp b/apex/key/Android.bp new file mode 100644 index 0000000000..7fb3ada21b --- /dev/null +++ b/apex/key/Android.bp @@ -0,0 +1,16 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +apex_key { + name: "com.android.bt.key", + public_key: "com.android.bt.avbpubkey", + private_key: "com.android.bt.pem", + visibility: ["//packages/modules/Bluetooth/apex"], +} + +android_app_certificate { + name: "com.android.bt.certificate", + certificate: "com.android.bt", + visibility: ["//packages/modules/Bluetooth/apex"], +} diff --git a/apex/key/com.android.bt.avbpubkey b/apex/key/com.android.bt.avbpubkey Binary files differnew file mode 100644 index 0000000000..46122323d5 --- /dev/null +++ b/apex/key/com.android.bt.avbpubkey diff --git a/apex/key/com.android.bt.pem b/apex/key/com.android.bt.pem new file mode 100644 index 0000000000..90bda1b1b1 --- /dev/null +++ b/apex/key/com.android.bt.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQConPWgz/jp7N6g +7J1x8blR4w02RgiKQy0ViEbHo/UUOP1ctXxHz6Y0nmmAFCueiGbPgyjtlX0JNMki +RX7wd988BZNzhc1ngmYuNsS36YKSkZg3DMnGr6xMWhEUEVKn2Exb+TUA3q3IFKny +jrp637AJGebzLdgWldtdxxDqUU0xIRqggXL3UJAOHATPooQdpQ6dbHOr/jh68oYb ++FQE0gkjhh6ugNhMHNi6nQ+PwV0kacNy3cOPu/Kn6nTAw6OHdR+BXdL9IdOWO/rq +bIDXByoM2bOH1XrcBa9FmFWD0HOf451X0fRi2JtAY6A1AqquFgVcLNS3Ai3eQfI+ +J4ElO9dOlFiawbFM8qJSu4bGwUM3jBjF8QmHaxt0lmHBdcJwBeY8G1dtVwctOFS1 +F9L5gQok9jZzmZDiIs6EzDmjTENJ0W1GF+KxN18s9hrEgdAD7gPRmE87pjhlmTI+ +LEgGxqduezLph1fpKYe6i/RdHUGQ8vEov6DulVzgjJATlI+TneLBWkzksILgsn2F +lEMacEMCELY4jK06YL9N/CGSoPxa5T2RBIHCD8mjftfnSc2k3gt4sV+nsoXoA4n0 +PVb3n0cFNpY0egOaLCTtvqSsoFAC75K14MsumJ3KpBrs+A2tVtaXTt5HUbeqlhFa +GRS6WJ89T/S/19fDCX8dq1nyNRv1EQIDAQABAoICABO9dYXPV15Gpp1LOlcW1EoN +lZRFMHaEbRSsZNH2ZGjKz8HA9xT+ruAR7DrMEbvAjx3MBpiDE/WGFiaioX0EYbCY +nAXONEkiI3WVYKRB7LEdZcdLIlvWIQs6z61MZmODX5Q0ebO5fjmyjp94vYUJZ2X3 +X/oYI8hoILuqxLSUEmAWNrR0F0o0Rrs6JUeMSKCiXHLKecPY8lKVjmUbpnV+hAai +SvkF2VzW0zpMzT+4pP+QeMr7wsuS9tKZELRpKx3elNLL0dnkEg4QhVyY2aAjBPWm +bp3wXK8B5N5WWmuhVcoSJy97A3wnjQyFpWHTZzlUl2nOT1SCJy3SRWqorBZYekk2 +fBgrLM9RRBByXpoWIzkB73BPBf7oifqYWjtQv26fxVNAnWPqoGF8KiSTEBCisPnI +EhGoziOOPOfjsxg01G6iF2u/m/Cxys6UEafZlUJjvueovOCmomV8FylwlcoU/M1z +Zvg0Xn2McqR0TmLXYlioSaJMLmAwm9tGpa2IXmQllkfszb7Figrso93q8O2NTxV0 +jEuzw8NU3ScPt8s21wtmxwmT9FxhS8f55uTo6h1IZPdgKRIDEF/ReTv5f7HLUnK+ +zIypddT3KbFhdlhD9/4OrLV9K5nVLXhhA5NfEtEOGqtywT6xOOVd9o5A3yyEomRI +A91RYtECA1UkbIoOtWPpAoIBAQDb0TxtcUbbvTEMnDLT6X76RgebWyZ1Ycs1GR7g +ZRNIrLwkWZYsU6H7BxcjKDNmS3PGdspJ7kwgBqp0lIM/OIYgZCuTynzbkFmZTtyQ +uBcFtge3y4Wx9lmpJYKBrGDXbGcJX2iwzzxmTuGxF/RdP/xlKg2Jg/xbj/YNIZCn +f2RD6ejuqrxCW2Qxy+jPplf1GpeMYZ96Kk6jUrc/XZXDBtyFGLUyQfG0RJ/oifRG +yd6cjNKzKfFe6Pq6KZ+NsiPvgsng+qqsjXT0H0YUr31Cf/wAiXafMcwaxngKRktM +i2uartskFptr1cet0hGdVCLopXyV4miTw8qP/0PIqZLAQgdJAoIBAQDEXg6ZBFAC +BUa0UNLFbDNXMIVwjQ2dvYcNe+l9XvOOb38spHqIDUnzQ+y7nJ/eF12eAnFdnzZe +dA0wC4bTwNUIS/Xq11HREPj+kr7ZOJdN4h0EzWKdiR+YMZ4TW/owg4epMtyz08/v +mIaLVOL5nRL1TyNkXk4PcQo6ZnfygAmmOQGwyj1fzyT2ae7rg19/vKZNouCUpEZR +irynMPyCD+TwNZii3knLO+bB6+lRMoVlvfeFb/MsF2G6f/Vv1uYHVY58NKUbjrrU +UA7qjL1YtRrCfAXPLH0HUkSb2+rW49myMCqXourHwTGTMF2io036OD2bszro1f5p +Ns8jMNBBqpeJAoIBAFyCYTj57EU8CPFypx1x7gNsB3WFVdkesAOHr+vl/cifEYsG +vOGozMm1gTJFj+QkKvRCN/4HMLhltcTcX6PyqNfm8EQBeqx4wox3mPG6182KfxMF +rx1nPPKnm5XxMsxWjcJvCcMnZBb2aZs7Jpc7/3+fxBS0nDb4jieWkac8QLkoLuSK +rDIM/kzKsQ76xZecel0VFKW4OmIXxJEBVK7AK3xEGrWs9QjVvKqDrG9CM9KUnXPT +WgaEbEh+jex3JRJ+a3GobL2zxp2ar8ay2DdtX2PmdiqnLWuzTP+d98CeCEoP/p/g +d8ZZMPDhMJKSvyJDyvvVTgep5YzWc/N2EhGheCkCggEAWOZUKYZASonk9qFYqrRP +kfuPjeLt9SnmyH3+uRmRnjxwEN/t8UZC4nUv6cgAwGVHps9oT4BS9vQHPGcbhSZZ +y8XH8afFjKbocygwatsp9MKXbKMnMDbyLM7Zx93uKKSDeGBkdq1ACDK/RrkJgQRR +nulTFo9zO55riQD28LxyoeRRFZ6ZppR7IWvYFJuHUqcvixpgHis5ApWSzwZoauGT +PgSls6Z7Nh1KXSTWnkIHDoawVqLWLnHvdqWTkixyO4PKXAThctZtUs1j02AgINbs +5HuNjbuQZw4hrYYkjWDeH8OapMfHDuQx83cEDSttlAh+K806sEqhQTTlFgN1ZSU8 +MQKCAQBwvij8CZ5LnNatLJo3mBC+dHmBSjIHvzAFvuVqeRarjPU0V4OubykZzDQ0 +9rxDwo3+Nd+hwlB+Di7BUTG7/yZDP8qNWMCker73sG1Z9ymgVzmKSx/Sgl0lOVcA +/pZVTx1MX6pRjaM8WlrYLsFKq++95Gvj8Hk3TCsT6vl9IJ485jnS+xYP22YeU/Qh +CGfqfcEnExJUx4AoNdhuVgpZ1/DXMFOgF2ETzxL4xLxDk9bGvM2HApLe084Es5MT +mO2ugkGWJaabt+N0jIveIeIAbF7yJjrTPxLTJZnD9DwbXM5m9AGA8TVOq5ByrLpz +OAbnJ6ncUN/kW2Ycz7J+ojDBHxcp +-----END PRIVATE KEY----- diff --git a/apex/key/com.android.bt.pk8 b/apex/key/com.android.bt.pk8 Binary files differnew file mode 100644 index 0000000000..8dc45f73ae --- /dev/null +++ b/apex/key/com.android.bt.pk8 diff --git a/apex/key/com.android.bt.x509.pem b/apex/key/com.android.bt.x509.pem new file mode 100644 index 0000000000..5c56158c3b --- /dev/null +++ b/apex/key/com.android.bt.x509.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF6TCCA9GgAwIBAgIUDb1AaDxPOs2NLScNM0HjN5HDJxYwDQYJKoZIhvcNAQEL +BQAwgZsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMRcwFQYDVQQD +DA5jb20uYW5kcm9pZC5idDAgFw0yNDEyMTIyMzI1MjJaGA80NzYyMTEwODIzMjUy +MlowgZsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMRcwFQYDVQQD +DA5jb20uYW5kcm9pZC5idDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALII9yRq95uVLXRn7hai0yfwIaITGdD0md9wsdyj8pgEZkQq7hkBTdGZeSNB8KOo +SkG9Teb9tgxlDcdSip45SX1snnpK0oZMXvHdmp4Wi6468kRte7MWr2Vv75RV6/Ye +msnyczM9SGQKWOn8dF+aRo36YWD8k4xhpL3nIWM3WLH8D1q78WsLHPsvcVPTRfxe +dq2/l5AoAgbCqCP+fofLTdW/X60Sp/jKs/LYT/XeuBfxpp4O7WVzXgnuXUZ+AdCZ +jCeo3LJgb55Z/ITEByK3o4YV4Kp5/nzD9o48/GEXkc9n8SQF85WskHjv2ff4Uaah +hnYKGIQ8CG3r3kD+hdV5o2AY+fvB66fsb/Q/VkeAR5I9Oj0CkoNwgfCc7ovU4FW4 +CbWvGBKUWChYlvEkmhtCGWKZX3sCiYUoSwlZmPI/HRFqs2YGSPda2d9XGEowu7j5 +EDRg83IOBNhBLzgpJ1iyb5uDadvGSxbC5DlXekNUzotmTQE5GRNLZMTud3HYmHft +YuIXr9mZgAiUNi9pzsMpbJpaUrp/cBIQOdq/hFMfLixSyNg8pDaOAXZcEglYa1H7 +WvGi3C0Dk5PgfoIQh3wKqzb8wfJ77cFpD7uNUQ8+Bx90stYRX65aA3HRYW3eo8EL +4y4+ppizueX3An9g88liKKM/BUuYI9qnrfd7dVLyFONrAgMBAAGjITAfMB0GA1Ud +DgQWBBSf94jmG7aFyKSFnsWHOfe0294ogjANBgkqhkiG9w0BAQsFAAOCAgEAISH8 +BVtMbY9jyV3DmWLeXoSgunCSm9FKMSFGvvPHUaNhvvYddAiYYZ2UJryR1U8Mqv2I +ehEOhZcwOi1Bz6gu4Pv0Qrz1sbBUd/f2jFJb77z2tNrUKPQUiW4nN6EhxW4RmraI +LtN8RaQPmI6ZYXpz8i8n/AIpq2GsfK8bYNhTE4efOWf3p70oGrQpkADOSepcs5Es +EXy3xPLGaduQn1Pj5QAXQKhxZ2JQS102R7dKOyClomp7ZM3yzmngpFmC21QWoXG1 +BaOfs3y7y1gJs5rH1gzjhay9CvzgIe6FdS1KD946bS+sp1NF55qiz0zwLEi0ZDnS +mn1jYuzdF2C3CFtjgLRQmz+3pDTu3adai7HcDbUdCutOadBbJM8ikRQkKH/1ggNh +94l7bT2hflTRCw2AXanWkT9mH5XPGAGuzImfMdGYCpxjOJLGQROoczHOKrLwwjdm +ig9v5BXdJk+DA6QowvgvCwK+rrbwf8j68CJv8njp/1I+mek314R8UNu26wrWu0Yd +NPvCXAtYPPrywCEPhUkaHI/63uP4OY6LyJDJb4j5gK5htnyI7nN1yJmIxLdrbCCi +BUceEk3rhRArO/0iqhsQgHNj3K95DzQmNENAQrnxamw2tHkuC6j7KcPFCOLbV+n9 +7bFbeEUk2LVtFp2ZwdzzHAMwer4zQBhxOX5C0go= +-----END CERTIFICATE----- diff --git a/apex/manifest.json b/apex/manifest.json new file mode 100644 index 0000000000..e5f06386a2 --- /dev/null +++ b/apex/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "com.android.bt", + + // Placeholder module version to be replaced during build. + // Do not change! + "version": 0 +} diff --git a/flags/Android.bp b/flags/Android.bp index 63f4903b18..f43322d4ed 100644 --- a/flags/Android.bp +++ b/flags/Android.bp @@ -13,7 +13,6 @@ aconfig_declarations { "a2dp.aconfig", "active_device_manager.aconfig", "adapter.aconfig", - "asha.aconfig", "avrcp.aconfig", "avrcp_controller.aconfig", "bta_dm.aconfig", @@ -34,7 +33,6 @@ aconfig_declarations { "l2cap.aconfig", "le_advertising.aconfig", "leaudio.aconfig", - "map.aconfig", "mapclient.aconfig", "mcp.aconfig", "metric.aconfig", diff --git a/flags/BUILD.gn b/flags/BUILD.gn index 773f237870..6c7185a24a 100644 --- a/flags/BUILD.gn +++ b/flags/BUILD.gn @@ -6,7 +6,6 @@ aconfig("bluetooth_flags_c_lib") { "a2dp.aconfig", "active_device_manager.aconfig", "adapter.aconfig", - "asha.aconfig", "avrcp.aconfig", "avrcp_controller.aconfig", "bta_dm.aconfig", @@ -27,7 +26,6 @@ aconfig("bluetooth_flags_c_lib") { "l2cap.aconfig", "le_advertising.aconfig", "leaudio.aconfig", - "map.aconfig", "mapclient.aconfig", "mcp.aconfig", "metric.aconfig", diff --git a/flags/a2dp.aconfig b/flags/a2dp.aconfig index 25686f0c34..e6275bf010 100644 --- a/flags/a2dp.aconfig +++ b/flags/a2dp.aconfig @@ -2,26 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.btservices" flag { - name: "a2dp_service_looper" - namespace: "bluetooth" - description: "Inject looper into A2dpService" - bug: "337348333" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "avdtp_error_codes" - namespace: "bluetooth" - description: "Use compliant error codes in AVDTP responses" - bug: "338139069" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "bta_av_use_peer_codec" namespace: "bluetooth" description: "Deprecate bta av codec state" @@ -52,16 +32,6 @@ flag { } flag { - name: "a2dp_check_lea_iso_channel" - namespace: "bluetooth" - description: "Prevent A2DP stream from starting when LEA ISO channels are in use" - bug: "346475618" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "a2dp_variable_aac_capability" namespace: "bluetooth" description: "Enable AAC 48 kHz sampling rate for sink devices in the allow list" @@ -69,26 +39,6 @@ flag { } flag { - name: "stop_on_offload_fail" - namespace: "bluetooth" - description: "Stop instead of disconnecting AVDT when offload fails" - bug: "345709100" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "a2dp_aidl_encoding_interval" - namespace: "bluetooth" - description: "Configure the data interval in audio BT HAL's PcmConfig" - bug: "347781402" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "av_stream_reconfigure_fix" namespace: "bluetooth" description: "Handle AVDT stream reconfiguration in an event" @@ -99,16 +49,6 @@ flag { } flag { - name: "avdt_discover_seps_as_acceptor" - namespace: "bluetooth" - description: "Discover stream end points when in acceptor role." - bug: "351158317" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "avdt_prioritize_mandatory_codec" namespace: "bluetooth" description: "Reject remote peer HD codec configuration if mandatory codec is preferred in the stack" @@ -129,16 +69,6 @@ flag { } flag { - name: "a2dp_fix_codec_type_in_java" - namespace: "bluetooth" - description: "Mask out codec IDs such that they properly reflect unsigned value" - bug: "361742051" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "fix_avdt_rconfig_not_setting_l2cap" namespace: "bluetooth" description: "Set L2CAP flushable and high priority after A2DP reconfigure" @@ -185,3 +115,33 @@ flag { is_exported: true bug: "380118954" } + +flag { + name: "a2dp_cleanup_on_remove_device" + namespace: "bluetooth" + description: "Always remove A2DP state machine when device is removed" + bug: "381313622" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "avdt_handle_signaling_on_peer_failure" + namespace: "bluetooth" + description: "Abort AVDTP on failed connection as the acceptor and retry as the initiator" + bug: "381388684" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "a2dp_sm_ignore_connect_events_in_connecting_state" + namespace: "bluetooth" + description: "When received CONNECT event in Connecting state, with no prior DISCONNECT - ignore the event" + bug: "383576378" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/flags/asha.aconfig b/flags/asha.aconfig deleted file mode 100644 index ec130c0cfd..0000000000 --- a/flags/asha.aconfig +++ /dev/null @@ -1,12 +0,0 @@ -package: "com.android.bluetooth.flags" -container: "com.android.btservices" - -flag { - name: "asha_encrypted_l2c_coc" - namespace: "bluetooth" - description: "Request encryption in LE CoC channel creation" - bug: "348505552" - metadata { - purpose: PURPOSE_BUGFIX - } -} diff --git a/flags/avrcp.aconfig b/flags/avrcp.aconfig index 1c50da0949..5e806efb11 100644 --- a/flags/avrcp.aconfig +++ b/flags/avrcp.aconfig @@ -12,16 +12,6 @@ flag { } flag { - name: "avrcp_connect_a2dp_with_delay" - namespace: "bluetooth" - description: "When peer device connects AVRCP but not A2DP we initiate A2DP connection after some time delay" - bug: "328406945" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "browsing_refactor" namespace: "bluetooth" description: "Broadcast all players instead of BT player, remove MediaBrowser preload, support package modification" diff --git a/flags/bta_dm.aconfig b/flags/bta_dm.aconfig index e0ed3358b6..f35d562ebf 100644 --- a/flags/bta_dm.aconfig +++ b/flags/bta_dm.aconfig @@ -9,13 +9,6 @@ flag { } flag { - name: "bt_system_context_report" - namespace: "bluetooth" - description: "Report Bluetooth system context through bta_ble_energy_info_cmpl" - bug: "323083457" -} - -flag { name: "bta_dm_discover_both" namespace: "bluetooth" description: "perform both LE and Classic service discovery simulteanously on capable devices" diff --git a/flags/framework.aconfig b/flags/framework.aconfig index 934a3f6bb5..3a797af994 100644 --- a/flags/framework.aconfig +++ b/flags/framework.aconfig @@ -2,16 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.btservices" flag { - name: "override_context_to_specify_device_id" - namespace: "bluetooth" - description: "In Bluetooth Manager, override the context with createDeviceContext to pin a specific DeviceId" - bug: "349657939" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "support_metadata_device_types_apis" is_exported: true namespace: "bluetooth" diff --git a/flags/gap.aconfig b/flags/gap.aconfig index 646e844a63..0ef4a7aa20 100644 --- a/flags/gap.aconfig +++ b/flags/gap.aconfig @@ -54,26 +54,6 @@ flag { } flag { - name: "ble_check_data_length_on_legacy_advertising" - namespace: "bluetooth" - description: "Add data length checks for legacy advertising" - bug: "329011868" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "ble_gatt_server_use_address_type_in_connection" - namespace: "bluetooth" - description: "Use address type when initiating connection from BluetoothGattServer" - bug: "331147673" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "scan_record_manufacturer_data_merge" namespace: "bluetooth" description: "If a scan record has multiple datas under same manufacturer id, merge the values" @@ -94,26 +74,6 @@ flag { } flag { - name: "gatt_rediscover_on_canceled" - namespace: "bluetooth" - description: "Re-initiate discovery if the previous discovery is canceled for multiple GATT clients" - bug: "335082571" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "le_scan_use_uid_for_importance" - namespace: "bluetooth" - description: "Use uid instead of the first package name for fetching the importance of clients" - bug: "336965663" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "sec_dont_clear_keys_on_encryption_err" namespace: "bluetooth" description: "Do not clear security record on encryption error" @@ -124,16 +84,6 @@ flag { } flag { - name: "le_scan_use_address_type" - namespace: "bluetooth" - description: "Fix an issue that le scanner omits address type for MATCH_FIRST scan" - bug: "342127181" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "gatt_client_dynamic_allocation" namespace: "bluetooth" description: "Allocate GATT clients in heap to unlimit the number of clients" @@ -301,3 +251,33 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "configure_scan_on_resume" + namespace: "bluetooth" + description: "Configure scan paramters when start scanning from onResume" + bug: "383436218" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "unregister_gatt_client_disconnected" + namespace: "bluetooth" + description: "Unregister gatt client when disconneted to prevent client leaks" + bug: "380388092" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "fix_execute_write_no_pending" + namespace: "bluetooth" + description: "Send ATT_EXECUTE_WRITE_RSP when no pending write requests" + bug: "385118069" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/gatt.aconfig b/flags/gatt.aconfig index 99b0305291..55e5b27077 100644 --- a/flags/gatt.aconfig +++ b/flags/gatt.aconfig @@ -10,3 +10,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "le_subrate_api" + namespace: "bluetooth" + description: "New API to control LE subrate parameters" + bug: "384794418" + is_exported: true +} diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig index ea5c23af22..a4eda31433 100644 --- a/flags/leaudio.aconfig +++ b/flags/leaudio.aconfig @@ -32,20 +32,6 @@ flag { } flag { - name: "asymmetric_phy_for_unidirectional_cis" - namespace: "bluetooth" - description: "Apply asymetric PHY setting for unidirectional CIS" - bug: "314176433" -} - -flag { - name: "leaudio_hal_client_asrc" - namespace: "bluetooth" - description: "Sync audio stream with the bluetooth link clock" - bug: "312273987" -} - -flag { name: "metadata_api_inactive_audio_device_upon_connection" is_exported: true namespace: "bluetooth" @@ -204,16 +190,6 @@ flag { } flag { - name: "leaudio_call_start_scan_directly" - namespace: "bluetooth" - description: "Calling the framework is working only when we are expecting IPC. This will call the underlying implementation directly" - bug: "348562830" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "leaudio_no_context_validate_streaming_request" namespace: "bluetooth" description: "Request streaming would be send without available cotext validation" @@ -224,16 +200,6 @@ flag { } flag { - name: "leaudio_synchronize_start" - namespace: "bluetooth" - description: "Do not post on main looper when starting le_audio" - bug: "341385684" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "headtracker_codec_capability" namespace: "bluetooth" description: "Use Android Headtracker codec capabilities" @@ -461,4 +427,34 @@ flag { namespace: "bluetooth" description: "Add OPUS codec type" bug: "380029892" -}
\ No newline at end of file +} + +flag { + name: "leaudio_broadcast_receive_state_processing_refactor" + namespace: "bluetooth" + description: "Fix parsing empty receive states and refactor its processing" + bug: "380231464" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "dsa_hw_transparent_codec" + namespace: "bluetooth" + description: "Use trasparent codec for DSA hardware path" + bug: "382263607" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "leaudio_use_audio_recording_listener" + namespace: "bluetooth" + description: "Use Audio Recording listener instead of monitoring sink session" + bug: "381054654" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/map.aconfig b/flags/map.aconfig deleted file mode 100644 index 205627b727..0000000000 --- a/flags/map.aconfig +++ /dev/null @@ -1,9 +0,0 @@ -package: "com.android.bluetooth.flags" -container: "com.android.btservices" - -flag { - name: "map_limit_notification" - namespace: "bluetooth" - description: "Skip notyfing old messages in MAP" - bug: "325863406" -} diff --git a/flags/opp.aconfig b/flags/opp.aconfig index 6e32ab0b75..bd0f380c45 100644 --- a/flags/opp.aconfig +++ b/flags/opp.aconfig @@ -2,23 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.btservices" flag { - name: "opp_start_activity_directly_from_notification" - namespace: "bluetooth" - description: "Make OPP notification start activities directly from it, not via OppReceiver" - bug: "319050411" -} - -flag { - name: "opp_fix_multiple_notifications_issues" - namespace: "bluetooth" - description: "Fix UI issues related to multiple OPP notifications" - bug: "323096132" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "opp_ignore_content_observer_after_service_stop" namespace: "bluetooth" description: "When OppService.stop() is called, previously posted ContentObserver.onChange() should not run" diff --git a/flags/pairing.aconfig b/flags/pairing.aconfig index 3a96c4d3b4..b60302f5d1 100644 --- a/flags/pairing.aconfig +++ b/flags/pairing.aconfig @@ -112,16 +112,6 @@ flag { } flag { - name: "use_encrypt_req_for_av" - namespace: "bluetooth" - description: "Use encrypted link for AVDTP and AVCTP channel" - bug: "357662929" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "donot_queue_dup_rnr" namespace: "bluetooth" description: "Avoid queueing second RNR as part of ssp process" @@ -240,3 +230,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "bonded_device_smp_failure_handling" + namespace: "bluetooth" + description: "Don't remove bond on SMP failure for bonded devices in peripheral role" + bug: "385181815" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "peripheral_auth_req" + namespace: "bluetooth" + description: "Use appropriate authentication requirements for SMP security request in peripheral role" + bug: "385202199" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/security.aconfig b/flags/security.aconfig index c014a2a16d..2bb69eac95 100644 --- a/flags/security.aconfig +++ b/flags/security.aconfig @@ -25,3 +25,22 @@ flag { } } +flag { + name: "btsec_check_valid_discovery_database" + namespace: "bluetooth" + description: "Check for a valid discovery database before using it, and don't set up a discovery database for a new HF client if one has already been defined for it" + bug: "356201480" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "le_enc_on_reconnection" + namespace: "bluetooth" + description: "Encrypt LE link on reconnection with bonded devices" + bug: "356201480" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/service_discovery.aconfig b/flags/service_discovery.aconfig index 9ffc86a138..f5569086b9 100644 --- a/flags/service_discovery.aconfig +++ b/flags/service_discovery.aconfig @@ -10,3 +10,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "separate_service_storage" + namespace: "bluetooth" + description: "Store LE and Classic services separately" + bug: "382015666" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/sockets.aconfig b/flags/sockets.aconfig index 5f00813a8b..688d27323c 100644 --- a/flags/sockets.aconfig +++ b/flags/sockets.aconfig @@ -62,3 +62,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "fix_buf_len_check_for_first_k_frame" + namespace: "bluetooth" + description: "Fix L2cap buffer length check against MPS for the first k-frame" + bug: "386382446" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/system_service.aconfig b/flags/system_service.aconfig index 73f30611a6..fba0792eca 100644 --- a/flags/system_service.aconfig +++ b/flags/system_service.aconfig @@ -52,26 +52,6 @@ flag { } flag { - name: "respect_ble_scan_setting" - namespace: "bluetooth" - description: "No longer allow BLE_ON mode when the settings is explicitly disable" - metadata { - purpose: PURPOSE_BUGFIX - } - bug: "341783936" -} - -flag { - name: "ble_scan_setting_does_not_disconnect_if_bt_on" - namespace: "bluetooth" - description: "Stop calling unregAllGattClient when toggling the ble scan setting and bluetooth is ON" - metadata { - purpose: PURPOSE_BUGFIX - } - bug: "379166793" -} - -flag { name: "system_server_messenger" namespace: "bluetooth" description: "Replace binder call to the system server with a Messenger to enforce thread safety" diff --git a/flags/vcp.aconfig b/flags/vcp.aconfig index bdc00477c7..43d5ec4c1c 100644 --- a/flags/vcp.aconfig +++ b/flags/vcp.aconfig @@ -7,3 +7,13 @@ flag { description: "Allow muting/unmuting the remote device" bug: "318775507" } + +flag { + name: "vcp_device_volume_api_improvements" + namespace: "bluetooth" + description: "Refactor Device Volume API for generic usage" + bug: "381507732" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/floss/pandora/floss/qa_client.py b/floss/pandora/floss/qa_client.py index 4fb7c99eb4..2e71fd526c 100644 --- a/floss/pandora/floss/qa_client.py +++ b/floss/pandora/floss/qa_client.py @@ -81,6 +81,14 @@ class BluetoothQACallbacks: """ pass + def on_send_hid_virtual_unplug_completed(self, status): + """Called when send hid virtual unplug completed. + + Args: + status: BtStatus. + """ + pass + class FlossQAClient(BluetoothQACallbacks): """Handles method calls to and callbacks from the QA interface.""" @@ -187,6 +195,15 @@ class FlossQAClient(BluetoothQACallbacks): for observer in self.observers.values(): observer.on_send_hid_data_completed(status) + def OnSendHIDVirtualUnplugComplete(self, status): + """Handles send HID virtual unplug complete callback. + + Args: + status: BtStatus. + """ + for observer in self.observers.values(): + observer.on_send_hid_virtual_unplug_completed(status) + def __init__(self, bus, hci): """Constructs the client. @@ -269,6 +286,15 @@ class FlossQAClient(BluetoothQACallbacks): """ logging.debug('on_send_hid_data_completed: status: %s', status) + @utils.glib_callback() + def on_send_hid_data_completed(self, status): + """Handles send HID virtual unplug completed callback. + + Args: + status: BtStatus. + """ + logging.debug('on_send_hid_virtual_unplug_completed: status: %s', status) + @utils.glib_call(False) def has_proxy(self): """Checks whether QA proxy can be acquired.""" diff --git a/framework/Android.bp b/framework/Android.bp index e6a14f79f9..6e5dafc3e1 100644 --- a/framework/Android.bp +++ b/framework/Android.bp @@ -78,7 +78,7 @@ java_sdk_library { "//packages/modules/Bluetooth/service", "//packages/services/Car/car-builtin-lib", // TODO(240720385) - "//packages/services/Car/tests/carservice_unit_test", + "//packages/services/Car/tests/CarBuiltinLibUnitTest", ":__subpackages__", ], diff --git a/framework/api/current.txt b/framework/api/current.txt index 8bec61f117..a9bc28a28a 100644 --- a/framework/api/current.txt +++ b/framework/api/current.txt @@ -52,7 +52,7 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException; method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException; - method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException; + method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public android.bluetooth.BluetoothServerSocket listenUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery(); method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback); @@ -541,6 +541,7 @@ package android.bluetooth { method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getAlias(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothClass getBluetoothClass(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getBondState(); + method @FlaggedApi("com.android.bluetooth.flags.identity_address_type_api") @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothDevice.BluetoothAddress getIdentityAddressWithType(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getType(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.os.ParcelUuid[] getUuids(); @@ -607,6 +608,15 @@ package android.bluetooth { field public static final int TRANSPORT_LE = 2; // 0x2 } + @FlaggedApi("com.android.bluetooth.flags.identity_address_type_api") public static final class BluetoothDevice.BluetoothAddress implements android.os.Parcelable { + ctor public BluetoothDevice.BluetoothAddress(@Nullable String, int); + method public int describeContents(); + method @Nullable public String getAddress(); + method public int getAddressType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothDevice.BluetoothAddress> CREATOR; + } + public final class BluetoothGatt implements android.bluetooth.BluetoothProfile { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite(); method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite(android.bluetooth.BluetoothDevice); @@ -1084,7 +1094,7 @@ package android.bluetooth { public final class BluetoothSocket implements java.io.Closeable { method public void close() throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void connect() throws java.io.IOException; + method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public void connect() throws java.io.IOException; method public int getConnectionType(); method public java.io.InputStream getInputStream() throws java.io.IOException; method public int getMaxReceivePacketSize(); @@ -1122,7 +1132,7 @@ package android.bluetooth { field public static final int SOCKET_CLOSED = 17; // 0x11 field public static final int SOCKET_CONNECTION_FAILURE = 18; // 0x12 field public static final int SOCKET_MANAGER_FAILURE = 16; // 0x10 - field @FlaggedApi("com.android.bluetooth.flags.unix_file_socket_creation_failure") public static final int UNIX_FILE_SOCKET_CREATION_FAILURE = 21; // 0x15 + field public static final int UNIX_FILE_SOCKET_CREATION_FAILURE = 21; // 0x15 field public static final int UNSPECIFIED = 0; // 0x0 } @@ -1232,7 +1242,7 @@ package android.bluetooth.le { public final class AdvertisingSet { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void enableAdvertising(boolean, int, int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingData(android.bluetooth.le.AdvertiseData); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters); diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 06f611fcaa..8368f676b8 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -127,7 +127,9 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAutoOnSupported(); method public boolean isBleScanAlwaysAvailable(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isDistanceMeasurementSupported(); + method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isLeCocSocketOffloadSupported(); method public boolean isLeEnabled(); + method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isRfcommSocketOffloadSupported(); method @NonNull public static String nameForState(int); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int notifyActiveDeviceChangeApplied(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean registerBluetoothConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.BluetoothConnectionCallback); @@ -1152,6 +1154,25 @@ package android.bluetooth { public final class BluetoothSocket implements java.io.Closeable { method @FlaggedApi("com.android.bluetooth.flags.bt_socket_api_l2cap_cid") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getL2capLocalChannelId() throws java.io.IOException; method @FlaggedApi("com.android.bluetooth.flags.bt_socket_api_l2cap_cid") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getL2capRemoteChannelId() throws java.io.IOException; + method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public long getSocketId() throws java.io.IOException; + } + + @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public final class BluetoothSocketSettings { + method public int getDataPath(); + method public long getEndpointId(); + method public long getHubId(); + method public int getRequestedMaximumPacketSize(); + method @NonNull public String getSocketName(); + field public static final int DATA_PATH_HARDWARE_OFFLOAD = 1; // 0x1 + field public static final int DATA_PATH_NO_OFFLOAD = 0; // 0x0 + } + + @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public static final class BluetoothSocketSettings.Builder { + method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setDataPath(int); + method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setEndpointId(long); + method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setHubId(long); + method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setRequestedMaximumPacketSize(int); + method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setSocketName(@NonNull String); } public final class BluetoothStatusCodes { diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt index 93ec44901c..e32ce85165 100644 --- a/framework/jarjar-rules.txt +++ b/framework/jarjar-rules.txt @@ -12,4 +12,14 @@ rule com.android.internal.util.** com.android.bluetooth.jarjar.@0 rule com.android.modules.expresslog.** com.android.bluetooth.jarjar.@0 rule com.android.bluetooth.flags.** com.android.bluetooth.jarjar.@0 rule bluetooth.constants.** com.android.bluetooth.jarjar.@0 -rule android.media.audio.Flags com.android.bluetooth.jarjar.audio.Flags +rule android.os.Flags com.android.bluetooth.jarjar.@0 +rule android.os.CustomFeatureFlags com.android.bluetooth.jarjar.@0 +rule android.os.FeatureFlagsImpl com.android.bluetooth.jarjar.@0 +rule android.os.FakeFeatureFlagsImpl com.android.bluetooth.jarjar.@0 +rule android.os.FeatureFlags com.android.bluetooth.jarjar.@0 + +rule android.media.audio.Flags com.android.bluetooth.jarjar.@0 +rule android.media.audio.CustomFeatureFlags com.android.bluetooth.jarjar.@0 +rule android.media.audio.FeatureFlagsImpl com.android.bluetooth.jarjar.@0 +rule android.media.audio.FakeFeatureFlagsImpl com.android.bluetooth.jarjar.@0 +rule android.media.audio.FeatureFlags com.android.bluetooth.jarjar.@0 diff --git a/framework/java/android/bluetooth/AudioInputControl.java b/framework/java/android/bluetooth/AudioInputControl.java index a8e30328f4..b39458b15f 100644 --- a/framework/java/android/bluetooth/AudioInputControl.java +++ b/framework/java/android/bluetooth/AudioInputControl.java @@ -432,6 +432,8 @@ public class AudioInputControl { /** * Gets the minimum value for the gain setting. * + * <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units. + * * @return The minimum Gain Setting as defined in AICS 1.0 - 3.2.2. */ @RequiresBluetoothConnectPermission @@ -446,6 +448,8 @@ public class AudioInputControl { /** * Gets the maximum value for the gain setting. * + * <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units. + * * @return The maximum Gain Setting as defined in AICS 1.0 - 3.2.3. */ @RequiresBluetoothConnectPermission @@ -544,6 +548,8 @@ public class AudioInputControl { * <p>Register an {@link AudioInputCallback} to be notified via {@link * AudioInputCallback#onGainSettingChanged} when the gain setting changes. * + * <p>The value return is relative to {@link #getGainSettingUnit} in 0.1 decibel units. + * * @return The current gain setting as defined in AICS 1.0 - 2.2.1.1. */ @RequiresBluetoothConnectPermission diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2d26bf3e1d..2ffc178e6e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -4564,6 +4564,14 @@ public final class BluetoothAdapter { * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. * + * <p>Use {@link BluetoothDevice#createUsingSocketSettings(BluetoothSocketSettings)} to connect + * to this server socket from another Android device using the L2cap protocol/service + * multiplexer(PSM) value or the RFCOMM service UUID as input. + * + * <p>This API requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission + * only when {@code settings.getDataPath()} is different from {@link + * BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}. + * * <p>This API supports {@link BluetoothSocket#TYPE_RFCOMM} and {{@link BluetoothSocket#TYPE_LE} * only, which can be set using {@link BluetoothSocketSettings#setSocketType()}. * <li>For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link @@ -4574,10 +4582,6 @@ public final class BluetoothAdapter { * application exits unexpectedly. The mechanism for disclosing the PSM value to the client * is application-defined. * - * <p>Use {@link BluetoothDevice#createUsingSocketSettings(BluetoothSocketSettings)} to - * connect to this server socket from another Android device using the L2cap - * protocol/service multiplexer(PSM) value or the RFCOMM service UUID as input. - * * @param settings Bluetooth socket settings {@link BluetoothSocketSettings}. * @return a {@link BluetoothServerSocket} * @throws IllegalArgumentException if BluetoothSocket#TYPE_RFCOMM socket is requested with no @@ -4586,7 +4590,9 @@ public final class BluetoothAdapter { * Connection-oriented Channel (CoC). */ @RequiresBluetoothConnectPermission - @RequiresPermission(BLUETOOTH_CONNECT) + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API) public @NonNull BluetoothServerSocket listenUsingSocketSettings( @NonNull BluetoothSocketSettings settings) throws IOException { @@ -4597,24 +4603,64 @@ public final class BluetoothAdapter { if (settings.getRfcommUuid() == null) { throw new IllegalArgumentException("RFCOMM server missing UUID"); } - return createNewRfcommSocketAndRecord( - settings.getRfcommServiceName(), - settings.getRfcommUuid(), - settings.isAuthenticationRequired(), - settings.isEncryptionRequired()); + if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + socket = + new BluetoothServerSocket( + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + new ParcelUuid(settings.getRfcommUuid())); + } else { + socket = + new BluetoothServerSocket( + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + -1, + new ParcelUuid(settings.getRfcommUuid()), + false, + false, + settings.getDataPath(), + settings.getSocketName(), + settings.getHubId(), + settings.getEndpointId(), + settings.getRequestedMaximumPacketSize()); + } + socket.setServiceName(settings.getRfcommServiceName()); } else if (type == BluetoothSocket.TYPE_LE) { - socket = - new BluetoothServerSocket( - settings.getSocketType(), - settings.isAuthenticationRequired(), - settings.isEncryptionRequired(), - SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, - false, - false); + if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + socket = + new BluetoothServerSocket( + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, + false, + false); + } else { + socket = + new BluetoothServerSocket( + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, + null, + false, + false, + settings.getDataPath(), + settings.getSocketName(), + settings.getHubId(), + settings.getEndpointId(), + settings.getRequestedMaximumPacketSize()); + } } else { - throw new IOException("Error: Invalid socket type: " + type); + throw new IllegalArgumentException("Error: Invalid socket type: " + type); } - int errno = socket.mSocket.bindListen(); + int errno; + errno = + (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) + ? socket.mSocket.bindListen() + : socket.mSocket.bindListenWithOffload(); if (errno != 0) { throw new IOException("Error: " + errno); } @@ -5916,4 +5962,98 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } } + + /** + * Returns whether LE CoC socket hardware offload is supported. + * + * <p>Bluetooth socket hardware offload allows the system to handle Bluetooth communication on a + * low-power processor, improving efficiency and reducing power consumption. This is achieved by + * providing channel information of an already connected {@link BluetoothSocket} to offload + * endpoints (e.g., offload stacks and applications). The offload stack can then decode received + * packets and pass them to the appropriate offload application without waking up the main + * application processor. This API allows offload endpoints to utilize Bluetooth sockets while + * the host stack retains control over the connection. + * + * <p>To configure a socket for hardware offload, use the following {@link + * BluetoothSocketSettings} methods: + * + * <ul> + * <li>{@link BluetoothSocketSettings#setDataPath(int)} with {@link + * BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD} + * <li>{@link BluetoothSocketSettings#setHubId(long)} + * <li>{@link BluetoothSocketSettings#setEndpointId(long)} + * </ul> + * + * <p>This functionality is provided as a System API because only OEM specific system + * applications can be offloaded as endpoints in the low-power processor. + * + * @return {@code true} if LE CoC socket hardware offload is supported, {@code false} otherwise. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API) + @RequiresPermission(BLUETOOTH_PRIVILEGED) + public boolean isLeCocSocketOffloadSupported() { + if (!isEnabled()) { + return false; + } + mServiceLock.readLock().lock(); + try { + if (mService != null) { + return mService.isLeCocSocketOffloadSupported(mAttributionSource); + } + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + + /** + * Returns whether RFCOMM socket hardware offload is supported. + * + * <p>Bluetooth socket hardware offload allows the system to handle Bluetooth communication on a + * low-power processor, improving efficiency and reducing power consumption. This is achieved by + * providing channel information of an already connected {@link BluetoothSocket} to offload + * endpoints (e.g., offload stacks and applications). The offload stack can then decode received + * packets and pass them to the appropriate offload application without waking up the main + * application processor. This API allows offload endpoints to utilize Bluetooth sockets while + * the host stack retains control over the connection. + * + * <p>To configure a socket for hardware offload, use the following {@link + * BluetoothSocketSettings} methods: + * + * <ul> + * <li>{@link BluetoothSocketSettings#setDataPath(int)} with {@link + * BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD} + * <li>{@link BluetoothSocketSettings#setHubId(long)} + * <li>{@link BluetoothSocketSettings#setEndpointId(long)} + * </ul> + * + * <p>This functionality is provided as a System API because only OEM specific system + * applications can be offloaded as endpoints in the low-power processor. + * + * @return {@code true} if RFCOMM socket hardware offload is supported, {@code false} otherwise. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API) + @RequiresPermission(BLUETOOTH_PRIVILEGED) + public boolean isRfcommSocketOffloadSupported() { + if (!isEnabled()) { + return false; + } + mServiceLock.readLock().lock(); + try { + if (mService != null) { + return mService.isRfcommSocketOffloadSupported(mAttributionSource); + } + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } } diff --git a/framework/java/android/bluetooth/BluetoothCodecType.java b/framework/java/android/bluetooth/BluetoothCodecType.java index 6bd5045c56..54547fe3f5 100644 --- a/framework/java/android/bluetooth/BluetoothCodecType.java +++ b/framework/java/android/bluetooth/BluetoothCodecType.java @@ -37,11 +37,7 @@ public final class BluetoothCodecType implements Parcelable { private BluetoothCodecType(Parcel in) { mNativeCodecType = in.readInt(); - if (Flags.a2dpFixCodecTypeInJava()) { - mCodecId = in.readLong() & 0xFFFFFFFFL; - } else { - mCodecId = in.readLong(); - } + mCodecId = in.readLong() & 0xFFFFFFFFL; mCodecName = in.readString(); } @@ -75,11 +71,7 @@ public final class BluetoothCodecType implements Parcelable { */ private BluetoothCodecType(@BluetoothCodecConfig.SourceCodecType int codecType, long codecId) { mNativeCodecType = codecType; - if (Flags.a2dpFixCodecTypeInJava()) { - mCodecId = codecId & 0xFFFFFFFFL; - } else { - mCodecId = codecId; - } + mCodecId = codecId & 0xFFFFFFFFL; mCodecName = BluetoothCodecConfig.getCodecName(codecType); } @@ -94,11 +86,7 @@ public final class BluetoothCodecType implements Parcelable { @SystemApi public BluetoothCodecType(int codecType, long codecId, @NonNull String codecName) { mNativeCodecType = codecType; - if (Flags.a2dpFixCodecTypeInJava()) { - mCodecId = codecId & 0xFFFFFFFFL; - } else { - mCodecId = codecId; - } + mCodecId = codecId & 0xFFFFFFFFL; mCodecName = codecName; } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index edccc5e027..58dd3ba1da 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -1726,6 +1726,33 @@ public final class BluetoothDevice implements Parcelable, Attributable { } /** + * Returns the identity address and identity address type of this BluetoothDevice. + * + * @return a {@link BluetoothAddress} containing identity address and identity address type. If + * Bluetooth is not enabled or identity address type is not available, it will return a + * {@link BluetoothAddress} containing {@link #ADDRESS_TYPE_UNKNOWN} device for the identity + * address type. + */ + @FlaggedApi(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + @NonNull + public BluetoothAddress getIdentityAddressWithType() { + if (DBG) log("getIdentityAddressWithType()"); + final IBluetooth service = getService(); + if (service == null || !isBluetoothEnabled()) { + Log.e(TAG, "BT not enabled. Cannot get identity address with type"); + } else { + try { + return service.getIdentityAddressWithType(mAddress); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + } + } + return new BluetoothAddress(null, BluetoothDevice.ADDRESS_TYPE_UNKNOWN); + } + + /** * Get the friendly Bluetooth name of the remote device. * * <p>The local adapter will automatically retrieve remote names when performing a device scan, @@ -3339,13 +3366,30 @@ public final class BluetoothDevice implements Parcelable, Attributable { throw new IllegalArgumentException("Invalid PSM/Channel value: " + psm); } } - return new BluetoothSocket( - this, - settings.getSocketType(), - settings.isAuthenticationRequired(), - settings.isEncryptionRequired(), - psm, - uuid); + if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + return new BluetoothSocket( + this, + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + psm, + uuid); + } else { + return new BluetoothSocket( + this, + settings.getSocketType(), + settings.isAuthenticationRequired(), + settings.isEncryptionRequired(), + psm, + uuid, + false, + false, + settings.getDataPath(), + settings.getSocketName(), + settings.getHubId(), + settings.getEndpointId(), + settings.getRequestedMaximumPacketSize()); + } } /** @@ -3748,4 +3792,66 @@ public final class BluetoothDevice implements Parcelable, Attributable { private static void log(String msg) { Log.d(TAG, msg); } + + /** A data class for Bluetooth address and address type. */ + @FlaggedApi(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) + public static final class BluetoothAddress implements Parcelable { + private final @Nullable String mAddress; + private final @AddressType int mAddressType; + + public BluetoothAddress(@Nullable String address, @AddressType int addressType) { + mAddress = address; + mAddressType = addressType; + } + + /** + * Returns the address of this {@link BluetoothAddress}. + * + * <p>For example, "00:11:22:AA:BB:CC". + * + * @return Bluetooth address as string + */ + @Nullable + public String getAddress() { + return mAddress; + } + + /** + * Returns the address type of this {@link BluetoothAddress}, one of {@link + * #ADDRESS_TYPE_PUBLIC}, {@link #ADDRESS_TYPE_RANDOM}, or {@link #ADDRESS_TYPE_UNKNOWN}. + * + * @return Bluetooth address type + */ + @AddressType + public int getAddressType() { + return mAddressType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + BluetoothUtils.writeStringToParcel(out, mAddress); + out.writeInt(mAddressType); + } + + private BluetoothAddress(@NonNull Parcel in) { + this(in.readString(), in.readInt()); + } + + /** {@link Parcelable.Creator} interface implementation. */ + public static final @NonNull Parcelable.Creator<BluetoothAddress> CREATOR = + new Parcelable.Creator<BluetoothAddress>() { + public @NonNull BluetoothAddress createFromParcel(Parcel in) { + return new BluetoothAddress(in); + } + + public @NonNull BluetoothAddress[] newArray(int size) { + return new BluetoothAddress[size]; + } + }; + } } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index e63b5987ac..e67d823c7e 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -17,6 +17,8 @@ package android.bluetooth; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothUtils.logRemoteException; import android.annotation.IntDef; import android.annotation.NonNull; @@ -368,7 +370,8 @@ public final class BluetoothGatt implements BluetoothProfile { + (" status=" + status) + (" clientIf=" + clientIf) + (" connected=" + connected) - + (" device=" + address)); + + (" device=" + + BluetoothUtils.toAnonymizedAddress(address))); } if (!address.equals(mDevice.getAddress())) { return; @@ -477,7 +480,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onCharacteristicRead() -" - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" handle=" + handle) + (" Status=" + status)); } @@ -544,7 +547,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onCharacteristicWrite() -" - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" handle=" + handle) + (" Status=" + status)); } @@ -817,7 +820,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onReadRemoteRssi() -" - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" rssi=" + rssi) + (" status=" + status)); } @@ -847,7 +850,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onConfigureMTU() -" - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" mtu=" + mtu) + (" status=" + status)); } @@ -879,7 +882,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onConnectionUpdated() -" - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" interval=" + interval) + (" latency=" + latency) + (" timeout=" + timeout) @@ -914,7 +917,10 @@ public final class BluetoothGatt implements BluetoothProfile { @Override public void onServiceChanged(String address) { if (DBG) { - Log.d(TAG, "onServiceChanged() - Device=" + address); + Log.d( + TAG, + "onServiceChanged() - Device=" + + BluetoothUtils.toAnonymizedAddress(address)); } if (!address.equals(mDevice.getAddress())) { @@ -949,7 +955,7 @@ public final class BluetoothGatt implements BluetoothProfile { Log.d( TAG, "onSubrateChange() - " - + (" Device=" + address) + + (" Device=" + BluetoothUtils.toAnonymizedAddress(address)) + (" subrateFactor=" + subrateFactor) + (" latency=" + latency) + (" contNum=" + contNum) @@ -2118,85 +2124,40 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>This function will send a LE subrate request to the remote device. * + * <p>This method requires the calling app to have the {@link + * android.Manifest.permission#BLUETOOTH_CONNECT} permission. Additionally, an app must either + * have the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} or be associated with the + * Companion Device manager (see {@link android.companion.CompanionDeviceManager#associate( + * AssociationRequest, android.companion.CompanionDeviceManager.Callback, Handler)}) + * * @param subrateMode Request a specific subrate mode. * @throws IllegalArgumentException If the parameters are outside of their specified range. * @return true, if the request is send to the Bluetooth stack. * @hide */ @RequiresBluetoothConnectPermission - @RequiresPermission(BLUETOOTH_CONNECT) - public boolean requestSubrateMode(@SubrateRequestMode int subrateMode) { + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) + public int requestSubrateMode(@SubrateRequestMode int subrateMode) { if (subrateMode < SUBRATE_REQUEST_MODE_BALANCED || subrateMode > SUBRATE_REQUEST_MODE_LOW_POWER) { throw new IllegalArgumentException("Subrate Mode not within valid range"); } if (DBG) { - Log.d(TAG, "requestsubrateMode() - subrateMode: " + subrateMode); + Log.d(TAG, "requestsubrateMode(" + subrateMode + ")"); } if (mService == null || mClientIf == 0) { - return false; + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } try { - mService.subrateModeRequest( - mClientIf, mDevice.getAddress(), subrateMode, mAttributionSource); + return mService.subrateModeRequest(mClientIf, mDevice, subrateMode, mAttributionSource); } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; + logRemoteException(TAG, e); + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } - return true; - } - - /** - * Request a LE subrate request. - * - * <p>This function will send a LE subrate request to the remote device. - * - * @return true, if the request is send to the Bluetooth stack. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(BLUETOOTH_CONNECT) - public boolean bleSubrateRequest( - int subrateMin, - int subrateMax, - int maxLatency, - int contNumber, - int supervisionTimeout) { - if (DBG) { - Log.d( - TAG, - "bleSubrateRequest() - subrateMin=" - + subrateMin - + " subrateMax=" - + (subrateMax) - + " maxLatency= " - + maxLatency - + "contNumber=" - + contNumber - + " supervisionTimeout=" - + supervisionTimeout); - } - if (mService == null || mClientIf == 0) { - return false; - } - - try { - mService.leSubrateRequest( - mClientIf, - mDevice.getAddress(), - subrateMin, - subrateMax, - maxLatency, - contNumber, - supervisionTimeout, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - return true; } /** diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java index 53b491e1b3..94bf7ae4c4 100644 --- a/framework/java/android/bluetooth/BluetoothGattServer.java +++ b/framework/java/android/bluetooth/BluetoothGattServer.java @@ -115,7 +115,7 @@ public final class BluetoothGattServer implements BluetoothProfile { + " connected=" + connected + " device=" - + address); + + BluetoothUtils.toAnonymizedAddress(address)); } try { mCallback.onConnectionStateChange( @@ -305,7 +305,7 @@ public final class BluetoothGattServer implements BluetoothProfile { TAG, "onExecuteWrite() - " + "device=" - + address + + BluetoothUtils.toAnonymizedAddress(address) + ", transId=" + transId + "execWrite=" @@ -334,7 +334,7 @@ public final class BluetoothGattServer implements BluetoothProfile { TAG, "onNotificationSent() - " + "device=" - + address + + BluetoothUtils.toAnonymizedAddress(address) + ", status=" + status); } @@ -388,7 +388,7 @@ public final class BluetoothGattServer implements BluetoothProfile { TAG, "onPhyUpdate() - " + "device=" - + address + + BluetoothUtils.toAnonymizedAddress(address) + ", txPHy=" + txPhy + ", rxPHy=" @@ -417,7 +417,7 @@ public final class BluetoothGattServer implements BluetoothProfile { TAG, "onPhyUpdate() - " + "device=" - + address + + BluetoothUtils.toAnonymizedAddress(address) + ", txPHy=" + txPhy + ", rxPHy=" @@ -446,7 +446,7 @@ public final class BluetoothGattServer implements BluetoothProfile { Log.d( TAG, "onConnectionUpdated() - Device=" - + address + + BluetoothUtils.toAnonymizedAddress(address) + " interval=" + interval + " latency=" diff --git a/framework/java/android/bluetooth/BluetoothManager.java b/framework/java/android/bluetooth/BluetoothManager.java index 7f6a0e4e42..00522d4f89 100644 --- a/framework/java/android/bluetooth/BluetoothManager.java +++ b/framework/java/android/bluetooth/BluetoothManager.java @@ -60,7 +60,7 @@ public final class BluetoothManager { /** @hide */ public BluetoothManager(Context context) { - if (com.android.bluetooth.flags.Flags.overrideContextToSpecifyDeviceId() && isAtLeastU()) { + if (isAtLeastU()) { // Pin the context DeviceId prevent the associated attribution source to be obsolete // TODO: b/343739429 -- pass the context to BluetoothAdapter constructor instead mContext = context.createDeviceContext(Context.DEVICE_ID_DEFAULT); diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java index b1a0188626..f20a03d1cc 100644 --- a/framework/java/android/bluetooth/BluetoothServerSocket.java +++ b/framework/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.NonNull; import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; @@ -154,6 +155,60 @@ public final class BluetoothServerSocket implements Closeable { } /** + * Construct a socket for incoming connections. + * + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @param uuid uuid + * @param pitm enforce person-in-the-middle protection for authentication. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection + * @param dataPath data path used for this socket + * @param socketName user-friendly name for this socket + * @param hubId ID of the hub to which the end point belongs + * @param endpointId ID of the endpoint within the hub that is associated with this socket + * @param maximumPacketSize The maximum size (in bytes) of a single data packet + * @throws IOException On error, for example Bluetooth not available, or insufficient privileges + */ + /*package*/ BluetoothServerSocket( + int type, + boolean auth, + boolean encrypt, + int port, + ParcelUuid uuid, + boolean pitm, + boolean min16DigitPin, + int dataPath, + @NonNull String socketName, + long hubId, + long endpointId, + int maximumPacketSize) + throws IOException { + mSocketCreationTimeMillis = System.currentTimeMillis(); + mType = type; + mChannel = port; + mSocket = + new BluetoothSocket( + type, + auth, + encrypt, + port, + uuid, + pitm, + min16DigitPin, + dataPath, + socketName, + hubId, + endpointId, + maximumPacketSize); + if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + mSocket.setExcludeSdp(true); + } + mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis; + } + + /** * Block until a connection is established. * * <p>Returns a connected {@link BluetoothSocket} on successful connection. diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 901407c00b..d33542e6d6 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -157,6 +157,8 @@ public final class BluetoothSocket implements Closeable { /*package*/ static final int SEC_FLAG_AUTH_PITM = 1 << 3; /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; + /*package*/ static final String DEFAULT_SOCKET_NAME = "default_name"; + private final int mType; /* one of TYPE_RFCOMM etc */ private BluetoothDevice mDevice; /* remote device */ private String mAddress; /* remote address */ @@ -165,6 +167,11 @@ public final class BluetoothSocket implements Closeable { private final BluetoothInputStream mInputStream; private final BluetoothOutputStream mOutputStream; private final ParcelUuid mUuid; + private final int mDataPath; + private final String mSocketName; + private final long mHubId; + private final long mEndpointId; + private final int mMaximumPacketSize; /** when true no SPP SDP record will be created */ private boolean mExcludeSdp = false; @@ -184,12 +191,15 @@ public final class BluetoothSocket implements Closeable { @UnsupportedAppUsage private int mPort; /* RFCOMM channel or L2CAP psm */ private String mServiceName; - private static final int SOCK_SIGNAL_SIZE = 36; + private static final int SOCK_CONNECTION_SIGNAL_SIZE = 44; + private static final long INVALID_SOCKET_ID = 0; + private static final int SOCK_ACCEPT_SIGNAL_SIZE = 4; private ByteBuffer mL2capBuffer = null; private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer. private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received. private ParcelUuid mConnectionUuid; + private long mSocketId; // Socket ID in connected state. private long mSocketCreationTimeNanos = 0; private long mSocketCreationLatencyNanos = 0; @@ -245,6 +255,41 @@ public final class BluetoothSocket implements Closeable { boolean pitm, boolean min16DigitPin) throws IOException { + this(type, auth, encrypt, port, uuid, pitm, min16DigitPin, 0, DEFAULT_SOCKET_NAME, 0, 0, 0); + } + + /** + * Construct a BluetoothSocket. + * + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @param uuid SDP uuid + * @param pitm enforce person-in-the-middle protection. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection + * @param dataPath data path used for this socket + * @param socketName user-friendly name for this socket + * @param hubId ID of the hub to which the end point belongs + * @param endpointId ID of the endpoint within the hub that is associated with this socket + * @param maximumPacketSize The maximum size (in bytes) of a single data packet + * @throws IOException On error, for example Bluetooth not available, or insufficient privileges + */ + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, LOCAL_MAC_ADDRESS}) + /*package*/ BluetoothSocket( + int type, + boolean auth, + boolean encrypt, + int port, + ParcelUuid uuid, + boolean pitm, + boolean min16DigitPin, + int dataPath, + @NonNull String socketName, + long hubId, + long endpointId, + int maximumPacketSize) + throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); mSocketCreationTimeNanos = System.nanoTime(); if (type == BluetoothSocket.TYPE_RFCOMM @@ -267,6 +312,11 @@ public final class BluetoothSocket implements Closeable { mPort = port; // this constructor to be called only from BluetoothServerSocket mDevice = null; + mDataPath = dataPath; + mSocketName = socketName; + mHubId = hubId; + mEndpointId = endpointId; + mMaximumPacketSize = maximumPacketSize; mSocketState = SocketState.INIT; @@ -322,6 +372,55 @@ public final class BluetoothSocket implements Closeable { boolean pitm, boolean min16DigitPin) throws IOException { + this( + device, + type, + auth, + encrypt, + port, + uuid, + pitm, + min16DigitPin, + 0, + DEFAULT_SOCKET_NAME, + 0, + 0, + 0); + } + + /** + * Construct a BluetoothSocket. + * + * @param device remote device that this socket can connect to + * @param type type of socket + * @param auth require the remote device to be authenticated + * @param encrypt require the connection to be encrypted + * @param port remote port + * @param uuid SDP uuid + * @param pitm enforce person-in-the-middle protection. + * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection + * @param dataPath data path used for this socket + * @param socketName user-friendly name for this socket + * @param hubId ID of the hub to which the end point belongs + * @param endpointId ID of the endpoint within the hub that is associated with this socket + * @param maximumPacketSize The maximum size (in bytes) of a single data packet + * @throws IOException On error, for example Bluetooth not available, or insufficient privileges + */ + /*package*/ BluetoothSocket( + @NonNull BluetoothDevice device, + int type, + boolean auth, + boolean encrypt, + int port, + ParcelUuid uuid, + boolean pitm, + boolean min16DigitPin, + int dataPath, + @NonNull String socketName, + long hubId, + long endpointId, + int maximumPacketSize) + throws IOException { if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); mSocketCreationTimeNanos = System.nanoTime(); if (type == BluetoothSocket.TYPE_RFCOMM @@ -343,6 +442,11 @@ public final class BluetoothSocket implements Closeable { mEncrypt = encrypt; mDevice = device; mPort = port; + mDataPath = dataPath; + mSocketName = socketName; + mHubId = hubId; + mEndpointId = endpointId; + mMaximumPacketSize = maximumPacketSize; mSocketState = SocketState.INIT; @@ -393,11 +497,17 @@ public final class BluetoothSocket implements Closeable { mMaxRxPacketSize = s.mMaxRxPacketSize; mMaxTxPacketSize = s.mMaxTxPacketSize; mConnectionUuid = s.mConnectionUuid; + mSocketId = s.mSocketId; mServiceName = s.mServiceName; mExcludeSdp = s.mExcludeSdp; mAuthPitm = s.mAuthPitm; mMin16DigitPin = s.mMin16DigitPin; + mDataPath = s.mDataPath; + mSocketName = s.mSocketName; + mHubId = s.mHubId; + mEndpointId = s.mEndpointId; + mMaximumPacketSize = s.mMaximumPacketSize; mSocketCreationTimeNanos = s.mSocketCreationTimeNanos; mSocketCreationLatencyNanos = s.mSocketCreationLatencyNanos; } @@ -524,11 +634,17 @@ public final class BluetoothSocket implements Closeable { * * <p>{@link #close} can be used to abort this call from another thread. * + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * {@code mDataPath} is different from {@link BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}. + * * @throws BluetoothSocketException in case of failure, with the corresponding error code. * @throws IOException for other errors (eg: InputStream read failures etc.). */ + @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API) @RequiresBluetoothConnectPermission - @RequiresPermission(BLUETOOTH_CONNECT) + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) public void connect() throws IOException { IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); long socketConnectionTimeNanos = System.nanoTime(); @@ -547,7 +663,30 @@ public final class BluetoothSocket implements Closeable { if (socketManager == null) { throw new BluetoothSocketException(BluetoothSocketException.SOCKET_MANAGER_FAILURE); } - mPfd = socketManager.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); + if (Flags.socketSettingsApi()) { + if (mDataPath == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + mPfd = + socketManager.connectSocket( + mDevice, mType, mUuid, mPort, getSecurityFlags()); + } else { + mPfd = + socketManager.connectSocketwithOffload( + mDevice, + mType, + mUuid, + mPort, + getSecurityFlags(), + mDataPath, + mSocketName, + mHubId, + mEndpointId, + mMaximumPacketSize); + } + } else { + mPfd = + socketManager.connectSocket( + mDevice, mType, mUuid, mPort, getSecurityFlags()); + } synchronized (this) { Log.i(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if (mSocketState == SocketState.CLOSED) { @@ -675,7 +814,7 @@ public final class BluetoothSocket implements Closeable { if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort); if (mPort <= -1) { mPort = channel; - } // else ASSERT(mPort == channel) + } ret = 0; } catch (IOException e) { if (mPfd != null) { @@ -692,6 +831,98 @@ public final class BluetoothSocket implements Closeable { return ret; } + /** + * Currently returns unix errno instead of throwing IOException, so that BluetoothAdapter can + * check the error code for EADDRINUSE + */ + @RequiresBluetoothConnectPermission + @RequiresPermission( + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) + /*package*/ int bindListenWithOffload() { + int ret; + if (mSocketState == SocketState.CLOSED) return EBADFD; + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); + if (bluetoothProxy == null) { + Log.e(TAG, "bindListenWithOffload() fail, reason: bluetooth is off"); + return -1; + } + try { + if (DBG) Log.d(TAG, "bindListenWithOffload(): mPort=" + mPort + ", mType=" + mType); + IBluetoothSocketManager socketManager = bluetoothProxy.getSocketManager(); + if (socketManager == null) { + Log.e(TAG, "bindListenWithOffload() bt get socket manager failed"); + return -1; + } + mPfd = + socketManager.createSocketChannelWithOffload( + mType, + mServiceName, + mUuid, + mPort, + getSecurityFlags(), + mDataPath, + mSocketName, + mHubId, + mEndpointId, + mMaximumPacketSize); + } catch (RemoteException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return -1; + } + + // read out port number + try { + synchronized (this) { + if (DBG) { + Log.d( + TAG, + "bindListenWithOffload(), SocketState: " + + mSocketState + + ", mPfd: " + + mPfd); + } + if (mSocketState != SocketState.INIT) return EBADFD; + if (mPfd == null) return -1; + FileDescriptor fd = mPfd.getFileDescriptor(); + if (fd == null) { + Log.e(TAG, "bindListenWithOffload(), null file descriptor"); + return -1; + } + + if (DBG) Log.d(TAG, "bindListenWithOffload(), Create LocalSocket"); + mSocket = new LocalSocket(fd); + if (DBG) Log.d(TAG, "bindListenWithOffload(), new LocalSocket.getInputStream()"); + mSocketIS = mSocket.getInputStream(); + mSocketOS = mSocket.getOutputStream(); + } + if (DBG) Log.d(TAG, "bindListenWithOffload(), readInt mSocketIS: " + mSocketIS); + int channel = readInt(mSocketIS); + synchronized (this) { + if (mSocketState == SocketState.INIT) { + mSocketState = SocketState.LISTENING; + } + } + if (DBG) Log.d(TAG, "bindListenWithOffload(): channel=" + channel + ", mPort=" + mPort); + if (mPort <= -1) { + mPort = channel; + } + ret = 0; + } catch (IOException e) { + if (mPfd != null) { + try { + mPfd.close(); + } catch (IOException e1) { + Log.e(TAG, "bindListenWithOffload, close mPfd: " + e1); + } + mPfd = null; + } + Log.e(TAG, "bindListenWithOffload, fail to get port number, exception: " + e); + return -1; + } + return ret; + } + /*package*/ BluetoothSocket accept(int timeout) throws IOException { BluetoothSocket acceptedSocket; if (mSocketState != SocketState.LISTENING) { @@ -701,7 +932,13 @@ public final class BluetoothSocket implements Closeable { if (timeout > 0) { mSocket.setSoTimeout(timeout); } - String RemoteAddr = waitSocketSignal(mSocketIS); + sendSocketAcceptSignal(mSocketOS, true); + String RemoteAddr; + try { + RemoteAddr = waitSocketSignal(mSocketIS); + } finally { + sendSocketAcceptSignal(mSocketOS, false); + } if (timeout > 0) { mSocket.setSoTimeout(0); } @@ -757,7 +994,7 @@ public final class BluetoothSocket implements Closeable { ret = mSocketIS.read(b, offset, length); } if (ret < 0) { - throw new IOException("bt socket closed, read return: " + ret); + return -1; } if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); return ret; @@ -836,6 +1073,7 @@ public final class BluetoothSocket implements Closeable { mPfd = null; } mConnectionUuid = null; + mSocketId = INVALID_SOCKET_ID; } } } @@ -1016,6 +1254,25 @@ public final class BluetoothSocket implements Closeable { return cid; } + /** + * Returns the socket ID assigned to the open connection on this BluetoothSocket. This socket ID + * is a unique identifier for the socket. It is valid only while the socket is connected. + * + * @return The socket ID in connected state. + * @throws BluetoothSocketException If the socket is not connected or an error occurs while + * retrieving the socket ID. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API) + @RequiresNoPermission + public long getSocketId() throws IOException { + if (mSocketState != SocketState.CONNECTED || mSocketId == INVALID_SOCKET_ID) { + throw new BluetoothSocketException(BluetoothSocketException.SOCKET_CLOSED); + } + return mSocketId; + } + /** @hide */ @RequiresNoPermission public ParcelFileDescriptor getParcelFileDescriptor() { @@ -1034,17 +1291,53 @@ public final class BluetoothSocket implements Closeable { addr[5]); } + /** + * Sends a socket accept signal to the host stack. + * + * <p>This method is used to notify the host stack whether the host application is actively + * accepting a new connection or not. It sends a signal containing the acceptance status to the + * output stream associated with the socket. + * + * <p>This method is only effective when the data path is not {@link + * BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}. + * + * @param os The output stream to write the signal to. + * @param isAccepting {@code true} if the socket connection is being accepted, {@code false} + * otherwise. + * @throws IOException If an I/O error occurs while writing to the output stream. + * @hide + */ + private void sendSocketAcceptSignal(OutputStream os, boolean isAccepting) throws IOException { + if (Flags.socketSettingsApi()) { + if (mDataPath == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) { + return; + } + Log.d(TAG, "sendSocketAcceptSignal" + " isAccepting " + isAccepting); + byte[] sig = new byte[SOCK_ACCEPT_SIGNAL_SIZE]; + ByteBuffer bb = ByteBuffer.wrap(sig); + bb.order(ByteOrder.nativeOrder()); + bb.putShort((short) SOCK_ACCEPT_SIGNAL_SIZE); + bb.putShort((short) (isAccepting ? 1 : 0)); + os.write(sig, 0, SOCK_ACCEPT_SIGNAL_SIZE); + } + } + private String waitSocketSignal(InputStream is) throws IOException { - byte[] sig = new byte[SOCK_SIGNAL_SIZE]; + byte[] sig = new byte[SOCK_CONNECTION_SIGNAL_SIZE]; int ret = readAll(is, sig); if (VDBG) { - Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret); + Log.d( + TAG, + "waitSocketSignal read " + + SOCK_CONNECTION_SIGNAL_SIZE + + " bytes signal ret: " + + ret); } ByteBuffer bb = ByteBuffer.wrap(sig); /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); - if (size != SOCK_SIGNAL_SIZE) { + if (size != SOCK_CONNECTION_SIGNAL_SIZE) { throw new IOException("Connection failure, wrong signal size: " + size); } byte[] addr = new byte[6]; @@ -1056,6 +1349,7 @@ public final class BluetoothSocket implements Closeable { long uuidLsb = bb.getLong(); long uuidMsb = bb.getLong(); mConnectionUuid = new ParcelUuid(new UUID(uuidMsb, uuidLsb)); + mSocketId = bb.getLong(); String RemoteAddr = convertAddr(addr); if (VDBG) { Log.d( @@ -1073,7 +1367,9 @@ public final class BluetoothSocket implements Closeable { + " MaxTxPktSize: " + mMaxTxPacketSize + " mConnectionUuid: " - + mConnectionUuid.toString()); + + mConnectionUuid.toString() + + " mSocketId: " + + mSocketId); } if (status != 0) { throw new IOException("Connection failure, status: " + status); diff --git a/framework/java/android/bluetooth/BluetoothSocketException.java b/framework/java/android/bluetooth/BluetoothSocketException.java index aba95a375a..4e163b7313 100644 --- a/framework/java/android/bluetooth/BluetoothSocketException.java +++ b/framework/java/android/bluetooth/BluetoothSocketException.java @@ -18,21 +18,18 @@ package android.bluetooth; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresNoPermission; -import com.android.bluetooth.flags.Flags; - import java.io.IOException; import java.lang.annotation.Retention; /** * Thrown when an error occurs during a Bluetooth Socket related exception. * - * <p>This is currently only intended to be thrown for a failure during {@link - * BluetoothSocket#connect()} operation. + * <p>This is currently intended to be thrown for a failure during {@link BluetoothSocket} + * operations. */ public class BluetoothSocketException extends IOException { @@ -70,8 +67,8 @@ public class BluetoothSocketException extends IOException { public static final int UNSPECIFIED = 0; /** - * Error code during connect when socket connection fails for unknown reason during L2CAP - * connection. + * Error code returned by {@link BluetoothSocket} during a L2CAP-related socket operation that + * failed for an unknown reason. */ public static final int L2CAP_UNKNOWN = 1; @@ -141,13 +138,22 @@ public class BluetoothSocketException extends IOException { /** Error code during connect when L2CAP connection timeout. */ public static final int L2CAP_TIMEOUT = 14; - /** Error code during connect when Bluetooth is off and socket connection is triggered. */ + /** + * Error code returned by {@link BluetoothSocket} during a socket operation that failed because + * Bluetooth is turned off. + */ public static final int BLUETOOTH_OFF_FAILURE = 15; - /** Error code during connect when socket manager is not available. */ + /** + * Error code returned by {@link BluetoothSocket} during a socket operation that failed because + * socket manager is not available. + */ public static final int SOCKET_MANAGER_FAILURE = 16; - /** Error code during connect when socket is closed. */ + /** + * Error code returned by {@link BluetoothSocket} during a socket operation that failed because + * the socket has been closed. + */ public static final int SOCKET_CLOSED = 17; /** Error code during connect for generic socket connection failures. */ @@ -160,7 +166,6 @@ public class BluetoothSocketException extends IOException { public static final int RPC_FAILURE = 20; /** Error code during connect when the UNIX socket connection creation fails. */ - @FlaggedApi(Flags.FLAG_UNIX_FILE_SOCKET_CREATION_FAILURE) public static final int UNIX_FILE_SOCKET_CREATION_FAILURE = 21; /* Corresponding messages for respective error codes. */ diff --git a/framework/java/android/bluetooth/BluetoothSocketSettings.java b/framework/java/android/bluetooth/BluetoothSocketSettings.java index 6fa8b0dc8b..b7706d50fe 100644 --- a/framework/java/android/bluetooth/BluetoothSocketSettings.java +++ b/framework/java/android/bluetooth/BluetoothSocketSettings.java @@ -18,14 +18,21 @@ package android.bluetooth; import static android.bluetooth.BluetoothSocket.SocketType; +import static java.util.Objects.requireNonNull; + import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresNoPermission; +import android.annotation.SystemApi; import com.android.bluetooth.flags.Flags; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.charset.StandardCharsets; import java.util.UUID; /** @@ -42,6 +49,64 @@ public final class BluetoothSocketSettings { private static final int L2CAP_PSM_UNSPECIFIED = -1; + /** + * Annotation to define the data path used for Bluetooth socket communication. This determines + * how data flows between the application and the Bluetooth controller. + * + * @hide + */ + @IntDef( + prefix = {"DATA_PATH_"}, + value = {DATA_PATH_NO_OFFLOAD, DATA_PATH_HARDWARE_OFFLOAD}) + @Retention(RetentionPolicy.SOURCE) + public @interface SocketDataPath {} + + /** + * Non-offload data path where the application's socket data is processed by the main Bluetooth + * stack. + * + * @hide + */ + @SystemApi public static final int DATA_PATH_NO_OFFLOAD = 0; + + /** + * Hardware offload data path where the application's socket data is processed by a offloaded + * application running on the low-power processor. + * + * <p>Using this data path requires the {@code BLUETOOTH_PRIVILEGED} permission, which will be + * checked when a socket connection or channel is created. + * + * @hide + */ + @SystemApi public static final int DATA_PATH_HARDWARE_OFFLOAD = 1; + + /** + * Maximum size (in bytes) of a data packet that can be received from the endpoint when using + * {@link #DATA_PATH_HARDWARE_OFFLOAD}. + */ + @SystemApi private static final int HARDWARE_OFFLOAD_PACKET_MAX_SIZE = 65535; + + /** + * Maximum length (in bytes) of a socket name when using {@link #DATA_PATH_HARDWARE_OFFLOAD}. + */ + @SystemApi private static final int HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH = 127; + + /** + * Constant representing an invalid hub ID. This value indicates that a hub ID has not been + * assigned or is not valid. + * + * @hide + */ + private static final long INVALID_HUB_ID = 0; + + /** + * Constant representing an invalid hub endpoint ID. This value indicates that an endpoint ID + * has not been assigned or is not valid. + * + * @hide + */ + private static final long INVALID_ENDPOINT_ID = 0; + /** Type of the Bluetooth socket */ @SocketType private int mSocketType; @@ -61,6 +126,50 @@ public final class BluetoothSocketSettings { private UUID mRfcommUuid; /** + * Specifies the data path used for this socket, influencing how data is transmitted and + * processed. Select the appropriate data path based on performance and power consumption + * requirements: + * + * <ul> + * <li>{@link #DATA_PATH_NO_OFFLOAD}: Suitable for applications that require the full + * processing capabilities of the main Bluetooth stack. + * <li>{@link #DATA_PATH_HARDWARE_OFFLOAD}: Optimized for lower power consumption by utilizing + * an offloaded application running on a dedicated low-power processor. + * </ul> + */ + @SocketDataPath private int mDataPath; + + /** + * A user-friendly name for this socket, primarily for debugging and logging. This name should + * be descriptive and can help identify the socket during development and troubleshooting. + * + * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the offloaded + * application running on the low-power processor. This allows the offloaded application to + * identify and manage the socket. + */ + private String mSocketName; + + /** + * When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this identifies the hub hosting the endpoint. + * + * <p>Hub represents a logical/physical representation of multiple endpoints. A pair of {@code + * mHubId} and {@code mEndpointId} uniquely identifies the endpoint globally. + */ + private long mHubId; + + /** + * When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this identifies the specific endpoint within + * the hub that is associated with this socket. + */ + private long mEndpointId; + + /** + * The maximum size (in bytes) of a single data packet that can be received from the endpoint + * when using {@link #DATA_PATH_HARDWARE_OFFLOAD}. + */ + private int mMaximumPacketSize; + + /** * Returns the type of the Bluetooth socket. * * <p>Defaults to {@code BluetoothSocket#TYPE_RFCOMM}. @@ -120,35 +229,137 @@ public final class BluetoothSocketSettings { } /** + * Returns the data path used for this socket. The data path determines how data is routed and + * processed for the socket connection. + * + * <p>Defaults to {@link #DATA_PATH_NO_OFFLOAD}. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @hide + */ + @SystemApi + @RequiresNoPermission + public @SocketDataPath int getDataPath() { + return mDataPath; + } + + /** + * Returns the user-friendly name assigned to this socket. This name is primarily used for + * debugging and logging purposes. + * + * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the offloaded + * application running on the low-power processor. + * + * <p>Defaults to {@code null} if no name was explicitly set. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public String getSocketName() { + return mSocketName; + } + + /** + * Returns the ID of the hub associated with this socket when using {@link + * #DATA_PATH_HARDWARE_OFFLOAD}. + * + * <p>If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns + * {@link #INVALID_HUB_ID}. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @hide + */ + @SystemApi + @RequiresNoPermission + public long getHubId() { + if (mDataPath != DATA_PATH_HARDWARE_OFFLOAD) { + return INVALID_HUB_ID; + } + return mHubId; + } + + /** + * Returns the ID of the endpoint within the hub associated with this socket when using {@link + * #DATA_PATH_HARDWARE_OFFLOAD}. An endpoint represents a specific point of communication within + * the hub. + * + * <p>If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns + * {@link #INVALID_ENDPOINT_ID}. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @hide + */ + @SystemApi + @RequiresNoPermission + public long getEndpointId() { + if (mDataPath != DATA_PATH_HARDWARE_OFFLOAD) { + return INVALID_ENDPOINT_ID; + } + return mEndpointId; + } + + /** + * Returns the requested maximum size (in bytes) of a data packet that can be received from the + * endpoint associated with this socket when using {@link #DATA_PATH_HARDWARE_OFFLOAD}. + * + * <p>Defaults to {@link #HARDWARE_OFFLOAD_PACKET_MAX_SIZE}. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @hide + */ + @SystemApi + @RequiresNoPermission + public int getRequestedMaximumPacketSize() { + return mMaximumPacketSize; + } + + /** * Returns a {@link String} that describes each BluetoothSocketSettings parameter current value. */ @Override public String toString() { + StringBuilder builder = new StringBuilder("BluetoothSocketSettings{"); + builder.append("mSocketType=") + .append(mSocketType) + .append(", mEncryptionRequired=") + .append(mEncryptionRequired) + .append(", mAuthenticationRequired=") + .append(mAuthenticationRequired); if (mSocketType == BluetoothSocket.TYPE_RFCOMM) { - return "BluetoothSocketSettings{" - + "mSocketType=" - + mSocketType - + ", mEncryptionRequired=" - + mEncryptionRequired - + ", mAuthenticationRequired=" - + mAuthenticationRequired - + ", mRfcommServiceName=" - + mRfcommServiceName - + ", mRfcommUuid=" - + mRfcommUuid - + "}"; + builder.append(", mRfcommServiceName=") + .append(mRfcommServiceName) + .append(", mRfcommUuid=") + .append(mRfcommUuid); } else { - return "BluetoothSocketSettings{" - + "mSocketType=" - + mSocketType - + ", mL2capPsm=" - + mL2capPsm - + ", mEncryptionRequired=" - + mEncryptionRequired - + ", mAuthenticationRequired=" - + mAuthenticationRequired - + "}"; + builder.append(", mL2capPsm=").append(mL2capPsm); + } + if (mDataPath == DATA_PATH_HARDWARE_OFFLOAD) { + builder.append(", mDataPath=") + .append(mDataPath) + .append(", mSocketName=") + .append(mSocketName) + .append(", mHubId=") + .append(mHubId) + .append(", mEndpointId=") + .append(mEndpointId) + .append(", mMaximumPacketSize=") + .append(mMaximumPacketSize); } + builder.append("}"); + return builder.toString(); } private BluetoothSocketSettings( @@ -157,13 +368,23 @@ public final class BluetoothSocketSettings { boolean encryptionRequired, boolean authenticationRequired, String rfcommServiceName, - UUID rfcommUuid) { + UUID rfcommUuid, + int dataPath, + String socketName, + long hubId, + long endpointId, + int maximumPacketSize) { mSocketType = socketType; mL2capPsm = l2capPsm; mEncryptionRequired = encryptionRequired; mAuthenticationRequired = authenticationRequired; mRfcommUuid = rfcommUuid; mRfcommServiceName = rfcommServiceName; + mDataPath = dataPath; + mSocketName = socketName; + mHubId = hubId; + mEndpointId = endpointId; + mMaximumPacketSize = maximumPacketSize; } /** Builder for {@link BluetoothSocketSettings}. */ @@ -175,6 +396,11 @@ public final class BluetoothSocketSettings { private boolean mAuthenticationRequired = false; private String mRfcommServiceName = null; private UUID mRfcommUuid = null; + private int mDataPath = DATA_PATH_NO_OFFLOAD; + private String mSocketName = BluetoothSocket.DEFAULT_SOCKET_NAME; + private long mHubId = INVALID_HUB_ID; + private long mEndpointId = INVALID_ENDPOINT_ID; + private int mMaximumPacketSize = HARDWARE_OFFLOAD_PACKET_MAX_SIZE; public Builder() {} @@ -303,6 +529,126 @@ public final class BluetoothSocketSettings { } /** + * Sets the data path for this socket. The data path determines how data is routed and + * processed for the socket connection. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @param dataPath The desired data path for the socket. + * @return This Builder object to allow for method chaining. + * @throws IllegalArgumentException If {@code dataPath} is an invalid value. + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public Builder setDataPath(@SocketDataPath int dataPath) { + if (dataPath < DATA_PATH_NO_OFFLOAD || dataPath > DATA_PATH_HARDWARE_OFFLOAD) { + throw new IllegalArgumentException("Invalid dataPath - " + dataPath); + } + mDataPath = dataPath; + return this; + } + + /** + * Sets a user-friendly name for this socket. This name is primarily used for debugging and + * logging purposes. + * + * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the + * offloaded application running on low-power processor. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @param socketName The desired name for the socket. This should be a descriptive name that + * helps identify the socket during development and troubleshooting. The socket name + * cannot exceed {@link #HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH} bytes in length when + * encoded in UTF-8. + * @return This Builder object to allow for method chaining. + * @throws IllegalArgumentException if the provided `socketName` exceeds {@link + * #HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH} bytes when encoded in UTF-8. + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public Builder setSocketName(@NonNull String socketName) { + byte[] socketNameBytes = requireNonNull(socketName).getBytes(StandardCharsets.UTF_8); + if (socketNameBytes.length > HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH) { + throw new IllegalArgumentException( + "Socket name cannot exceed " + + HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH + + " bytes in length when encoded in UTF-8."); + } + mSocketName = requireNonNull(socketName); + return this; + } + + /** + * Sets the ID of the hub to be associated with this socket when using {@link + * #DATA_PATH_HARDWARE_OFFLOAD}. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @param hubId The ID of the hub. + * @return This Builder object to allow for method chaining. + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public Builder setHubId(long hubId) { + mHubId = hubId; + return this; + } + + /** + * Sets the ID of the endpoint within the hub to be associated with this socket when using + * {@link #DATA_PATH_HARDWARE_OFFLOAD}. An endpoint represents a specific point of + * communication within the hub. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @param endpointId The ID of the endpoint within the hub. + * @return This Builder object to allow for method chaining. + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public Builder setEndpointId(long endpointId) { + mEndpointId = endpointId; + return this; + } + + /** + * Sets the requested maximum size (in bytes) of a data packet that can be received from the + * endpoint associated with this socket when using {@link #DATA_PATH_HARDWARE_OFFLOAD}. + * + * <p>The main Bluetooth stack may adjust this value based on the actual capabilities + * negotiated with the peer device during connection establishment. To get the final + * negotiated value, use {@link BluetoothSocket#getMaxReceivePacketSize()} after the socket + * is connected. + * + * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only + * available through the System API. + * + * @param maximumPacketSize The maximum packet size in bytes. + * @return This Builder object to allow for method chaining. + * @hide + */ + @SystemApi + @NonNull + @RequiresNoPermission + public Builder setRequestedMaximumPacketSize(int maximumPacketSize) { + mMaximumPacketSize = maximumPacketSize; + return this; + } + + /** * Builds a {@link BluetoothSocketSettings} object. * * @return A new {@link BluetoothSocketSettings} object with the configured parameters. @@ -336,14 +682,32 @@ public final class BluetoothSocketSettings { + mRfcommUuid); } } - + if (mDataPath == DATA_PATH_HARDWARE_OFFLOAD) { + if (mHubId == INVALID_HUB_ID || mEndpointId == INVALID_ENDPOINT_ID) { + throw new IllegalArgumentException( + "Hub ID and endpoint ID must be set for hardware data path"); + } + if (mMaximumPacketSize < 0) { + throw new IllegalArgumentException("invalid packet size " + mMaximumPacketSize); + } + } else { + if (mHubId != INVALID_HUB_ID || mEndpointId != INVALID_ENDPOINT_ID) { + throw new IllegalArgumentException( + "Hub ID and endpoint ID may not be set for software data path"); + } + } return new BluetoothSocketSettings( mSocketType, mL2capPsm, mEncryptionRequired, mAuthenticationRequired, mRfcommServiceName, - mRfcommUuid); + mRfcommUuid, + mDataPath, + mSocketName, + mHubId, + mEndpointId, + mMaximumPacketSize); } } } diff --git a/framework/java/android/bluetooth/le/AdvertisingSet.java b/framework/java/android/bluetooth/le/AdvertisingSet.java index 2a5b2dc323..8632fcf671 100644 --- a/framework/java/android/bluetooth/le/AdvertisingSet.java +++ b/framework/java/android/bluetooth/le/AdvertisingSet.java @@ -136,11 +136,17 @@ public final class AdvertisingSet { * advertising is not active. This method returns immediately, the operation status is delivered * through {@code callback.onAdvertisingParametersUpdated}. * + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when + * {@code parameters.getOwnAddressType()} is different from {@code + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true. + * * @param parameters advertising set parameters. */ @RequiresLegacyBluetoothAdminPermission @RequiresBluetoothAdvertisePermission - @RequiresPermission(BLUETOOTH_ADVERTISE) + @RequiresPermission( + allOf = {BLUETOOTH_ADVERTISE, BLUETOOTH_PRIVILEGED}, + conditional = true) public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); diff --git a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 7b29261fe0..057053e817 100644 --- a/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/framework/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -269,9 +269,9 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through {@code * callback.onAdvertisingSetStarted()}. * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when * {@code parameters.getOwnAddressType()} is different from {@code - * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT}. + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true. * * <p>The {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission is always enforced. * @@ -319,9 +319,9 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through {@code * callback.onAdvertisingSetStarted()}. * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when * {@code parameters.getOwnAddressType()} is different from {@code - * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT}. + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true. * * <p>The {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission is always enforced. * @@ -371,9 +371,9 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through {@code * callback.onAdvertisingSetStarted()}. * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when * {@code parameters.getOwnAddressType()} is different from {@code - * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT}. + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true. * * <p>The {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission is always enforced. * @@ -428,9 +428,9 @@ public final class BluetoothLeAdvertiser { * method returns immediately, the operation status is delivered through {@code * callback.onAdvertisingSetStarted()}. * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when * {@code parameters.getOwnAddressType()} is different from {@code - * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT}. + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true. * * <p>The {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission is always enforced. * @@ -494,10 +494,10 @@ public final class BluetoothLeAdvertiser { * services/characteristics in this server, rather than the union of all GATT services (across * all opened servers). * - * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when + * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission when * {@code parameters.getOwnAddressType()} is different from {@code - * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or when the {@code gattServer} is already - * registered + * AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT} or {@code parameters.isDirected()} is true or + * when the {@code gattServer} is already registered * * <p>The {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission is always enforced. * diff --git a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java index c5f07d2092..1760bc0bf1 100644 --- a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java +++ b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java @@ -166,7 +166,8 @@ public final class DistanceMeasurementManager { * Get the maximum supported security level of channel sounding between the local device and a * specific remote device. * - * <p>See: https://bluetooth.com/specifications/specs/channel-sounding-cr-pr/ + * <p>See: Vol 3 Part C, Chapter 10.11.1 of + * https://bluetooth.com/specifications/specs/core60-html/ * * @param remoteDevice remote device of channel sounding * @return max supported security level, {@link ChannelSoundingParams#CS_SECURITY_LEVEL_UNKNOWN} @@ -200,7 +201,8 @@ public final class DistanceMeasurementManager { /** * Get the maximum supported security level of channel sounding of the local device. * - * <p>See: https://bluetooth.com/specifications/specs/channel-sounding-cr-pr/ + * <p>See: Vol 3 Part C, Chapter 10.11.1 of + * https://bluetooth.com/specifications/specs/core60-html/ * * @return max supported security level, {@link ChannelSoundingParams#CS_SECURITY_LEVEL_UNKNOWN} * when Channel Sounding is not supported or encounters an internal error. @@ -230,10 +232,13 @@ public final class DistanceMeasurementManager { /** * Get the set of supported security levels of channel sounding. * - * <p>See: https://bluetooth.com/specifications/specs/core60-html/ + * <p>See: Vol 3 Part C, Chapter 10.11.1 of + * https://bluetooth.com/specifications/specs/core60-html/ * - * @return the set of supported security levels, empty when Channel Sounding is not supported or - * encounters an internal error. + * @return the set of supported security levels, empty when encounters an internal error. + * @throws UnsupportedOperationException if the {@link + * android.content.pm.PackageManager#FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING} is not + * supported. * @hide */ @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS) diff --git a/framework/java/android/bluetooth/le/DistanceMeasurementResult.java b/framework/java/android/bluetooth/le/DistanceMeasurementResult.java index e69e895fba..2292371e56 100644 --- a/framework/java/android/bluetooth/le/DistanceMeasurementResult.java +++ b/framework/java/android/bluetooth/le/DistanceMeasurementResult.java @@ -16,7 +16,6 @@ package android.bluetooth.le; -import android.annotation.ElapsedRealtimeLong; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; @@ -133,7 +132,7 @@ public final class DistanceMeasurementResult implements Parcelable { private final double mConfidenceLevel; private final int mDetectedAttackLevel; private final double mVelocityMetersPerSecond; - private final @ElapsedRealtimeLong long mMeasurementTimestampNanos; + private final long mMeasurementTimestampNanos; private DistanceMeasurementResult( double meters, @@ -146,7 +145,7 @@ public final class DistanceMeasurementResult implements Parcelable { double confidenceLevel, @Nadm int detectedAttackLevel, double velocityMetersPerSecond, - @ElapsedRealtimeLong long measurementTimestampNanos) { + long measurementTimestampNanos) { mMeters = meters; mErrorMeters = errorMeters; mAzimuthAngle = azimuthAngle; @@ -313,7 +312,7 @@ public final class DistanceMeasurementResult implements Parcelable { */ @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS) @SystemApi - public @ElapsedRealtimeLong long getMeasurementTimestampNanos() { + public long getMeasurementTimestampNanos() { return mMeasurementTimestampNanos; } @@ -419,7 +418,7 @@ public final class DistanceMeasurementResult implements Parcelable { private double mConfidenceLevel = Double.NaN; private int mDetectedAttackLevel = NADM_UNKNOWN; private double mVelocityMetersPerSecond = Double.NaN; - private @ElapsedRealtimeLong long mMeasurementTimestampNanos = -1L; + private long mMeasurementTimestampNanos = -1L; /** * Constructor of the Builder. @@ -602,8 +601,7 @@ public final class DistanceMeasurementResult implements Parcelable { @FlaggedApi(Flags.FLAG_CHANNEL_SOUNDING_25Q2_APIS) @SystemApi @NonNull - public Builder setMeasurementTimestampNanos( - @ElapsedRealtimeLong long measurementTimestampNanos) { + public Builder setMeasurementTimestampNanos(long measurementTimestampNanos) { mMeasurementTimestampNanos = measurementTimestampNanos; return this; } diff --git a/framework/tests/bumble/Android.bp b/framework/tests/bumble/Android.bp index 6c516acf11..968a458bad 100644 --- a/framework/tests/bumble/Android.bp +++ b/framework/tests/bumble/Android.bp @@ -18,6 +18,8 @@ android_test_helper_app { "libprotobuf-java-micro", ], + jarjar_rules: ":bluetooth-jarjar-rules", + static_libs: [ "TestParameterInjector", "androidx.core_core", diff --git a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java index 38671a28e9..c9fa50bbd9 100644 --- a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java +++ b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java @@ -21,6 +21,8 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static com.google.common.truth.Truth.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assume.assumeThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; @@ -33,6 +35,7 @@ import static org.mockito.Mockito.verify; import android.bluetooth.test_utils.EnableBluetoothRule; import android.content.Context; +import android.os.SystemProperties; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -93,6 +96,11 @@ public class GattClientTest { private static final UUID TEST_CHARACTERISTIC_UUID = UUID.fromString("00010001-0000-0000-0000-000000000000"); + private static final int MIN_CONN_INTERVAL_RELAXED = + SystemProperties.getInt("bluetooth.core.le.min_connection_interval_relaxed", 0x0018); + private static final int MAX_CONN_INTERVAL_RELAXED = + SystemProperties.getInt("bluetooth.core.le.max_connection_interval_relaxed", 0x0028); + @Rule(order = 0) public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -172,6 +180,36 @@ public class GattClientTest { disconnectAndWaitDisconnection(gatt, gattCallback); } + @RequiresFlagsEnabled(Flags.FLAG_INITIAL_CONN_PARAMS_P1) + @Test + public void onConnectionUpdatedIsCalledOnlyOnceForRelaxingConnectionParameters_noGattCache() { + int aggressiveConnectionThreshold = + SystemProperties.getInt("bluetooth.core.le.aggressive_connection_threshold", 2); + // This test is for the case where aggressive initial parameters are used. + assumeThat(aggressiveConnectionThreshold, greaterThan(0)); + + BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); + ArgumentCaptor<Integer> connectionIntervalCaptor = ArgumentCaptor.forClass(Integer.class); + + BluetoothGatt gatt = connectGattAndWaitConnection(gattCallback, false); + + // Wait until service discovery is done and parameters are relaxed. + verify(gattCallback, timeout(10_000).times(1)) + .onConnectionUpdated( + any(), connectionIntervalCaptor.capture(), anyInt(), anyInt(), anyInt()); + + List<Integer> capturedConnectionIntervals = connectionIntervalCaptor.getAllValues(); + assertThat(capturedConnectionIntervals).hasSize(1); + + // Since aggressive parameters are used in the initial connection, + // there should be only one connection parameters update event for relaxing them. + int relaxedConnIntervalAfterServiceDiscovery = capturedConnectionIntervals.get(0); + assertThat(relaxedConnIntervalAfterServiceDiscovery).isAtLeast(MIN_CONN_INTERVAL_RELAXED); + assertThat(relaxedConnIntervalAfterServiceDiscovery).isAtMost(MAX_CONN_INTERVAL_RELAXED); + + disconnectAndWaitDisconnection(gatt, gattCallback); + } + @Test public void reconnectExistingClient() throws Exception { advertiseWithBumble(); diff --git a/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithScanTest.java b/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithScanTest.java index 62e3f4f81e..1e718a7874 100644 --- a/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithScanTest.java +++ b/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithScanTest.java @@ -31,15 +31,11 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Log; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import org.junit.Ignore; @@ -62,14 +58,11 @@ public class GattServerConnectWithScanTest { private static final int TIMEOUT_SCANNING_MS = 2_000; private static final int TIMEOUT_GATT_CONNECTION_MS = 2_000; - @Rule(order = 2) - public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); - @Rule(order = 1) - public final PandoraDevice mBumble = new PandoraDevice(); + public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); @Rule(order = 0) - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + public final PandoraDevice mBumble = new PandoraDevice(); private final Context mContext = ApplicationProvider.getApplicationContext(); private final BluetoothManager mBluetoothManager = @@ -132,7 +125,6 @@ public class GattServerConnectWithScanTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_BLE_GATT_SERVER_USE_ADDRESS_TYPE_IN_CONNECTION) @Ignore("b/343749428: Remove hidden api's dependencies to enable the test.") public void serverConnectToPublicAddress_withTransportAuto() throws Exception { String publicAddress = mBumble.getRemoteDevice().getAddress(); diff --git a/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithoutScanTest.java b/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithoutScanTest.java index ff676a069d..05af3c0b0e 100644 --- a/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithoutScanTest.java +++ b/framework/tests/bumble/src/android/bluetooth/GattServerConnectWithoutScanTest.java @@ -26,14 +26,10 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.Context; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import org.junit.Ignore; @@ -50,14 +46,11 @@ public class GattServerConnectWithoutScanTest { private static final String TAG = "GattServerConnectWithoutScanTest"; private static final int TIMEOUT_GATT_CONNECTION_MS = 2_000; - @Rule(order = 2) - public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); - @Rule(order = 1) - public final PandoraDevice mBumble = new PandoraDevice(); + public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); @Rule(order = 0) - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + public final PandoraDevice mBumble = new PandoraDevice(); private final Context mContext = ApplicationProvider.getApplicationContext(); private final BluetoothManager mBluetoothManager = @@ -65,7 +58,6 @@ public class GattServerConnectWithoutScanTest { private final BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter(); @Test - @RequiresFlagsEnabled(Flags.FLAG_BLE_GATT_SERVER_USE_ADDRESS_TYPE_IN_CONNECTION) @Ignore("b/343749428: Remove hidden api's dependencies to enable the test.") public void serverConnectToRandomAddress_withTransportAuto() throws Exception { advertiseWithBumble(OwnAddressType.RANDOM); @@ -92,7 +84,6 @@ public class GattServerConnectWithoutScanTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_BLE_GATT_SERVER_USE_ADDRESS_TYPE_IN_CONNECTION) @Ignore("b/343749428: Remove hidden api's dependencies to enable the test.") public void serverConnectToRandomAddress_withTransportLE() throws Exception { advertiseWithBumble(OwnAddressType.RANDOM); diff --git a/framework/tests/bumble/src/android/bluetooth/LeLegacyAdvertisingTest.java b/framework/tests/bumble/src/android/bluetooth/LeLegacyAdvertisingTest.java index 4927871280..39abb62c9c 100644 --- a/framework/tests/bumble/src/android/bluetooth/LeLegacyAdvertisingTest.java +++ b/framework/tests/bumble/src/android/bluetooth/LeLegacyAdvertisingTest.java @@ -24,13 +24,9 @@ import android.bluetooth.le.AdvertisingSetCallback; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.BluetoothLeAdvertiser; import android.os.ParcelUuid; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import org.junit.Rule; @@ -48,10 +44,6 @@ public class LeLegacyAdvertisingTest { @Rule(order = 1) public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); - @Rule(order = 0) - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @RequiresFlagsEnabled(Flags.FLAG_BLE_CHECK_DATA_LENGTH_ON_LEGACY_ADVERTISING) @Test public void setAdvertisingDataOver31Bytes() throws Exception { final BluetoothLeAdvertiser advertiser = @@ -103,7 +95,6 @@ public class LeLegacyAdvertisingTest { } } - @RequiresFlagsEnabled(Flags.FLAG_BLE_CHECK_DATA_LENGTH_ON_LEGACY_ADVERTISING) @Test public void setScanResponseDataOver31Bytes() throws Exception { final BluetoothLeAdvertiser advertiser = diff --git a/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt b/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt index 64c29070ac..a952f54950 100644 --- a/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt +++ b/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt @@ -122,18 +122,6 @@ class RfcommTest { mRemoteDevice = mBumble.remoteDevice mHost = Host(mContext) - // Set Bonding - val pairingConfig = - BumbleConfigProto.PairingConfig.newBuilder() - .setBonding(false) - .setMitm(false) - .setSc(false) - .setIdentityAddressType(HostProto.OwnAddressType.PUBLIC) - .build() - val overrideRequest = - BumbleConfigProto.OverrideRequest.newBuilder().setPairingConfig(pairingConfig).build() - mBumble.bumbleConfigBlocking().override(overrideRequest) - val bluetoothA2dp = getProfileProxy(mContext, BluetoothProfile.A2DP) as BluetoothA2dp bluetoothA2dp.setConnectionPolicy( mRemoteDevice, @@ -176,6 +164,7 @@ class RfcommTest { */ @Test fun clientConnectToOpenServerSocketInsecure() { + updateSecurityConfig() startServer { serverId -> createConnectAcceptSocket(isSecure = false, serverId) } } @@ -187,6 +176,7 @@ class RfcommTest { */ @Test fun clientConnectToOpenServerSocketSecure() { + updateSecurityConfig() startServer { serverId -> createConnectAcceptSocket(isSecure = true, serverId) } } @@ -200,6 +190,7 @@ class RfcommTest { */ @Test fun clientSendDataOverInsecureSocket() { + updateSecurityConfig() startServer { serverId -> val (insecureSocket, connection) = createConnectAcceptSocket(isSecure = false, serverId) val data: ByteArray = "Test data for clientSendDataOverInsecureSocket".toByteArray() @@ -225,6 +216,7 @@ class RfcommTest { */ @Test fun clientSendDataOverSecureSocket() { + updateSecurityConfig() startServer { serverId -> val (secureSocket, connection) = createConnectAcceptSocket(isSecure = true, serverId) val data: ByteArray = "Test data for clientSendDataOverSecureSocket".toByteArray() @@ -250,6 +242,7 @@ class RfcommTest { */ @Test fun clientReceiveDataOverInsecureSocket() { + updateSecurityConfig() startServer { serverId -> val (insecureSocket, connection) = createConnectAcceptSocket(isSecure = false, serverId) val buffer = ByteArray(64) @@ -276,6 +269,7 @@ class RfcommTest { */ @Test fun clientReceiveDataOverSecureSocket() { + updateSecurityConfig() startServer { serverId -> val (secureSocket, connection) = createConnectAcceptSocket(isSecure = true, serverId) val buffer = ByteArray(64) @@ -303,6 +297,7 @@ class RfcommTest { */ @Test fun connectTwoInsecureClientsSimultaneously() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket1 = createSocket(mRemoteDevice, isSecure = false, TEST_UUID) @@ -326,6 +321,7 @@ class RfcommTest { */ @Test fun connectTwoInsecureClientsSequentially() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket1 = createSocket(mRemoteDevice, isSecure = false, TEST_UUID) @@ -350,6 +346,7 @@ class RfcommTest { */ @Test fun connectTwoSecureClientsSimultaneously() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket2 = createSocket(mRemoteDevice, isSecure = true, SERIAL_PORT_UUID) @@ -373,6 +370,7 @@ class RfcommTest { */ @Test fun connectTwoSecureClientsSequentially() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket1 = createSocket(mRemoteDevice, isSecure = true, TEST_UUID) @@ -396,6 +394,7 @@ class RfcommTest { @Test @Ignore("b/380091558") fun connectTwoMixedClientsInsecureThenSecure() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket2 = createSocket(mRemoteDevice, isSecure = false, SERIAL_PORT_UUID) @@ -418,6 +417,7 @@ class RfcommTest { */ @Test fun connectTwoMixedClientsSecureThenInsecure() { + updateSecurityConfig() startServer("ServerPort1", TEST_UUID) { serverId1 -> startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 -> val socket2 = createSocket(mRemoteDevice, isSecure = true, SERIAL_PORT_UUID) @@ -439,6 +439,7 @@ class RfcommTest { @RequiresFlagsEnabled(Flags.FLAG_TRIGGER_SEC_PROC_ON_INC_ACCESS_REQ) @Test fun serverSecureConnectThenRemoteDisconnect() { + updateSecurityConfig() // step 1 val (serverSock, connection) = connectRemoteToListeningSocket() val disconnectRequest = @@ -456,6 +457,7 @@ class RfcommTest { @RequiresFlagsEnabled(Flags.FLAG_TRIGGER_SEC_PROC_ON_INC_ACCESS_REQ) @Test fun serverSecureConnectThenLocalDisconnect() { + updateSecurityConfig() // step 1 val (serverSock, _) = connectRemoteToListeningSocket() // step 2 @@ -463,6 +465,127 @@ class RfcommTest { Truth.assertThat(serverSock.channel).isEqualTo(-1) // ensure disconnected at RFCOMM Layer } + /* + Test Steps: + 1. Create an insecure socket + 2. Connect to the socket + 3. Verify that devices are connected + 4. Write data to socket output stream + 5. Verify bumble received that data + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API) + fun clientSendDataOverInsecureSocketUsingSocketSettings() { + updateSecurityConfig() + startServer { serverId -> + val (insecureSocket, connection) = createConnectAcceptSocketUsingSettings(serverId) + val data: ByteArray = + "Test data for clientSendDataOverInsecureSocketUsingSocketSettings".toByteArray() + val socketOs = insecureSocket.outputStream + + socketOs.write(data) + val rxResponse: RfcommProto.RxResponse = + mBumble + .rfcommBlocking() + .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build()) + Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data)) + } + } + + /* + Test Steps: + 1. Create an encrypt only socket + 2. Connect to the socket + 3. Verify that devices are connected + 4. Write data to socket output stream + 5. Verify bumble received that data + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API) + fun clientSendDataOverEncryptedOnlySocketUsingSocketSettings() { + updateSecurityConfig(true, false) + startServer { serverId -> + val (encryptOnlySocket, connection) = + createConnectAcceptSocketUsingSettings(serverId, TEST_UUID, true, false) + + val data: ByteArray = + "Test data for clientSendDataOverEncryptedOnlySocketUsingSocketSettings" + .toByteArray() + val socketOs = encryptOnlySocket.outputStream + + socketOs.write(data) + val rxResponse: RfcommProto.RxResponse = + mBumble + .rfcommBlocking() + .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build()) + Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data)) + } + } + + /* + Test Steps: + 1. Create an secure socket + 2. Connect to the socket + 3. Verify that devices are connected + 4. Write data to socket output stream + 5. Verify bumble received that data + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API) + fun clientSendDataOverSecureSocketUsingSocketSettings() { + updateSecurityConfig(true, true) + startServer { serverId -> + val (secureSocket, connection) = + createConnectAcceptSocketUsingSettings(serverId, TEST_UUID, true, false) + val data: ByteArray = + "Test data for clientSendDataOverSecureSocketUsingSocketSettings".toByteArray() + val socketOs = secureSocket.outputStream + + socketOs.write(data) + val rxResponse: RfcommProto.RxResponse = + mBumble + .rfcommBlocking() + .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build()) + Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data)) + } + } + + // helper to update the security config for remote bumble device + private fun updateSecurityConfig( + isEncrypted: Boolean = false, + isAuthenticated: Boolean = false, + ) { + val pairingConfig = + BumbleConfigProto.PairingConfig.newBuilder() + .setBonding(isEncrypted) + .setMitm(isAuthenticated) + .setSc(isEncrypted) + .setIdentityAddressType(HostProto.OwnAddressType.PUBLIC) + .build() + val overrideRequest = + BumbleConfigProto.OverrideRequest.newBuilder().setPairingConfig(pairingConfig).build() + mBumble.bumbleConfigBlocking().override(overrideRequest) + } + + private fun createConnectAcceptSocketUsingSettings( + server: ServerId, + uuid: String = TEST_UUID, + isEncrypted: Boolean = false, + isAuthenticated: Boolean = false, + ): Pair<BluetoothSocket, RfcommProto.RfcommConnection> { + val socket = + createClientSocketUsingSocketSettings(uuid, mRemoteDevice, isEncrypted, isAuthenticated) + + val connection = acceptSocket(server) + + Truth.assertThat(socket.isConnected).isTrue() + + return Pair(socket, connection) + } + private fun createConnectAcceptSocket( isSecure: Boolean, server: ServerId, @@ -475,6 +598,47 @@ class RfcommTest { return Pair(socket, connection) } + private fun createClientSocketUsingSocketSettings( + uuid: String, + remoteDevice: BluetoothDevice, + isEncrypted: Boolean = false, + isAuthenticated: Boolean = false, + ): BluetoothSocket { + var socket: BluetoothSocket + + socket = + remoteDevice.createUsingSocketSettings( + BluetoothSocketSettings.Builder() + .setSocketType(BluetoothSocket.TYPE_RFCOMM) + .setEncryptionRequired(isEncrypted) + .setAuthenticationRequired(isAuthenticated) + .setRfcommUuid(UUID.fromString(uuid)) + .build() + ) + + runBlocking(mScope.coroutineContext) { + withTimeout(CONNECT_TIMEOUT.toMillis()) { + // We need to reply to the pairing request in the case where the devices aren't + // bonded yet + if ( + (isEncrypted || isAuthenticated) && + !mAdapter.bondedDevices.contains(remoteDevice) + ) { + launch { + Log.i(TAG, "Waiting for ACTION_PAIRING_REQUEST") + mFlow + .filter { it.action == BluetoothDevice.ACTION_PAIRING_REQUEST } + .filter { it.getBluetoothDeviceExtra() == remoteDevice } + .first() + remoteDevice.setPairingConfirmation(true) + } + } + socket.connect() + } + } + return socket + } + private fun createSocket( device: BluetoothDevice, isSecure: Boolean, diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java new file mode 100644 index 0000000000..b879053de8 --- /dev/null +++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.hid; + +import static android.bluetooth.BluetoothDevice.TRANSPORT_BREDR; +import static android.bluetooth.BluetoothDevice.TRANSPORT_LE; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; + +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; + +import static com.google.common.truth.Truth.assertThat; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.oneOf; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothHidHost; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothStatusCodes; +import android.bluetooth.Host; +import android.bluetooth.PandoraDevice; +import android.bluetooth.test_utils.EnableBluetoothRule; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.ParcelUuid; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.bluetooth.flags.Flags; +import com.android.compatibility.common.util.AdoptShellPermissionsRule; + +import org.hamcrest.CustomTypeSafeMatcher; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.core.AllOf; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.hamcrest.MockitoHamcrest; + +import pandora.GattProto; +import pandora.GattProto.GattCharacteristicParams; +import pandora.HIDGrpc; +import pandora.HidProto.HidServiceType; +import pandora.HidProto.ServiceRequest; +import pandora.HostProto.AdvertiseRequest; +import pandora.HostProto.DiscoverabilityMode; +import pandora.HostProto.OwnAddressType; +import pandora.HostProto.SetDiscoverabilityModeRequest; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class HidHeadTrackerTest { + private static final String TAG = HidHeadTrackerTest.class.getSimpleName(); + private static final String BUMBLE_DEVICE_NAME = "Bumble"; + private static final Duration BOND_INTENT_TIMEOUT = Duration.ofSeconds(10); + private static final Duration INTENT_TIMEOUT = Duration.ofSeconds(10); + private static final int DISCOVERY_TIMEOUT = 2000; // 2 seconds + private CompletableFuture<BluetoothDevice> mDeviceFound; + private static final ParcelUuid HEADTRACKER_UUID = + ParcelUuid.fromString("109b862f-50e3-45cc-8ea1-ac62de4846d1"); + private static final ParcelUuid HEADTRACKER_VERSION_CHARACTERISTIC_UUID = + ParcelUuid.fromString("b4eb9919-a910-46a2-a9dd-fec2525196fd"); + private static final ParcelUuid HEADTRACKER_CONTROL_CHARACTERISTIC_UUID = + ParcelUuid.fromString("8584cbb5-2d58-45a3-ab9d-583e0958b067"); + private static final ParcelUuid HEADTRACKER_REPORT_CHARACTERISTIC_UUID = + ParcelUuid.fromString("e66dd173-b2ae-4f5a-ae16-0162af8038ae"); + + private static final Context sTargetContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + private static final BluetoothAdapter sAdapter = + sTargetContext.getSystemService(BluetoothManager.class).getAdapter(); + private HIDGrpc.HIDBlockingStub mHidBlockingStub; + + @Rule(order = 0) + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule(order = 1) + public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); + + @Rule(order = 2) + public final PandoraDevice mBumble = new PandoraDevice(); + + @Rule(order = 3) + public final EnableBluetoothRule mEnableBluetoothRule = + new EnableBluetoothRule(false /* enableTestMode */, true /* toggleBluetooth */); + + @Mock private BroadcastReceiver mReceiver; + @Mock private BluetoothProfile.ServiceListener mProfileServiceListener; + private final Map<String, Integer> mActionRegistrationCounts = new HashMap<>(); + private InOrder mInOrder = null; + private BluetoothDevice mBumbleDevice; + private Host mHost; + private BluetoothHidHost mHidService; + private BluetoothHeadset mHfpService; + private BluetoothA2dp mA2dpService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mHidBlockingStub = mBumble.hidBlocking(); + + doAnswer( + inv -> { + Log.d( + TAG, + "onReceive(): intent=" + Arrays.toString(inv.getArguments())); + Intent intent = inv.getArgument(1); + String action = intent.getAction(); + if (BluetoothDevice.ACTION_UUID.equals(action)) { + ParcelUuid[] uuids = + intent.getParcelableArrayExtra( + BluetoothDevice.EXTRA_UUID, ParcelUuid.class); + Log.d(TAG, "onReceive(): UUID=" + Arrays.toString(uuids)); + } else if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) { + BluetoothDevice device = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice.class); + String deviceName = + String.valueOf( + intent.getStringExtra(BluetoothDevice.EXTRA_NAME)); + Log.i( + TAG, + "Discovered device: " + + device + + " with name: " + + deviceName); + if (deviceName != null && BUMBLE_DEVICE_NAME.equals(deviceName)) { + if (mDeviceFound != null) { + mDeviceFound.complete(device); + } + } + } else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals( + intent.getAction())) { + BluetoothDevice device = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice.class); + int transport = + intent.getIntExtra( + BluetoothDevice.EXTRA_TRANSPORT, + BluetoothDevice.TRANSPORT_AUTO); + Log.i( + TAG, + "ACL connected for device=" + + device + + " with transport: " + + transport); + } else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals( + intent.getAction())) { + BluetoothDevice device = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice.class); + int transport = + intent.getIntExtra( + BluetoothDevice.EXTRA_TRANSPORT, + BluetoothDevice.TRANSPORT_AUTO); + Log.i( + TAG, + "ACL Disconnected for device=" + + device + + " with transport: " + + transport); + } else if (BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED.equals( + intent.getAction())) { + BluetoothDevice device = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice.class); + int state = + intent.getIntExtra( + BluetoothProfile.EXTRA_STATE, + BluetoothAdapter.ERROR); + int transport = + intent.getIntExtra( + BluetoothDevice.EXTRA_TRANSPORT, + BluetoothDevice.TRANSPORT_AUTO); + Log.i( + TAG, + "Connection state change: device=" + + device + + " " + + BluetoothProfile.getConnectionStateName(state) + + "(" + + state + + "), transport: " + + transport); + } + return null; + }) + .when(mReceiver) + .onReceive(any(), any()); + + mInOrder = inOrder(mReceiver); + mHost = new Host(sTargetContext); + // Get profile proxies + sAdapter.getProfileProxy( + sTargetContext, mProfileServiceListener, BluetoothProfile.HID_HOST); + mHidService = (BluetoothHidHost) verifyProfileServiceConnected(BluetoothProfile.HID_HOST); + sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, BluetoothProfile.A2DP); + mA2dpService = (BluetoothA2dp) verifyProfileServiceConnected(BluetoothProfile.A2DP); + sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, BluetoothProfile.HEADSET); + mHfpService = (BluetoothHeadset) verifyProfileServiceConnected(BluetoothProfile.HEADSET); + } + + @After + public void tearDown() throws Exception { + if ((mBumbleDevice != null) + && mBumbleDevice.getBondState() == BluetoothDevice.BOND_BONDED) { + mHost.removeBondAndVerify(mBumbleDevice); + } + mHost.close(); + mBumbleDevice = null; + if (getTotalActionRegistrationCounts() > 0) { + sTargetContext.unregisterReceiver(mReceiver); + mActionRegistrationCounts.clear(); + } + } + + /** + * Ensure that successful HID connection over LE Transport. + * + * <p>Prerequisites: + * + * <ol> + * <li>Bumble has Android Headtracker Service + * <li>Bumble does not support HID and HOGP + * <li>Bummble is connectable over LE + * </ol> + * + * <p>Steps: + * + * <ol> + * <li>Android pairs with Bumble + * <li>Android Bluetooth reports HID host connection + * <li>Disconnect and reconnect + * <li>Android Bluetooth reports HID host connection + * </ol> + * + * Expectation: successful HID connection over LE Transport + */ + @SuppressLint("MissingPermission") + @Test + @RequiresFlagsEnabled({ + Flags.FLAG_ALLOW_SWITCHING_HID_AND_HOGP, + Flags.FLAG_SAVE_INITIAL_HID_CONNECTION_POLICY + }) + public void connectWithoutHidServiceTest() { + + registerIntentActions( + BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothDevice.ACTION_ACL_DISCONNECTED, + BluetoothDevice.ACTION_UUID, + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, + BluetoothDevice.ACTION_FOUND); + + pairAndConnect(); + // Verify ACL connection on classic transport first and then LE transport + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR)); + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE)); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_UUID), + hasExtra(BluetoothDevice.EXTRA_UUID, Matchers.hasItemInArray(HEADTRACKER_UUID))); + + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_CONNECTING)); + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_CONNECTED)); + + // Disable a2dp and HFP connetcion policy + + if (mA2dpService.getConnectionPolicy(mBumbleDevice) + == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + assertThat( + mA2dpService.setConnectionPolicy( + mBumbleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)) + .isTrue(); + } + if (mHfpService.getConnectionPolicy(mBumbleDevice) + == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + assertThat( + mHfpService.setConnectionPolicy( + mBumbleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)) + .isTrue(); + } + + // Disconnect and Reconnect + assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS); + + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_DISCONNECTING)); + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_DISCONNECTED)); + // Wait for ACL to get disconnected + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice)); + + assertThat(mBumbleDevice.connect()).isEqualTo(BluetoothStatusCodes.SUCCESS); + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_CONNECTING)); + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice)); + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_LE), equalTo(STATE_CONNECTED)); + unregisterIntentActions( + BluetoothDevice.ACTION_UUID, + BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothDevice.ACTION_ACL_DISCONNECTED, + BluetoothDevice.ACTION_FOUND); + } + + /** + * Ensure that successful HID connection over BREDR Transport. + * + * <p>Prerequisites: + * + * <ol> + * <li>Bumble has Android Headtracker Service + * <li>Bumble supports only HID but not HOGP + * <li>Bummble is connectable over LE + * </ol> + * + * <p>Steps: + * + * <ol> + * <li>Android pairs with Bumble + * <li>Android Bluetooth reports HID host connection + * <li>Change the preferred transport to LE + * <li>Android Bluetooth reports HID host connection over LE + * </ol> + * + * Expectation: successful HID connection over BREDR Transport and Preferred transport selection + * success + */ + @SuppressLint("MissingPermission") + @Test + @RequiresFlagsEnabled({ + Flags.FLAG_ALLOW_SWITCHING_HID_AND_HOGP, + Flags.FLAG_SAVE_INITIAL_HID_CONNECTION_POLICY + }) + public void connectWithHidServiceTest() { + + registerIntentActions( + BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothDevice.ACTION_UUID, + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, + BluetoothDevice.ACTION_FOUND); + mHidBlockingStub.registerService( + ServiceRequest.newBuilder() + .setServiceType(HidServiceType.SERVICE_TYPE_HID) + .build()); + pairAndConnect(); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_UUID), + hasExtra(BluetoothDevice.EXTRA_UUID, Matchers.hasItemInArray(HEADTRACKER_UUID))); + + verifyConnectionState(mBumbleDevice, equalTo(TRANSPORT_BREDR), equalTo(STATE_CONNECTED)); + // Switch to LE Transport + mHidService.setPreferredTransport(mBumbleDevice, TRANSPORT_LE); + verifyTransportSwitch(mBumbleDevice, TRANSPORT_BREDR, TRANSPORT_LE); + + unregisterIntentActions( + BluetoothDevice.ACTION_UUID, + BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothDevice.ACTION_FOUND); + } + + private void pairAndConnect() { + + // Register Head tracker services on Bumble + GattCharacteristicParams characteristicVersion = + GattCharacteristicParams.newBuilder() + .setProperties( + BluetoothGattCharacteristic.PROPERTY_READ + | BluetoothGattCharacteristic.PROPERTY_WRITE) + .setUuid(HEADTRACKER_VERSION_CHARACTERISTIC_UUID.toString()) + .build(); + + GattCharacteristicParams characteristicControl = + GattCharacteristicParams.newBuilder() + .setProperties( + BluetoothGattCharacteristic.PROPERTY_READ + | BluetoothGattCharacteristic.PROPERTY_WRITE) + .setUuid(HEADTRACKER_CONTROL_CHARACTERISTIC_UUID.toString()) + .build(); + GattCharacteristicParams characteristicReport = + GattCharacteristicParams.newBuilder() + .setProperties( + BluetoothGattCharacteristic.PROPERTY_READ + | BluetoothGattCharacteristic.PROPERTY_WRITE) + .setUuid(HEADTRACKER_REPORT_CHARACTERISTIC_UUID.toString()) + .build(); + mBumble.gattBlocking() + .registerService( + GattProto.RegisterServiceRequest.newBuilder() + .setService( + GattProto.GattServiceParams.newBuilder() + .addCharacteristics(characteristicVersion) + .addCharacteristics(characteristicControl) + .addCharacteristics(characteristicReport) + .setUuid(HEADTRACKER_UUID.toString()) + .build()) + .build()); + + // Make Bumble connectable + mBumble.hostBlocking() + .advertise( + AdvertiseRequest.newBuilder() + .setLegacy(true) + .setConnectable(true) + .setOwnAddressType(OwnAddressType.RANDOM) + .build()); + // Make Bumble discoverable over BR/EDR + mBumble.hostBlocking() + .setDiscoverabilityMode( + SetDiscoverabilityModeRequest.newBuilder() + .setMode(DiscoverabilityMode.DISCOVERABLE_GENERAL) + .build()); + // Start Discovery + mDeviceFound = new CompletableFuture<>(); + assertThat(sAdapter.startDiscovery()).isTrue(); + mBumbleDevice = + mDeviceFound + .completeOnTimeout(null, DISCOVERY_TIMEOUT, TimeUnit.MILLISECONDS) + .join(); + assertThat(sAdapter.cancelDiscovery()).isTrue(); + // Create Bond + mHost.createBondAndVerify(mBumbleDevice); + } + + /** + * CONNECTING and DISCONNECTING intents can go out of order, hence need a special function to + * verify transport switches if we want to verify connecting and disconnected states + * + * <p>Four intents are expected: 1. fromTransport DISCONNECTING 2. toTransport CONNECTING 3. + * fromTransport DISCONNECTED 4. toTransport CONNECTED + * + * <p>Currently, the order of 2 and 3 is unstable and hence we need this method to work with + * both 2 -> 3 AND 3 -> 2 + * + * <p>This function is complicated because we cannot mix ordered verification and unordered + * verification if the same set of argument will appear more than once. + * + * @param device target dual mode HID device + * @param fromTransport from which transport + * @param toTransport to which transport + */ + private void verifyTransportSwitch(BluetoothDevice device, int fromTransport, int toTransport) { + assertThat(fromTransport).isNotEqualTo(toTransport); + verifyConnectionState(mBumbleDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTING)); + + // Capture the next intent with filter + // Filter is necessary as otherwise it will corrupt all other unordered verifications + final Intent[] savedIntent = {null}; + verifyIntentReceived( + new CustomTypeSafeMatcher<>("Intent Matcher") { + public boolean matchesSafely(Intent intent) { + savedIntent[0] = intent; + return AllOf.allOf( + hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), + hasExtra( + BluetoothDevice.EXTRA_TRANSPORT, + oneOf(fromTransport, toTransport)), + hasExtra( + BluetoothProfile.EXTRA_STATE, + oneOf(STATE_CONNECTING, STATE_DISCONNECTED))) + .matches(intent); + } + }); + + // Verify saved intent is correct + assertThat(savedIntent[0]).isNotNull(); + Intent intent = savedIntent[0]; + assertThat(intent.getAction()).isNotNull(); + assertThat(intent.getAction()).isEqualTo(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); + assertThat(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class)) + .isEqualTo(device); + assertThat(intent.hasExtra(BluetoothProfile.EXTRA_STATE)).isTrue(); + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, STATE_CONNECTED); + assertThat(state).isAnyOf(STATE_CONNECTING, STATE_DISCONNECTED); + assertThat(intent.hasExtra(BluetoothDevice.EXTRA_TRANSPORT)).isTrue(); + int transport = + intent.getIntExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_AUTO); + assertThat(transport).isAnyOf(TRANSPORT_BREDR, TRANSPORT_LE); + + // Conditionally verify the next intent + if (transport == fromTransport) { + assertThat(state).isEqualTo(STATE_DISCONNECTED); + verifyConnectionState(mBumbleDevice, equalTo(toTransport), equalTo(STATE_CONNECTING)); + } else { + assertThat(state).isEqualTo(STATE_CONNECTING); + verifyConnectionState( + mBumbleDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTED)); + } + verifyConnectionState(mBumbleDevice, equalTo(toTransport), equalTo(STATE_CONNECTED)); + } + + @SafeVarargs + private void verifyIntentReceived(Matcher<Intent>... matchers) { + mInOrder.verify(mReceiver, timeout(BOND_INTENT_TIMEOUT.toMillis())) + .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers))); + } + + private void verifyConnectionState( + BluetoothDevice device, Matcher<Integer> transport, Matcher<Integer> state) { + + verifyIntentReceived( + hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, transport), + hasExtra(BluetoothProfile.EXTRA_STATE, state)); + } + + /** + * Helper function to add reference count to registered intent actions + * + * @param actions new intent actions to add. If the array is empty, it is a no-op. + */ + private void registerIntentActions(String... actions) { + if (actions.length == 0) { + return; + } + if (getTotalActionRegistrationCounts() > 0) { + Log.d(TAG, "registerIntentActions(): unregister ALL intents"); + sTargetContext.unregisterReceiver(mReceiver); + } + for (String action : actions) { + mActionRegistrationCounts.merge(action, 1, Integer::sum); + } + IntentFilter filter = new IntentFilter(); + mActionRegistrationCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .forEach( + entry -> { + Log.d( + TAG, + "registerIntentActions(): Registering action = " + + entry.getKey()); + filter.addAction(entry.getKey()); + }); + sTargetContext.registerReceiver(mReceiver, filter); + } + + /** + * Helper function to reduce reference count to registered intent actions If total reference + * count is zero after removal, no broadcast receiver will be registered. + * + * @param actions intent actions to be removed. If some action is not registered, it is no-op + * for that action. If the actions array is empty, it is also a no-op. + */ + private void unregisterIntentActions(String... actions) { + if (actions.length == 0) { + return; + } + if (getTotalActionRegistrationCounts() <= 0) { + return; + } + Log.d(TAG, "unregisterIntentActions(): unregister ALL intents"); + sTargetContext.unregisterReceiver(mReceiver); + for (String action : actions) { + if (!mActionRegistrationCounts.containsKey(action)) { + continue; + } + mActionRegistrationCounts.put(action, mActionRegistrationCounts.get(action) - 1); + if (mActionRegistrationCounts.get(action) <= 0) { + mActionRegistrationCounts.remove(action); + } + } + if (getTotalActionRegistrationCounts() > 0) { + IntentFilter filter = new IntentFilter(); + mActionRegistrationCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .forEach( + entry -> { + Log.d( + TAG, + "unregisterIntentActions(): Registering action = " + + entry.getKey()); + filter.addAction(entry.getKey()); + }); + sTargetContext.registerReceiver(mReceiver, filter); + } + } + + /** + * Get sum of reference count from all registered actions + * + * @return sum of reference count from all registered actions + */ + private int getTotalActionRegistrationCounts() { + return mActionRegistrationCounts.values().stream().reduce(0, Integer::sum); + } + + private BluetoothProfile verifyProfileServiceConnected(int profile) { + ArgumentCaptor<BluetoothProfile> proxyCaptor = + ArgumentCaptor.forClass(BluetoothProfile.class); + verify(mProfileServiceListener, timeout(INTENT_TIMEOUT.toMillis())) + .onServiceConnected(eq(profile), proxyCaptor.capture()); + return proxyCaptor.getValue(); + } +} diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java index cddee2a9ad..3d7587c5c8 100644 --- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java +++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java @@ -84,6 +84,9 @@ import org.mockito.MockitoAnnotations; import org.mockito.hamcrest.MockitoHamcrest; import org.mockito.stubbing.Answer; +import pandora.HIDGrpc; +import pandora.HidProto.HidServiceType; +import pandora.HidProto.ServiceRequest; import pandora.HostProto.AdvertiseRequest; import pandora.HostProto.OwnAddressType; @@ -108,6 +111,7 @@ public class HidHostDualModeTest { InstrumentationRegistry.getInstrumentation().getTargetContext(); private final BluetoothAdapter mAdapter = mContext.getSystemService(BluetoothManager.class).getAdapter(); + private HIDGrpc.HIDBlockingStub mHidBlockingStub; @Rule(order = 0) public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -275,6 +279,11 @@ public class HidHostDualModeTest { BluetoothHeadset hfpService = (BluetoothHeadset) verifyProfileServiceConnected(BluetoothProfile.HEADSET); + mHidBlockingStub = mBumble.hidBlocking(); + mHidBlockingStub.registerService( + ServiceRequest.newBuilder() + .setServiceType(HidServiceType.SERVICE_TYPE_BOTH) + .build()); AdvertiseRequest request = AdvertiseRequest.newBuilder() .setLegacy(true) diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java index 76eda63d41..a4e61aa198 100644 --- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java +++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java @@ -81,8 +81,10 @@ import org.mockito.hamcrest.MockitoHamcrest; import org.mockito.stubbing.Answer; import pandora.HIDGrpc; +import pandora.HidProto.HidServiceType; import pandora.HidProto.ProtocolModeEvent; import pandora.HidProto.ReportEvent; +import pandora.HidProto.ServiceRequest; import java.time.Duration; import java.util.Arrays; @@ -286,7 +288,10 @@ public class HidHostTest { (BluetoothHeadset) verifyProfileServiceConnected(BluetoothProfile.HEADSET); mHidBlockingStub = mBumble.hidBlocking(); - + mHidBlockingStub.registerService( + ServiceRequest.newBuilder() + .setServiceType(HidServiceType.SERVICE_TYPE_HID) + .build()); mDevice = mBumble.getRemoteDevice(); // Remove bond if the device is already bonded if (mDevice.getBondState() == BluetoothDevice.BOND_BONDED) { @@ -662,6 +667,12 @@ public class HidHostTest { mHidBlockingStub .withDeadlineAfter(PROTO_MODE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) .onSetReport(Empty.getDefaultInstance()); + + // Todo: as a workaround added 50ms delay. + // To be removed once root cause is identified for b/382180335 + final CompletableFuture<Integer> future = new CompletableFuture<>(); + future.completeOnTimeout(null, 50, TimeUnit.MILLISECONDS).join(); + // Keyboard report String kbReportData = "010203040506070809"; mHidService.setReport(mDevice, BluetoothHidHost.REPORT_TYPE_INPUT, kbReportData); diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/EncryptionChangeTest.java b/framework/tests/bumble/src/android/bluetooth/pairing/EncryptionChangeTest.java new file mode 100644 index 0000000000..ce508aebb3 --- /dev/null +++ b/framework/tests/bumble/src/android/bluetooth/pairing/EncryptionChangeTest.java @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.pairing; + +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothHidHost; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.PandoraDevice; +import android.bluetooth.StreamObserverSpliterator; +import android.bluetooth.test_utils.EnableBluetoothRule; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.ParcelUuid; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.bluetooth.flags.Flags; +import com.android.compatibility.common.util.AdoptShellPermissionsRule; + +import io.grpc.stub.StreamObserver; + +import org.hamcrest.Matcher; +import org.hamcrest.core.AllOf; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.hamcrest.MockitoHamcrest; + +import pandora.GattProto; +import pandora.HostProto.AdvertiseRequest; +import pandora.HostProto.OwnAddressType; +import pandora.SecurityProto.PairingEvent; +import pandora.SecurityProto.PairingEventAnswer; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class EncryptionChangeTest { + private static final String TAG = "EncryptionChangeTest"; + private static final Duration BOND_INTENT_TIMEOUT = Duration.ofSeconds(10); + + private static final ParcelUuid BATTERY_UUID = + ParcelUuid.fromString("0000180F-0000-1000-8000-00805F9B34FB"); + + private static final ParcelUuid HOGP_UUID = + ParcelUuid.fromString("00001812-0000-1000-8000-00805F9B34FB"); + + private static final Context sTargetContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + private static final BluetoothAdapter sAdapter = + sTargetContext.getSystemService(BluetoothManager.class).getAdapter(); + + @Rule(order = 0) + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule(order = 1) + public final AdoptShellPermissionsRule mPermissionRule = new AdoptShellPermissionsRule(); + + @Rule(order = 2) + public final PandoraDevice mBumble = new PandoraDevice(); + + @Rule(order = 3) + public final EnableBluetoothRule mEnableBluetoothRule = + new EnableBluetoothRule(false /* enableTestMode */, true /* toggleBluetooth */); + + private final Map<String, Integer> mActionRegistrationCounts = new HashMap<>(); + private final StreamObserverSpliterator<PairingEvent> mPairingEventStreamObserver = + new StreamObserverSpliterator<>(); + @Mock private BroadcastReceiver mReceiver; + @Mock private BluetoothProfile.ServiceListener mProfileServiceListener; + private InOrder mInOrder = null; + private BluetoothDevice mBumbleDevice; + private BluetoothHidHost mHidService; + private BluetoothHeadset mHfpService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doAnswer( + inv -> { + Log.d( + TAG, + "onReceive(): intent=" + Arrays.toString(inv.getArguments())); + Intent intent = inv.getArgument(1); + String action = intent.getAction(); + if (BluetoothDevice.ACTION_UUID.equals(action)) { + ParcelUuid[] uuids = + intent.getParcelableArrayExtra( + BluetoothDevice.EXTRA_UUID, ParcelUuid.class); + Log.d(TAG, "onReceive(): UUID=" + Arrays.toString(uuids)); + } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { + int bondState = + intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); + Log.d(TAG, "onReceive(): bondState=" + bondState); + } + return null; + }) + .when(mReceiver) + .onReceive(any(), any()); + + mInOrder = inOrder(mReceiver); + + // Get profile proxies + mHidService = (BluetoothHidHost) getProfileProxy(BluetoothProfile.HID_HOST); + mHfpService = (BluetoothHeadset) getProfileProxy(BluetoothProfile.HEADSET); + + mBumbleDevice = mBumble.getRemoteDevice(); + Set<BluetoothDevice> bondedDevices = sAdapter.getBondedDevices(); + if (bondedDevices.contains(mBumbleDevice)) { + removeBond(mBumbleDevice); + } + } + + @After + public void tearDown() throws Exception { + Set<BluetoothDevice> bondedDevices = sAdapter.getBondedDevices(); + if (bondedDevices.contains(mBumbleDevice)) { + removeBond(mBumbleDevice); + } + mBumbleDevice = null; + if (getTotalActionRegistrationCounts() > 0) { + sTargetContext.unregisterReceiver(mReceiver); + mActionRegistrationCounts.clear(); + } + } + + /** + * Test Encryption change event on LE Secure link: + * + * <ol> + * <li>1. Android initiate create bond over LE link + * <li>2. Android confirms the pairing via pairing request intent + * <li>3. Bumble confirms the pairing internally (optional, added only for test confirmation) + * <li>4. Android verifies Encryption change Intent and bonded intent + * </ol> + */ + @Test + @RequiresFlagsEnabled({Flags.FLAG_ENCRYPTION_CHANGE_BROADCAST}) + public void encryptionChangeSecureLeLink() { + registerIntentActions( + BluetoothDevice.ACTION_BOND_STATE_CHANGED, + BluetoothDevice.ACTION_ENCRYPTION_CHANGE, + BluetoothDevice.ACTION_PAIRING_REQUEST); + mBumble.gattBlocking() + .registerService( + GattProto.RegisterServiceRequest.newBuilder() + .setService( + GattProto.GattServiceParams.newBuilder() + .setUuid(HOGP_UUID.toString()) + .build()) + .build()); + + mBumble.hostBlocking() + .advertise( + AdvertiseRequest.newBuilder() + .setLegacy(true) + .setConnectable(true) + .setOwnAddressType(OwnAddressType.PUBLIC) + .build()); + + StreamObserver<PairingEventAnswer> pairingEventAnswerObserver = + mBumble.security() + .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .onPairing(mPairingEventStreamObserver); + + assertThat(mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_LE)).isTrue(); + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING)); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra( + BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_CONSENT)); + mBumbleDevice.setPairingConfirmation(true); + + PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next(); + assertThat(pairingEvent.hasJustWorks()).isTrue(); + pairingEventAnswerObserver.onNext( + PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build()); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ENCRYPTION_CHANGE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE), + hasExtra(BluetoothDevice.EXTRA_ENCRYPTION_STATUS, 0), + hasExtra(BluetoothDevice.EXTRA_ENCRYPTION_ENABLED, true), + hasExtra(BluetoothDevice.EXTRA_KEY_SIZE, 16), + hasExtra( + BluetoothDevice.EXTRA_ENCRYPTION_ALGORITHM, + BluetoothDevice.ENCRYPTION_ALGORITHM_AES)); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED)); + + verifyNoMoreInteractions(mReceiver); + + unregisterIntentActions( + BluetoothDevice.ACTION_BOND_STATE_CHANGED, + BluetoothDevice.ACTION_ENCRYPTION_CHANGE, + BluetoothDevice.ACTION_PAIRING_REQUEST); + } + + /** + * Test Encryption change event on classic link: + * + * <ol> + * <li>1. Android initiate create bond over Classic link + * <li>2. Android confirms the pairing via pairing request intent + * <li>3. Bumble confirms the pairing internally (optional, added only for test confirmation) + * <li>4. Android verifies Encryption change Intent and bonded intent + * </ol> + */ + @Test + @RequiresFlagsEnabled({Flags.FLAG_ENCRYPTION_CHANGE_BROADCAST}) + public void encryptionChangeSecureClassicLink() { + registerIntentActions( + BluetoothDevice.ACTION_BOND_STATE_CHANGED, + BluetoothDevice.ACTION_ENCRYPTION_CHANGE, + BluetoothDevice.ACTION_PAIRING_REQUEST); + + StreamObserver<PairingEventAnswer> pairingEventAnswerObserver = + mBumble.security() + .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .onPairing(mPairingEventStreamObserver); + + assertThat(mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_BREDR)).isTrue(); + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING)); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra( + BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_CONSENT)); + mBumbleDevice.setPairingConfirmation(true); + + PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next(); + assertThat(pairingEvent.hasJustWorks()).isTrue(); + pairingEventAnswerObserver.onNext( + PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build()); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_ENCRYPTION_CHANGE), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR), + hasExtra(BluetoothDevice.EXTRA_ENCRYPTION_STATUS, 0), + hasExtra(BluetoothDevice.EXTRA_ENCRYPTION_ENABLED, true), + hasExtra(BluetoothDevice.EXTRA_KEY_SIZE, 16), + hasExtra( + BluetoothDevice.EXTRA_ENCRYPTION_ALGORITHM, + BluetoothDevice.ENCRYPTION_ALGORITHM_AES)); + + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED)); + + verifyNoMoreInteractions(mReceiver); + unregisterIntentActions( + BluetoothDevice.ACTION_BOND_STATE_CHANGED, + BluetoothDevice.ACTION_ENCRYPTION_CHANGE, + BluetoothDevice.ACTION_PAIRING_REQUEST); + } + + private void removeBond(BluetoothDevice device) { + registerIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + + assertThat(device.removeBond()).isTrue(); + verifyIntentReceived( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE)); + + unregisterIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + } + + @SafeVarargs + private void verifyIntentReceived(Matcher<Intent>... matchers) { + mInOrder.verify(mReceiver, timeout(BOND_INTENT_TIMEOUT.toMillis())) + .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers))); + } + + @SafeVarargs + private void verifyIntentReceivedUnordered(int num, Matcher<Intent>... matchers) { + verify(mReceiver, timeout(BOND_INTENT_TIMEOUT.toMillis()).times(num)) + .onReceive(any(Context.class), MockitoHamcrest.argThat(AllOf.allOf(matchers))); + } + + @SafeVarargs + private void verifyIntentReceivedUnordered(Matcher<Intent>... matchers) { + verifyIntentReceivedUnordered(1, matchers); + } + + /** + * Helper function to add reference count to registered intent actions + * + * @param actions new intent actions to add. If the array is empty, it is a no-op. + */ + private void registerIntentActions(String... actions) { + if (actions.length == 0) { + return; + } + if (getTotalActionRegistrationCounts() > 0) { + Log.d(TAG, "registerIntentActions(): unregister ALL intents"); + sTargetContext.unregisterReceiver(mReceiver); + } + for (String action : actions) { + mActionRegistrationCounts.merge(action, 1, Integer::sum); + } + IntentFilter filter = new IntentFilter(); + mActionRegistrationCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .forEach( + entry -> { + Log.d( + TAG, + "registerIntentActions(): Registering action = " + + entry.getKey()); + filter.addAction(entry.getKey()); + }); + sTargetContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED); + } + + /** + * Helper function to reduce reference count to registered intent actions If total reference + * count is zero after removal, no broadcast receiver will be registered. + * + * @param actions intent actions to be removed. If some action is not registered, it is no-op + * for that action. If the actions array is empty, it is also a no-op. + */ + private void unregisterIntentActions(String... actions) { + if (actions.length == 0) { + return; + } + if (getTotalActionRegistrationCounts() <= 0) { + return; + } + Log.d(TAG, "unregisterIntentActions(): unregister ALL intents"); + sTargetContext.unregisterReceiver(mReceiver); + for (String action : actions) { + if (!mActionRegistrationCounts.containsKey(action)) { + continue; + } + mActionRegistrationCounts.put(action, mActionRegistrationCounts.get(action) - 1); + if (mActionRegistrationCounts.get(action) <= 0) { + mActionRegistrationCounts.remove(action); + } + } + if (getTotalActionRegistrationCounts() > 0) { + IntentFilter filter = new IntentFilter(); + mActionRegistrationCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .forEach( + entry -> { + Log.d( + TAG, + "unregisterIntentActions(): Registering action = " + + entry.getKey()); + filter.addAction(entry.getKey()); + }); + sTargetContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED); + } + } + + /** + * Get sum of reference count from all registered actions + * + * @return sum of reference count from all registered actions + */ + private int getTotalActionRegistrationCounts() { + return mActionRegistrationCounts.values().stream().reduce(0, Integer::sum); + } + + private BluetoothProfile getProfileProxy(int profile) { + sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, profile); + ArgumentCaptor<BluetoothProfile> proxyCaptor = + ArgumentCaptor.forClass(BluetoothProfile.class); + verify(mProfileServiceListener, timeout(BOND_INTENT_TIMEOUT.toMillis())) + .onServiceConnected(eq(profile), proxyCaptor.capture()); + return proxyCaptor.getValue(); + } +} diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java index 72cc903319..44c6564803 100644 --- a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java +++ b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDevice.BluetoothAddress; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHidHost; import android.bluetooth.BluetoothManager; @@ -39,6 +40,7 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.PandoraDevice; import android.bluetooth.StreamObserverSpliterator; +import android.bluetooth.Utils; import android.bluetooth.test_utils.BlockingBluetoothAdapter; import android.bluetooth.test_utils.EnableBluetoothRule; import android.content.BroadcastReceiver; @@ -51,12 +53,14 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Log; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; +import com.google.testing.junit.testparameterinjector.TestParameter; +import com.google.testing.junit.testparameterinjector.TestParameterInjector; + import io.grpc.stub.StreamObserver; import org.hamcrest.Matcher; @@ -76,7 +80,9 @@ import org.mockito.hamcrest.MockitoHamcrest; import pandora.GattProto; import pandora.HostProto.AdvertiseRequest; import pandora.HostProto.AdvertiseResponse; +import pandora.HostProto.ConnectabilityMode; import pandora.HostProto.OwnAddressType; +import pandora.HostProto.SetConnectabilityModeRequest; import pandora.SecurityProto.LESecurityLevel; import pandora.SecurityProto.PairingEvent; import pandora.SecurityProto.PairingEventAnswer; @@ -88,12 +94,14 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -@RunWith(AndroidJUnit4.class) +@RunWith(TestParameterInjector.class) public class PairingTest { private static final String TAG = "PairingTest"; private static final Duration BOND_INTENT_TIMEOUT = Duration.ofSeconds(10); + private static final int TEST_DELAY_MS = 1000; private static final ParcelUuid BATTERY_UUID = ParcelUuid.fromString("0000180F-0000-1000-8000-00805F9B34FB"); @@ -126,6 +134,7 @@ public class PairingTest { @Mock private BluetoothProfile.ServiceListener mProfileServiceListener; private InOrder mInOrder = null; private BluetoothDevice mBumbleDevice; + private BluetoothDevice mRemoteLeDevice; private BluetoothHidHost mHidService; private BluetoothHeadset mHfpService; @@ -162,9 +171,12 @@ public class PairingTest { mHfpService = (BluetoothHeadset) getProfileProxy(BluetoothProfile.HEADSET); mBumbleDevice = mBumble.getRemoteDevice(); - Set<BluetoothDevice> bondedDevices = sAdapter.getBondedDevices(); - if (bondedDevices.contains(mBumbleDevice)) { - removeBond(mBumbleDevice); + mRemoteLeDevice = + sAdapter.getRemoteLeDevice( + Utils.BUMBLE_RANDOM_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM); + + for (BluetoothDevice device : sAdapter.getBondedDevices()) { + removeBond(device); } } @@ -174,7 +186,11 @@ public class PairingTest { if (bondedDevices.contains(mBumbleDevice)) { removeBond(mBumbleDevice); } + if (bondedDevices.contains(mRemoteLeDevice)) { + removeBond(mRemoteLeDevice); + } mBumbleDevice = null; + mRemoteLeDevice = null; if (getTotalActionRegistrationCounts() > 0) { sTargetContext.unregisterReceiver(mReceiver); mActionRegistrationCounts.clear(); @@ -481,7 +497,7 @@ public class PairingTest { public void testBondLe_Reconnect() { registerIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED); - testStep_BondLe(); + testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC); assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice); testStep_restartBt(); @@ -513,7 +529,56 @@ public class PairingTest { unregisterIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED); } - private void testStep_BondLe() { + /** + * Test if bonded LE device's identity address and type can be read + * + * <p>Prerequisites: + * + * <ol> + * <li>Bumble and Android are not bonded + * </ol> + * + * <p>Steps: + * + * <ol> + * <li>Bumble is discoverable and connectable over LE + * <li>Bumble device's identity address and type unknown + * <li>Android pairs with Bumble over LE + * <li>Bumble device's identity address and type are retrievable + * </ol> + * + * <p>Expectation: Bumble device's identity address and type are present + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_IDENTITY_ADDRESS_TYPE_API) + public void testBondLe_identityAddressWithType(@TestParameter boolean isRandom) { + if (isRandom) { + doTestIdentityAddressWithType(mRemoteLeDevice, OwnAddressType.RANDOM); + } else { + doTestIdentityAddressWithType(mBumbleDevice, OwnAddressType.PUBLIC); + } + } + + private void doTestIdentityAddressWithType( + BluetoothDevice device, OwnAddressType ownAddressType) { + BluetoothAddress identityAddress = device.getIdentityAddressWithType(); + assertThat(identityAddress.getAddress()).isNull(); + assertThat(identityAddress.getAddressType()) + .isEqualTo(BluetoothDevice.ADDRESS_TYPE_UNKNOWN); + + testStep_BondLe(device, ownAddressType); + assertThat(sAdapter.getBondedDevices()).contains(device); + + identityAddress = device.getIdentityAddressWithType(); + assertThat(identityAddress.getAddress()).isEqualTo(device.getAddress()); + assertThat(identityAddress.getAddressType()) + .isEqualTo( + ownAddressType == OwnAddressType.RANDOM + ? BluetoothDevice.ADDRESS_TYPE_RANDOM + : BluetoothDevice.ADDRESS_TYPE_PUBLIC); + } + + private void testStep_BondLe(BluetoothDevice device, OwnAddressType ownAddressType) { registerIntentActions( BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothDevice.ACTION_ACL_CONNECTED, @@ -541,7 +606,7 @@ public class PairingTest { AdvertiseRequest.newBuilder() .setLegacy(true) .setConnectable(true) - .setOwnAddressType(OwnAddressType.PUBLIC) + .setOwnAddressType(ownAddressType) .build()); StreamObserver<PairingEventAnswer> pairingEventAnswerObserver = @@ -549,25 +614,25 @@ public class PairingTest { .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) .onPairing(mPairingEventStreamObserver); - assertThat(mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_LE)).isTrue(); + assertThat(device.createBond(BluetoothDevice.TRANSPORT_LE)).isTrue(); verifyIntentReceivedUnordered( hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), - hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING)); verifyIntentReceived( hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), - hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE)); verifyIntentReceivedUnordered( hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST), - hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), hasExtra( BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_CONSENT)); // Approve pairing from Android - assertThat(mBumbleDevice.setPairingConfirmation(true)).isTrue(); + assertThat(device.setPairingConfirmation(true)).isTrue(); PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next(); assertThat(pairingEvent.hasJustWorks()).isTrue(); @@ -577,7 +642,7 @@ public class PairingTest { // Ensure that pairing succeeds verifyIntentReceived( hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), - hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED)); unregisterIntentActions( @@ -618,6 +683,11 @@ public class PairingTest { assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice); + SetConnectabilityModeRequest request = + SetConnectabilityModeRequest.newBuilder() + .setMode(ConnectabilityMode.CONNECTABLE) + .build(); + mBumble.hostBlocking().setConnectabilityMode(request); assertThat(mBumbleDevice.connect()).isEqualTo(BluetoothStatusCodes.SUCCESS); verifyIntentReceived( hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), @@ -653,7 +723,7 @@ public class PairingTest { registerIntentActions( BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED); - testStep_BondLe(); + testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC); assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice); assertThat(mBumbleDevice.removeBond()).isTrue(); @@ -748,7 +818,7 @@ public class PairingTest { BluetoothDevice.ACTION_BOND_STATE_CHANGED, BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); - testStep_BondLe(); + testStep_BondLe(mBumbleDevice, OwnAddressType.PUBLIC); assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice); // Wait for profiles to get connected @@ -845,6 +915,10 @@ public class PairingTest { hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_CONNECTED), hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice)); + // Todo: b/382118305 - due to settings app interference, profile connection initiate twice + // after bonding. Introduced 1 second delay after first profile connection success + final CompletableFuture<Integer> future = new CompletableFuture<>(); + future.completeOnTimeout(null, TEST_DELAY_MS, TimeUnit.MILLISECONDS).join(); // Disconnect all profiles assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS); verifyIntentReceived( @@ -1031,7 +1105,7 @@ public class PairingTest { assertThat(device.removeBond()).isTrue(); verifyIntentReceived( hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), - hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_DEVICE, device), hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE)); unregisterIntentActions(BluetoothDevice.ACTION_BOND_STATE_CHANGED); diff --git a/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java b/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java index 2f6a56c134..b5757c310f 100644 --- a/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java +++ b/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java @@ -271,13 +271,11 @@ public class LeAudioServiceDiscoveryTest { BluetoothDevice.EXTRA_UUID, Matchers.allOf( Matchers.hasItemInArray(BluetoothUuid.HFP), - Matchers.hasItemInArray(BluetoothUuid.HID), Matchers.hasItemInArray(BluetoothUuid.A2DP_SOURCE), Matchers.hasItemInArray(BluetoothUuid.A2DP_SINK), Matchers.hasItemInArray(BluetoothUuid.AVRCP), Matchers.hasItemInArray(BluetoothUuid.LE_AUDIO), - Matchers.hasItemInArray(BluetoothUuid.BATTERY), - Matchers.hasItemInArray(BluetoothUuid.HOGP)))); + Matchers.hasItemInArray(BluetoothUuid.BATTERY)))); unregisterIntentActions( BluetoothDevice.ACTION_UUID, BluetoothDevice.ACTION_ACL_CONNECTED, diff --git a/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java b/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java index 8c59c37ec4..15a44c3f44 100644 --- a/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java +++ b/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java @@ -38,6 +38,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.ParcelUuid; +import android.os.Parcelable; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -115,10 +116,32 @@ public class ServiceDiscoveryTest { Intent intent = inv.getArgument(1); String action = intent.getAction(); if (BluetoothDevice.ACTION_UUID.equals(action)) { - ParcelUuid[] uuids = + BluetoothDevice device = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + BluetoothDevice.class); + Parcelable[] uuidsRaw = intent.getParcelableArrayExtra( BluetoothDevice.EXTRA_UUID, ParcelUuid.class); - Log.d(TAG, "onReceive(): UUID=" + Arrays.toString(uuids)); + if (uuidsRaw == null) { + Log.e(TAG, "onReceive(): device " + device + " null uuid list"); + } else if (uuidsRaw.length == 0) { + Log.e( + TAG, + "onReceive(): device " + + device + + " 0 length uuid list"); + } else { + ParcelUuid[] uuids = + Arrays.copyOf( + uuidsRaw, uuidsRaw.length, ParcelUuid[].class); + Log.d( + TAG, + "onReceive(): device " + + device + + ", UUID=" + + Arrays.toString(uuids)); + } } return null; }) diff --git a/framework/tests/unit/Android.bp b/framework/tests/unit/Android.bp index 39b3c29861..df2dc8b9dc 100644 --- a/framework/tests/unit/Android.bp +++ b/framework/tests/unit/Android.bp @@ -34,5 +34,6 @@ android_test { test_suites: [ "general-tests", "mts-bluetooth", + "mts-bt", ], } diff --git a/framework/tests/unit/src/android/bluetooth/BluetoothDeviceTest.java b/framework/tests/unit/src/android/bluetooth/BluetoothDeviceTest.java new file mode 100644 index 0000000000..55263c4400 --- /dev/null +++ b/framework/tests/unit/src/android/bluetooth/BluetoothDeviceTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.BluetoothDevice.AddressType; +import android.bluetooth.BluetoothDevice.BluetoothAddress; +import android.os.Parcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test cases for {@link BluetoothDevice}. */ +@RunWith(JUnit4.class) +public class BluetoothDeviceTest { + + @Test + public void testBluetoothAddress_empty() { + BluetoothAddress bluetoothAddress = + new BluetoothAddress(null, BluetoothDevice.ADDRESS_TYPE_UNKNOWN); + + assertThat(bluetoothAddress.getAddress()).isNull(); + assertThat(bluetoothAddress.getAddressType()) + .isEqualTo(BluetoothDevice.ADDRESS_TYPE_UNKNOWN); + } + + @Test + public void testBluetoothAddress_addressWithTypePublic() { + doTestAddressWithType("00:11:22:AA:BB:CC", BluetoothDevice.ADDRESS_TYPE_PUBLIC); + } + + @Test + public void testBluetoothAddress_addressWithTypeRandom() { + doTestAddressWithType("51:F7:A8:75:AC:5E", BluetoothDevice.ADDRESS_TYPE_RANDOM); + } + + @Test + public void testBluetoothAddress_Parcelable() { + String address = "00:11:22:AA:BB:CC"; + int addressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC; + + BluetoothAddress bluetoothAddress = new BluetoothAddress(address, addressType); + + doAssertBluetoothAddress(bluetoothAddress, address, addressType); + + Parcel parcel = Parcel.obtain(); + bluetoothAddress.writeToParcel(parcel, 0 /* flags */); + parcel.setDataPosition(0); + BluetoothAddress bluetoothAddressOut = BluetoothAddress.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + doAssertBluetoothAddress(bluetoothAddressOut, address, addressType); + } + + private void doTestAddressWithType(String address, @AddressType int addressType) { + BluetoothAddress bluetoothAddress = new BluetoothAddress(address, addressType); + + doAssertBluetoothAddress(bluetoothAddress, address, addressType); + } + + private void doAssertBluetoothAddress( + BluetoothAddress bluetoothAddress, String address, @AddressType int addressType) { + assertThat(bluetoothAddress.getAddress()).isEqualTo(address); + assertThat(bluetoothAddress.getAddressType()) + .isEqualTo( + addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM + ? BluetoothDevice.ADDRESS_TYPE_RANDOM + : BluetoothDevice.ADDRESS_TYPE_PUBLIC); + } +} diff --git a/framework/tests/util/Android.bp b/framework/tests/util/Android.bp index f1e942f4c6..dc42f777a5 100644 --- a/framework/tests/util/Android.bp +++ b/framework/tests/util/Android.bp @@ -43,6 +43,7 @@ java_library { "//packages/modules/Connectivity/nearby/tests/cts/fastpair", "//packages/modules/Permission/tests/cts/permission", "//packages/modules/Permission/tests/cts/permissionui", + "//packages/modules/Uwb/ranging/tests/cts/tests", "//test/cts-root/tests/bluetooth", ], } diff --git a/pandora/interfaces/pandora_experimental/hid.proto b/pandora/interfaces/pandora_experimental/hid.proto index 709d1577ff..258b72dad0 100644 --- a/pandora/interfaces/pandora_experimental/hid.proto +++ b/pandora/interfaces/pandora_experimental/hid.proto @@ -7,6 +7,8 @@ option java_outer_classname = "HidProto"; import "google/protobuf/empty.proto"; service HID { + // Register service + rpc RegisterService(ServiceRequest) returns (google.protobuf.Empty); // Connect HID Host rpc ConnectHost(google.protobuf.Empty) returns (google.protobuf.Empty); // Disconnect HID Host @@ -39,6 +41,16 @@ enum HidReportId { HID_INVALID_RPT_ID = 3; } +enum HidServiceType { + SERVICE_TYPE_HID = 0; + SERVICE_TYPE_HOGP = 1; + SERVICE_TYPE_BOTH = 2; +} + +message ServiceRequest { + HidServiceType service_type = 1; +} + message SendHostReportRequest { bytes address = 1; HidReportType report_type = 2; diff --git a/pandora/server/bumble_experimental/hid.py b/pandora/server/bumble_experimental/hid.py index 78491f2547..23b1c76047 100644 --- a/pandora/server/bumble_experimental/hid.py +++ b/pandora/server/bumble_experimental/hid.py @@ -18,6 +18,8 @@ from pandora_experimental.hid_pb2 import ( PROTOCOL_REPORT_MODE, PROTOCOL_BOOT_MODE, PROTOCOL_UNSUPPORTED_MODE, + SERVICE_TYPE_HID, + SERVICE_TYPE_HOGP, ) from bumble.core import ( @@ -705,6 +707,21 @@ def on_virtual_cable_unplug_cb(): hid_protoMode_queue = None hid_report_queue = None +hid_device = None + + +def register_hid(self) -> None: + self.device.sdp_service_records.update(sdp_records()) + global hid_device + hid_device = HID_Device(self.device) + # Register for call backs + hid_device.register_get_report_cb(on_get_report_cb) + hid_device.register_set_report_cb(on_set_report_cb) + hid_device.register_get_protocol_cb(on_get_protocol_cb) + hid_device.register_set_protocol_cb(on_set_protocol_cb) + # Register for virtual cable unplug call back + hid_device.on('virtual_cable_unplug', on_virtual_cable_unplug_cb) + # This class implements the Hid Pandora interface. class HIDService(HIDServicer): @@ -714,19 +731,23 @@ class HIDService(HIDServicer): def __init__(self, device: Device) -> None: super().__init__() self.device = device - self.device.sdp_service_records.update(sdp_records()) self.event_queue: Optional[asyncio.Queue[ProtocolModeEvent]] = None - hogp_device(self.device) - logging.info(f'Hid device register: ') - global hid_device - hid_device = HID_Device(self.device) - # Register for call backs - hid_device.register_get_report_cb(on_get_report_cb) - hid_device.register_set_report_cb(on_set_report_cb) - hid_device.register_get_protocol_cb(on_get_protocol_cb) - hid_device.register_set_protocol_cb(on_set_protocol_cb) - # Register for virtual cable unplug call back - hid_device.on('virtual_cable_unplug', on_virtual_cable_unplug_cb) + + @utils.rpc + async def RegisterService(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty: + + if request.service_type == SERVICE_TYPE_HID: + logging.info(f'Registering HID') + register_hid(self) + elif request.service_type == SERVICE_TYPE_HOGP: + logging.info(f'Registering HOGP') + hogp_device(self.device) + else: + logging.info(f'Registering both HID and HOGP') + register_hid(self) + hogp_device(self.device) + + return empty_pb2.Empty() @utils.rpc async def ConnectHost(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty: diff --git a/service/Android.bp b/service/Android.bp index ccb7f7d63b..5fbf29b91c 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -93,6 +93,7 @@ java_library { } // Apply jarjaring before using library in the apex +// TODO b/383863941 delete and merge with service-bluetooth-new java_library { name: "service-bluetooth", static_libs: ["service-bluetooth-pre-jarjar"], @@ -116,6 +117,30 @@ java_library { visibility: ["//packages/modules/Bluetooth/apex"], } +// Apply jarjaring before using library in the apex +java_library { + name: "service-bluetooth-new", + static_libs: ["service-bluetooth-pre-jarjar"], + installable: true, + + jarjar_rules: ":bluetooth-jarjar-rules", + + optimize: { + enabled: true, + shrink: true, + proguard_flags_files: ["proguard.flags"], + }, + + libs: [ + "framework-bluetooth.impl", + ], + + sdk_version: "system_server_current", + min_sdk_version: "Tiramisu", + apex_available: ["com.android.bt"], + visibility: ["//packages/modules/Bluetooth/apex"], +} + java_library { name: "bluetooth-manager-service-proto-java-gen", srcs: [":srcs_bluetooth_manager_service_proto"], diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java index a78d85797d..0cfdfea987 100644 --- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java @@ -62,7 +62,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.database.ContentObserver; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -79,7 +78,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.sysprop.BluetoothProperties; import android.util.proto.ProtoOutputStream; @@ -589,11 +587,7 @@ class BluetoothManagerService { mHandler = new BluetoothHandler(mLooper); // Observe BLE scan only mode settings change. - if (Flags.respectBleScanSetting()) { - BleScanSettingListener.initialize(mLooper, mContentResolver, this::onBleScanDisabled); - } else { - registerForBleScanModeChange(); - } + BleScanSettingListener.initialize(mLooper, mContentResolver, this::onBleScanDisabled); // Disable ASHA if BLE is not supported, overriding any system property if (!isBleSupported(mContext)) { @@ -684,13 +678,6 @@ class BluetoothManagerService { } clearBleApps(); - if (!Flags.bleScanSettingDoesNotDisconnectIfBtOn()) { - try { - mAdapter.unregAllGattClient(mContext.getAttributionSource()); - } catch (RemoteException e) { - Log.e(TAG, "onBleScanDisabled: unregAllGattClient failed", e); - } - } if (mState.oneOf(STATE_BLE_ON)) { Log.i(TAG, "onBleScanDisabled: Shutting down BLE_ON mode"); bleOnToOff(); @@ -868,20 +855,10 @@ class BluetoothManagerService { if (AirplaneModeListener.isOn() && !mEnable) { return false; } - if (Flags.respectBleScanSetting()) { - if (SatelliteModeListener.isOn()) { - return false; - } - return BleScanSettingListener.isScanAllowed(); - } - try { - return Settings.Global.getInt( - mContentResolver, BleScanSettingListener.BLE_SCAN_ALWAYS_AVAILABLE) - != 0; - } catch (SettingNotFoundException e) { - // The settings is considered as false by default. + if (SatelliteModeListener.isOn()) { return false; } + return BleScanSettingListener.isScanAllowed(); } boolean isHearingAidProfileSupported() { @@ -899,32 +876,6 @@ class BluetoothManagerService { return mAdapter.isMediaProfileConnected(mContext.getAttributionSource()); } - // Monitor change of BLE scan only mode settings. - private void registerForBleScanModeChange() { - ContentObserver contentObserver = - new ContentObserver(new Handler(mLooper)) { - @Override - public void onChange(boolean selfChange) { - if (isBleScanAvailable()) { - // Nothing to do - return; - } - // BLE scan is not available. - disableBleScanMode(); - clearBleApps(); - if (mState.oneOf(STATE_BLE_ON)) { - ActiveLogs.add(ENABLE_DISABLE_REASON_APPLICATION_REQUEST, false); - bleOnToOff(); - } - } - }; - - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(BleScanSettingListener.BLE_SCAN_ALWAYS_AVAILABLE), - false, - contentObserver); - } - // Disable ble scan only mode. private void disableBleScanMode() { if (mState.oneOf(STATE_ON)) { @@ -979,7 +930,7 @@ class BluetoothManagerService { return false; } - if (Flags.respectBleScanSetting() && !BleScanSettingListener.isScanAllowed()) { + if (!BleScanSettingListener.isScanAllowed()) { Log.d(TAG, "enableBle: not enabling - Scan mode is not allowed."); return false; } @@ -1012,12 +963,6 @@ class BluetoothManagerService { + (" isBinding=" + isBinding()) + (" mState=" + mState)); - // Remove this with flag, preventing a "disable" make no sense, even in satellite mode - if (!Flags.respectBleScanSetting() && isSatelliteModeOn()) { - Log.d(TAG, "disableBle: not disabling - satellite mode is on."); - return false; - } - if (mState.oneOf(STATE_OFF)) { Log.i(TAG, "disableBle: Already disabled"); return false; @@ -1082,9 +1027,9 @@ class BluetoothManagerService { Log.d(TAG, "sendBrEdrDownCallback: mAdapter is null"); return; } - boolean scanIsAllowed = - !Flags.respectBleScanSetting() || BleScanSettingListener.isScanAllowed(); - if (!AirplaneModeListener.isOn() && isBleAppPresent() && scanIsAllowed) { + if (BleScanSettingListener.isScanAllowed() + && !AirplaneModeListener.isOn() + && isBleAppPresent()) { // Need to stay at BLE ON. Disconnect all Gatt connections Log.i(TAG, "sendBrEdrDownCallback: Staying in BLE_ON"); try { diff --git a/service/tests/Android.bp b/service/tests/Android.bp index e3b70c81d0..217bd36f70 100644 --- a/service/tests/Android.bp +++ b/service/tests/Android.bp @@ -58,6 +58,7 @@ android_test { test_suites: [ "general-tests", "mts-bluetooth", + "mts-bt", ], min_sdk_version: "Tiramisu", diff --git a/system/Android.bp b/system/Android.bp index b79fa12489..1ecb8b81d7 100644 --- a/system/Android.bp +++ b/system/Android.bp @@ -11,140 +11,12 @@ package { } filegroup { - name: "BlueberryFacadeProto", - srcs: [ - "blueberry/facade/common.proto", - "blueberry/facade/hal/hal_facade.proto", - "blueberry/facade/hci/acl_manager_facade.proto", - "blueberry/facade/hci/controller_facade.proto", - "blueberry/facade/hci/hci_facade.proto", - "blueberry/facade/hci/le_acl_manager_facade.proto", - "blueberry/facade/hci/le_advertising_manager_facade.proto", - "blueberry/facade/hci/le_initiator_address_facade.proto", - "blueberry/facade/hci/le_scanning_manager_facade.proto", - "blueberry/facade/l2cap/classic/facade.proto", - "blueberry/facade/l2cap/le/facade.proto", - "blueberry/facade/neighbor/facade.proto", - "blueberry/facade/rootservice.proto", - "blueberry/facade/topshim/facade.proto", - ], -} - -filegroup { name: "TestMockCodecManager", srcs: [ "bta/le_audio/mock_codec_manager.cc", ], } -genrule { - name: "BlueberryFacadeGeneratedStub_h", - tools: [ - "aprotoc", - "protoc-gen-grpc-cpp-plugin", - ], - cmd: "$(location aprotoc) -Ipackages/modules/Bluetooth/system -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(locations :BlueberryFacadeProto) --grpc_out=$(genDir) --cpp_out=$(genDir)", - srcs: [ - ":BlueberryFacadeProto", - ":libprotobuf-internal-protos", - ], - out: [ - "blueberry/facade/common.grpc.pb.h", - "blueberry/facade/common.pb.h", - "blueberry/facade/hal/hal_facade.grpc.pb.h", - "blueberry/facade/hal/hal_facade.pb.h", - "blueberry/facade/hci/acl_manager_facade.grpc.pb.h", - "blueberry/facade/hci/acl_manager_facade.pb.h", - "blueberry/facade/hci/controller_facade.grpc.pb.h", - "blueberry/facade/hci/controller_facade.pb.h", - "blueberry/facade/hci/hci_facade.grpc.pb.h", - "blueberry/facade/hci/hci_facade.pb.h", - "blueberry/facade/hci/le_acl_manager_facade.grpc.pb.h", - "blueberry/facade/hci/le_acl_manager_facade.pb.h", - "blueberry/facade/hci/le_advertising_manager_facade.grpc.pb.h", - "blueberry/facade/hci/le_advertising_manager_facade.pb.h", - "blueberry/facade/hci/le_initiator_address_facade.grpc.pb.h", - "blueberry/facade/hci/le_initiator_address_facade.pb.h", - "blueberry/facade/hci/le_scanning_manager_facade.grpc.pb.h", - "blueberry/facade/hci/le_scanning_manager_facade.pb.h", - "blueberry/facade/l2cap/classic/facade.grpc.pb.h", - "blueberry/facade/l2cap/classic/facade.pb.h", - "blueberry/facade/l2cap/le/facade.grpc.pb.h", - "blueberry/facade/l2cap/le/facade.pb.h", - "blueberry/facade/neighbor/facade.grpc.pb.h", - "blueberry/facade/neighbor/facade.pb.h", - "blueberry/facade/rootservice.grpc.pb.h", - "blueberry/facade/rootservice.pb.h", - ], -} - -genrule { - name: "BlueberryFacadeGeneratedStub_cc", - tools: [ - "aprotoc", - "protoc-gen-grpc-cpp-plugin", - ], - cmd: "$(location aprotoc) -Ipackages/modules/Bluetooth/system -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(locations :BlueberryFacadeProto) --grpc_out=$(genDir) --cpp_out=$(genDir)", - srcs: [ - ":BlueberryFacadeProto", - ":libprotobuf-internal-protos", - ], - out: [ - "blueberry/facade/common.grpc.pb.cc", - "blueberry/facade/common.pb.cc", - "blueberry/facade/hal/hal_facade.grpc.pb.cc", - "blueberry/facade/hal/hal_facade.pb.cc", - "blueberry/facade/hci/acl_manager_facade.grpc.pb.cc", - "blueberry/facade/hci/acl_manager_facade.pb.cc", - "blueberry/facade/hci/controller_facade.grpc.pb.cc", - "blueberry/facade/hci/controller_facade.pb.cc", - "blueberry/facade/hci/hci_facade.grpc.pb.cc", - "blueberry/facade/hci/hci_facade.pb.cc", - "blueberry/facade/hci/le_acl_manager_facade.grpc.pb.cc", - "blueberry/facade/hci/le_acl_manager_facade.pb.cc", - "blueberry/facade/hci/le_advertising_manager_facade.grpc.pb.cc", - "blueberry/facade/hci/le_advertising_manager_facade.pb.cc", - "blueberry/facade/hci/le_initiator_address_facade.grpc.pb.cc", - "blueberry/facade/hci/le_initiator_address_facade.pb.cc", - "blueberry/facade/hci/le_scanning_manager_facade.grpc.pb.cc", - "blueberry/facade/hci/le_scanning_manager_facade.pb.cc", - "blueberry/facade/l2cap/classic/facade.grpc.pb.cc", - "blueberry/facade/l2cap/classic/facade.pb.cc", - "blueberry/facade/l2cap/le/facade.grpc.pb.cc", - "blueberry/facade/l2cap/le/facade.pb.cc", - "blueberry/facade/neighbor/facade.grpc.pb.cc", - "blueberry/facade/neighbor/facade.pb.cc", - "blueberry/facade/rootservice.grpc.pb.cc", - "blueberry/facade/rootservice.pb.cc", - ], -} - -genrule { - name: "BlueberryFacadeAndCertGeneratedStub_py", - tools: [ - "aprotoc", - "protoc-gen-grpc-python-plugin", - "soong_zip", - ], - cmd: "mkdir -p $(genDir)/files && " + - "$(location aprotoc) -Ipackages/modules/Bluetooth/system -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(locations :BlueberryFacadeProto) --grpc_out=$(genDir)/files --python_out=$(genDir)/files && " + - "for dir in `find $(genDir)/files -type d`; do touch $$dir/__init__.py; done &&" + - "$(location soong_zip) -C $(genDir)/files -D $(genDir)/files -o $(out)", - srcs: [ - ":BlueberryFacadeProto", - ":libprotobuf-internal-protos", - ], - out: ["blueberry_facade_generated_py.zip"], -} - -rust_protobuf { - name: "libbt_topshim_facade_protobuf", - crate_name: "bt_topshim_facade_protobuf", - source_stem: "topshim_facade", - host_supported: true, - grpc_protos: ["blueberry/facade/topshim/facade.proto"], -} - // Export system headers for rules that can't simply use `include_dirs` cc_library_headers { name: "libbluetooth_system_headers", @@ -160,168 +32,3 @@ cc_library_headers { ], min_sdk_version: "30", } - -genrule { - name: "bluetooth_cert_test_sources-zip", - tools: [ - "soong_zip", - ], - srcs: [ - "blueberry/**/*.py", - "blueberry/**/*.yaml", - "setup.py", - ], - out: ["bluetooth_cert_test_sources.zip"], - cmd: "echo $(in) > $(genDir)/file_list.txt && " + - "$(location soong_zip) -C packages/modules/Bluetooth/system -l $(genDir)/file_list.txt -o $(out)", -} - -genrule { - name: "gd_hci_packets_python3_gen-zip", - tools: [ - "soong_zip", - ], - srcs: [ - ":gd_hci_packets_python3_gen", - ], - out: ["gd_hci_packets_python3_gen.zip"], - cmd: "echo $(in) > $(genDir)/file_list.txt && " + - "$(location soong_zip) -j -l $(genDir)/file_list.txt -o $(out)", -} - -genrule { - name: "gd_smp_packets_python3_gen-zip", - tools: [ - "soong_zip", - ], - srcs: [ - ":gd_smp_packets_python3_gen", - ], - out: ["gd_smp_packets_python3_gen.zip"], - cmd: "echo $(in) > $(genDir)/file_list.txt && " + - "$(location soong_zip) -j -l $(genDir)/file_list.txt -o $(out)", -} - -cc_genrule { - name: "bluetooth_cert_test_host_deps-zip", - host_supported: true, - device_supported: false, - compile_multilib: "first", - tools: [ - "bluetooth_stack_with_facade", - "bt_topshim_facade", - "root-canal", - "soong_zip", - ], - srcs: [ - ":libbase", - ":libbinder", - ":libbinder_ndk", - ":libbluetooth", - ":libbluetooth_gd", - ":libc++", - ":libchrome", - ":libcrypto", - ":libcutils", - ":libevent", - ":libflatbuffers-cpp", - ":libgrpc++", - ":libgrpc_wrap", - ":liblog", - ":liblzma", - ":libprotobuf-cpp-full", - ":libssl", - ":libunwindstack", - ":libutils", - ":libz", - ":server_configurable_flags", - ], - out: ["bluetooth_cert_test_host_deps.zip"], - cmd: "echo $(in) > $(genDir)/file_list.txt && " + - "$(location soong_zip) -j -f $(location bluetooth_stack_with_facade) -f $(location bt_topshim_facade) -f $(location root-canal) -P lib64 -l $(genDir)/file_list.txt -o $(out)", -} - -cc_genrule { - name: "bluetooth_cert_test_target_deps-zip", - compile_multilib: "first", - tools: [ - "soong_zip", - ], - srcs: [ - // executables - ":bluetooth_stack_with_facade", - // libs - ":android.hardware.bluetooth@1.0", - ":android.hardware.bluetooth@1.1", - ":libandroid_runtime_lazy", - ":libbase", - ":libbinder", - ":libbinder_ndk", - ":libc++", - ":libcrypto", - ":libcutils", - ":libgrpc++", - ":libgrpc_wrap", - ":libhidlbase", - ":liblog", - ":liblzma", - ":libprotobuf-cpp-full", - ":libssl", - ":libunwindstack", - ":libutils", - ":libz", - ":server_configurable_flags", - ], - out: ["bluetooth_cert_test_target_deps.zip"], - cmd: "echo $(in) > $(genDir)/file_list.txt && " + - "$(location soong_zip) -j -P target -l $(genDir)/file_list.txt -o $(out)", -} - -genrule { - name: "llvm-tools-zip", - tools: [ - "soong_zip", - ], - srcs: [ - ":llvm-tools", - ], - out: ["llvm-tools.zip"], - cmd: "mkdir -p $(genDir)/llvm_binutils/bin && mkdir -p $(genDir)/llvm_binutils/lib/x86_64-unknown-linux-gnu && " + - "cp prebuilts/clang/host/linux-x86/*/bin/llvm-cov $(genDir)/llvm_binutils/bin && " + - "cp prebuilts/clang/host/linux-x86/*/bin/llvm-profdata $(genDir)/llvm_binutils/bin && " + - "cp prebuilts/clang/host/linux-x86/*/bin/llvm-symbolizer $(genDir)/llvm_binutils/bin && " + - "cp prebuilts/clang/host/linux-x86/*/lib/x86_64-unknown-linux-gnu/libc++.so $(genDir)/llvm_binutils/lib/x86_64-unknown-linux-gnu && " + - "$(location soong_zip) -C $(genDir) -D $(genDir)/llvm_binutils -o $(out)", -} - -cc_genrule { - name: "bluetooth_cert_tests.zip", - host_supported: true, - device_supported: false, - compile_multilib: "first", - tools: [ - "merge_zips", - "soong_zip", - ], - srcs: [ - ":BlueberryFacadeAndCertGeneratedStub_py", - ":bluetooth_cert_test_host_deps-zip", - ":bluetooth_cert_test_sources-zip", - ":gd_hci_packets_python3_gen-zip", - ":gd_smp_packets_python3_gen-zip", - ":llvm-tools-zip", - ], - device_first_srcs: [ - ":bluetooth_cert_test_target_deps-zip", - ], - out: ["bluetooth_cert_tests.zip"], - cmd: "$(location merge_zips) $(genDir)/temp.zip $(in) && " + - "unzip -q -d $(genDir)/files $(genDir)/temp.zip && " + - "for d in $$(find $(genDir)/files/blueberry -type d -name '*'); do touch -a $$d/__init__.py; done && " + - "$(location soong_zip) -C $(genDir)/files -D $(genDir)/files -o $(out)", - dist: { - targets: [ - "bluetooth_stack_with_facade", - ], - }, -} diff --git a/system/BUILD.gn b/system/BUILD.gn index 783428604a..01286ec4e8 100644 --- a/system/BUILD.gn +++ b/system/BUILD.gn @@ -55,7 +55,6 @@ group("bluetooth") { group("tools") { deps = [ - "//bt/system/gd/dumpsys/bundler:bluetooth_flatbuffer_bundler", "//bt/system/gd/packet/parser:bluetooth_packetgen", ] } diff --git a/system/OWNERS b/system/OWNERS index 15ec13fb04..d60842e9c9 100644 --- a/system/OWNERS +++ b/system/OWNERS @@ -10,4 +10,3 @@ poahlo@google.com rongxuan@google.com rwt@google.com wescande@google.com -yuyangh@google.com diff --git a/system/audio/Android.bp b/system/audio/Android.bp index d5dde594e1..481182dc5f 100644 --- a/system/audio/Android.bp +++ b/system/audio/Android.bp @@ -83,9 +83,6 @@ cc_library_host_shared { "liblog", ], stl: "libc++_static", - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], } python_test_host { diff --git a/system/audio/asrc/asrc_resampler.cc b/system/audio/asrc/asrc_resampler.cc index 81d46f1121..7e44d9ade6 100644 --- a/system/audio/asrc/asrc_resampler.cc +++ b/system/audio/asrc/asrc_resampler.cc @@ -16,7 +16,6 @@ #include "asrc_resampler.h" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> diff --git a/system/audio_bluetooth_hw/Android.bp b/system/audio_bluetooth_hw/Android.bp index fe53c511a2..030fef1942 100644 --- a/system/audio_bluetooth_hw/Android.bp +++ b/system/audio_bluetooth_hw/Android.bp @@ -51,7 +51,7 @@ cc_library_shared { "libhidlbase", ], visibility: [ - "//device/generic/goldfish/audio", + "//device/generic/goldfish/hals/audio", ], } diff --git a/system/audio_hal_interface/Android.bp b/system/audio_hal_interface/Android.bp index f485de80aa..363627401f 100644 --- a/system/audio_hal_interface/Android.bp +++ b/system/audio_hal_interface/Android.bp @@ -228,7 +228,6 @@ cc_defaults { "android.hardware.common.fmq-V1-ndk", "libFraunhoferAAC", "libbase", - "libbluetooth-dumpsys", "libbluetooth-protos", "libbluetooth-types", "libbluetooth_core_rs", @@ -292,6 +291,7 @@ cc_test { "aidl/le_audio_utils_unittest.cc", ], shared_libs: [ + "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libbinder_ndk", diff --git a/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc b/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc index 6b8a2b4195..cfb0d020d4 100644 --- a/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc +++ b/system/audio_hal_interface/aidl/a2dp/a2dp_encoding_aidl.cc @@ -306,10 +306,7 @@ static bool a2dp_get_selected_hal_pcm_config(A2dpCodecConfig* a2dp_codec_configs pcm_config->sampleRateHz = A2dpCodecToHalSampleRate(current_codec); pcm_config->bitsPerSample = A2dpCodecToHalBitsPerSample(current_codec); pcm_config->channelMode = A2dpCodecToHalChannelMode(current_codec); - - if (com::android::bluetooth::flags::a2dp_aidl_encoding_interval()) { - pcm_config->dataIntervalUs = preferred_encoding_interval_us; - } + pcm_config->dataIntervalUs = preferred_encoding_interval_us; return pcm_config->sampleRateHz > 0 && pcm_config->bitsPerSample > 0 && pcm_config->channelMode != ChannelMode::UNKNOWN; diff --git a/system/audio_hal_interface/fuzzer/Android.bp b/system/audio_hal_interface/fuzzer/Android.bp index 9a32f910e3..f2301b6d00 100644 --- a/system/audio_hal_interface/fuzzer/Android.bp +++ b/system/audio_hal_interface/fuzzer/Android.bp @@ -59,7 +59,6 @@ cc_defaults { "bluetooth_flags_c_lib", "libFraunhoferAAC", "libbase", - "libbluetooth-dumpsys", "libbluetooth-protos", "libbluetooth-types", "libbluetooth_core_rs", diff --git a/system/blueberry/facade/common.proto b/system/blueberry/facade/common.proto deleted file mode 100644 index 42c6aaa3f7..0000000000 --- a/system/blueberry/facade/common.proto +++ /dev/null @@ -1,35 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade; - -message Data { - bytes payload = 1; -} - -message BluetoothAddress { - bytes address = 1; -} - -enum BluetoothAddressTypeEnum { - PUBLIC_DEVICE_ADDRESS = 0x0; - RANDOM_DEVICE_ADDRESS = 0x1; - PUBLIC_IDENTITY_ADDRESS = 0x2; - RANDOM_IDENTITY_ADDRESS = 0x3; -} - -enum BluetoothOwnAddressTypeEnum { - USE_PUBLIC_DEVICE_ADDRESS = 0x0; - USE_RANDOM_DEVICE_ADDRESS = 0x1; - RESOLVABLE_OR_PUBLIC_ADDRESS = 0x2; - RESOLVABLE_OR_RANDOM_ADDRESS = 0x3; -} - -message BluetoothAddressWithType { - BluetoothAddress address = 1; - BluetoothAddressTypeEnum type = 2; -} - -enum BluetoothPeerAddressTypeEnum { - PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x0; - RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x1; -} diff --git a/system/blueberry/facade/hal/hal_facade.proto b/system/blueberry/facade/hal/hal_facade.proto deleted file mode 100644 index d09d0e4923..0000000000 --- a/system/blueberry/facade/hal/hal_facade.proto +++ /dev/null @@ -1,20 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hal; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service HciHalFacade { - rpc SendCommand(blueberry.facade.Data) returns (google.protobuf.Empty) {} - rpc StreamEvents(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} - - rpc SendAcl(blueberry.facade.Data) returns (google.protobuf.Empty) {} - rpc StreamAcl(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} - - rpc SendSco(blueberry.facade.Data) returns (google.protobuf.Empty) {} - rpc StreamSco(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} - - rpc SendIso(blueberry.facade.Data) returns (google.protobuf.Empty) {} - rpc StreamIso(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} -} diff --git a/system/blueberry/facade/hci/acl_manager_facade.proto b/system/blueberry/facade/hci/acl_manager_facade.proto deleted file mode 100644 index 69f408a34d..0000000000 --- a/system/blueberry/facade/hci/acl_manager_facade.proto +++ /dev/null @@ -1,53 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; - -service AclManagerFacade { - rpc CreateConnection(ConnectionMsg) returns (stream ConnectionEvent) {} - rpc CancelConnection(ConnectionMsg) returns (google.protobuf.Empty) {} - rpc Disconnect(HandleMsg) returns (google.protobuf.Empty) {} - rpc WriteDefaultLinkPolicySettings(PolicyMsg) returns (google.protobuf.Empty) {} - rpc AuthenticationRequested(HandleMsg) returns (google.protobuf.Empty) {} - rpc ConnectionCommand(ConnectionCommandMsg) returns (google.protobuf.Empty) {} - rpc SwitchRole(RoleMsg) returns (google.protobuf.Empty) {} - rpc SendAclData(AclData) returns (google.protobuf.Empty) {} - rpc FetchAclData(HandleMsg) returns (stream AclData) {} - rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream ConnectionEvent) {} -} - -message HandleMsg { - uint32 handle = 1; -} - -message ConnectionMsg { - bytes address = 1; -} - -message PolicyMsg { - uint32 policy = 1; -} - -enum NewRole { - CENTRAL = 0; - PERIPHERAL = 1; -} - -message RoleMsg { - bytes address = 1; - NewRole role = 2; -} - -message ConnectionCommandMsg { - bytes packet = 1; -} - -message ConnectionEvent { - bytes payload = 1; -} - -message AclData { - uint32 handle = 1; - bytes payload = 2; -} diff --git a/system/blueberry/facade/hci/controller_facade.proto b/system/blueberry/facade/hci/controller_facade.proto deleted file mode 100644 index 7eb46a6abb..0000000000 --- a/system/blueberry/facade/hci/controller_facade.proto +++ /dev/null @@ -1,96 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service ControllerFacade { - rpc GetMacAddress(google.protobuf.Empty) returns (blueberry.facade.BluetoothAddress) {} - rpc WriteLocalName(NameMsg) returns (google.protobuf.Empty) {} - rpc GetLocalName(google.protobuf.Empty) returns (NameMsg) {} - rpc IsSupportedCommand(OpCodeMsg) returns (SupportedMsg) {} - rpc GetLeNumberOfSupportedAdvertisingSets(google.protobuf.Empty) returns (SingleValueMsg) {} - rpc SupportsSimplePairing(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsSecureConnections(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsSimultaneousLeBrEdr(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsInterlacedInquiryScan(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsRssiWithInquiryResults(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsExtendedInquiryResponse(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsRoleSwitch(google.protobuf.Empty) returns (SupportedMsg) {} - rpc Supports3SlotPackets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc Supports5SlotPackets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsClassic2mPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsClassic3mPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc Supports3SlotEdrPackets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc Supports5SlotEdrPackets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsSco(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsHv2Packets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsHv3Packets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEv3Packets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEv4Packets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEv5Packets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEsco2mPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEsco3mPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc Supports3SlotEscoEdrPackets(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsHoldMode(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsSniffMode(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsParkMode(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsNonFlushablePb(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsSniffSubrating(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsEncryptionPause(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBle(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleEncryption(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectionParametersRequest(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleExtendedReject(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePeripheralInitiatedFeaturesExchange(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePing(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleDataPacketLengthExtension(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePrivacy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleExtendedScannerFilterPolicies(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBle2mPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleStableModulationIndexTx(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleStableModulationIndexRx(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleCodedPhy(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleExtendedAdvertising(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePeriodicAdvertising(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleChannelSelectionAlgorithm2(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePowerClass1(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleMinimumUsedChannels(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectionCteRequest(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectionCteResponse(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectionlessCteTransmitter(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectionlessCteReceiver(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleAntennaSwitchingDuringCteTx(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleAntennaSwitchingDuringCteRx(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleReceivingConstantToneExtensions(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePeriodicAdvertisingSyncTransferSender(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePeriodicAdvertisingSyncTransferRecipient(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleSleepClockAccuracyUpdates(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleRemotePublicKeyValidation(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectedIsochronousStreamCentral(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleConnectedIsochronousStreamPeripheral(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleIsochronousBroadcaster(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleSynchronizedReceiver(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBleIsochronousChannelsHostSupport(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePowerControlRequest(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePowerChangeIndication(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePathLossMonitoring(google.protobuf.Empty) returns (SupportedMsg) {} - rpc SupportsBlePeriodicAdvertisingAdi(google.protobuf.Empty) returns (SupportedMsg) {} -} - -message NameMsg { - bytes name = 1; -} - -message OpCodeMsg { - uint32 op_code = 1; -} - -message SupportedMsg { - bool supported = 1; -} - -message SingleValueMsg { - uint64 value = 1; -}
\ No newline at end of file diff --git a/system/blueberry/facade/hci/hci_facade.proto b/system/blueberry/facade/hci/hci_facade.proto deleted file mode 100644 index 253708ce2f..0000000000 --- a/system/blueberry/facade/hci/hci_facade.proto +++ /dev/null @@ -1,23 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service HciFacade { - rpc SendCommand(blueberry.facade.Data) returns (google.protobuf.Empty) {} - - rpc RequestEvent(EventRequest) returns (google.protobuf.Empty) {} - rpc StreamEvents(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} - - rpc RequestLeSubevent(EventRequest) returns (google.protobuf.Empty) {} - rpc StreamLeSubevents(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} - - rpc SendAcl(blueberry.facade.Data) returns (google.protobuf.Empty) {} - rpc StreamAcl(google.protobuf.Empty) returns (stream blueberry.facade.Data) {} -} - -message EventRequest { - uint32 code = 1; -} diff --git a/system/blueberry/facade/hci/le_acl_manager_facade.proto b/system/blueberry/facade/hci/le_acl_manager_facade.proto deleted file mode 100644 index ad6423fb36..0000000000 --- a/system/blueberry/facade/hci/le_acl_manager_facade.proto +++ /dev/null @@ -1,56 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service LeAclManagerFacade { - rpc CreateConnection(CreateConnectionMsg) returns (stream LeConnectionEvent) {} - rpc CancelConnection(blueberry.facade.BluetoothAddressWithType) returns (google.protobuf.Empty) {} - rpc Disconnect(LeHandleMsg) returns (google.protobuf.Empty) {} - rpc ConnectionCommand(LeConnectionCommandMsg) returns (google.protobuf.Empty) {} - rpc SendAclData(LeAclData) returns (google.protobuf.Empty) {} - rpc FetchAclData(LeHandleMsg) returns (stream LeAclData) {} - rpc FetchIncomingConnection(google.protobuf.Empty) returns (stream LeConnectionEvent) {} - rpc AddDeviceToResolvingList(IrkMsg) returns (google.protobuf.Empty) {} - rpc IsOnBackgroundList(BackgroundRequestMsg) returns (BackgroundResultMsg) {} - rpc RemoveFromBackgroundList(BackgroundRequestMsg) - returns (google.protobuf.Empty) {} -} - -message LeHandleMsg { - uint32 handle = 1; -} - -message LeConnectionEvent { - bytes payload = 1; -} - -message LeConnectionCommandMsg { - bytes packet = 1; -} - -message LeAclData { - uint32 handle = 1; - bytes payload = 2; -} - -message CreateConnectionMsg { - blueberry.facade.BluetoothAddressWithType peer_address = 1; - bool is_direct = 2; -} - -message IrkMsg { - blueberry.facade.BluetoothAddressWithType peer = 1; - bytes peer_irk = 2; - bytes local_irk = 3; -} - -message BackgroundRequestMsg { - blueberry.facade.BluetoothAddressWithType peer_address = 1; -} - -message BackgroundResultMsg { - bool is_on_background_list = 1; -} diff --git a/system/blueberry/facade/hci/le_advertising_manager_facade.proto b/system/blueberry/facade/hci/le_advertising_manager_facade.proto deleted file mode 100644 index bec06edf86..0000000000 --- a/system/blueberry/facade/hci/le_advertising_manager_facade.proto +++ /dev/null @@ -1,180 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service LeAdvertisingManagerFacade { - rpc CreateAdvertiser(CreateAdvertiserRequest) returns (CreateAdvertiserResponse) {} - rpc ExtendedCreateAdvertiser(ExtendedCreateAdvertiserRequest) returns (ExtendedCreateAdvertiserResponse) {} - rpc EnableAdvertiser(EnableAdvertiserRequest) returns (google.protobuf.Empty) {} - rpc SetData(SetDataRequest) returns (google.protobuf.Empty) {} - rpc SetParameters(SetParametersRequest) returns (google.protobuf.Empty) {} - rpc SetPeriodicParameters(SetPeriodicParametersRequest) returns (google.protobuf.Empty) {} - rpc SetPeriodicData(SetPeriodicDataRequest) returns (google.protobuf.Empty) {} - rpc EnablePeriodicAdvertising(EnablePeriodicAdvertisingRequest) returns (google.protobuf.Empty) {} - rpc GetOwnAddress(GetOwnAddressRequest) returns (google.protobuf.Empty) {} - rpc GetNumberOfAdvertisingInstances(google.protobuf.Empty) returns (GetNumberOfAdvertisingInstancesResponse) {} - rpc RemoveAdvertiser(RemoveAdvertiserRequest) returns (google.protobuf.Empty) {} - rpc FetchCallbackEvents(google.protobuf.Empty) returns (stream AdvertisingCallbackMsg) {} - rpc FetchAddressEvents(google.protobuf.Empty) returns (stream AddressMsg) {} -} - -message GapDataMsg { - bytes data = 1; -} - -enum AdvertisingEventType { - ADV_IND = 0x0; - ADV_DIRECT_IND = 0x1; - ADV_SCAN_IND = 0x2; - ADV_NONCONN_IND = 0x3; - ADV_DIRECT_IND_LOW = 0x4; -} - -enum AdvertisingFilterPolicy { - ALL_DEVICES = 0x0; - LISTED_SCAN = 0x1; - LISTED_CONNECT = 0x2; - LISTED_SCAN_AND_CONNECT = 0x3; -}; - -enum AdvertisingProperty { - NONE = 0x00; - INCLUDE_TX_POWER = 0x06; -}; - -enum AdvertisingStatus { - ADV_SUCCESS = 0x00; - ADV_DATA_TOO_LARGE = 0x01; - ADV_TOO_MANY_ADVERTISERS = 0x02; - ADV_ALREADY_STARTED = 0x03; - ADV_INTERNAL_ERROR = 0x04; - ADV_FEATURE_UNSUPPORTED = 0x05; -}; - -message AdvertisingConfig { - repeated GapDataMsg advertisement = 1; - repeated GapDataMsg scan_response = 2; - // Unit: number of Bluetooth slots in 0.125 ms increment - int32 interval_min = 4; - // Unit: number of Bluetooth slots in 0.125 ms increment - int32 interval_max = 5; - AdvertisingEventType advertising_type = 6; - blueberry.facade.BluetoothOwnAddressTypeEnum own_address_type = 7; - blueberry.facade.BluetoothPeerAddressTypeEnum peer_address_type = 8; - blueberry.facade.BluetoothAddress peer_address = 9; - int32 channel_map = 10; - AdvertisingFilterPolicy filter_policy = 11; - int32 tx_power = 12; -} - -message ExtendedAdvertisingConfig { - AdvertisingConfig advertising_config = 1; - bool connectable = 2; - bool scannable = 3; - bool directed = 4; - bool high_duty_directed_connectable = 5; - bool legacy_pdus = 6; - bool anonymous = 7; - bool include_tx_power = 8; - bool use_le_coded_phy = 9; - int32 secondary_max_skip = 10; - int32 secondary_advertising_phy = 11; - int32 sid = 12; - bool enable_scan_request_notifications = 13; -} - -message PeriodicAdvertisingParameters { - int32 min_interval = 1; - int32 max_interval = 2; - AdvertisingProperty advertising_property = 3; -} - -message CreateAdvertiserRequest { - AdvertisingConfig config = 1; -} - -message CreateAdvertiserResponse { - // -1 on error - int32 advertiser_id = 1; -} - -message ExtendedCreateAdvertiserRequest { - ExtendedAdvertisingConfig config = 1; -} - -message ExtendedCreateAdvertiserResponse { - // -1 on error - int32 advertiser_id = 1; -} - -message EnableAdvertiserRequest { - int32 advertiser_id = 1; - bool enable = 2; -} - -message SetDataRequest { - int32 advertiser_id = 1; - bool set_scan_rsp = 2; - repeated GapDataMsg data = 3; -} - -message SetParametersRequest { - int32 advertiser_id = 1; - AdvertisingConfig config = 2; -} - -message SetPeriodicParametersRequest { - int32 advertiser_id = 1; - PeriodicAdvertisingParameters config = 2; -} - -message SetPeriodicDataRequest { - int32 advertiser_id = 1; - repeated GapDataMsg data = 2; -} - -message EnablePeriodicAdvertisingRequest { - int32 advertiser_id = 1; - bool enable = 2; - bool include_adi = 3; -} - -message GetOwnAddressRequest { - int32 advertiser_id = 1; -} - -message GetNumberOfAdvertisingInstancesResponse { - int32 num_advertising_instances = 1; -} - -message RemoveAdvertiserRequest { - int32 advertiser_id = 1; -} - -enum AdvertisingCallbackMsgType { - ADVERTISING_SET_STARTED = 0; - ADVERTISING_ENABLED = 1; - ADVERTISING_DATA_SET = 2; - SCAN_RESPONSE_DATA_SET = 3; - ADVERTISING_PARAMETERS_UPDATED = 4; - PERIODIC_ADVERTISING_PARAMETERS_UPDATED = 5; - PERIODIC_ADVERTISING_DATA_SET = 6; - PERIODIC_ADVERTISING_ENABLED = 7; - OWN_ADDRESS_READ = 8; -} - -message AdvertisingCallbackMsg { - AdvertisingCallbackMsgType message_type = 1; - uint32 advertiser_id = 2; - AdvertisingStatus status = 3; - uint32 data = 4; -} - -message AddressMsg { - AdvertisingCallbackMsgType message_type = 1; - uint32 advertiser_id = 2; - blueberry.facade.BluetoothAddressWithType address = 3; -} diff --git a/system/blueberry/facade/hci/le_initiator_address_facade.proto b/system/blueberry/facade/hci/le_initiator_address_facade.proto deleted file mode 100644 index 8a0361cc7d..0000000000 --- a/system/blueberry/facade/hci/le_initiator_address_facade.proto +++ /dev/null @@ -1,28 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service LeInitiatorAddressFacade { - rpc SetPrivacyPolicyForInitiatorAddress(PrivacyPolicy) returns (google.protobuf.Empty) {} - rpc GetCurrentInitiatorAddress(google.protobuf.Empty) returns (blueberry.facade.BluetoothAddressWithType) {} - rpc NewResolvableAddress(google.protobuf.Empty) returns (blueberry.facade.BluetoothAddressWithType) {} -} - -enum AddressPolicy { - POLICY_NOT_SET = 0x00; - USE_PUBLIC_ADDRESS = 0x01; - USE_STATIC_ADDRESS = 0x02; - USE_NON_RESOLVABLE_ADDRESS = 0x03; - USE_RESOLVABLE_ADDRESS = 0x04; -} - -message PrivacyPolicy { - AddressPolicy address_policy = 1; - blueberry.facade.BluetoothAddressWithType address_with_type = 2; - bytes rotation_irk = 3; - uint64 minimum_rotation_time = 4; - uint64 maximum_rotation_time = 5; -} diff --git a/system/blueberry/facade/hci/le_scanning_manager_facade.proto b/system/blueberry/facade/hci/le_scanning_manager_facade.proto deleted file mode 100644 index 4dc14b5566..0000000000 --- a/system/blueberry/facade/hci/le_scanning_manager_facade.proto +++ /dev/null @@ -1,69 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.hci; - -import "google/protobuf/empty.proto"; - -service LeScanningManagerFacade { - rpc RegisterScanner(RegisterScannerRequest) returns (google.protobuf.Empty) {} - rpc Unregister(UnregisterRequest) returns (google.protobuf.Empty) {} - rpc Scan(ScanRequest) returns (google.protobuf.Empty) {} - rpc SetScanParameters(SetScanParametersRequest) returns (google.protobuf.Empty) {} - rpc FetchCallbackEvents(google.protobuf.Empty) returns (stream ScanningCallbackMsg) {} - rpc FetchAdvertisingReports(google.protobuf.Empty) returns (stream AdvertisingReportMsg) {} -} - -enum LeScanType { - PASSIVE = 0x0; - ACTIVE = 0x1; -} - -enum ScanningCallbackMsgType { - SCANNER_REGISTERED = 0; - SET_SCANNER_PARAMETER_COMPLETE = 1; - SCAN_RESULT = 2; - TRACK_ADV_FOUND_LOST = 3; - BATCH_SCAN_REPORTS = 4; - BATCH_SCAN_THRESHOLD_CROSSED = 5; - TIMEOUT = 6; - FILTER_ENABLE = 7; - FILTER_PARAMETER_SETUP = 8; - FILTER_CONFIG = 9; -} - -enum ScanningStatus { - SCAN_SUCCESS = 0x00; - SCAN_NO_RESOURCES = 0x80; - SCAN_INTERNAL_ERROR = 0x85; - SCAN_ILLEGAL_PARAMETER = 0x87; -}; - -message RegisterScannerRequest { - uint32 uuid = 1; -} - -message UnregisterRequest { - uint32 scanner_id = 1; -} - -message ScanRequest { - bool start = 1; -} - -message SetScanParametersRequest { - uint32 scanner_id = 1; - LeScanType scan_type = 2; - uint32 scan_interval = 3; - uint32 scan_window = 4; - uint32 scan_phy = 5; -} - -message AdvertisingReportMsg { - bytes event = 1; -} - -message ScanningCallbackMsg { - ScanningCallbackMsgType message_type = 1; - ScanningStatus status = 2; - uint32 data = 3; -} diff --git a/system/blueberry/facade/l2cap/classic/facade.proto b/system/blueberry/facade/l2cap/classic/facade.proto deleted file mode 100644 index 1017d507da..0000000000 --- a/system/blueberry/facade/l2cap/classic/facade.proto +++ /dev/null @@ -1,130 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.l2cap.classic; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service L2capClassicModuleFacade { - rpc FetchConnectionComplete(google.protobuf.Empty) returns (stream ConnectionCompleteEvent) { - // Testing Android Bluetooth stack only. Optional for other stack. - } - rpc FetchConnectionClose(google.protobuf.Empty) returns (stream ConnectionCloseEvent) { - // Testing Android Bluetooth stack only. Optional for other stack. - } - rpc OpenChannel(OpenChannelRequest) returns (google.protobuf.Empty) {} - rpc CloseChannel(CloseChannelRequest) returns (google.protobuf.Empty) {} - rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {} - rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {} - rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {} - rpc SetTrafficPaused(SetTrafficPausedRequest) returns (google.protobuf.Empty) {} - rpc GetChannelQueueDepth(google.protobuf.Empty) returns (GetChannelQueueDepthResponse) { - // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer - // before L2CAP user dequeues. - } - rpc InitiateConnectionForSecurity(blueberry.facade.BluetoothAddress) returns (google.protobuf.Empty) {} - rpc FetchSecurityConnectionEvents(google.protobuf.Empty) returns (stream LinkSecurityInterfaceCallbackEvent) {} - rpc SecurityLinkEnsureAuthenticated(blueberry.facade.BluetoothAddress) returns (google.protobuf.Empty) {} - rpc SecurityLinkHold(blueberry.facade.BluetoothAddress) returns (google.protobuf.Empty) {} - rpc SecurityLinkDisconnect(blueberry.facade.BluetoothAddress) returns (google.protobuf.Empty) {} - rpc SecurityLinkRelease(blueberry.facade.BluetoothAddress) returns (google.protobuf.Empty) {} -} - -enum LinkSecurityInterfaceCallbackEventType { - ON_CONNECTED = 0; - ON_DISCONNECTED = 1; - ON_AUTHENTICATION_COMPLETE = 2; - ON_ENCRYPTION_CHANGE = 3; - ON_READ_REMOTE_VERSION_INFO = 4; - ON_READ_REMOTE_EXTENDED_FEATURES = 5; -} - -message LinkSecurityInterfaceCallbackEvent { - blueberry.facade.BluetoothAddress address = 1; - LinkSecurityInterfaceCallbackEventType event_type = 2; -} - -message RegisterChannelRequest { - uint32 channel = 1; -} - -message ConnectionCompleteEvent { - blueberry.facade.BluetoothAddress remote = 1; -} - -message ConnectionCloseEvent { - blueberry.facade.BluetoothAddress remote = 1; - uint32 reason = 2; -} - -enum RetransmissionFlowControlMode { - BASIC = 0; - ERTM = 1; - ERTM_OPTIONAL = 2; -} - -message OpenChannelRequest { - blueberry.facade.BluetoothAddress remote = 1; - uint32 psm = 2; - RetransmissionFlowControlMode mode = 3; -} - -message CloseChannelRequest { - uint32 psm = 1; -} - -enum ChannelSignalEventType { - OPEN = 0; - CLOSE = 1; - CONFIGURE = 2; -} - -message ChannelSignalEvent { - uint32 cid = 1; - ChannelSignalEventType type = 2; -} - -enum SendL2capPacketResultType { - OK = 0; - BAD_CID = 1; -} - -message SendL2capPacketResult { - SendL2capPacketResultType result_type = 1; -} - -message L2capPacket { - oneof channel_type { - uint32 psm = 1; - uint32 fixed_cid = 2; - } - bytes payload = 3; -} - -message SetEnableDynamicChannelRequest { - uint32 psm = 1; - bool enable = 2; - RetransmissionFlowControlMode retransmission_mode = 3; -} - -message DynamicChannelPacket { - blueberry.facade.BluetoothAddress remote = 1; - uint32 psm = 2; - bytes payload = 3; -} - -message SetTrafficPausedRequest { - bool paused = 1; - uint32 psm = 2; -} - -message GetChannelQueueDepthResponse { - uint32 size = 1; -} - -enum ClassicSecurityPolicy { - ENCRYPTED_TRANSPORT = 0; - AUTHENTICATED_ENCRYPTED_TRANSPORT = 1; - BEST = 2; - _SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK = 3; -} diff --git a/system/blueberry/facade/l2cap/le/facade.proto b/system/blueberry/facade/l2cap/le/facade.proto deleted file mode 100644 index 842fde3a6a..0000000000 --- a/system/blueberry/facade/l2cap/le/facade.proto +++ /dev/null @@ -1,85 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.l2cap.le; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service L2capLeModuleFacade { - rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {} - // Initiate a credit based connection request and block until response is received for up to some timeout (2s) - rpc OpenDynamicChannel(OpenDynamicChannelRequest) returns (OpenDynamicChannelResponse) {} - rpc CloseDynamicChannel(CloseDynamicChannelRequest) returns (google.protobuf.Empty) {} - rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {} - rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {} - rpc SetFixedChannel(SetEnableFixedChannelRequest) returns (google.protobuf.Empty) {} - rpc SendFixedChannelPacket(FixedChannelPacket) returns (google.protobuf.Empty) {} - rpc SendConnectionParameterUpdate(ConnectionParameter) returns (google.protobuf.Empty) {} -} - -message L2capPacket { - oneof channel_type { - uint32 psm = 1; - uint32 fixed_cid = 2; - } - bytes payload = 3; -} - -message DynamicChannelOpenEvent { - uint32 psm = 1; - uint32 connection_response_result = 2; -} - -message OpenDynamicChannelRequest { - blueberry.facade.BluetoothAddressWithType remote = 1; - uint32 psm = 2; -} - -message OpenDynamicChannelResponse { - uint32 status = 1; -} - -message CloseDynamicChannelRequest { - blueberry.facade.BluetoothAddressWithType remote = 1; - uint32 psm = 2; -} - -enum SecurityLevel { - NO_SECURITY = 0; - UNAUTHENTICATED_PAIRING_WITH_ENCRYPTION = 1; - AUTHENTICATED_PAIRING_WITH_ENCRYPTION = 2; - AUTHENTICATED_PAIRING_WITH_128_BIT_KEY = 3; - AUTHORIZATION = 4; -} - -message SetEnableDynamicChannelRequest { - uint32 psm = 1; - bool enable = 2; - SecurityLevel security_level = 3; -} - -message DynamicChannelPacket { - blueberry.facade.BluetoothAddressWithType remote = 1; - uint32 psm = 2; - bytes payload = 3; -} - -message SetEnableFixedChannelRequest { - uint32 cid = 1; - bool enable = 2; -} - -message FixedChannelPacket { - blueberry.facade.BluetoothAddressWithType remote = 1; - uint32 cid = 2; - bytes payload = 3; -} - -message ConnectionParameter { - uint32 conn_interval_min = 2; - uint32 conn_interval_max = 3; - uint32 conn_latency = 4; - uint32 supervision_timeout = 5; - uint32 min_ce_length = 6; - uint32 max_ce_length = 7; -} diff --git a/system/blueberry/facade/neighbor/facade.proto b/system/blueberry/facade/neighbor/facade.proto deleted file mode 100644 index 027855f031..0000000000 --- a/system/blueberry/facade/neighbor/facade.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade.neighbor; - -import "google/protobuf/empty.proto"; - -service NeighborFacade { - rpc EnablePageScan(EnableMsg) returns (google.protobuf.Empty) {} -} - -message EnableMsg { - bool enabled = 1; -} diff --git a/system/blueberry/facade/rootservice.proto b/system/blueberry/facade/rootservice.proto deleted file mode 100644 index 0b9b164218..0000000000 --- a/system/blueberry/facade/rootservice.proto +++ /dev/null @@ -1,34 +0,0 @@ -syntax = "proto3"; - -package blueberry.facade; - -import "google/protobuf/empty.proto"; -import "blueberry/facade/common.proto"; - -service RootFacade { - rpc StartStack(StartStackRequest) returns (StartStackResponse) {} - rpc StopStack(StopStackRequest) returns (StopStackResponse) {} -} - -enum BluetoothModule { - HAL = 0; - HCI = 1; - HCI_INTERFACES = 2; - L2CAP = 3; - SECURITY = 4; - SHIM = 5; -} - -message StartStackRequest { - BluetoothModule module_under_test = 1; -} - -message StartStackResponse {} - -message StopStackRequest {} - -message StopStackResponse {} - -service ReadOnlyProperty { - rpc ReadLocalAddress(google.protobuf.Empty) returns (blueberry.facade.BluetoothAddress) {} -} diff --git a/system/blueberry/facade/topshim/facade.proto b/system/blueberry/facade/topshim/facade.proto deleted file mode 100644 index c29891d49a..0000000000 --- a/system/blueberry/facade/topshim/facade.proto +++ /dev/null @@ -1,266 +0,0 @@ -syntax = "proto3"; - -import "google/protobuf/empty.proto"; - -package blueberry.facade.topshim; - -service AdapterService { - rpc FetchEvents(FetchEventsRequest) returns (stream FetchEventsResponse) {} - rpc ToggleStack(ToggleStackRequest) returns (ToggleStackResponse) {} - rpc SetDiscoveryMode(SetDiscoveryModeRequest) returns (google.protobuf.Empty) {} - rpc ClearEventFilter(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClearEventMask(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClearFilterAcceptList(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc DisconnectAllAcls(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc LeRand(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetEventFilterConnectionSetupAllDevices(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc AllowWakeByHid(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc RestoreFilterAcceptList(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetDefaultEventMaskExcept(SetDefaultEventMaskExceptRequest) returns (google.protobuf.Empty) {} - rpc SetEventFilterInquiryResultAllDevices(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetLocalIoCaps(SetLocalIoCapsRequest) returns (SetLocalIoCapsResponse) {} - rpc ToggleDiscovery(ToggleDiscoveryRequest) returns (ToggleDiscoveryResponse) {} -} - -service SecurityService { - rpc RemoveBond(RemoveBondRequest) returns (google.protobuf.Empty) {} - rpc GenerateLocalOobData(GenerateOobDataRequest) returns (google.protobuf.Empty) {} - rpc CreateBond(CreateBondRequest) returns (CreateBondResponse) {} -} - -service GattService { - // Advertiser - rpc RegisterAdvertiser(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterAdvertiser(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetOwnAddress(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetParameters(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetData(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc AdvertisingEnable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc AdvertisingDisable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetPeriodicAdvertisingParameters(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetPeriodicAdvertisingData(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetPeriodicAdvertisingEnable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StartAdvertising(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StartAdvertisingSet(google.protobuf.Empty) returns (google.protobuf.Empty) {} - - // Scanner - rpc RegisterScanner(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterScanner(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StartScan(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StopScan(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScanFilterSetup(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScanFilterAdd(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScanFilterClear(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScanFilterEnable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ScanFilterDisable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SetScanParameters(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc BatchScanConfigStorage(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc BatchScanEnable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc BatchScanDisable(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc BatchScanReadReports(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StartSync(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StopSync(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc CancelCreateSync(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc TransferSync(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc TransferSetInfo(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SyncTxParameters(google.protobuf.Empty) returns (google.protobuf.Empty) {} - - // Gatt Client - rpc RegisterClient(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterClient(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClientConnect(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClientDisconnect(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc Refresh(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SearchService(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc BtifGattcDiscoverServiceByUuid(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ReadCharacteristic(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ReadUsingCharacteristicUuid(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc WriteCharacteristic(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ReadDescriptor(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc WriteDescriptor(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ExecuteWrite(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc RegisterForNotification(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc DeregisterForNotification(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ReadRemoteRssi(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetDeviceType(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ConfigureMtu(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ConnParameterUpdate(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClientSetPreferredPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ClientReadPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc TestCommand(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc GetGattDb(google.protobuf.Empty) returns (google.protobuf.Empty) {} - - // Gatt Server - rpc RegisterServer(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc UnregisterServer(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ServerConnect(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ServerDisconnect(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc AddService(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc StopService(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc DeleteService(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SendIndication(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc SendResponse(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ServerSetPreferredPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} - rpc ServerReadPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} -} - -service HfpService { - rpc StartSlc(StartSlcRequest) returns (google.protobuf.Empty) {} - rpc StopSlc(StopSlcRequest) returns (google.protobuf.Empty) {} - rpc ConnectAudio(ConnectAudioRequest) returns (google.protobuf.Empty) {} - rpc DisconnectAudio(DisconnectAudioRequest) returns (google.protobuf.Empty) {} - rpc SetVolume(SetVolumeRequest) returns (google.protobuf.Empty) {} - rpc FetchEvents(FetchEventsRequest) returns (stream FetchEventsResponse) {} -} - -service HfClientService { - rpc StartSlc(StartSlcRequest) returns (StartSlcResponse) {} - rpc StopSlc(StopSlcRequest) returns (StopSlcResponse) {} - rpc ConnectAudio(ConnectAudioRequest) returns (ConnectAudioResponse) {} - rpc DisconnectAudio(DisconnectAudioRequest) returns (DisconnectAudioResponse) {} -} - -enum EventType { - ADAPTER_STATE = 0; - SSP_REQUEST = 1; - LE_RAND = 2; - GENERATE_LOCAL_OOB_DATA = 3; - HFP_CONNECTION_STATE = 4; - ADAPTER_PROPERTY = 5; - DISCOVERY_STATE = 6; - DEVICE_FOUND = 7; - BOND_STATE = 8; -} - -message FetchEventsRequest {} - -message FetchEventsResponse { - EventType event_type = 1; - string data = 2; - // Storing all event related data as a key-value pair. - map<string, EventData> params = 3; -} - -message EventData { - repeated string data = 1; -} - -message ToggleStackRequest { - bool start_stack = 1; // True for enable; false for disable -} - -message ToggleStackResponse {} - -message SetDiscoveryModeRequest { - bool enable_page_scan = 1; - bool enable_inquiry_scan = 2; -} - -service MediaService { - rpc StartA2dp(StartA2dpRequest) returns (StartA2dpResponse) {} - rpc A2dpSourceConnect(A2dpSourceConnectRequest) returns (A2dpSourceConnectResponse) {} - rpc A2dpSourceSetActiveDevice(A2dpSourceSetActiveDeviceRequest) returns (A2dpSourceSetActiveDevicetResponse) {} -} - -message StartA2dpRequest { - bool start_a2dp_source = 1; - bool start_a2dp_sink = 2; -} - -message StartA2dpResponse {} - -message A2dpSourceConnectRequest { - string address = 1; -} - -message A2dpSourceConnectResponse {} - -message A2dpSourceSetActiveDeviceRequest { - string address = 1; -} - -message A2dpSourceSetActiveDevicetResponse {} - -message RemoveBondRequest { - string address = 1; -} - -message StartSlcRequest { - Connection connection = 1; -} - -message StartSlcResponse { - int32 status = 1; -} - -message StopSlcRequest { - Connection connection = 1; -} - -message StopSlcResponse { - int32 status = 1; -} - -message ConnectAudioRequest { - Connection connection = 1; - bool is_sco_offload_enabled = 2; - int32 disabled_codecs = 3; -} - -message ConnectAudioResponse { - int32 status = 1; -} - -message DisconnectAudioRequest { - Connection connection = 1; -} - -message DisconnectAudioResponse { - int32 status = 1; -} - -message SetVolumeRequest { - Connection connection = 1; - int32 volume = 2; -} - -message SetDefaultEventMaskExceptRequest { - uint64 mask = 1; - uint64 le_mask = 2; -} - -// A Token representing an ACL connection. -// It's acquired via a Connect on the Host service (Bluetooth Core stack in our case). -message Connection { -// For our HFP APIs this would store the bluetooth address but staying consistent with Pandora naming. - bytes cookie = 1; -} - -message GenerateOobDataRequest { - int32 transport = 1; -} - -message SetLocalIoCapsRequest { - int32 io_capability = 1; -} - -message SetLocalIoCapsResponse { - int32 status = 1; -} - -message ToggleDiscoveryRequest { - bool is_start = 1; -} - -message ToggleDiscoveryResponse { - int32 status = 1; -} - -message CreateBondRequest { - string address = 1; - int32 transport = 2; -} - -message CreateBondResponse { - int32 status = 1; -} diff --git a/system/blueberry/tests/gd/cert/adb.py b/system/blueberry/tests/gd/cert/adb.py deleted file mode 100644 index d28c24dfb2..0000000000 --- a/system/blueberry/tests/gd/cert/adb.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2016 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import encodings -import logging -import shlex -import shutil - -from mobly.controllers.android_device_lib.adb import AdbError -from mobly.controllers.android_device_lib.adb import AdbProxy - -ROOT_USER_ID = '0' -SHELL_USER_ID = '2000' -UTF_8 = encodings.utf_8.getregentry().name - - -class BlueberryAdbProxy(AdbProxy): - """Proxy class for ADB. - - For syntactic reasons, the '-' in adb commands need to be replaced with - '_'. Can directly execute adb commands on an object: - >> adb = BlueberryAdbProxy(<serial>) - >> adb.start_server() - >> adb.devices() # will return the console output of "adb devices". - """ - - def __init__(self, serial="", ssh_connection=None): - """Construct an instance of AdbProxy. - - Args: - serial: str serial number of Android device from `adb devices` - ssh_connection: SshConnection instance if the Android device is - connected to a remote host that we can reach via SSH. - """ - super().__init__(serial) - self._server_local_port = None - adb_path = shutil.which('adb') - adb_cmd = [shlex.quote(adb_path)] - if serial: - adb_cmd.append("-s %s" % serial) - if ssh_connection is not None: - # Kill all existing adb processes on the remote host (if any) - # Note that if there are none, then pkill exits with non-zero status - ssh_connection.run("pkill adb", ignore_status=True) - # Copy over the adb binary to a temp dir - temp_dir = ssh_connection.run("mktemp -d").stdout.strip() - ssh_connection.send_file(adb_path, temp_dir) - # Start up a new adb server running as root from the copied binary. - remote_adb_cmd = "%s/adb %s root" % (temp_dir, "-s %s" % serial if serial else "") - ssh_connection.run(remote_adb_cmd) - # Proxy a local port to the adb server port - local_port = ssh_connection.create_ssh_tunnel(5037) - self._server_local_port = local_port - - if self._server_local_port: - adb_cmd.append("-P %d" % local_port) - self.adb_str = " ".join(adb_cmd) - self._ssh_connection = ssh_connection - - def get_user_id(self): - """Returns the adb user. Either 2000 (shell) or 0 (root).""" - return self.shell('id -u').decode(UTF_8).rstrip() - - def is_root(self, user_id=None): - """Checks if the user is root. - - Args: - user_id: if supplied, the id to check against. - Returns: - True if the user is root. False otherwise. - """ - if not user_id: - user_id = self.get_user_id() - return user_id == ROOT_USER_ID - - def ensure_root(self): - """Ensures the user is root after making this call. - - Note that this will still fail if the device is a user build, as root - is not accessible from a user build. - - Returns: - False if the device is a user build. True otherwise. - """ - self.ensure_user(ROOT_USER_ID) - return self.is_root() - - def ensure_user(self, user_id=SHELL_USER_ID): - """Ensures the user is set to the given user. - - Args: - user_id: The id of the user. - """ - if self.is_root(user_id): - self.root() - else: - self.unroot() - self.wait_for_device() - return self.get_user_id() == user_id - - def tcp_forward(self, host_port, device_port): - """Starts tcp forwarding from localhost to this android device. - - Args: - host_port: Port number to use on localhost - device_port: Port number to use on the android device. - - Returns: - Forwarded port on host as int or command output string on error - """ - if self._ssh_connection: - # We have to hop through a remote host first. - # 1) Find some free port on the remote host's localhost - # 2) Setup forwarding between that remote port and the requested - # device port - remote_port = self._ssh_connection.find_free_port() - host_port = self._ssh_connection.create_ssh_tunnel(remote_port, local_port=host_port) - try: - output = self.forward(["tcp:%d" % host_port, "tcp:%d" % device_port]) - except AdbError as error: - return error - # If hinted_port is 0, the output will be the selected port. - # Otherwise, there will be no output upon successfully - # forwarding the hinted port. - if not output: - return host_port - try: - output_int = int(output) - except ValueError: - return output - return output_int - - def remove_tcp_forward(self, host_port): - """Stop tcp forwarding a port from localhost to this android device. - - Args: - host_port: Port number to use on localhost - """ - if self._ssh_connection: - remote_port = self._ssh_connection.close_ssh_tunnel(host_port) - if remote_port is None: - logging.warning("Cannot close unknown forwarded tcp port: %d", host_port) - return - # The actual port we need to disable via adb is on the remote host. - host_port = remote_port - self.forward(["--remove", "tcp:%d" % host_port]) - - def path_exists(self, path): - """Check if a file path exists on an Android device - - :param path: file path, could be a directory - :return: True if file path exists - """ - try: - ret = self.shell("ls {}".format(path)) - if ret is not None and len(ret) > 0: - return True - else: - return False - except AdbError as e: - logging.debug("path {} does not exist, error={}".format(path, e)) - return False diff --git a/system/blueberry/tests/gd/cert/asserts.py b/system/blueberry/tests/gd/cert/asserts.py deleted file mode 100644 index 0be88498bd..0000000000 --- a/system/blueberry/tests/gd/cert/asserts.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2018 - 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. - -from mobly.asserts import * - - -# Have an instance of unittest.TestCase so we could reuse some logic from -# python's own unittest. -# _ProxyTest is required because py2 does not allow instantiating -# unittest.TestCase directly. -class _ProxyTest(unittest.TestCase): - - def runTest(self): - pass - - -_pyunit_proxy = _ProxyTest() - - -def assert_almost_equal(first, second, places=7, msg=None, delta=None, extras=None): - """ - Assert FIRST to be within +/- DELTA to SECOND, otherwise fail the - test. - :param first: The first argument, LHS - :param second: The second argument, RHS - :param places: For floating points, how many decimal places to look into - :param msg: Message to display on failure - :param delta: The +/- first and second could be apart from each other - :param extras: Extra object passed to test failure handler - :return: - """ - my_msg = None - try: - if delta: - _pyunit_proxy.assertAlmostEqual(first, second, msg=msg, delta=delta) - else: - _pyunit_proxy.assertAlmostEqual(first, second, places=places, msg=msg) - except Exception as e: - my_msg = str(e) - if msg: - my_msg = "%s %s" % (my_msg, msg) - # This is a hack to remove the stacktrace produced by the above exception. - if my_msg is not None: - fail(my_msg, extras=extras) diff --git a/system/blueberry/tests/gd/cert/async_subprocess_logger.py b/system/blueberry/tests/gd/cert/async_subprocess_logger.py deleted file mode 100644 index 79a85f7984..0000000000 --- a/system/blueberry/tests/gd/cert/async_subprocess_logger.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import concurrent.futures -import logging -import re -import subprocess -from blueberry.tests.gd.cert.os_utils import TerminalColor -from contextlib import ExitStack - - -class AsyncSubprocessLogger: - """ - An asynchronous logger for subprocesses.Popen object's STDOUT - - Contains threading functionality that allows asynchronous handling of lines - from STDOUT from subprocess.Popen - """ - WAIT_TIMEOUT_SECONDS = 10 - PROCESS_TAG_MIN_WIDTH = 24 - - def __init__(self, - process: subprocess.Popen, - log_file_paths, - log_to_stdout=False, - tag=None, - color: TerminalColor = None): - """ - :param process: a subprocess.Popen object with STDOUT - :param log_file_paths: list of log files to redirect log to - :param log_to_stdout: whether to dump logs to stdout in the format of - "[tag] logline" - :param tag: tag to be used in above format - :param color: when dumping to stdout, what color to use for tag - """ - if not process: - raise ValueError("process cannot be None") - if not process.stdout: - raise ValueError("process.stdout cannot be None") - if log_to_stdout: - if not tag or type(tag) is not str: - raise ValueError("When logging to stdout, log tag must be set") - self.log_file_paths = log_file_paths - self.log_to_stdout = log_to_stdout - self.tag = tag - self.color = color - self.process = process - self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) - self.future = self.executor.submit(self.__logging_loop) - - def stop(self): - """ - Stop this logger and this object can no longer be used after this call - """ - try: - result = self.future.result(timeout=self.WAIT_TIMEOUT_SECONDS) - if result: - logging.error("logging thread %s produced an error when executing: %s" % (self.tag, str(result))) - except concurrent.futures.TimeoutError: - logging.error("logging thread %s failed to finish after %d seconds" % (self.tag, self.WAIT_TIMEOUT_SECONDS)) - self.executor.shutdown(wait=False) - - def __logging_loop(self): - if self.color: - loggableTag = "[%s%s%s]" % (self.color, self.tag, TerminalColor.END) - else: - loggableTag = "[%s]" % self.tag - tagLength = len(re.sub('[^\w\s]', '', loggableTag)) - if tagLength < self.PROCESS_TAG_MIN_WIDTH: - loggableTag += " " * (self.PROCESS_TAG_MIN_WIDTH - tagLength) - with ExitStack() as stack: - log_files = [stack.enter_context(open(file_path, 'w')) for file_path in self.log_file_paths] - for line in self.process.stdout: - for log_file in log_files: - log_file.write(line) - if self.log_to_stdout: - print("{}{}".format(loggableTag, line.strip())) diff --git a/system/blueberry/tests/gd/cert/behavior.py b/system/blueberry/tests/gd/cert/behavior.py deleted file mode 100644 index 10fc494a73..0000000000 --- a/system/blueberry/tests/gd/cert/behavior.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from abc import ABC, abstractmethod -from datetime import datetime, timedelta -from mobly import signals -from threading import Condition - -from blueberry.tests.gd.cert.event_stream import static_remaining_time_delta -from blueberry.tests.gd.cert.truth import assertThat - - -class IHasBehaviors(ABC): - - @abstractmethod - def get_behaviors(self): - pass - - -def anything(): - return lambda obj: True - - -def when(has_behaviors): - assertThat(isinstance(has_behaviors, IHasBehaviors)).isTrue() - return has_behaviors.get_behaviors() - - -def IGNORE_UNHANDLED(obj): - pass - - -class SingleArgumentBehavior(object): - - def __init__(self, reply_stage_factory): - self._reply_stage_factory = reply_stage_factory - self._instances = [] - self._invoked_obj = [] - self._invoked_condition = Condition() - self.set_default_to_crash() - - def begin(self, matcher): - return PersistenceStage(self, matcher, self._reply_stage_factory) - - def append(self, behavior_instance): - self._instances.append(behavior_instance) - - def set_default(self, fn): - assertThat(fn).isNotNone() - self._default_fn = fn - - def set_default_to_crash(self): - self._default_fn = None - - def set_default_to_ignore(self): - self._default_fn = IGNORE_UNHANDLED - - def run(self, obj): - for instance in self._instances: - if instance.try_run(obj): - self.__obj_invoked(obj) - return - if self._default_fn is not None: - # IGNORE_UNHANDLED is also a default fn - self._default_fn(obj) - self.__obj_invoked(obj) - else: - raise signals.TestFailure( - "%s: behavior for %s went unhandled" % (self._reply_stage_factory().__class__.__name__, obj), - extras=None) - - def __obj_invoked(self, obj): - self._invoked_condition.acquire() - self._invoked_obj.append(obj) - self._invoked_condition.notify() - self._invoked_condition.release() - - def wait_until_invoked(self, matcher, times, timeout): - end_time = datetime.now() + timeout - invoked_times = 0 - while datetime.now() < end_time and invoked_times < times: - remaining = static_remaining_time_delta(end_time) - invoked_times = sum((matcher(i) for i in self._invoked_obj)) - self._invoked_condition.acquire() - self._invoked_condition.wait(remaining.total_seconds()) - self._invoked_condition.release() - return invoked_times == times - - -class PersistenceStage(object): - - def __init__(self, behavior, matcher, reply_stage_factory): - self._behavior = behavior - self._matcher = matcher - self._reply_stage_factory = reply_stage_factory - - def then(self, times=1): - reply_stage = self._reply_stage_factory() - reply_stage.init(self._behavior, self._matcher, times) - return reply_stage - - def always(self): - return self.then(times=-1) - - -class ReplyStage(object): - - def init(self, behavior, matcher, persistence): - self._behavior = behavior - self._matcher = matcher - self._persistence = persistence - - def _commit(self, fn): - self._behavior.append(BehaviorInstance(self._matcher, self._persistence, fn)) - - -class BehaviorInstance(object): - - def __init__(self, matcher, persistence, fn): - self._matcher = matcher - self._persistence = persistence - self._fn = fn - self._called_count = 0 - - def try_run(self, obj): - if not self._matcher(obj): - return False - if self._persistence >= 0: - if self._called_count >= self._persistence: - return False - self._called_count += 1 - self._fn(obj) - return True - - -class BoundVerificationStage(object): - - def __init__(self, behavior, matcher, timeout): - self._behavior = behavior - self._matcher = matcher - self._timeout = timeout - - def times(self, times=1): - return self._behavior.wait_until_invoked(self._matcher, times, self._timeout) - - -class WaitForBehaviorSubject(object): - - def __init__(self, behaviors, timeout): - self._behaviors = behaviors - self._timeout = timeout - - def __getattr__(self, item): - behavior = getattr(self._behaviors, item + "_behavior") - t = self._timeout - return lambda matcher: BoundVerificationStage(behavior, matcher, t) - - -def wait_until(i_has_behaviors, timeout=timedelta(seconds=3)): - return WaitForBehaviorSubject(i_has_behaviors.get_behaviors(), timeout) diff --git a/system/blueberry/tests/gd/cert/capture.py b/system/blueberry/tests/gd/cert/capture.py deleted file mode 100644 index e37dc7dbbc..0000000000 --- a/system/blueberry/tests/gd/cert/capture.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class Capture(object): - """ - Wrap a match function and use in its place, to capture the value - that matched. Specify an optional |capture_fn| to transform the - captured value. - """ - - def __init__(self, match_fn, capture_fn=None): - self._match_fn = match_fn - self._capture_fn = capture_fn - self._value = None - - def __call__(self, obj): - if self._match_fn(obj) != True: - return False - - if self._capture_fn is not None: - self._value = self._capture_fn(obj) - else: - self._value = obj - return True - - def get(self): - return self._value diff --git a/system/blueberry/tests/gd/cert/captures.py b/system/blueberry/tests/gd/cert/captures.py deleted file mode 100644 index 7358ad1e70..0000000000 --- a/system/blueberry/tests/gd/cert/captures.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from blueberry.tests.gd.cert.capture import Capture -from blueberry.tests.gd.cert.matchers import HciMatchers -from blueberry.tests.gd.cert.matchers import SecurityMatchers -import hci_packets as hci - - -class HalCaptures(object): - - @staticmethod - def ReadBdAddrCompleteCapture(): - return Capture(lambda packet: packet.payload[0:5] == b'\x0e\x0a\x01\x09\x10', - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def ConnectionRequestCapture(): - return Capture(lambda packet: packet.payload[0:2] == b'\x04\x0a', - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def ConnectionCompleteCapture(): - return Capture(lambda packet: packet.payload[0:3] == b'\x03\x0b\x00', - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def DisconnectionCompleteCapture(): - return Capture(lambda packet: packet.payload[0:2] == b'\x05\x04', - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def LeConnectionCompleteCapture(): - return Capture( - lambda packet: packet.payload[0] == 0x3e and (packet.payload[2] == 0x01 or packet.payload[2] == 0x0a), - lambda packet: hci.Event.parse_all(packet.payload)) - - -class HciCaptures(object): - - @staticmethod - def ReadLocalOobDataCompleteCapture(): - return Capture( - HciMatchers.CommandComplete(hci.OpCode.READ_LOCAL_OOB_DATA), - lambda packet: HciMatchers.ExtractMatchingCommandComplete(packet.payload, hci.OpCode.READ_LOCAL_OOB_DATA)) - - @staticmethod - def ReadLocalOobExtendedDataCompleteCapture(): - return Capture( - HciMatchers.CommandComplete(hci.OpCode.READ_LOCAL_OOB_EXTENDED_DATA), lambda packet: HciMatchers. - ExtractMatchingCommandComplete(packet.payload, hci.OpCode.READ_LOCAL_OOB_EXTENDED_DATA)) - - @staticmethod - def ReadBdAddrCompleteCapture(): - return Capture(HciMatchers.CommandComplete(hci.OpCode.READ_BD_ADDR), - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def ConnectionRequestCapture(): - return Capture(HciMatchers.EventWithCode(hci.EventCode.CONNECTION_REQUEST), - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def ConnectionCompleteCapture(): - return Capture(HciMatchers.EventWithCode(hci.EventCode.CONNECTION_COMPLETE), - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def DisconnectionCompleteCapture(): - return Capture(HciMatchers.EventWithCode(hci.EventCode.DISCONNECTION_COMPLETE), - lambda packet: hci.Event.parse_all(packet.payload)) - - @staticmethod - def LeConnectionCompleteCapture(): - return Capture(HciMatchers.LeConnectionComplete(), - lambda packet: HciMatchers.ExtractLeConnectionComplete(packet.payload)) - - @staticmethod - def SimplePairingCompleteCapture(): - return Capture(HciMatchers.EventWithCode(hci.EventCode.SIMPLE_PAIRING_COMPLETE), - lambda packet: hci.Event.parse_all(packet.payload)) diff --git a/system/blueberry/tests/gd/cert/cert_self_test.py b/system/blueberry/tests/gd/cert/cert_self_test.py deleted file mode 100644 index 5ccd8f9ecc..0000000000 --- a/system/blueberry/tests/gd/cert/cert_self_test.py +++ /dev/null @@ -1,659 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import time -import traceback -from datetime import datetime, timedelta -from threading import Timer - -from blueberry.tests.gd.cert.behavior import when, wait_until -from blueberry.tests.gd.cert.behavior import IHasBehaviors -from blueberry.tests.gd.cert.behavior import anything -from blueberry.tests.gd.cert.behavior import SingleArgumentBehavior -from blueberry.tests.gd.cert.behavior import ReplyStage -from blueberry.tests.gd.cert.event_stream import EventStream, FilteringEventStream -from blueberry.tests.gd.cert.metadata import metadata -from blueberry.tests.gd.cert.truth import assertThat -import hci_packets as hci - -from mobly import asserts -from mobly import signals -from mobly import test_runner -from mobly import base_test - - -class BogusProto: - - class BogusType: - - def __init__(self): - self.name = "BogusProto" - self.is_extension = False - self.cpp_type = False - - def type(self): - return 'BogusRpc' - - def label(self): - return "label" - - class BogusDescriptor: - - def __init__(self, name): - self.full_name = name - - def __init__(self, value): - self.value_ = value - self.DESCRIPTOR = BogusProto.BogusDescriptor(str(value)) - - def __str__(self): - return "BogusRpc value = " + str(self.value_) - - def ListFields(self): - for field in [BogusProto.BogusType()]: - yield [field, self.value_] - - -class FetchEvents: - - def __init__(self, events, delay_ms): - self.events_ = events - self.sleep_time_ = (delay_ms * 1.0) / 1000 - self.index_ = 0 - self.done_ = False - self.then_ = datetime.now() - - def __iter__(self): - for event in self.events_: - time.sleep(self.sleep_time_) - if self.done_: - return - logging.debug("yielding %d" % event) - yield BogusProto(event) - - def done(self): - return self.done_ - - def cancel(self): - logging.debug("cancel") - self.done_ = True - return None - - -class TestBehaviors(object): - - def __init__(self, parent): - self.test_request_behavior = SingleArgumentBehavior(lambda: TestBehaviors.TestRequestReplyStage(parent)) - - def test_request(self, matcher): - return self.test_request_behavior.begin(matcher) - - class TestRequestReplyStage(ReplyStage): - - def __init__(self, parent): - self._parent = parent - - def increment_count(self): - self._commit(lambda obj: self._increment_count(obj)) - return self - - def _increment_count(self, obj): - self._parent.count += 1 - self._parent.captured.append(obj) - - -class ObjectWithBehaviors(IHasBehaviors): - - def __init__(self): - self.behaviors = TestBehaviors(self) - self.count = 0 - self.captured = [] - self.unhandled_count = 0 - - def get_behaviors(self): - return self.behaviors - - def increment_unhandled(self): - self.unhandled_count += 1 - - -class CertSelfTest(base_test.BaseTestClass): - - def setup_test(self): - return True - - def teardown_test(self): - return True - - def test_assert_occurs_at_least_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3, 1, 2, 3], delay_ms=40)) as event_stream: - event_stream.assert_event_occurs(lambda data: data.value_ == 1, - timeout=timedelta(milliseconds=300), - at_least_times=2) - - def test_assert_occurs_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - event_stream.assert_event_occurs(lambda data: data.value_ == 1, timeout=timedelta(seconds=1)) - - def test_assert_occurs_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - event_stream.assert_event_occurs(lambda data: data.value_ == 4, timeout=timedelta(seconds=1)) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assert_occurs_at_most_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream: - event_stream.assert_event_occurs_at_most(lambda data: data.value_ < 4, - timeout=timedelta(seconds=1), - at_most_times=3) - - def test_assert_occurs_at_most_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream: - event_stream.assert_event_occurs_at_most(lambda data: data.value_ > 1, - timeout=timedelta(seconds=1), - at_most_times=2) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_skip_a_test(self): - asserts.skip("Skipping this test because it's blocked by b/xyz") - assert False - - def test_nested_packets(self): - handle = 123 - inside = hci.ReadScanEnable() - logging.debug(inside.serialize()) - logging.debug("building outside") - outside = hci.Acl(handle=handle, - packet_boundary_flag=hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, - broadcast_flag=hci.BroadcastFlag.POINT_TO_POINT, - payload=inside.serialize()) - logging.debug(outside.serialize()) - logging.debug("Done!") - - def test_assertThat_boolean_success(self): - assertThat(True).isTrue() - assertThat(False).isFalse() - - def test_assertThat_boolean_falseIsTrue(self): - try: - assertThat(False).isTrue() - except Exception as e: - return True - return False - - def test_assertThat_boolean_trueIsFalse(self): - try: - assertThat(True).isFalse() - except Exception as e: - return True - return False - - def test_assertThat_object_success(self): - assertThat("this").isEqualTo("this") - assertThat("this").isNotEqualTo("that") - assertThat(None).isNone() - assertThat("this").isNotNone() - - def test_assertThat_object_isEqualToFails(self): - try: - assertThat("this").isEqualTo("that") - except Exception as e: - return True - return False - - def test_assertThat_object_isNotEqualToFails(self): - try: - assertThat("this").isNotEqualTo("this") - except Exception as e: - return True - return False - - def test_assertThat_object_isNoneFails(self): - try: - assertThat("this").isNone() - except Exception as e: - return True - return False - - def test_assertThat_object_isNotNoneFails(self): - try: - assertThat(None).isNotNone() - except Exception as e: - return True - return False - - def test_assertThat_eventStream_emits_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 1) - - def test_assertThat_eventStream_emits_then_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 1).then(lambda data: data.value_ == 3) - - def test_assertThat_eventStream_emits_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 4) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assertThat_eventStream_emits_then_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 1).emits(lambda data: data.value_ == 4) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assertThat_eventStream_emitsInOrder_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 1, lambda data: data.value_ == 2).inOrder() - - def test_assertThat_eventStream_emitsInAnyOrder_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits( - lambda data: data.value_ == 2, - lambda data: data.value_ == 1).inAnyOrder().then(lambda data: data.value_ == 3) - - def test_assertThat_eventStream_emitsInOrder_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 2, lambda data: data.value_ == 1).inOrder() - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assertThat_eventStream_emitsInAnyOrder_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emits(lambda data: data.value_ == 4, - lambda data: data.value_ == 1).inAnyOrder() - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assertThat_emitsNone_passes(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emitsNone(lambda data: data.value_ == 4, timeout=timedelta(seconds=0.15)).thenNone( - lambda data: data.value_ == 5, timeout=timedelta(seconds=0.15)) - - def test_assertThat_emitsNone_passes_after_1_second(self): - with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=400)) as event_stream: - assertThat(event_stream).emitsNone(lambda data: data.value_ == 4, timeout=timedelta(seconds=1)) - - def test_assertThat_emitsNone_fails(self): - try: - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - assertThat(event_stream).emitsNone(lambda data: data.value_ == 2, timeout=timedelta(seconds=1)) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_assertThat_emitsNone_zero_passes(self): - with EventStream(FetchEvents(events=[], delay_ms=50)) as event_stream: - assertThat(event_stream).emitsNone(timeout=timedelta(milliseconds=10)).thenNone(timeout=timedelta( - milliseconds=10)) - - def test_assertThat_emitsNone_zero_passes_after_one_second(self): - with EventStream(FetchEvents([1], delay_ms=1500)) as event_stream: - assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1.0)) - - def test_assertThat_emitsNone_zero_fails(self): - try: - with EventStream(FetchEvents(events=[17], delay_ms=50)) as event_stream: - assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1)) - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_filtering_event_stream_none_filter_function(self): - with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: - filtered_event_stream = FilteringEventStream(event_stream, None) - assertThat(filtered_event_stream) \ - .emits(lambda data: data.value_ == 1) \ - .then(lambda data: data.value_ == 3) - - def test_metadata_empty(self): - - @metadata() - def simple_pass_test(arg): - pass - - try: - simple_pass_test(1) - except signals.TestFailure: - pass - except Exception as e: - asserts.fail("@metadata() should only raise signals.TestFailure, " - "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) - else: - asserts.fail("@metadata() should not work") - - def test_metadata_empty_no_function_call(self): - - @metadata - def simple_pass_test(arg): - pass - - try: - simple_pass_test(1) - except signals.TestFailure: - pass - except Exception as e: - asserts.fail("@metadata should only raise signals.TestFailure, " - "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) - else: - asserts.fail("@metadata should not work") - - def test_metadata_pts_missing_id(self): - - @metadata(pts_test_name="Hello world") - def simple_pass_test(arg): - pass - - try: - simple_pass_test(1) - except signals.TestFailure: - pass - except Exception as e: - asserts.fail("should only raise signals.TestFailure, " - "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) - else: - asserts.fail("missing pts_test_id should not work") - - def test_metadata_pts_missing_name(self): - - @metadata(pts_test_id="A/B/C") - def simple_pass_test(arg): - pass - - try: - simple_pass_test(1) - except signals.TestFailure: - pass - except Exception as e: - asserts.fail("should only raise signals.TestFailure, " - "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) - else: - asserts.fail("missing pts_test_name should not work") - - def test_metadata_pts_test_id_and_description(self): - - @metadata(pts_test_id="A/B/C", pts_test_name="Hello world") - def simple_pass_test(arg): - pass - - try: - simple_pass_test(1) - except signals.TestPass as e: - asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras))) - asserts.assert_equal(e.extras["pts_test_id"], "A/B/C") - asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras))) - asserts.assert_equal(e.extras["pts_test_name"], "Hello world") - else: - asserts.fail("Must throw an exception using @metadata decorator") - - def test_metadata_test_with_exception_stacktrace(self): - - @metadata(pts_test_id="A/B/C", pts_test_name="Hello world") - def simple_fail_test(failure_argument): - raise ValueError(failure_argument) - - try: - simple_fail_test("BEEFBEEF") - except signals.TestError as e: - asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras))) - asserts.assert_equal(e.extras["pts_test_id"], "A/B/C") - asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras))) - asserts.assert_equal(e.extras["pts_test_name"], "Hello world") - trace_str = traceback.format_exc() - asserts.assert_true("raise ValueError(failure_argument)" in trace_str, - msg="Failed test method not in error stack trace: %s" % trace_str) - else: - asserts.fail("Must throw an exception using @metadata decorator") - - def test_fluent_behavior_simple(self): - thing = ObjectWithBehaviors() - - when(thing).test_request(anything()).then().increment_count() - - thing.behaviors.test_request_behavior.run("A") - - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["A"]) - - def test_fluent_behavior__then_single__captures_one(self): - thing = ObjectWithBehaviors() - - thing.behaviors.test_request_behavior.set_default_to_ignore() - - when(thing).test_request(anything()).then().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("A") - - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["A"]) - - def test_fluent_behavior__then_times__captures_all(self): - thing = ObjectWithBehaviors() - - when(thing).test_request(anything()).then(times=3).increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("C") - - assertThat(thing.count).isEqualTo(3) - assertThat(thing.captured).isEqualTo(["A", "B", "C"]) - - def test_fluent_behavior__always__captures_all(self): - thing = ObjectWithBehaviors() - - when(thing).test_request(anything()).always().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("C") - - assertThat(thing.count).isEqualTo(3) - assertThat(thing.captured).isEqualTo(["A", "B", "C"]) - - def test_fluent_behavior__matcher__captures_relevant(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default_to_ignore() - - when(thing).test_request(lambda obj: obj == "B").always().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("C") - - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["B"]) - - def test_fluent_behavior__then_repeated__captures_relevant(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default_to_ignore() - - when(thing).test_request(anything()).then().increment_count().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("A") - - assertThat(thing.count).isEqualTo(2) - assertThat(thing.captured).isEqualTo(["A", "B"]) - - def test_fluent_behavior__fallback__captures_relevant(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default_to_ignore() - - when(thing).test_request(lambda obj: obj == "B").then(times=1).increment_count() - when(thing).test_request(lambda obj: obj == "C").always().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("C") - thing.behaviors.test_request_behavior.run("B") - thing.behaviors.test_request_behavior.run("C") - - assertThat(thing.count).isEqualTo(3) - assertThat(thing.captured).isEqualTo(["B", "C", "C"]) - - def test_fluent_behavior__default_unhandled_crash(self): - thing = ObjectWithBehaviors() - - when(thing).test_request(anything()).then().increment_count() - - thing.behaviors.test_request_behavior.run("A") - try: - thing.behaviors.test_request_behavior.run("A") - except Exception as e: - logging.debug(e) - return True # Failed as expected - return False - - def test_fluent_behavior__set_default_works(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled()) - - when(thing).test_request(anything()).then().increment_count() - - thing.behaviors.test_request_behavior.run("A") - thing.behaviors.test_request_behavior.run("A") - assertThat(thing.unhandled_count).isEqualTo(1) - - def test_fluent_behavior__wait_until_done(self): - thing = ObjectWithBehaviors() - is_a = lambda obj: obj == "A" - when(thing).test_request(is_a).then().increment_count() - - closure = lambda: thing.behaviors.test_request_behavior.run("A") - t = Timer(0.5, closure) - t.start() - - wait_until(thing).test_request(is_a).times(1) - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["A"]) - - def test_fluent_behavior__wait_until_done_different_lambda(self): - thing = ObjectWithBehaviors() - when(thing).test_request(lambda obj: obj == "A").then().increment_count() - - closure = lambda: thing.behaviors.test_request_behavior.run("A") - t = Timer(0.5, closure) - t.start() - - wait_until(thing).test_request(lambda obj: obj == "A").times(1) - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["A"]) - - def test_fluent_behavior__wait_until_done_anything(self): - thing = ObjectWithBehaviors() - when(thing).test_request(lambda obj: obj == "A").then().increment_count() - - closure = lambda: thing.behaviors.test_request_behavior.run("A") - t = Timer(0.5, closure) - t.start() - - wait_until(thing).test_request(anything()).times(1) - assertThat(thing.count).isEqualTo(1) - assertThat(thing.captured).isEqualTo(["A"]) - - def test_fluent_behavior__wait_until_done_not_happened(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default_to_ignore() - when(thing).test_request(lambda obj: obj == "A").then().increment_count() - - closure = lambda: thing.behaviors.test_request_behavior.run("B") - t = Timer(0.5, closure) - t.start() - assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(1)).isFalse() - - def test_fluent_behavior__wait_until_done_with_default(self): - thing = ObjectWithBehaviors() - thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled()) - - closure = lambda: thing.behaviors.test_request_behavior.run("A") - t = Timer(0.5, closure) - t.start() - - wait_until(thing).test_request(anything()).times(1) - assertThat(thing.unhandled_count).isEqualTo(1) - - def test_fluent_behavior__wait_until_done_two_events_AA(self): - thing = ObjectWithBehaviors() - when(thing).test_request(lambda obj: obj == "A").then().increment_count().increment_count() - - closure1 = lambda: thing.behaviors.test_request_behavior.run("A") - t1 = Timer(0.5, closure1) - t1.start() - closure2 = lambda: thing.behaviors.test_request_behavior.run("A") - t2 = Timer(0.5, closure2) - t2.start() - - wait_until(thing).test_request(lambda obj: obj == "A").times(2) - assertThat(thing.count).isEqualTo(2) - assertThat(thing.captured).isEqualTo(["A", "A"]) - - def test_fluent_behavior__wait_until_done_two_events_AB(self): - thing = ObjectWithBehaviors() - when(thing).test_request(anything()).always().increment_count() - - closure1 = lambda: thing.behaviors.test_request_behavior.run("A") - t1 = Timer(0.5, closure1) - t1.start() - closure2 = lambda: thing.behaviors.test_request_behavior.run("B") - t2 = Timer(1, closure2) - t2.start() - - wait_until(thing).test_request(anything()).times(2) - assertThat(thing.count).isEqualTo(2) - assertThat(thing.captured).isEqualTo(["A", "B"]) - - def test_fluent_behavior__wait_until_done_only_one_event_is_done(self): - thing = ObjectWithBehaviors() - when(thing).test_request(anything()).always().increment_count() - - closure1 = lambda: thing.behaviors.test_request_behavior.run("A") - t1 = Timer(1, closure1) - t1.start() - closure2 = lambda: thing.behaviors.test_request_behavior.run("B") - t2 = Timer(3, closure2) - t2.start() - assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(2)).isFalse() - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/cert/closable.py b/system/blueberry/tests/gd/cert/closable.py deleted file mode 100644 index 06a6d0df18..0000000000 --- a/system/blueberry/tests/gd/cert/closable.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import time -from abc import ABC, abstractmethod -import logging - - -class Closable(ABC): - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - try: - self.close() - except Exception: - logging.warning("Failed to close or already closed") - return traceback is None - - def __del__(self): - try: - self.close() - except Exception: - logging.warning("Failed to close or already closed") - - @abstractmethod - def close(self): - pass - - -def safeClose(closable): - if closable is not None: - closable.close() - # sleep for 100ms because GrpcEventQueue takes at most 100 ms to close - time.sleep(0.1) diff --git a/system/blueberry/tests/gd/cert/context.py b/system/blueberry/tests/gd/cert/context.py deleted file mode 100644 index 1e592ab5f3..0000000000 --- a/system/blueberry/tests/gd/cert/context.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2018 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import enum -import logging -import os - - -class ContextLevel(enum.IntEnum): - ROOT = 0 - TESTCLASS = 1 - TESTCASE = 2 - - -def get_current_context(depth=None): - """Get the current test context at the specified depth. - Pulls the most recently created context, with a level at or below the given - depth, from the _contexts stack. - - Args: - depth: The desired context level. For example, the TESTCLASS level would - yield the current test class context, even if the test is currently - within a test case. - - Returns: An instance of TestContext. - """ - if depth is None: - return _contexts[-1] - return _contexts[min(depth, len(_contexts) - 1)] - - -def append_test_context(test_class_name, test_name): - """Add test-specific context to the _contexts stack. - A test should should call append_test_context() at test start and - pop_test_context() upon test end. - - Args: - test_class_name: name of the test class. - test_name: name of the test. - """ - if _contexts: - _contexts.append(TestCaseContext(test_class_name, test_name)) - - -def pop_test_context(): - """Remove the latest test-specific context from the _contexts stack. - A test should should call append_test_context() at test start and - pop_test_context() upon test end. - """ - if _contexts: - _contexts.pop() - - -class TestContext(object): - """An object representing the current context in which a test is executing. - - The context encodes the current state of the test runner with respect to a - particular scenario in which code is being executed. For example, if some - code is being executed as part of a test case, then the context should - encode information about that test case such as its name or enclosing - class. - - The subcontext specifies a relative path in which certain outputs, - e.g. logcat, should be kept for the given context. - - The full output path is given by - <base_output_path>/<context_dir>/<subcontext>. - - Attributes: - _base_output_paths: a dictionary mapping a logger's name to its base - output path - _subcontexts: a dictionary mapping a logger's name to its - subcontext-level output directory - """ - - _base_output_paths = {} - _subcontexts = {} - - def get_base_output_path(self, log_name=None): - """Gets the base output path for this logger. - - The base output path is interpreted as the reporting root for the - entire test runner. - - If a path has been added with add_base_output_path, it is returned. - Otherwise, a default is determined by _get_default_base_output_path(). - - Args: - log_name: The name of the logger. - - Returns: - The output path. - """ - if log_name in self._base_output_paths: - return self._base_output_paths[log_name] - return self._get_default_base_output_path() - - def get_subcontext(self, log_name=None): - """Gets the subcontext for this logger. - - The subcontext is interpreted as the directory, relative to the - context-level path, where all outputs of the given logger are stored. - - If a path has been added with add_subcontext, it is returned. - Otherwise, the empty string is returned. - - Args: - log_name: The name of the logger. - - Returns: - The output path. - """ - return self._subcontexts.get(log_name, '') - - def get_full_output_path(self, log_name=None): - """Gets the full output path for this context. - - The full path represents the absolute path to the output directory, - as given by <base_output_path>/<context_dir>/<subcontext> - - Args: - log_name: The name of the logger. Used to specify the base output - path and the subcontext. - - Returns: - The output path. - """ - - path = os.path.join( - self.get_base_output_path(log_name), self._get_default_context_dir(), self.get_subcontext(log_name)) - os.makedirs(path, exist_ok=True) - return path - - def _get_default_base_output_path(self): - """Gets the default base output path. - - This will attempt to use logging path set up in the global - logger. - - Returns: - The logging path. - - Raises: - EnvironmentError: If logger has not been initialized. - """ - try: - return logging.log_path - except AttributeError as e: - raise EnvironmentError('The Mobly logger has not been set up and' - ' "base_output_path" has not been set.') from e - - def _get_default_context_dir(self): - """Gets the default output directory for this context.""" - raise NotImplementedError() - - -class RootContext(TestContext): - """A TestContext that represents a test run.""" - - @property - def identifier(self): - return 'root' - - def _get_default_context_dir(self): - """Gets the default output directory for this context. - - Logs at the root level context are placed directly in the base level - directory, so no context-level path exists.""" - return '' - - -class TestCaseContext(TestContext): - """A TestContext that represents a test case. - - Attributes: - test_case: the name of the test case. - test_class: the name of the test class. - """ - - def __init__(self, test_class, test_case): - """Initializes a TestCaseContext for the given test case. - - Args: - test_class: test-class name. - test_case: test name. - """ - self.test_class = test_class - self.test_case = test_case - - @property - def test_case_name(self): - return self.test_case - - @property - def test_class_name(self): - return self.test_class - - @property - def identifier(self): - return '%s.%s' % (self.test_class_name, self.test_case_name) - - def _get_default_context_dir(self): - """Gets the default output directory for this context. - - For TestCaseContexts, this will be the name of the test itself. - """ - return self.test_case_name - - -# stack for keeping track of the current test context -_contexts = [RootContext()] diff --git a/system/blueberry/tests/gd/cert/event_stream.py b/system/blueberry/tests/gd/cert/event_stream.py deleted file mode 100644 index ee3db7596a..0000000000 --- a/system/blueberry/tests/gd/cert/event_stream.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from abc import ABC, abstractmethod -from concurrent.futures import ThreadPoolExecutor -from datetime import datetime, timedelta -import logging -from queue import SimpleQueue, Empty - -from mobly import asserts - -from google.protobuf import text_format - -from grpc import RpcError - -from blueberry.tests.gd.cert.closable import Closable - - -class IEventStream(ABC): - - @abstractmethod - def get_event_queue(self): - pass - - -class FilteringEventStream(IEventStream): - - def __init__(self, stream, filter_fn): - self.filter_fn = filter_fn if filter_fn else lambda x: x - self.event_queue = SimpleQueue() - self.stream = stream - - self.stream.register_callback(self.__event_callback, lambda packet: self.filter_fn(packet) is not None) - - def __event_callback(self, event): - self.event_queue.put(self.filter_fn(event)) - - def get_event_queue(self): - return self.event_queue - - def unregister(self): - self.stream.unregister(self.__event_callback) - - -def pretty_print(proto_event): - return '{} {}'.format(type(proto_event).__name__, text_format.MessageToString(proto_event, as_one_line=True)) - - -DEFAULT_TIMEOUT_SECONDS = 30 - - -class EventStream(IEventStream, Closable): - """ - A class that streams events from a gRPC stream, which you can assert on. - - Don't use these asserts directly, use the ones from cert.truth. - """ - - def __init__(self, server_stream_call): - if server_stream_call is None: - raise ValueError("server_stream_call cannot be None") - - self.server_stream_call = server_stream_call - self.event_queue = SimpleQueue() - self.handlers = [] - self.executor = ThreadPoolExecutor() - self.future = self.executor.submit(EventStream.__event_loop, self) - - def get_event_queue(self): - return self.event_queue - - def close(self): - """ - Stop the gRPC lambda so that event_callback will not be invoked after - the method returns. - - This object will be useless after this call as there is no way to - restart the gRPC callback. You would have to create a new EventStream - - :raise None on success, or the same exception as __event_loop(), or - concurrent.futures.TimeoutError if underlying stream failed to - terminate within DEFAULT_TIMEOUT_SECONDS - """ - # Try to cancel the execution, don't care the result, non-blocking - self.server_stream_call.cancel() - try: - # cancelling gRPC stream should cause __event_loop() to quit - # same exception will be raised by future.result() or - # concurrent.futures.TimeoutError will be raised after timeout - self.future.result(timeout=DEFAULT_TIMEOUT_SECONDS) - finally: - # Make sure we force shutdown the executor regardless of the result - self.executor.shutdown(wait=False) - - def register_callback(self, callback, matcher_fn=None): - """ - Register a callback to handle events. Event will be handled by callback - if matcher_fn(event) returns True - - callback and matcher are registered as a tuple. Hence the same callback - with different matcher are considered two different handler units. Same - matcher, but different callback are also considered different handling - unit - - Callback will be invoked on a ThreadPoolExecutor owned by this - EventStream - - :param callback: Will be called as callback(event) - :param matcher_fn: A boolean function that returns True or False when - calling matcher_fn(event), if None, all event will - be matched - """ - if callback is None: - raise ValueError("callback must not be None") - self.handlers.append((callback, matcher_fn)) - - def unregister_callback(self, callback, matcher_fn=None): - """ - Unregister callback and matcher_fn from the event stream. Both objects - must match exactly the ones when calling register_callback() - - :param callback: callback used in register_callback() - :param matcher_fn: matcher_fn used in register_callback() - :raises ValueError when (callback, matcher_fn) tuple is not found - """ - if callback is None: - raise ValueError("callback must not be None") - self.handlers.remove((callback, matcher_fn)) - - def __event_loop(self): - """ - Main loop for consuming the gRPC stream events. - Blocks until computation is cancelled - :raise grpc.Error on failure - """ - try: - for event in self.server_stream_call: - self.event_queue.put(event) - for (callback, matcher_fn) in self.handlers: - if not matcher_fn or matcher_fn(event): - callback(event) - except RpcError as exp: - # Underlying gRPC stream should run indefinitely until cancelled - # Hence any other reason besides CANCELLED is raised as an error - if self.server_stream_call.cancelled(): - logging.debug("Cancelled") - else: - raise exp - - def assert_event_occurs(self, match_fn, at_least_times=1, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)): - """ - Assert at least |at_least_times| instances of events happen where - match_fn(event) returns True within timeout period - - :param match_fn: returns True/False on match_fn(event) - :param timeout: a timedelta object - :param at_least_times: how many times at least a matching event should - happen - :return: - """ - NOT_FOR_YOU_assert_event_occurs(self, match_fn, at_least_times, timeout) - - def assert_event_occurs_at_most(self, match_fn, at_most_times, timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)): - """ - Assert at most |at_most_times| instances of events happen where - match_fn(event) returns True within timeout period - - :param match_fn: returns True/False on match_fn(event) - :param at_most_times: how many times at most a matching event should - happen - :param timeout:a timedelta object - :return: - """ - logging.debug("assert_event_occurs_at_most") - event_list = [] - end_time = datetime.now() + timeout - while len(event_list) <= at_most_times and datetime.now() < end_time: - remaining = static_remaining_time_delta(end_time) - logging.debug("Waiting for event iteration (%fs remaining)" % (remaining.total_seconds())) - try: - current_event = self.event_queue.get(timeout=remaining.total_seconds()) - if match_fn(current_event): - event_list.append(current_event) - except Empty: - continue - logging.debug("Done waiting, got %d events" % len(event_list)) - assert_true( - self, - len(event_list) <= at_most_times, - msg=("Expected at most %d events, but got %d" % (at_most_times, len(event_list)))) - - -def static_remaining_time_delta(end_time): - remaining = end_time - datetime.now() - if remaining < timedelta(milliseconds=0): - remaining = timedelta(milliseconds=0) - return remaining - - -def assert_true(istream, expr, msg, extras=None): - if not expr: - istream.close() - asserts.fail(msg, extras) - - -def NOT_FOR_YOU_assert_event_occurs(istream, - match_fn, - at_least_times=1, - timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)): - logging.debug("assert_event_occurs %d %fs" % (at_least_times, timeout.total_seconds())) - event_list = [] - end_time = datetime.now() + timeout - while len(event_list) < at_least_times and datetime.now() < end_time: - remaining = static_remaining_time_delta(end_time) - logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds())) - try: - current_event = istream.get_event_queue().get(timeout=remaining.total_seconds()) - logging.debug("current_event: %s", current_event) - if match_fn(current_event): - event_list.append(current_event) - except Empty: - continue - logging.debug("Done waiting for event, received %d", len(event_list)) - - assert_true( - istream, - len(event_list) >= at_least_times, - msg=("Expected at least %d events, but got %d" % (at_least_times, len(event_list)))) - - -def NOT_FOR_YOU_assert_all_events_occur(istream, - match_fns, - order_matters, - timeout=timedelta(seconds=DEFAULT_TIMEOUT_SECONDS)): - logging.debug("assert_all_events_occur %fs" % timeout.total_seconds()) - pending_matches = list(match_fns) - matched_order = [] - end_time = datetime.now() + timeout - while len(pending_matches) > 0 and datetime.now() < end_time: - remaining = static_remaining_time_delta(end_time) - logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds())) - try: - current_event = istream.get_event_queue().get(timeout=remaining.total_seconds()) - for match_fn in pending_matches: - if match_fn(current_event): - pending_matches.remove(match_fn) - matched_order.append(match_fn) - except Empty: - continue - logging.debug("Done waiting for event") - assert_true( - istream, - len(matched_order) == len(match_fns), - msg=("Expected at least %d events, but got %d" % (len(match_fns), len(matched_order)))) - if order_matters: - correct_order = True - i = 0 - while i < len(match_fns): - if match_fns[i] is not matched_order[i]: - correct_order = False - break - i += 1 - assert_true(istream, correct_order, "Events not received in correct order %s %s" % (match_fns, matched_order)) - - -def NOT_FOR_YOU_assert_none_matching(istream, match_fn, timeout): - logging.debug("assert_none_matching %fs" % (timeout.total_seconds())) - event = None - end_time = datetime.now() + timeout - while event is None and datetime.now() < end_time: - remaining = static_remaining_time_delta(end_time) - logging.debug("Waiting for event (%fs remaining)" % (remaining.total_seconds())) - try: - current_event = istream.get_event_queue().get(timeout=remaining.total_seconds()) - if match_fn(current_event): - event = current_event - except Empty: - continue - logging.debug("Done waiting for an event") - if event is None: - return # Avoid an assert in MessageToString(None, ...) - assert_true(istream, event is None, msg='Expected None matching, but got {}'.format(pretty_print(event))) - - -def NOT_FOR_YOU_assert_none(istream, timeout): - logging.debug("assert_none %fs" % (timeout.total_seconds())) - try: - event = istream.get_event_queue().get(timeout=timeout.total_seconds()) - assert_true(istream, event is None, msg='Expected None, but got {}'.format(pretty_print(event))) - except Empty: - return diff --git a/system/blueberry/tests/gd/cert/gd_base_test.py b/system/blueberry/tests/gd/cert/gd_base_test.py deleted file mode 100644 index c964815c8e..0000000000 --- a/system/blueberry/tests/gd/cert/gd_base_test.py +++ /dev/null @@ -1,305 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import importlib -import logging -import os -import traceback -import signal -import subprocess - -from blueberry.facade import rootservice_pb2 as facade_rootservice -from functools import wraps -from grpc import RpcError - -from blueberry.tests.gd.cert.async_subprocess_logger import AsyncSubprocessLogger -from blueberry.tests.gd.cert.context import append_test_context, get_current_context, pop_test_context, ContextLevel -from blueberry.tests.gd.cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME -from blueberry.tests.gd.cert.os_utils import get_gd_root -from blueberry.tests.gd.cert.os_utils import read_crash_snippet_and_log_tail -from blueberry.tests.gd.cert.os_utils import is_subprocess_alive -from blueberry.tests.gd.cert.os_utils import make_ports_available -from blueberry.tests.gd.cert.os_utils import TerminalColor -from blueberry.tests.gd.cert.tracelogger import TraceLogger - -from mobly import asserts, signals -from mobly import base_test - - -class Timeout: - - def __init__(self, seconds=1, error_message='Timeout'): - self.seconds = seconds - self.error_message = error_message - - def handle_timeout(self, signum, frame): - raise TimeoutError(self.error_message) - - def __enter__(self): - signal.signal(signal.SIGALRM, self.handle_timeout) - signal.alarm(self.seconds) - - def __exit__(self, type, value, traceback): - signal.alarm(0) - - -class GdBaseTestClass(base_test.BaseTestClass): - - FUNCTION_CALL_TIMEOUT_SECONDS = 5 - SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10 - - def setup_class(self, dut_module, cert_module): - self.dut_module = dut_module - self.cert_module = cert_module - self.log = TraceLogger(logging.getLogger()) - self.dut_coverage_info = None - self.cert_coverage_info = None - - def teardown_class(self): - # assume each test runs the same binary for dut and cert - # generate coverage report after running all tests in a class - if self.dut_coverage_info: - self.dut.generate_coverage_report_for_host(self.dut_coverage_info) - self.dut_coverage_info = None - if self.cert_coverage_info: - self.cert.generate_coverage_report_for_host(self.cert_coverage_info) - self.cert_coverage_info = None - - def set_controller_properties_path(self, path): - GD_DIR = os.path.join(os.getcwd(), os.pardir) - self.controller_properties_file = os.path.join(GD_DIR, path) - - def setup_test(self): - append_test_context(test_class_name=self.TAG, test_name=self.current_test_info.name) - self.log_path_base = get_current_context().get_full_output_path() - self.verbose_mode = bool(self.user_params.get('verbose_mode', False)) - for config in self.controller_configs[CONTROLLER_CONFIG_NAME]: - config['verbose_mode'] = self.verbose_mode - - try: - controller_properties_file = self.controller_properties_file - except AttributeError: - controller_properties_file = '' - - self.setup_rootcanal(controller_properties_file) - - # Parse and construct GD device objects - self.register_controller(importlib.import_module('blueberry.tests.gd.cert.gd_device'), builtin=True) - self.dut = self.gd_device[1] - self.cert = self.gd_device[0] - if self.dut.host_only_device: - new_dut_coverage_info = self.dut.get_coverage_info() - if self.dut_coverage_info: - asserts.assert_true(self.dut_coverage_info == new_dut_coverage_info, - msg="DUT coverage info must be the same for each test run, old: {}, new: {}".format( - self.dut_coverage_info, new_dut_coverage_info)) - self.dut_coverage_info = new_dut_coverage_info - if self.cert.host_only_device: - new_cert_coverage_info = self.cert.get_coverage_info() - if self.cert_coverage_info: - asserts.assert_true( - self.cert_coverage_info == new_cert_coverage_info, - msg="CERT coverage info must be the same for each test run, old: {}, new: {}".format( - self.cert_coverage_info, new_cert_coverage_info)) - self.cert_coverage_info = new_cert_coverage_info - - try: - self.dut.rootservice.StartStack( - facade_rootservice.StartStackRequest( - module_under_test=facade_rootservice.BluetoothModule.Value(self.dut_module))) - except RpcError as rpc_error: - asserts.fail("Failed to start DUT stack, RpcError={!r}".format(rpc_error)) - try: - self.cert.rootservice.StartStack( - facade_rootservice.StartStackRequest( - module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module))) - except RpcError as rpc_error: - asserts.fail("Failed to start CERT stack, RpcError={!r}".format(rpc_error)) - self.dut.wait_channel_ready() - self.cert.wait_channel_ready() - - def teardown_test(self): - stack = "" - try: - with Timeout(seconds=self.FUNCTION_CALL_TIMEOUT_SECONDS): - stack = "CERT" - self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest()) - stack = "DUT" - self.dut.rootservice.StopStack(facade_rootservice.StopStackRequest()) - except RpcError as rpc_error: - asserts.fail("Failed to stop {} stack, RpcError={!r}".format(stack, rpc_error)) - except TimeoutError: - logging.error("Failed to stop {} stack in {} s".format(stack, self.FUNCTION_CALL_TIMEOUT_SECONDS)) - finally: - # Destroy GD device objects - self._controller_manager.unregister_controllers() - self.teardown_rootcanal() - pop_test_context() - - def setup_rootcanal(self, controller_properties_file=''): - # Start root-canal if needed - self.rootcanal_running = False - self.rootcanal_logpath = None - self.rootcanal_process = None - self.rootcanal_logger = None - if 'rootcanal' in self.controller_configs: - self.rootcanal_running = True - # Get root canal binary - rootcanal = os.path.join(get_gd_root(), "root-canal") - asserts.assert_true(os.path.isfile(rootcanal), "Root canal does not exist at %s" % rootcanal) - - # Get root canal log - self.rootcanal_logpath = os.path.join(self.log_path_base, 'rootcanal_logs.txt') - # Make sure ports are available - rootcanal_config = self.controller_configs['rootcanal'] - rootcanal_test_port = int(rootcanal_config.get("test_port", "6401")) - rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402")) - rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403")) - asserts.assert_true( - make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)), - "Failed to free ports rootcanal_test_port={}, rootcanal_hci_port={}, rootcanal_link_layer_port={}". - format(rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)) - - # Start root canal process - rootcanal_cmd = [ - rootcanal, '-test_port', - str(rootcanal_test_port), '-hci_port', - str(rootcanal_hci_port), '-link_port', - str(rootcanal_link_layer_port), '-controller_properties_file=' + controller_properties_file - ] - self.log.debug("Running %s" % " ".join(rootcanal_cmd)) - self.rootcanal_process = subprocess.Popen(rootcanal_cmd, - cwd=get_gd_root(), - env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - asserts.assert_true(self.rootcanal_process, msg="Cannot start root-canal at " + str(rootcanal)) - asserts.assert_true(is_subprocess_alive(self.rootcanal_process), - msg="root-canal stopped immediately after running") - - self.rootcanal_logger = AsyncSubprocessLogger(self.rootcanal_process, [self.rootcanal_logpath], - log_to_stdout=self.verbose_mode, - tag="rootcanal", - color=TerminalColor.MAGENTA) - - # Modify the device config to include the correct root-canal port - for gd_device_config in self.controller_configs.get("GdDevice"): - gd_device_config["rootcanal_port"] = str(rootcanal_hci_port) - - def teardown_rootcanal(self): - if self.rootcanal_running: - stop_signal = signal.SIGINT - self.rootcanal_process.send_signal(stop_signal) - try: - return_code = self.rootcanal_process.wait(timeout=self.SUBPROCESS_WAIT_TIMEOUT_SECONDS) - except subprocess.TimeoutExpired: - logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL") - stop_signal = signal.SIGKILL - self.rootcanal_process.kill() - try: - return_code = self.rootcanal_process.wait(timeout=self.SUBPROCESS_WAIT_TIMEOUT_SECONDS) - except subprocess.TimeoutExpired: - logging.error("Failed to kill root canal") - return_code = -65536 - if return_code != 0 and return_code != -stop_signal: - logging.error("rootcanal stopped with code: %d" % return_code) - self.rootcanal_logger.stop() - - @staticmethod - def get_module_reference_name(a_module): - """Returns the module's module's submodule name as reference name. - - Args: - a_module: Any module. Ideally, a controller module. - Returns: - A string corresponding to the module's name. - """ - return a_module.__name__.split('.')[-1] - - def register_controller(self, controller_module, required=True, builtin=False): - """Registers an controller module for a test class. Invokes Mobly's - implementation of register_controller. - """ - module_ref_name = self.get_module_reference_name(controller_module) - module_config_name = controller_module.MOBLY_CONTROLLER_CONFIG_NAME - - # Get controller objects from Mobly's register_controller - controllers = self._controller_manager.register_controller(controller_module, required=required) - if not controllers: - return None - - # Log controller information - # Implementation of "get_info" is optional for a controller module. - if hasattr(controller_module, "get_info"): - controller_info = controller_module.get_info(controllers) - self.log.info("Controller %s: %s", module_config_name, controller_info) - - if builtin: - setattr(self, module_ref_name, controllers) - return controllers - - def __getattribute__(self, name): - attr = super().__getattribute__(name) - if not callable(attr) or not GdBaseTestClass.__is_entry_function(name): - return attr - - @wraps(attr) - def __wrapped(*args, **kwargs): - try: - return attr(*args, **kwargs) - except RpcError as e: - exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__)) - raise signals.TestFailure("RpcError during test\n\nRpcError:\n\n%s\n%s" % - (exception_info, self.__dump_crashes())) - - return __wrapped - - __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"} - - @staticmethod - def __is_entry_function(name): - return name.startswith("test_") or name in GdBaseTestClass.__ENTRY_METHODS - - def __dump_crashes(self): - """ - return: formatted stack traces if found, or last few lines of log - """ - dut_crash, dut_log_tail = self.dut.get_crash_snippet_and_log_tail() - cert_crash, cert_log_tail = self.cert.get_crash_snippet_and_log_tail() - rootcanal_crash = None - rootcanal_log_tail = None - if self.rootcanal_running and not is_subprocess_alive(self.rootcanal_process): - rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(self.rootcanal_logpath) - - crash_detail = "" - if dut_crash or cert_crash or rootcanal_crash: - if rootcanal_crash: - crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash - if dut_crash: - crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash - if cert_crash: - crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash - else: - if rootcanal_log_tail: - crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail - if dut_log_tail: - crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail - if cert_log_tail: - crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail - - return crash_detail diff --git a/system/blueberry/tests/gd/cert/gd_device.py b/system/blueberry/tests/gd/cert/gd_device.py deleted file mode 100644 index 99077d470f..0000000000 --- a/system/blueberry/tests/gd/cert/gd_device.py +++ /dev/null @@ -1,866 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from abc import ABC -from abc import abstractmethod -from datetime import datetime -import inspect -import logging -import os -import pathlib -import shutil -import signal -import subprocess -import time -from typing import List - -import grpc - -from google.protobuf import empty_pb2 as empty_proto - -from blueberry.tests.gd.cert import asserts -from blueberry.tests.gd.cert.adb import BlueberryAdbProxy -from blueberry.tests.gd.cert.adb import UTF_8 -from blueberry.tests.gd.cert.async_subprocess_logger import AsyncSubprocessLogger -from blueberry.tests.gd.cert.context import get_current_context -from blueberry.tests.gd.cert.logging_client_interceptor import LoggingClientInterceptor -from blueberry.tests.gd.cert.os_utils import get_gd_root -from blueberry.tests.gd.cert.os_utils import read_crash_snippet_and_log_tail -from blueberry.tests.gd.cert.os_utils import is_subprocess_alive -from blueberry.tests.gd.cert.os_utils import make_ports_available -from blueberry.tests.gd.cert.os_utils import TerminalColor - -from blueberry.facade import rootservice_pb2_grpc as facade_rootservice_pb2_grpc -from blueberry.facade.hal import hal_facade_pb2_grpc -from blueberry.facade.hci import hci_facade_pb2_grpc -from blueberry.facade.hci import acl_manager_facade_pb2_grpc -from blueberry.facade.hci import controller_facade_pb2_grpc -from blueberry.facade.hci import le_acl_manager_facade_pb2_grpc -from blueberry.facade.hci import le_advertising_manager_facade_pb2_grpc -from blueberry.facade.hci import le_initiator_address_facade_pb2_grpc -from blueberry.facade.hci import le_scanning_manager_facade_pb2_grpc -from blueberry.facade.l2cap.classic import facade_pb2_grpc as l2cap_facade_pb2_grpc -from blueberry.facade.l2cap.le import facade_pb2_grpc as l2cap_le_facade_pb2_grpc -from blueberry.facade.neighbor import facade_pb2_grpc as neighbor_facade_pb2_grpc - -from mobly import utils -from mobly.controllers.android_device_lib.adb import AdbError - -ADB_FILE_NOT_EXIST_ERROR = "No such file or directory" -GRPC_START_TIMEOUT_SEC = 15 -MOBLY_CONTROLLER_CONFIG_NAME = "GdDevice" -PORT_FORWARDING_ERROR_MSG_PREFIX = "During port forwarding cleanup: " -PULL_LOG_FILE_ERROR_MSG_PREFIX = "While trying to pull log files" - - -def create(configs): - if not configs: - raise Exception("Configuration is empty") - elif not isinstance(configs, list): - raise Exception("Configuration should be a list") - return get_instances_with_configs(configs) - - -def destroy(devices): - for device in devices: - try: - device.teardown() - except: - logging.exception("[%s] Failed to clean up properly due to" % device.label) - - -def get_info(devices): - return [] - - -def replace_vars(string, config): - serial_number = config.get("serial_number") - if serial_number is None: - serial_number = "" - rootcanal_port = config.get("rootcanal_port") - if rootcanal_port is None: - rootcanal_port = "" - if serial_number == "DUT" or serial_number == "CERT": - raise Exception("Did you forget to configure the serial number?") - return string.replace("$GD_ROOT", get_gd_root()) \ - .replace("$(grpc_port)", config.get("grpc_port")) \ - .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \ - .replace("$(rootcanal_port)", rootcanal_port) \ - .replace("$(signal_port)", config.get("signal_port")) \ - .replace("$(serial_number)", serial_number) - - -def get_instances_with_configs(configs): - print(configs) - devices = [] - for config in configs: - resolved_cmd = [] - for arg in config["cmd"]: - logging.debug(arg) - resolved_cmd.append(replace_vars(arg, config)) - verbose_mode = bool(config.get('verbose_mode', False)) - if config.get("serial_number"): - device = GdAndroidDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"], - resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"], - config["serial_number"], verbose_mode) - else: - device = GdHostOnlyDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"], - resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"], - verbose_mode) - device.setup() - devices.append(device) - return devices - - -class GdDeviceBase(ABC): - """ - Base GD device class that covers common traits which assumes that the - device must be driven by a driver-like backing process that takes following - command line arguments: - --grpc-port: main entry port for facade services - --root-server-port: management port for starting and stopping services - --btsnoop: path to btsnoop HCI log - --signal-port: signaling port to indicate that backing process is started - --rootcanal-port: root-canal HCI port, optional - """ - - WAIT_CHANNEL_READY_TIMEOUT_SECONDS = 10 - WAIT_SIGINT_TIMEOUT_SECONDS = 5 - WAIT_SIGKILL_TIMEOUT_SECONDS = 1 - - def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, - type_identifier: str, name: str, verbose_mode: bool): - """Verify arguments and log path, initialize Base GD device, common traits - for both device based and host only GD cert tests - :param grpc_port: main gRPC service port - :param grpc_root_server_port: gRPC root server port - :param signal_port: signaling port for backing process start up - :param cmd: list of arguments to run in backing process - :param label: device label used in logs - :param type_identifier: device type identifier used in logs - :param name: name of device used in logs - """ - # Must be at the first line of __init__ method - values = locals() - arguments = [values[arg] for arg in inspect.getfullargspec(GdDeviceBase.__init__).args if arg != "verbose_mode"] - asserts.assert_true(all(arguments), "All arguments to GdDeviceBase must not be None nor empty") - asserts.assert_true(all(cmd), "cmd list should not have None nor empty component") - self.verbose_mode = verbose_mode - self.host_only_device = False - self.grpc_root_server_port = int(grpc_root_server_port) - self.grpc_port = int(grpc_port) - self.signal_port = int(signal_port) - self.name = name - self.type_identifier = type_identifier - self.label = label - self.log_path_base = get_current_context().get_full_output_path() - self.test_runner_base_path = \ - get_current_context().get_base_output_path() - self.backing_process_log_path = os.path.join(self.log_path_base, - '%s_%s_backing_logs.txt' % (self.type_identifier, self.label)) - if "--btsnoop=" not in " ".join(cmd): - cmd.append("--btsnoop=%s" % os.path.join(self.log_path_base, '%s_btsnoop_hci.log' % self.label)) - if "--btsnooz=" not in " ".join(cmd): - cmd.append("--btsnooz=%s" % os.path.join(self.log_path_base, '%s_btsnooz_hci.log' % self.label)) - if "--btconfig=" not in " ".join(cmd): - cmd.append("--btconfig=%s" % os.path.join(self.log_path_base, '%s_bt_config.conf' % self.label)) - self.cmd = cmd - self.environment = os.environ.copy() - if "cert" in self.label: - self.terminal_color = TerminalColor.BLUE - else: - self.terminal_color = TerminalColor.YELLOW - - def setup(self): - """Set up this device for test, ensure signal port is available and backing process is started and alive, - must run before using this device. - - After calling this, teardown() must be called when test finishes - - Should be executed after children classes' setup() methods - :return: - """ - # Start backing process - logging.debug("[%s] Running %s %s" % (self.type_identifier, self.label, " ".join(self.cmd))) - self.backing_process = subprocess.Popen(self.cmd, - cwd=get_gd_root(), - env=self.environment, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - self.backing_process_logger = AsyncSubprocessLogger(self.backing_process, [self.backing_process_log_path], - log_to_stdout=self.verbose_mode, - tag=self.label, - color=self.terminal_color) - - asserts.assert_true(self.backing_process, - msg="[%s] failed to open backing process for %s" % (self.type_identifier, self.label)) - self.is_backing_process_alive = is_subprocess_alive(self.backing_process) - - asserts.assert_true(self.is_backing_process_alive, - msg="[%s] backing process for %s died after starting" % (self.type_identifier, self.label)) - - # If gRPC root server port is not specified, we can skip settings up the root server - if self.grpc_root_server_port != -1: - # Setup gRPC management channels - self.grpc_root_server_channel = grpc.insecure_channel("localhost:%d" % self.grpc_root_server_port) - - self.grpc_root_server_ready = False - - try: - logging.info("[%s] Waiting to connect to gRPC root server for %s, timeout is %d seconds" % - (self.type_identifier, self.label, GRPC_START_TIMEOUT_SEC)) - grpc.channel_ready_future(self.grpc_root_server_channel).result(timeout=GRPC_START_TIMEOUT_SEC) - logging.info("[%s] Successfully connected to gRPC root server for %s" % - (self.type_identifier, self.label)) - self.grpc_root_server_ready = True - except grpc.FutureTimeoutError: - logging.error("[%s] Failed to connect to gRPC root server for %s" % (self.type_identifier, self.label)) - - asserts.assert_true(self.grpc_root_server_ready, - msg="gRPC root server did not start after running " + " ".join(self.cmd)) - - self.grpc_channel = grpc.insecure_channel("localhost:%d" % self.grpc_port) - - if self.verbose_mode: - self.grpc_channel = grpc.intercept_channel(self.grpc_channel, LoggingClientInterceptor(self.label)) - - # Establish services from facades - if self.grpc_root_server_port != -1: - self.rootservice = facade_rootservice_pb2_grpc.RootFacadeStub(self.grpc_root_server_channel) - - self.hal = hal_facade_pb2_grpc.HciHalFacadeStub(self.grpc_channel) - self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel) - self.hci = hci_facade_pb2_grpc.HciFacadeStub(self.grpc_channel) - self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel) - self.l2cap_le = l2cap_le_facade_pb2_grpc.L2capLeModuleFacadeStub(self.grpc_channel) - self.hci_acl_manager = acl_manager_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel) - self.hci_le_acl_manager = le_acl_manager_facade_pb2_grpc.LeAclManagerFacadeStub(self.grpc_channel) - self.hci_le_initiator_address = le_initiator_address_facade_pb2_grpc.LeInitiatorAddressFacadeStub( - self.grpc_channel) - self.hci_controller = controller_facade_pb2_grpc.ControllerFacadeStub(self.grpc_channel) - self.hci_controller.GetMacAddressSimple = lambda: self.hci_controller.GetMacAddress(empty_proto.Empty()).address - self.hci_controller.GetLocalNameSimple = lambda: self.hci_controller.GetLocalName(empty_proto.Empty()).name - self.hci_le_advertising_manager = le_advertising_manager_facade_pb2_grpc.LeAdvertisingManagerFacadeStub( - self.grpc_channel) - self.hci_le_scanning_manager = le_scanning_manager_facade_pb2_grpc.LeScanningManagerFacadeStub( - self.grpc_channel) - self.neighbor = neighbor_facade_pb2_grpc.NeighborFacadeStub(self.grpc_channel) - - def get_crash_snippet_and_log_tail(self): - if is_subprocess_alive(self.backing_process): - return None, None - - return read_crash_snippet_and_log_tail(self.backing_process_log_path) - - def teardown(self): - """Tear down this device and clean up any resources. - - Must be called after setup() - - Should be executed before children classes' teardown() - :return: - """ - self.grpc_channel.close() - if self.grpc_root_server_port != -1: - self.grpc_root_server_channel.close() - stop_signal = self.gracefully_stop_backing_process() - try: - if stop_signal == 0: - raise RuntimeError("Failed to gracefully shutdown backing process") - return_code = self.backing_process.wait(timeout=self.WAIT_SIGINT_TIMEOUT_SECONDS) - except (subprocess.TimeoutExpired, RuntimeError): - logging.error("[%s] Failed to interrupt backing process via SIGINT, sending SIGKILL" % self.label) - stop_signal = signal.SIGKILL - self.backing_process.kill() - try: - return_code = self.backing_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) - except subprocess.TimeoutExpired: - logging.error("Failed to kill backing process") - return_code = -65536 - if return_code not in [-stop_signal, 0]: - logging.error("backing process %s stopped with code: %d" % (self.label, return_code)) - self.backing_process_logger.stop() - - def wait_channel_ready(self): - future = grpc.channel_ready_future(self.grpc_channel) - try: - future.result(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) - except grpc.FutureTimeoutError: - asserts.fail("[%s] wait channel ready timeout" % self.label) - - @abstractmethod - def gracefully_stop_backing_process(self): - return NotImplemented - - -class GdHostOnlyDevice(GdDeviceBase): - """ - Host only device where the backing process is running on the host machine - """ - - def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, - type_identifier: str, name: str, verbose_mode: bool): - super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, MOBLY_CONTROLLER_CONFIG_NAME, name, - verbose_mode) - self.host_only_device = True - # Enable LLVM code coverage output for host only tests - self.backing_process_profraw_path = pathlib.Path(self.log_path_base).joinpath( - "%s_%s_backing_coverage.profraw" % (self.type_identifier, self.label)) - self.environment["LLVM_PROFILE_FILE"] = str(self.backing_process_profraw_path) - llvm_binutils = pathlib.Path(get_gd_root()).joinpath("llvm_binutils").joinpath("bin") - llvm_symbolizer = llvm_binutils.joinpath("llvm-symbolizer") - if llvm_symbolizer.is_file(): - self.environment["ASAN_SYMBOLIZER_PATH"] = llvm_symbolizer - else: - logging.warning("[%s] Cannot find LLVM symbolizer at %s" % (self.label, str(llvm_symbolizer))) - self.profdata_path = self.get_coverage_profdata_path_for_host(self.test_runner_base_path, self.type_identifier, - self.label) - - def setup(self): - # Ensure ports are available - # Only check on host only test, for Android devices, these ports will - # be opened on Android device and host machine ports will be occupied - # by sshd or adb forwarding - ports_needed = [self.grpc_port, self.grpc_root_server_port - ] if self.grpc_root_server_port != -1 else [self.grpc_port] - asserts.assert_true(make_ports_available(ports_needed), - "[%s] Failed to make backing process ports available" % self.label) - super().setup() - - def teardown(self): - super().teardown() - self.merge_coverage_profdata_for_host(self.backing_process_profraw_path, self.profdata_path, self.label) - - def get_coverage_info(self): - """ - Get information needed for coverage reporting - :return: a dictionary with all information needed for coverage reporting - """ - return { - "profdata_path": self.profdata_path, - "label": self.label, - "test_runner_base_path": self.test_runner_base_path, - "type_identifier": self.type_identifier, - "stack_bin": self.cmd[0] - } - - def get_coverage_profdata_path_for_host(self, test_runner_base_path, type_identifier, label) -> pathlib.Path: - return pathlib.Path(test_runner_base_path).parent.parent.joinpath("%s_%s_backing_process_coverage.profdata" % - (type_identifier, label)) - - def merge_coverage_profdata_for_host(self, backing_process_profraw_path, profdata_path: pathlib.Path, label): - if not backing_process_profraw_path.is_file(): - logging.info("[%s] Skip coverage report as there is no profraw file at %s" % - (label, str(backing_process_profraw_path))) - return - try: - if backing_process_profraw_path.stat().st_size <= 0: - logging.info("[%s] Skip coverage report as profraw file is empty at %s" % - (label, str(backing_process_profraw_path))) - return - except OSError: - logging.info("[%s] Skip coverage report as profraw file is inaccessible at %s" % - (label, str(backing_process_profraw_path))) - return - llvm_binutils = pathlib.Path(get_gd_root()).joinpath("llvm_binutils").joinpath("bin") - llvm_profdata = llvm_binutils.joinpath("llvm-profdata") - if not llvm_profdata.is_file(): - logging.info("[%s] Skip coverage report as llvm-profdata is not found at %s" % (label, str(llvm_profdata))) - return - logging.info("[%s] Merging coverage profdata" % label) - profdata_path_tmp = profdata_path.parent / (profdata_path.stem + "_tmp" + profdata_path.suffix) - # Merge with existing profdata if possible - profdata_cmd = [str(llvm_profdata), "merge", "-sparse", str(backing_process_profraw_path)] - if profdata_path.is_file(): - profdata_cmd.append(str(profdata_path)) - profdata_cmd += ["-o", str(profdata_path_tmp)] - logging.debug("Running llvm_profdata: %s" % " ".join(profdata_cmd)) - result = subprocess.run(profdata_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if result.returncode != 0: - logging.warning("[%s] Failed to index profdata, cmd result: %r" % (label, result)) - profdata_path.unlink(missing_ok=True) - return - shutil.move(profdata_path_tmp, profdata_path) - - def generate_coverage_report_for_host(self, coverage_info): - label = coverage_info["label"] - test_runner_base_path = coverage_info["test_runner_base_path"] - type_identifier = coverage_info["type_identifier"] - profdata_path = coverage_info["profdata_path"] - stack_bin = coverage_info["stack_bin"] - llvm_binutils = pathlib.Path(get_gd_root()).joinpath("llvm_binutils").joinpath("bin") - llvm_cov = llvm_binutils.joinpath("llvm-cov") - if not llvm_cov.is_file(): - logging.info("[%s] Skip coverage report as llvm-cov is not found at %s" % (label, str(llvm_cov))) - return - logging.info("[%s] Generating coverage report in JSON" % label) - coverage_result_path = pathlib.Path(test_runner_base_path).parent.parent.joinpath( - "%s_%s_backing_process_coverage.json" % (type_identifier, label)) - with coverage_result_path.open("w") as coverage_result_file: - llvm_cov_export_cmd = [ - str(llvm_cov), "export", "--format=text", "--ignore-filename-regex", "(external|out).*", - "--instr-profile", - str(profdata_path), - str(stack_bin) - ] - logging.debug("Running llvm_cov export: %s" % " ".join(llvm_cov_export_cmd)) - result = subprocess.run(llvm_cov_export_cmd, - stderr=subprocess.PIPE, - stdout=coverage_result_file, - cwd=os.path.join(get_gd_root())) - if result.returncode != 0: - logging.warning("[%s] Failed to generated coverage report, cmd result: %r" % (label, result)) - coverage_result_path.unlink(missing_ok=True) - return - logging.info("[%s] Generating coverage summary in text" % label) - coverage_summary_path = pathlib.Path(test_runner_base_path).parent.parent.joinpath( - "%s_%s_backing_process_coverage_summary.txt" % (type_identifier, label)) - with coverage_summary_path.open("w") as coverage_summary_file: - llvm_cov_report_cmd = [ - str(llvm_cov), "report", "--ignore-filename-regex", "(external|out).*", "--instr-profile", - str(profdata_path), - str(stack_bin) - ] - logging.debug("Running llvm_cov report: %s" % " ".join(llvm_cov_report_cmd)) - result = subprocess.run(llvm_cov_report_cmd, - stderr=subprocess.PIPE, - stdout=coverage_summary_file, - cwd=os.path.join(get_gd_root())) - if result.returncode != 0: - logging.warning("[%s] Failed to generated coverage summary, cmd result: %r" % (label, result)) - coverage_summary_path.unlink(missing_ok=True) - - def gracefully_stop_backing_process(self): - stop_signal = signal.SIGINT - self.backing_process.send_signal(stop_signal) - return stop_signal - - -class GdAndroidDevice(GdDeviceBase): - """Real Android device where the backing process is running on it - """ - - WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 - WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS = 1 - ADB_ABORT_EXIT_CODE = 134 - DEVICE_LIB_DIR = "/system/lib64" - DEVICE_BIN_DIR = "/system/bin" - - def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, - type_identifier: str, name: str, serial_number: str, verbose_mode: bool): - super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, type_identifier, name, verbose_mode) - asserts.assert_true(serial_number, "serial_number must not be None nor empty") - self.serial_number = serial_number - self.adb = BlueberryAdbProxy(serial_number) - - def setup(self): - logging.info("Setting up device %s %s" % (self.label, self.serial_number)) - asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root", self.serial_number) - self.ensure_verity_disabled() - logging.info("Confirmed that verity is disabled on device %s %s" % (self.label, self.serial_number)) - - # Try freeing ports and ignore results - asserts.assert_true(make_ports_available((self.grpc_port, self.grpc_root_server_port, self.signal_port)), - "[%s] Failed to make backing process ports available" % self.label) - self.cleanup_port_forwarding() - self.sync_device_time() - logging.info("Ports cleaned up and clock is set for device %s %s" % (self.label, self.serial_number)) - - # Set up port forwarding or reverse or die - self.tcp_forward_or_die(self.grpc_port, self.grpc_port) - if self.grpc_root_server_port != -1: - self.tcp_forward_or_die(self.grpc_root_server_port, self.grpc_root_server_port) - self.tcp_reverse_or_die(self.signal_port, self.signal_port) - logging.info("Port forwarding done on device %s %s" % (self.label, self.serial_number)) - - # Push test binaries - local_dir = os.path.join(get_gd_root(), "target") - - def generate_dir_pair(local_dir, device_dir, filename): - return os.path.join(local_dir, filename), os.path.join(device_dir, filename) - - # Do not override exist libraries shared by other binaries on the Android device to avoid corrupting the Android device - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_BIN_DIR, "bluetooth_stack_with_facade")) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "android.hardware.bluetooth@1.0.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "android.hardware.bluetooth@1.1.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libandroid_runtime_lazy.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libbase.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libbinder_ndk.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libbinder.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libc++.so"), overwrite_existing=False) - # libclang_rt.asan-aarch64-android.so is only needed for ASAN build and is automatically included on device - #self.push_or_die( - # *generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libclang_rt.asan-aarch64-android.so"), - # overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libcrypto.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libcutils.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libgrpc_wrap.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libgrpc++.so")) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libhidlbase.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "liblog.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "liblzma.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libprotobuf-cpp-full.so")) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libssl.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libgrpc++.so")) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libunwindstack.so"), - overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libutils.so"), overwrite_existing=False) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libgrpc++.so")) - self.push_or_die(*generate_dir_pair(local_dir, self.DEVICE_LIB_DIR, "libz.so"), overwrite_existing=False) - - logging.info("Binaries pushed to device %s %s" % (self.label, self.serial_number)) - - try: - self.adb.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log") - except AdbError as error: - if ADB_FILE_NOT_EXIST_ERROR not in str(error): - logging.error("Error during setup: " + str(error)) - - try: - self.adb.shell("rm /data/misc/bluetooth/logs/btsnooz_hci.log") - except AdbError as error: - if ADB_FILE_NOT_EXIST_ERROR not in str(error): - logging.error("Error during setup: " + str(error)) - - try: - self.adb.shell("rm /data/misc/bluedroid/bt_config.conf") - except AdbError as error: - if ADB_FILE_NOT_EXIST_ERROR not in str(error): - logging.error("Error during setup: " + str(error)) - - logging.info("Old logs removed from device %s %s" % (self.label, self.serial_number)) - - # Ensure Bluetooth is disabled - self.ensure_no_output(self.adb.shell("settings put global ble_scan_always_enabled 0")) - self.adb.shell("cmd bluetooth_manager disable") - device_bt_state = int(self.adb.shell("settings get global bluetooth_on")) - asserts.assert_equal(device_bt_state, 0, - "Failed to disable Bluetooth on device %s %s" % (self.label, self.serial_number)) - logging.info("Bluetooth disabled on device %s %s" % (self.label, self.serial_number)) - - # Start logcat logging - self.logcat_output_path = os.path.join( - self.log_path_base, '%s_%s_%s_logcat_logs.txt' % (self.type_identifier, self.label, self.serial_number)) - self.logcat_cmd = ["adb", "-s", self.serial_number, "logcat", "-T", "1", "-v", "year", "-v", "uid"] - logging.debug("Running %s", " ".join(self.logcat_cmd)) - self.logcat_process = subprocess.Popen(self.logcat_cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - asserts.assert_true(self.logcat_process, msg="Cannot start logcat_process at " + " ".join(self.logcat_cmd)) - asserts.assert_true(is_subprocess_alive(self.logcat_process), - msg="logcat_process stopped immediately after running " + " ".join(self.logcat_cmd)) - self.logcat_logger = AsyncSubprocessLogger(self.logcat_process, [self.logcat_output_path], - log_to_stdout=self.verbose_mode, - tag="%s_%s" % (self.label, self.serial_number), - color=self.terminal_color) - - # Done run parent setup - logging.info("Done preparation for %s, starting backing process" % self.serial_number) - super().setup() - - def teardown(self): - super().teardown() - stop_signal = signal.SIGINT - self.logcat_process.send_signal(stop_signal) - try: - return_code = self.logcat_process.wait(timeout=self.WAIT_FOR_DEVICE_SIGINT_TIMEOUT_SECONDS) - except subprocess.TimeoutExpired: - logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % - (self.label, self.serial_number)) - stop_signal = signal.SIGKILL - self.logcat_process.kill() - try: - return_code = self.logcat_process.wait(timeout=self.WAIT_SIGKILL_TIMEOUT_SECONDS) - except subprocess.TimeoutExpired: - logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) - return_code = -65536 - if return_code not in [-stop_signal, 0]: - logging.error("logcat_process %s_%s stopped with code: %d" % (self.label, self.serial_number, return_code)) - self.logcat_logger.stop() - self.cleanup_port_forwarding() - self.pull_logs(self.log_path_base) - - def pull_logs(self, base_dir): - try: - self.adb.pull([ - "/data/misc/bluetooth/logs/btsnoop_hci.log", - str(os.path.join(base_dir, "%s_btsnoop_hci.log" % self.label)) - ]) - except AdbError as error: - # Some tests have no snoop logs, and that's OK - if ADB_FILE_NOT_EXIST_ERROR not in str(error): - logging.error(PULL_LOG_FILE_ERROR_MSG_PREFIX + str(error)) - try: - self.adb.pull( - ["/data/misc/bluedroid/bt_config.conf", - str(os.path.join(base_dir, "%s_bt_config.conf" % self.label))]) - except AdbError as error: - # Some tests have no config file, and that's OK - if ADB_FILE_NOT_EXIST_ERROR not in str(error): - logging.error(PULL_LOG_FILE_ERROR_MSG_PREFIX + str(error)) - - def cleanup_port_forwarding(self): - try: - self.adb.remove_tcp_forward(self.grpc_port) - except AdbError as error: - msg = PORT_FORWARDING_ERROR_MSG_PREFIX + str(error) - if "not found" in msg: - logging.debug(msg) - else: - logging.error(msg) - - try: - if self.grpc_root_server_port != -1: - self.adb.remove_tcp_forward(self.grpc_root_server_port) - except AdbError as error: - msg = PORT_FORWARDING_ERROR_MSG_PREFIX + str(error) - if "not found" in msg: - logging.debug(msg) - else: - logging.error(msg) - - try: - self.adb.reverse(["--remove", "tcp:%d" % self.signal_port]) - except AdbError as error: - msg = PORT_FORWARDING_ERROR_MSG_PREFIX + str(error) - if "not found" in msg: - logging.debug(msg) - else: - logging.error(msg) - - @staticmethod - def ensure_no_output(result): - """ - Ensure a command has not output - """ - asserts.assert_true(result is None or len(result) == 0, - msg="command returned something when it shouldn't: %s" % result) - - def sync_device_time(self): - self.adb.shell("settings put global auto_time 0") - self.adb.shell("settings put global auto_time_zone 0") - device_tz = self.adb.shell("date +%z").decode(UTF_8).rstrip() - asserts.assert_true(device_tz, "date +%z must return device timezone, " - "but returned {} instead".format(device_tz)) - host_tz = time.strftime("%z") - if device_tz != host_tz: - target_timezone = utils.get_timezone_olson_id() - logging.debug("Device timezone %s does not match host timezone %s, " - "syncing them by setting timezone to %s" % (device_tz, host_tz, target_timezone)) - self.adb.shell("setprop persist.sys.timezone %s" % target_timezone) - self.reboot() - self.adb.remount() - device_tz = self.adb.shell("date +%z").decode(UTF_8).rstrip() - asserts.assert_equal( - host_tz, device_tz, "Device timezone %s still does not match host " - "timezone %s after reset" % (device_tz, host_tz)) - self.adb.shell("date %s" % time.strftime("%m%d%H%M%Y.%S")) - datetime_format = "%Y-%m-%dT%H:%M:%S%z" - try: - device_time = datetime.strptime( - self.adb.shell("date +'%s'" % datetime_format).decode(UTF_8).rstrip(), datetime_format) - except ValueError: - asserts.fail("Failed to get time after sync") - return - # Include ADB delay that might be longer in SSH environment - max_delta_seconds = 3 - host_time = datetime.now(tz=device_time.tzinfo) - asserts.assert_almost_equal((device_time - host_time).total_seconds(), - 0, - msg="Device time %s and host time %s off by >%dms after sync" % - (device_time.isoformat(), host_time.isoformat(), int(max_delta_seconds * 1000)), - delta=max_delta_seconds) - - def push_or_die(self, src_file_path, dst_file_path, push_timeout=300, overwrite_existing=True): - """Pushes a file to the Android device - - Args: - src_file_path: The path to the file to install. - dst_file_path: The destination of the file. - push_timeout: How long to wait for the push to finish in seconds - """ - if not overwrite_existing and self.adb.path_exists(dst_file_path): - logging.debug("Skip pushing {} to {} as it already exists on device".format(src_file_path, dst_file_path)) - return - out = self.adb.push([src_file_path, dst_file_path], timeout=push_timeout).decode(UTF_8).rstrip() - if 'error' in out: - asserts.fail('Unable to push file %s to %s due to %s' % (src_file_path, dst_file_path, out)) - - def tcp_forward_or_die(self, host_port, device_port, num_retry=1): - """ - Forward a TCP port from host to device or fail - :param host_port: host port, int, 0 for adb to assign one - :param device_port: device port, int - :param num_retry: number of times to reboot and retry this before dying - :return: host port int - """ - error_or_port = self.adb.tcp_forward(host_port, device_port) - if not error_or_port: - logging.debug("host port %d was already forwarded" % host_port) - return host_port - if not isinstance(error_or_port, int): - if num_retry > 0: - # If requested, reboot an retry - num_retry -= 1 - logging.warning("[%s] Failed to TCP forward host port %d to " - "device port %d, num_retries left is %d" % - (self.label, host_port, device_port, num_retry)) - self.reboot() - self.adb.remount() - return self.tcp_forward_or_die(host_port, device_port, num_retry=num_retry) - asserts.fail('Unable to forward host port %d to device port %d, error %s' % - (host_port, device_port, error_or_port)) - return error_or_port - - def tcp_reverse_or_die(self, device_port, host_port, num_retry=1): - """ - Forward a TCP port from device to host or fail - :param device_port: device port, int, 0 for adb to assign one - :param host_port: host port, int - :param num_retry: number of times to reboot and retry this before dying - :return: device port int - """ - error_or_port = self.adb.reverse(["tcp:%d" % device_port, "tcp:%d" % host_port]) - if not error_or_port: - logging.debug("device port %d was already reversed" % device_port) - return device_port - try: - error_or_port = int(error_or_port) - except ValueError: - if num_retry > 0: - # If requested, reboot an retry - num_retry -= 1 - logging.warning("[%s] Failed to TCP reverse device port %d to " - "host port %d, num_retries left is %d" % - (self.label, device_port, host_port, num_retry)) - self.reboot() - self.adb.remount() - return self.tcp_reverse_or_die(device_port, host_port, num_retry=num_retry) - asserts.fail('Unable to reverse device port %d to host port %d, error %s' % - (device_port, host_port, error_or_port)) - return error_or_port - - def ensure_verity_disabled(self): - """Ensures that verity is enabled. - - If verity is not enabled, this call will reboot the phone. Note that - this only works on debuggable builds. - """ - logging.debug("Disabling verity and remount for %s", self.serial_number) - # The below properties will only exist if verity has been enabled. - system_verity = self.adb.getprop('partition.system.verified') - vendor_verity = self.adb.getprop('partition.vendor.verified') - if system_verity or vendor_verity: - self.adb.disable_verity() - self.reboot() - self.adb.remount() - self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS) - - def reboot(self, timeout_minutes=15.0): - """Reboots the device. - - Reboot the device, wait for device to complete booting. - """ - logging.debug("Rebooting %s", self.serial_number) - self.adb.reboot() - - timeout_start = time.time() - timeout = timeout_minutes * 60 - # Android sometimes return early after `adb reboot` is called. This - # means subsequent calls may make it to the device before the reboot - # goes through, return false positives for getprops such as - # sys.boot_completed. - while time.time() < timeout_start + timeout: - try: - logging.debug("waiting for device %s to turn off", self.serial_number) - self.adb.get_state() - logging.debug("device %s not turned off yet", self.serial_number) - time.sleep(.1) - except AdbError: - # get_state will raise an error if the device is not found. We - # want the device to be missing to prove the device has kicked - # off the reboot. - logging.debug("device %s is turned off, waiting for it to boot", self.serial_number) - break - minutes_left = timeout_minutes - (time.time() - timeout_start) / 60.0 - self.wait_for_boot_completion(timeout_minutes=minutes_left) - asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root after reboot" % self.serial_number) - - def wait_for_boot_completion(self, timeout_minutes=15.0): - """ - Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. - :param timeout_minutes: number of minutes to wait - """ - timeout_start = time.time() - timeout = timeout_minutes * 60 - - self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS) - while time.time() < timeout_start + timeout: - try: - completed = self.adb.getprop("sys.boot_completed") - if completed == '1': - return - except AdbError: - # adb shell calls may fail during certain period of booting - # process, which is normal. Ignoring these errors. - pass - time.sleep(5) - asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) - - def gracefully_stop_backing_process(self): - """ - Gracefully stops backing process - :return: expected backing process exit code on success, 0 on error - """ - backing_process_pid = None - # Since we do not know which segment of self.cmd is the command running - # on the Android device. We have to iterate with trial and error. - cmd = self.cmd - if len(self.cmd) >= 5: - # skip adb -s serial shell to speed up the search - # we don't know if any environment variables are set up before the - # actual command and hence has to try from the 4th argument - cmd = self.cmd[4:] + self.cmd[:4] - for segment in cmd: - try: - # pgrep only takes 16 bytes including null terminator - # -f cannot be used because that include the full command args - current_cmd = pathlib.Path(segment).stem[:15] - # -x matches whole command, cannot avoid as short segment may partially match - # -n returnes the newest command matched - backing_process_pid = int(self.adb.shell("pgrep -n -x {}".format(current_cmd))) - logging.debug("Found backing process name on Android as {}, pid is {}".format( - segment, backing_process_pid)) - except (AdbError, ValueError) as e: - logging.debug("Failed to run pgrep {}".format(e)) - if backing_process_pid is not None: - break - if backing_process_pid is None: - logging.warning("Failed to get pid for cmd {}".format(self.cmd)) - try: - logging.debug(self.adb.shell("ps -A | grep bluetooth")) - except AdbError: - pass - return 0 - stop_signal = signal.SIGINT - self.adb.shell("kill -{} {}".format(stop_signal, backing_process_pid)) - logging.debug("Sent SIGINT to backing process at pid {}".format(backing_process_pid)) - stop_signal = -self.ADB_ABORT_EXIT_CODE - return stop_signal diff --git a/system/blueberry/tests/gd/cert/logging_client_interceptor.py b/system/blueberry/tests/gd/cert/logging_client_interceptor.py deleted file mode 100644 index cd48395524..0000000000 --- a/system/blueberry/tests/gd/cert/logging_client_interceptor.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import grpc -import re - -from blueberry.facade import common_pb2 as common -from google.protobuf import text_format - - -def custom_message_formatter(m, ident, as_one_line): - if m.DESCRIPTOR == common.Data.DESCRIPTOR: - return 'payload: (hex) "{}"'.format(m.payload.hex(" ")) - return None - - -def pretty_print(request): - return '{} {}'.format( - type(request).__name__, - text_format.MessageToString(request, as_one_line=True, message_formatter=custom_message_formatter)) - - -class LoggingRandezvousWrapper(): - - def __init__(self, server_stream_call, logTag): - if server_stream_call is None: - raise ValueError("server_stream_call cannot be None") - self.server_stream_call = server_stream_call - self.logTag = logTag - - def cancel(self): - self.server_stream_call.cancel() - - def cancelled(self): - return self.server_stream_call.cancelled() - - def __iter__(self): - return self - - def __next__(self): - resp = self.server_stream_call.__next__() - print("%s %s" % (self.logTag, pretty_print(resp))) - return resp - - -class LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor): - - TAG_MIN_WIDTH = 24 - - def __init__(self, name): - self.name = name - self.inLogTag = "[host ▶▶▶▶▶ %s]" % self.name - self.outLogTag = "[host ◀◀◀◀◀ %s]" % self.name - tagLength = len(re.sub('[^\w\s]', '', self.inLogTag)) + 11 - if tagLength < self.TAG_MIN_WIDTH: - self.inLogTag += " " * (self.TAG_MIN_WIDTH - tagLength) - self.outLogTag += " " * (self.TAG_MIN_WIDTH - tagLength) - - def intercept_unary_unary(self, continuation, client_call_details, request): - """ - This interceptor logs the requests from host - """ - print("%s%s %s" % (self.inLogTag, client_call_details.method, pretty_print(request))) - return continuation(client_call_details, request) - - def intercept_unary_stream(self, continuation, client_call_details, request): - """ - This interceptor wraps the server response, and logs all the messages coming to host - """ - print("%s%s %s" % (self.inLogTag, client_call_details.method, pretty_print(request))) - server_stream_call = continuation(client_call_details, request) - retuningMsgLogTag = self.outLogTag + client_call_details.method - return LoggingRandezvousWrapper(server_stream_call, retuningMsgLogTag) diff --git a/system/blueberry/tests/gd/cert/matchers.py b/system/blueberry/tests/gd/cert/matchers.py deleted file mode 100644 index 780fe2ba08..0000000000 --- a/system/blueberry/tests/gd/cert/matchers.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import sys - -from blueberry.utils import bluetooth -import hci_packets as hci - - -class HciMatchers(object): - - @staticmethod - def CommandComplete(opcode): - return lambda msg: HciMatchers._is_matching_command_complete(msg.payload, opcode) - - @staticmethod - def ExtractMatchingCommandComplete(packet_bytes, opcode=None): - return HciMatchers._extract_matching_command_complete(packet_bytes, opcode) - - @staticmethod - def _is_matching_command_complete(packet_bytes, opcode=None): - return HciMatchers._extract_matching_command_complete(packet_bytes, opcode) is not None - - @staticmethod - def _extract_matching_command_complete(packet_bytes, opcode=None): - event = HciMatchers._extract_matching_event(packet_bytes, hci.EventCode.COMMAND_COMPLETE) - if not isinstance(event, hci.CommandComplete): - return None - if opcode and event.command_op_code != opcode: - return None - return event - - @staticmethod - def CommandStatus(opcode=None): - return lambda msg: HciMatchers._is_matching_command_status(msg.payload, opcode) - - @staticmethod - def ExtractMatchingCommandStatus(packet_bytes, opcode=None): - return HciMatchers._extract_matching_command_status(packet_bytes, opcode) - - @staticmethod - def _is_matching_command_status(packet_bytes, opcode=None): - return HciMatchers._extract_matching_command_status(packet_bytes, opcode) is not None - - @staticmethod - def _extract_matching_command_status(packet_bytes, opcode=None): - event = HciMatchers._extract_matching_event(packet_bytes, hci.EventCode.COMMAND_STATUS) - if not isinstance(event, hci.CommandStatus): - return None - if opcode and event.command_op_code != opcode: - return None - return event - - @staticmethod - def EventWithCode(event_code): - return lambda msg: HciMatchers._is_matching_event(msg.payload, event_code) - - @staticmethod - def ExtractEventWithCode(packet_bytes, event_code): - return HciMatchers._extract_matching_event(packet_bytes, event_code) - - @staticmethod - def _is_matching_event(packet_bytes, event_code): - return HciMatchers._extract_matching_event(packet_bytes, event_code) is not None - - @staticmethod - def _extract_matching_event(packet_bytes, event_code): - try: - event = hci.Event.parse_all(packet_bytes) - return event if event.event_code == event_code else None - except Exception as exn: - print(sys.stderr, f"Failed to parse incoming event: {exn}") - print(sys.stderr, f"Event data: {' '.join([f'{b:02x}' for b in packet_bytes])}") - return None - - @staticmethod - def LeEventWithCode(subevent_code): - return lambda msg: HciMatchers._extract_matching_le_event(msg.payload, subevent_code) is not None - - @staticmethod - def ExtractLeEventWithCode(packet_bytes, subevent_code): - return HciMatchers._extract_matching_le_event(packet_bytes, subevent_code) - - @staticmethod - def _extract_matching_le_event(packet_bytes, subevent_code): - event = HciMatchers._extract_matching_event(packet_bytes, hci.EventCode.LE_META_EVENT) - if (not isinstance(event, hci.LeMetaEvent) or event.subevent_code != subevent_code): - return None - - return event - - @staticmethod - def LeAdvertisement(subevent_code=hci.SubeventCode.EXTENDED_ADVERTISING_REPORT, address=None, data=None): - return lambda msg: HciMatchers._extract_matching_le_advertisement(msg.payload, subevent_code, address, data - ) is not None - - @staticmethod - def ExtractLeAdvertisement(packet_bytes, - subevent_code=hci.SubeventCode.EXTENDED_ADVERTISING_REPORT, - address=None, - data=None): - return HciMatchers._extract_matching_le_advertisement(packet_bytes, subevent_code, address, data) - - @staticmethod - def _extract_matching_le_advertisement(packet_bytes, - subevent_code=hci.SubeventCode.EXTENDED_ADVERTISING_REPORT, - address=None, - data=None): - event = HciMatchers._extract_matching_le_event(packet_bytes, subevent_code) - if event is None: - return None - - matched = False - for response in event.responses: - matched |= (address == None or response.address == bluetooth.Address(address)) and (data == None or - data in packet_bytes) - - return event if matched else None - - @staticmethod - def LeConnectionComplete(): - return lambda msg: HciMatchers._extract_le_connection_complete(msg.payload) is not None - - @staticmethod - def ExtractLeConnectionComplete(packet_bytes): - return HciMatchers._extract_le_connection_complete(packet_bytes) - - @staticmethod - def _extract_le_connection_complete(packet_bytes): - event = HciMatchers._extract_matching_le_event(packet_bytes, hci.SubeventCode.CONNECTION_COMPLETE) - if event is not None: - return event - - return HciMatchers._extract_matching_le_event(packet_bytes, hci.SubeventCode.ENHANCED_CONNECTION_COMPLETE) - - @staticmethod - def LogEventCode(): - return lambda event: logging.info("Received event: %x" % hci.Event.parse(event.payload).event_code) - - @staticmethod - def LinkKeyRequest(): - return HciMatchers.EventWithCode(hci.EventCode.LINK_KEY_REQUEST) - - @staticmethod - def IoCapabilityRequest(): - return HciMatchers.EventWithCode(hci.EventCode.IO_CAPABILITY_REQUEST) - - @staticmethod - def IoCapabilityResponse(): - return HciMatchers.EventWithCode(hci.EventCode.IO_CAPABILITY_RESPONSE) - - @staticmethod - def UserPasskeyNotification(): - return HciMatchers.EventWithCode(hci.EventCode.USER_PASSKEY_NOTIFICATION) - - @staticmethod - def UserPasskeyRequest(): - return HciMatchers.EventWithCode(hci.EventCode.USER_PASSKEY_REQUEST) - - @staticmethod - def UserConfirmationRequest(): - return HciMatchers.EventWithCode(hci.EventCode.USER_CONFIRMATION_REQUEST) - - @staticmethod - def LinkKeyNotification(): - return HciMatchers.EventWithCode(hci.EventCode.LINK_KEY_NOTIFICATION) - - @staticmethod - def SimplePairingComplete(): - return HciMatchers.EventWithCode(hci.EventCode.SIMPLE_PAIRING_COMPLETE) - - @staticmethod - def Disconnect(): - return HciMatchers.EventWithCode(hci.EventCode.DISCONNECT) - - @staticmethod - def DisconnectionComplete(): - return HciMatchers.EventWithCode(hci.EventCode.DISCONNECTION_COMPLETE) - - @staticmethod - def RemoteOobDataRequest(): - return HciMatchers.EventWithCode(hci.EventCode.REMOTE_OOB_DATA_REQUEST) - - @staticmethod - def PinCodeRequest(): - return HciMatchers.EventWithCode(hci.EventCode.PIN_CODE_REQUEST) - - @staticmethod - def LoopbackOf(packet): - return HciMatchers.Exactly(hci.LoopbackCommand(payload=packet)) - - @staticmethod - def Exactly(packet): - data = bytes(packet.serialize()) - return lambda event: data == event.payload - - -class AdvertisingMatchers(object): - - @staticmethod - def AdvertisingCallbackMsg(type, advertiser_id=None, status=None, data=None): - return lambda event: True if event.message_type == type and (advertiser_id == None or advertiser_id == event.advertiser_id) \ - and (status == None or status == event.status) and (data == None or data == event.data) else False - - @staticmethod - def AddressMsg(type, advertiser_id=None, address=None): - return lambda event: True if event.message_type == type and (advertiser_id == None or advertiser_id == event.advertiser_id) \ - and (address == None or address == event.address) else False - - -class ScanningMatchers(object): - - @staticmethod - def ScanningCallbackMsg(type, status=None, data=None): - return lambda event: True if event.message_type == type and (status == None or status == event.status) \ - and (data == None or data == event.data) else False - - -class NeighborMatchers(object): - - @staticmethod - def InquiryResult(address): - return lambda msg: NeighborMatchers._is_matching_inquiry_result(msg.packet, address) - - @staticmethod - def _is_matching_inquiry_result(packet, address): - event = HciMatchers.ExtractEventWithCode(packet, hci.EventCode.INQUIRY_RESULT) - if not isinstance(event, hci.InquiryResult): - return False - return any((bluetooth.Address(address) == response.bd_addr for response in event.responses)) - - @staticmethod - def InquiryResultwithRssi(address): - return lambda msg: NeighborMatchers._is_matching_inquiry_result_with_rssi(msg.packet, address) - - @staticmethod - def _is_matching_inquiry_result_with_rssi(packet, address): - event = HciMatchers.ExtractEventWithCode(packet, hci.EventCode.INQUIRY_RESULT_WITH_RSSI) - if not isinstance(event, hci.InquiryResultWithRssi): - return False - return any((bluetooth.Address(address) == response.address for response in event.responses)) - - @staticmethod - def ExtendedInquiryResult(address): - return lambda msg: NeighborMatchers._is_matching_extended_inquiry_result(msg.packet, address) - - @staticmethod - def _is_matching_extended_inquiry_result(packet, address): - event = HciMatchers.ExtractEventWithCode(packet, hci.EventCode.EXTENDED_INQUIRY_RESULT) - if not isinstance(event, (hci.ExtendedInquiryResult, hci.ExtendedInquiryResultRaw)): - return False - return bluetooth.Address(address) == event.address - - -class SecurityMatchers(object): - - @staticmethod - def UiMsg(type, address=None): - return lambda event: True if event.message_type == type and (address == None or address == event.peer - ) else False - - @staticmethod - def BondMsg(type, address=None, reason=None): - return lambda event: True if event.message_type == type and (address == None or address == event.peer) and ( - reason == None or reason == event.reason) else False - - @staticmethod - def HelperMsg(type, address=None): - return lambda event: True if event.message_type == type and (address == None or address == event.peer - ) else False - - -class IsoMatchers(object): - - @staticmethod - def Data(payload): - return lambda packet: packet.payload == payload - - @staticmethod - def PacketPayloadWithMatchingCisHandle(cis_handle): - return lambda packet: None if cis_handle != packet.handle else packet diff --git a/system/blueberry/tests/gd/cert/metadata.py b/system/blueberry/tests/gd/cert/metadata.py deleted file mode 100644 index aa1bc9a8bf..0000000000 --- a/system/blueberry/tests/gd/cert/metadata.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import functools -import inspect - -from mobly import asserts - -from blueberry.tests.gd.cert.test_decorators import test_info - - -def _fail_decorator(msg): - - def fail_decorator(func): - - @functools.wraps(func) - def fail(*args, **kwargs): - asserts.fail(msg) - - return fail - - return fail_decorator - - -def metadata(_do_not_use=None, pts_test_id=None, pts_test_name=None): - """ - Record a piece of test metadata in the Extra section of the test Record in - the test summary file. The metadata will come with a timestamp, but there - is no guarantee on the order of when the metadata will be written - - Note: - - Metadata is recorded per test case as key-value pairs. - - Metadata is only guaranteed to be written when the test result is PASS, - FAIL or SKIPPED. When there are test infrastructural errors, metadata - might not be written successfully - :param _do_not_use: a positional argument with default value. This argument - is to ensure that @metadata(key=value) is used in a - functional form instead of @metadata or @metadata(a) - :param pts_test_id: A fully qualified PTS test ID such as - L2CAP/COS/IEX/BV-01-C - :param pts_test_name: A human readable test name such as - "Request Connection" for the above example - :return: decorated test case function object - """ - if _do_not_use is not None: - - def fail(*args, **kwargs): - asserts.fail("@metadata must be used in functional form such " "as @metadta(key=value)") - - return fail - - # Create a dictionary of optional parameters - values = locals() - args = {arg: values[arg] for arg in inspect.getfullargspec(metadata).args} - del args["_do_not_use"] - - # Check if at least one optional parameter is valid - if not any(args.values()): - return _fail_decorator("at least one optional argument should be valid") - - # Validate pts_test_id and pts_test_name - if any((pts_test_id, pts_test_name)) and \ - not all((pts_test_id, pts_test_name)): - return _fail_decorator("pts_test_id and pts_test_name must both " "be valid if one of them is valid") - - return test_info(**args) diff --git a/system/blueberry/tests/gd/cert/os_utils.py b/system/blueberry/tests/gd/cert/os_utils.py deleted file mode 100644 index 31e4a0ebab..0000000000 --- a/system/blueberry/tests/gd/cert/os_utils.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -from pathlib import Path -import psutil -import re -import subprocess -from typing import Container -from collections import deque - - -class TerminalColor: - RED = "\033[31;1m" - BLUE = "\033[34;1m" - YELLOW = "\033[33;1m" - MAGENTA = "\033[35;1m" - END = "\033[0m" - - -def is_subprocess_alive(process, timeout_seconds=1): - """ - Check if a process is alive for at least timeout_seconds - :param process: a Popen object that represent a subprocess - :param timeout_seconds: process needs to be alive for at least - timeout_seconds - :return: True if process is alive for at least timeout_seconds - """ - try: - process.wait(timeout=timeout_seconds) - return False - except subprocess.TimeoutExpired as exp: - return True - - -def get_gd_root(): - """ - Return the root of the GD test library - - GD root is the parent directory of blueberry/tests/gd/cert - :return: root directory string of gd test library - """ - return str(Path(__file__).absolute().parents[4]) - - -def make_ports_available(ports: Container[int], timeout_seconds=10): - """Make sure a list of ports are available - kill occupying process if possible - :param ports: list of target ports - :param timeout_seconds: number of seconds to wait when killing processes - :return: True on success, False on failure - """ - if not ports: - logging.warning("Empty ports is given to make_ports_available()") - return True - # Get connections whose state are in LISTEN only - # Connections in other states won't affect binding as SO_REUSEADDR is used - listening_conns_for_port = filter( - lambda conn: (conn and conn.status == psutil.CONN_LISTEN and conn.laddr and conn.laddr.port in ports), - psutil.net_connections()) - success = True - killed_pids = set() - for conn in listening_conns_for_port: - logging.warning("Freeing port %d used by %s" % (conn.laddr.port, str(conn))) - if not conn.pid: - logging.error("Failed to kill process occupying port %d due to lack of pid" % conn.laddr.port) - continue - logging.warning("Killing pid %d that is using port port %d" % (conn.pid, conn.laddr.port)) - if conn.pid in killed_pids: - logging.warning("Pid %d is already killed in previous iteration" % (conn.pid)) - continue - try: - process = psutil.Process(conn.pid) - process.kill() - process.wait(timeout=timeout_seconds) - killed_pids.add(conn.pid) - except psutil.NoSuchProcess: - logging.warning("Pid %d is already dead before trying to kill it" % (conn.pid)) - killed_pids.add(conn.pid) - continue - except psutil.TimeoutExpired: - logging.error("SIGKILL timeout after %d seconds for pid %d" % (timeout_seconds, conn.pid)) - success = False - break - return success - - -# e.g. 2020-05-06 16:02:04.216 bt - packages/modules/Bluetooth/system/gd/facade/facade_main.cc:79 - crash_callback: #03 pc 0000000000013520 /lib/x86_64-linux-gnu/libpthread-2.29.so -HOST_CRASH_LINE_REGEX = re.compile(r"^.* - crash_callback: (?P<line>.*)$") -HOST_ABORT_HEADER = "Process crashed, signal: Aborted" -ASAN_OUTPUT_START_REGEX = re.compile(r"^==.*AddressSanitizer.*$") - - -def read_crash_snippet_and_log_tail(logpath): - """ - Get crash snippet if regex matched or last 20 lines of log - :return: crash_snippet, log_tail_20 - 1) crash snippet without timestamp in one string; - 2) last 20 lines of log in one string; - """ - gd_root_prefix = get_gd_root() + "/" - abort_line = None - last_20_lines = deque(maxlen=20) - crash_log_lines = [] - asan = False - asan_lines = [] - - try: - with open(logpath) as f: - for _, line in enumerate(f): - last_20_lines.append(line) - asan_match = ASAN_OUTPUT_START_REGEX.match(line) - if asan or asan_match: - asan_lines.append(line) - asan = True - continue - - host_crash_match = HOST_CRASH_LINE_REGEX.match(line) - if host_crash_match: - crash_line = host_crash_match.group("line").replace(gd_root_prefix, "") - if HOST_ABORT_HEADER in crash_line \ - and len(last_20_lines) > 1: - abort_line = last_20_lines[-2] - crash_log_lines.append(crash_line) - except EnvironmentError: - logging.error("Cannot open backing log file at {}".format(logpath)) - return None, None - - log_tail_20 = "".join(last_20_lines) - crash_snippet = "" - if abort_line is not None: - crash_snippet += "abort log line:\n\n%s\n" % abort_line - crash_snippet += "\n".join(crash_log_lines) - - if len(asan_lines) > 0: - return "".join(asan_lines), log_tail_20 - - if len(crash_log_lines) > 0: - return crash_snippet, log_tail_20 - - return None, log_tail_20 diff --git a/system/blueberry/tests/gd/cert/performance_test_logger.py b/system/blueberry/tests/gd/cert/performance_test_logger.py deleted file mode 100644 index 006bbd6aa6..0000000000 --- a/system/blueberry/tests/gd/cert/performance_test_logger.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from datetime import datetime - - -class PerformanceTestLogger(object): - """ - A helper class to log time points and intervals - """ - - def __init__(self): - self.base_timepoint = datetime.now() - # We use a dictionary of a list of timepoints - self.start_interval_points = {} - self.end_interval_points = {} - self.single_points = {} - - def log_single_point(self, label=""): - if label not in self.single_points: - self.single_points[label] = [] - self.single_points[label].append(datetime.now()) - - def start_interval(self, label=""): - if label not in self.start_interval_points: - self.start_interval_points[label] = [] - self.start_interval_points[label].append(datetime.now()) - - def end_interval(self, label=""): - if label not in self.end_interval_points: - self.end_interval_points[label] = [] - self.end_interval_points[label].append(datetime.now()) - - def _check_interval_label(self, label): - if label not in self.start_interval_points or label not in self.end_interval_points: - raise KeyError("label %s doesn't exist" % label) - if len(self.start_interval_points[label]) != len(self.end_interval_points[label]): - raise KeyError("label %s doesn't have correct start and end log" % label) - - def get_duration_of_intervals(self, label): - """ - Return the list of duration of the intervals with specified label. - """ - self._check_interval_label(label) - intervals = [] - for i in range(len(self.start_interval_points[label])): - interval = self.end_interval_points[label][i] - self.start_interval_points[label][i] - intervals.append(interval) - return intervals - - def dump_intervals(self): - """ - Gives an iterator of (iterator of label, start, end) over all labels - """ - for label in self.start_interval_points: - self._check_interval_label(label) - yield ((label, self.start_interval_points[label][i], self.end_interval_points[label][i]) - for i in range(len(self.start_interval_points[label]))) diff --git a/system/blueberry/tests/gd/cert/pts_base_test.py b/system/blueberry/tests/gd/cert/pts_base_test.py deleted file mode 100644 index 0e8b99a715..0000000000 --- a/system/blueberry/tests/gd/cert/pts_base_test.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from mobly import base_test - -import importlib - - -class PTSBaseTestClass(base_test.BaseTestClass): - - def __init__(self, configs): - BaseTestClass.__init__(self, configs) - - gd_devices = self.controller_configs.get("GdDevice") - - self.register_controller(importlib.import_module('cert.gd_device'), builtin=True) diff --git a/system/blueberry/tests/gd/cert/py_acl_manager.py b/system/blueberry/tests/gd/cert/py_acl_manager.py deleted file mode 100644 index ddb93d2430..0000000000 --- a/system/blueberry/tests/gd/cert/py_acl_manager.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from google.protobuf import empty_pb2 as empty_proto -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.event_stream import IEventStream -from blueberry.tests.gd.cert.captures import HciCaptures -from blueberry.tests.gd.cert.closable import Closable -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.facade.hci import acl_manager_facade_pb2 as acl_manager_facade -from blueberry.utils import bluetooth -import hci_packets as hci - - -class PyAclManagerAclConnection(IEventStream, Closable): - - def __init__(self, acl_manager, remote_addr, handle, event_stream): - self.acl_manager = acl_manager - self.handle = handle - self.remote_addr = remote_addr - self.connection_event_stream = event_stream - self.acl_stream = EventStream(self.acl_manager.FetchAclData(acl_manager_facade.HandleMsg(handle=self.handle))) - - def disconnect(self, reason): - packet_bytes = hci.Disconnect(connection_handle=self.handle, reason=reason).serialize() - self.acl_manager.ConnectionCommand(acl_manager_facade.ConnectionCommandMsg(packet=packet_bytes)) - - def close(self): - safeClose(self.connection_event_stream) - safeClose(self.acl_stream) - - def wait_for_disconnection_complete(self): - disconnection_complete = HciCaptures.DisconnectionCompleteCapture() - assertThat(self.connection_event_stream).emits(disconnection_complete) - self.disconnect_reason = disconnection_complete.get().reason - - def send(self, data): - self.acl_manager.SendAclData(acl_manager_facade.AclData(handle=self.handle, payload=bytes(data))) - - def get_event_queue(self): - return self.acl_stream.get_event_queue() - - -class PyAclManager: - - def __init__(self, device): - self.acl_manager = device.hci_acl_manager - self.incoming_connection_event_stream = None - self.outgoing_connection_event_stream = None - - def close(self): - safeClose(self.incoming_connection_event_stream) - safeClose(self.outgoing_connection_event_stream) - - def listen_for_an_incoming_connection(self): - assertThat(self.incoming_connection_event_stream).isNone() - self.incoming_connection_event_stream = EventStream( - self.acl_manager.FetchIncomingConnection(empty_proto.Empty())) - - def initiate_connection(self, remote_addr): - assertThat(self.outgoing_connection_event_stream).isNone() - remote_addr_bytes = remote_addr if isinstance(remote_addr, bytes) else remote_addr.encode('utf-8') - self.outgoing_connection_event_stream = EventStream( - self.acl_manager.CreateConnection(acl_manager_facade.ConnectionMsg(address=remote_addr_bytes))) - - def complete_connection(self, event_stream): - connection_complete = HciCaptures.ConnectionCompleteCapture() - assertThat(event_stream).emits(connection_complete) - complete = connection_complete.get() - handle = complete.connection_handle - address = repr(complete.bd_addr) - return PyAclManagerAclConnection(self.acl_manager, address, handle, event_stream) - - def complete_incoming_connection(self): - assertThat(self.incoming_connection_event_stream).isNotNone() - event_stream = self.incoming_connection_event_stream - self.incoming_connection_event_stream = None - return self.complete_connection(event_stream) - - def complete_outgoing_connection(self): - assertThat(self.outgoing_connection_event_stream).isNotNone() - event_stream = self.outgoing_connection_event_stream - self.outgoing_connection_event_stream = None - return self.complete_connection(event_stream) diff --git a/system/blueberry/tests/gd/cert/py_hal.py b/system/blueberry/tests/gd/cert/py_hal.py deleted file mode 100644 index 074b2ddb87..0000000000 --- a/system/blueberry/tests/gd/cert/py_hal.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from google.protobuf import empty_pb2 as empty_proto -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.event_stream import FilteringEventStream -from blueberry.tests.gd.cert.event_stream import IEventStream -from blueberry.tests.gd.cert.closable import Closable -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.captures import HciCaptures -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert.matchers import HciMatchers -from blueberry.facade import common_pb2 as common -import hci_packets as hci -from blueberry.utils import bluetooth - - -class PyHalAclConnection(IEventStream): - - def __init__(self, handle, acl_stream, device): - self.handle = int(handle) - self.device = device - self.our_acl_stream = FilteringEventStream(acl_stream, None) - - def send(self, pb_flag, b_flag, data: bytes): - assert isinstance(data, bytes) - acl = hci.Acl(handle=self.handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.device.hal.SendAcl(common.Data(payload=acl.serialize())) - - def send_first(self, data: bytes): - assert isinstance(data, bytes) - self.send(hci.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE, hci.BroadcastFlag.POINT_TO_POINT, data) - - def get_event_queue(self): - return self.our_acl_stream.get_event_queue() - - -class PyHalAdvertisement(object): - - def __init__(self, handle, py_hal, is_legacy): - self.handle = handle - self.py_hal = py_hal - self.legacy = is_legacy - - def set_data(self, complete_name): - advertising_data = [hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(complete_name))] - - if self.legacy: - self.py_hal.send_hci_command(hci.LeSetAdvertisingData(advertising_data=advertising_data)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_ADVERTISING_DATA) - else: - self.py_hal.send_hci_command( - hci.LeSetExtendedAdvertisingData(advertising_handle=self.handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - advertising_data=advertising_data)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_DATA) - - def set_scan_response(self, shortened_name): - advertising_data = [hci.GapData(data_type=hci.GapDataType.SHORTENED_LOCAL_NAME, data=list(shortened_name))] - - if self.legacy: - self.py_hal.send_hci_command(hci.LeSetScanResponseData(advertising_data=advertising_data)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_SCAN_RESPONSE_DATA) - else: - self.py_hal.send_hci_command( - hci.LeSetExtendedScanResponseData(advertising_handle=self.handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - scan_response_data=advertising_data)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_SCAN_RESPONSE_DATA) - - def start(self): - if self.legacy: - self.py_hal.send_hci_command(hci.LeSetAdvertisingEnable(advertising_enable=hci.Enable.ENABLED)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_ADVERTISING_ENABLE) - else: - self.py_hal.send_hci_command( - hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.ENABLED, - enabled_sets=[ - hci.EnabledSet(advertising_handle=self.handle, - duration=0, - max_extended_advertising_events=0) - ])) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_ENABLE) - - def stop(self): - if self.legacy: - self.py_hal.send_hci_command(hci.LeSetAdvertisingEnable(advertising_enable=hci.Enable.DISABLED)) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_ADVERTISING_ENABLE) - else: - self.py_hal.send_hci_command( - hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.DISABLED, - enabled_sets=[ - hci.EnabledSet(advertising_handle=self.handle, - duration=0, - max_extended_advertising_events=0) - ])) - self.py_hal.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_ENABLE) - - -class PyHal(Closable): - - def __init__(self, device): - self.device = device - - self.hci_event_stream = EventStream(self.device.hal.StreamEvents(empty_proto.Empty())) - self.acl_stream = EventStream(self.device.hal.StreamAcl(empty_proto.Empty())) - - self.event_mask = 0x1FFF_FFFF_FFFF # Default Event Mask (Core Vol 4 [E] 7.3.1) - self.le_event_mask = 0x0000_0000_001F # Default LE Event Mask (Core Vol 4 [E] 7.8.1) - - # We don't deal with SCO for now - - def close(self): - safeClose(self.hci_event_stream) - safeClose(self.acl_stream) - - def get_hci_event_stream(self): - return self.hci_event_stream - - def wait_for_complete(self, opcode): - assertThat(self.hci_event_stream).emits(HciMatchers.CommandComplete(opcode)) - - def wait_for_status(self, opcode): - assertThat(self.hci_event_stream).emits(HciMatchers.CommandStatus(opcode)) - - def get_acl_stream(self): - return self.acl_stream - - def send_hci_command(self, command: hci.Packet): - self.device.hal.SendCommand(common.Data(payload=command.serialize())) - - def send_acl(self, handle, pb_flag, b_flag, data: bytes): - acl = hci.Acl(handle=handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.device.hal.SendAcl(common.Data(payload=acl.serialize())) - - def send_acl_first(self, handle, data: bytes): - self.send_acl(handle, hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, - hci.BroadcastFlag.POINT_TO_POINT, data) - - def read_own_address(self) -> bluetooth.Address: - self.send_hci_command(hci.ReadBdAddr()) - read_bd_addr = HciCaptures.ReadBdAddrCompleteCapture() - assertThat(self.hci_event_stream).emits(read_bd_addr) - return read_bd_addr.get().bd_addr - - def set_random_le_address(self, addr): - self.send_hci_command(hci.LeSetRandomAddress(random_address=bluetooth.Address(addr))) - self.wait_for_complete(hci.OpCode.LE_SET_RANDOM_ADDRESS) - - def set_scan_parameters(self): - self.send_hci_command( - hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL, - scanning_phys=1, - parameters=[ - hci.PhyScanParameters(le_scan_type=hci.LeScanType.ACTIVE, - le_scan_interval=6553, - le_scan_window=6553) - ])) - self.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_SCAN_PARAMETERS) - - def unmask_event(self, *event_codes): - for event_code in event_codes: - self.event_mask |= 1 << (int(event_code) - 1) - self.send_hci_command(hci.SetEventMask(event_mask=self.event_mask)) - - def unmask_le_event(self, *subevent_codes): - for subevent_code in subevent_codes: - self.le_event_mask |= 1 << (int(subevent_code) - 1) - self.send_hci_command(hci.LeSetEventMask(le_event_mask=self.le_event_mask)) - - def start_scanning(self): - self.send_hci_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - self.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_SCAN_ENABLE) - - def stop_scanning(self): - self.send_hci_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.DISABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - self.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_SCAN_ENABLE) - - def reset(self): - self.send_hci_command(hci.Reset()) - self.wait_for_complete(hci.OpCode.RESET) - - def enable_inquiry_and_page_scan(self): - self.send_hci_command(hci.WriteScanEnable(scan_enable=hci.ScanEnable.INQUIRY_AND_PAGE_SCAN)) - - def initiate_connection(self, remote_addr): - self.send_hci_command( - hci.CreateConnection(bd_addr=bluetooth.Address(remote_addr), - packet_type=0xcc18, - page_scan_repetition_mode=hci.PageScanRepetitionMode.R1, - clock_offset=0x0, - clock_offset_valid=hci.ClockOffsetValid.INVALID, - allow_role_switch=hci.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH)) - - def accept_connection(self): - connection_request = HciCaptures.ConnectionRequestCapture() - assertThat(self.hci_event_stream).emits(connection_request) - - self.send_hci_command( - hci.AcceptConnectionRequest(bd_addr=connection_request.get().bd_addr, - role=hci.AcceptConnectionRequestRole.REMAIN_PERIPHERAL)) - return self.complete_connection() - - def complete_connection(self): - connection_complete = HciCaptures.ConnectionCompleteCapture() - assertThat(self.hci_event_stream).emits(connection_complete) - - handle = connection_complete.get().connection_handle - return PyHalAclConnection(handle, self.acl_stream, self.device) - - def initiate_le_connection(self, remote_addr): - self.send_hci_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_PEER_ADDRESS, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, - peer_address=bluetooth.Address(remote_addr), - initiating_phys=1, - phy_scan_parameters=[ - hci.LeCreateConnPhyScanParameters(scan_interval=0x60, - scan_window=0x30, - conn_interval_min=0x18, - conn_interval_max=0x28, - conn_latency=0, - supervision_timeout=0x1f4, - min_ce_length=0, - max_ce_length=0) - ])) - self.wait_for_status(hci.OpCode.LE_EXTENDED_CREATE_CONNECTION) - - def add_to_filter_accept_list(self, remote_addr): - self.send_hci_command( - hci.LeAddDeviceToFilterAcceptList(address_type=hci.FilterAcceptListAddressType.RANDOM, - address=bluetooth.Address(remote_addr))) - - def initiate_le_connection_by_filter_accept_list(self, remote_addr): - self.send_hci_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_FILTER_ACCEPT_LIST, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, - peer_address=bluetooth.Address('00:00:00:00:00:00'), - initiating_phys=1, - phy_scan_parameters=[ - hci.LeCreateConnPhyScanParameters(scan_interval=0x60, - scan_window=0x30, - conn_interval_min=0x18, - conn_interval_max=0x28, - conn_latency=0, - supervision_timeout=0x1f4, - min_ce_length=0, - max_ce_length=0) - ])) - - def complete_le_connection(self): - connection_complete = HciCaptures.LeConnectionCompleteCapture() - assertThat(self.hci_event_stream).emits(connection_complete) - - handle = connection_complete.get().connection_handle - return PyHalAclConnection(handle, self.acl_stream, self.device) - - def create_advertisement(self, - handle, - own_address: str, - properties=hci.LegacyAdvertisingEventProperties.ADV_IND, - min_interval=400, - max_interval=450, - channel_map=7, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - peer_address='00:00:00:00:00:00', - filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES, - tx_power=0xF8, - sid=1, - scan_request_notification=hci.Enable.DISABLED): - - self.send_hci_command( - hci.LeSetExtendedAdvertisingParametersLegacy(advertising_handle=handle, - legacy_advertising_event_properties=properties, - primary_advertising_interval_min=min_interval, - primary_advertising_interval_max=max_interval, - primary_advertising_channel_map=channel_map, - own_address_type=own_address_type, - peer_address_type=peer_address_type, - peer_address=bluetooth.Address(peer_address), - advertising_filter_policy=filter_policy, - advertising_tx_power=tx_power, - advertising_sid=sid, - scan_request_notification_enable=scan_request_notification)) - self.wait_for_complete(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_PARAMETERS) - self.send_hci_command( - hci.LeSetAdvertisingSetRandomAddress(advertising_handle=handle, - random_address=bluetooth.Address(own_address))) - self.wait_for_complete(hci.OpCode.LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) - - return PyHalAdvertisement(handle, self, False) - - def create_legacy_advertisement(self, - advertising_type=hci.AdvertisingType.ADV_IND, - min_interval=400, - max_interval=450, - channel_map=7, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - peer_address='00:00:00:00:00:00', - filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES): - - self.send_hci_command( - hci.LeSetAdvertisingParameters(advertising_interval_min=min_interval, - advertising_interval_max=max_interval, - advertising_type=advertising_type, - own_address_type=own_address_type, - peer_address_type=peer_address_type, - peer_address=bluetooth.Address(peer_address), - advertising_channel_map=channel_map, - advertising_filter_policy=filter_policy)) - self.wait_for_complete(hci.OpCode.LE_SET_ADVERTISING_PARAMETERS) - - return PyHalAdvertisement(None, self, True) diff --git a/system/blueberry/tests/gd/cert/py_hci.py b/system/blueberry/tests/gd/cert/py_hci.py deleted file mode 100644 index 67460657c9..0000000000 --- a/system/blueberry/tests/gd/cert/py_hci.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. -from datetime import timedelta - -from google.protobuf import empty_pb2 as empty_proto -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.event_stream import FilteringEventStream -from blueberry.tests.gd.cert.event_stream import IEventStream -from blueberry.tests.gd.cert.closable import Closable -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.captures import HciCaptures -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.facade.hci import hci_facade_pb2 as hci_facade -from blueberry.facade import common_pb2 as common -from blueberry.tests.gd.cert.matchers import HciMatchers -import hci_packets as hci -import blueberry.utils.bluetooth as bluetooth - - -class PyHciAclConnection(IEventStream): - - def __init__(self, handle, acl_stream, device): - self.handle = int(handle) - self.device = device - # todo, handle we got is 0, so doesn't match - fix before enabling filtering - self.our_acl_stream = FilteringEventStream(acl_stream, None) - - def send(self, pb_flag, b_flag, data: bytes): - assert isinstance(data, bytes) - acl = hci.Acl(handle=self.handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.device.hci.SendAcl(common.Data(payload=acl.serialize())) - - def send_first(self, data: bytes): - self.send(hci.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE, hci.BroadcastFlag.POINT_TO_POINT, data) - - def send_continuing(self, data): - self.send(hci.PacketBoundaryFlag.CONTINUING_FRAGMENT, hci.BroadcastFlag.POINT_TO_POINT, data) - - def get_event_queue(self): - return self.our_acl_stream.get_event_queue() - - -class PyHciLeAclConnection(IEventStream): - - def __init__(self, handle, acl_stream, device, peer, peer_type, peer_resolvable, local_resolvable): - self.handle = int(handle) - self.device = device - self.peer = peer - self.peer_type = peer_type - self.peer_resolvable = peer_resolvable - self.local_resolvable = local_resolvable - # todo, handle we got is 0, so doesn't match - fix before enabling filtering - self.our_acl_stream = FilteringEventStream(acl_stream, None) - - def send(self, pb_flag, b_flag, data: bytes): - assert isinstance(data, bytes) - acl = hci.Acl(handle=self.handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.device.hci.SendAcl(common.Data(payload=acl.serialize())) - - def send_first(self, data: bytes): - self.send(hci.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE, hci.BroadcastFlag.POINT_TO_POINT, data) - - def send_continuing(self, data: bytes): - self.send(hci.PacketBoundaryFlag.CONTINUING_FRAGMENT, hci.BroadcastFlag.POINT_TO_POINT, data) - - def get_event_queue(self): - return self.our_acl_stream.get_event_queue() - - def local_resolvable_address(self): - return self.local_resolvable - - def peer_resolvable_address(self): - return self.peer_resolvable - - def peer_address(self): - return self.peer - - -class PyHciAdvertisement(object): - - def __init__(self, handle, py_hci): - self.handle = handle - self.py_hci = py_hci - - def set_data(self, complete_name): - self.py_hci.send_command( - hci.LeSetExtendedAdvertisingData( - advertising_handle=self.handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - advertising_data=[hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, - data=list(complete_name))])) - - def set_scan_response(self, shortened_name): - self.py_hci.send_command( - hci.LeSetExtendedScanResponseData(advertising_handle=self.handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - scan_response_data=[ - hci.GapData(data_type=hci.GapDataType.SHORTENED_LOCAL_NAME, - data=list(shortened_name)) - ])) - - def start(self): - self.py_hci.send_command( - hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.ENABLED, - enabled_sets=[ - hci.EnabledSet(advertising_handle=self.handle, - duration=0, - max_extended_advertising_events=0) - ])) - assertThat(self.py_hci.get_event_stream()).emits( - HciMatchers.CommandComplete(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_ENABLE)) - - -class PyHci(Closable): - - event_stream = None - le_event_stream = None - acl_stream = None - - def __init__(self, device, acl_streaming=False): - """ - If you are planning on personally using the ACL data stream - coming from HCI, specify acl_streaming=True. You probably only - want this if you are testing HCI itself. - """ - self.device = device - self.event_stream = EventStream(self.device.hci.StreamEvents(empty_proto.Empty())) - self.le_event_stream = EventStream(self.device.hci.StreamLeSubevents(empty_proto.Empty())) - if acl_streaming: - self.register_for_events(hci.EventCode.ROLE_CHANGE, hci.EventCode.CONNECTION_REQUEST, - hci.EventCode.CONNECTION_COMPLETE, hci.EventCode.CONNECTION_PACKET_TYPE_CHANGED) - self.register_for_le_events(hci.SubeventCode.ENHANCED_CONNECTION_COMPLETE) - self.acl_stream = EventStream(self.device.hci.StreamAcl(empty_proto.Empty())) - - def close(self): - safeClose(self.event_stream) - safeClose(self.le_event_stream) - safeClose(self.acl_stream) - - def get_event_stream(self): - return self.event_stream - - def get_le_event_stream(self): - return self.le_event_stream - - def get_raw_acl_stream(self): - if self.acl_stream is None: - raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__) - return self.acl_stream - - def register_for_events(self, *event_codes): - for event_code in event_codes: - self.device.hci.RequestEvent(hci_facade.EventRequest(code=int(event_code))) - - def register_for_le_events(self, *event_codes): - for event_code in event_codes: - self.device.hci.RequestLeSubevent(hci_facade.EventRequest(code=int(event_code))) - - def send_command(self, command: hci.Packet): - self.device.hci.SendCommand(common.Data(payload=command.serialize())) - - def enable_inquiry_and_page_scan(self): - self.send_command(hci.WriteScanEnable(scan_enable=hci.ScanEnable.INQUIRY_AND_PAGE_SCAN)) - - def read_own_address(self) -> bluetooth.Address: - self.send_command(hci.ReadBdAddr()) - read_bd_addr = HciCaptures.ReadBdAddrCompleteCapture() - assertThat(self.event_stream).emits(read_bd_addr) - return read_bd_addr.get().bd_addr - - def initiate_connection(self, remote_addr): - self.send_command( - hci.CreateConnection(bd_addr=bluetooth.Address(remote_addr), - packet_type=0xcc18, - page_scan_repetition_mode=hci.PageScanRepetitionMode.R1, - clock_offset=0x0, - clock_offset_valid=hci.ClockOffsetValid.INVALID, - allow_role_switch=hci.CreateConnectionRoleSwitch.ALLOW_ROLE_SWITCH)) - - def accept_connection(self): - connection_request = HciCaptures.ConnectionRequestCapture() - assertThat(self.event_stream).emits(connection_request) - - self.send_command( - hci.AcceptConnectionRequest(bd_addr=bluetooth.Address(connection_request.get().bd_addr), - role=hci.AcceptConnectionRequestRole.REMAIN_PERIPHERAL)) - return self.complete_connection() - - def complete_connection(self): - connection_complete = HciCaptures.ConnectionCompleteCapture() - assertThat(self.event_stream).emits(connection_complete) - - handle = connection_complete.get().connection_handle - if self.acl_stream is None: - raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__) - return PyHciAclConnection(handle, self.acl_stream, self.device) - - def set_random_le_address(self, addr): - self.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address(addr))) - assertThat(self.event_stream).emits(HciMatchers.CommandComplete(hci.OpCode.LE_SET_RANDOM_ADDRESS)) - - def initiate_le_connection(self, remote_addr): - self.send_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_PEER_ADDRESS, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, - peer_address=bluetooth.Address(remote_addr), - initiating_phys=1, - phy_scan_parameters=[ - hci.LeCreateConnPhyScanParameters(scan_interval=0x60, - scan_window=0x30, - conn_interval_min=0x18, - conn_interval_max=0x28, - conn_latency=0, - supervision_timeout=0x1f4, - min_ce_length=0, - max_ce_length=0) - ])) - assertThat(self.event_stream).emits(HciMatchers.CommandStatus(hci.OpCode.LE_EXTENDED_CREATE_CONNECTION)) - - def incoming_le_connection(self): - connection_complete = HciCaptures.LeConnectionCompleteCapture() - assertThat(self.le_event_stream).emits(connection_complete) - - handle = connection_complete.get().connection_handle - peer = connection_complete.get().peer_address - peer_type = connection_complete.get().peer_address_type - local_resolvable = connection_complete.get().local_resolvable_private_address - peer_resolvable = connection_complete.get().peer_resolvable_private_address - if self.acl_stream is None: - raise Exception("Please construct '%s' with acl_streaming=True!" % self.__class__.__name__) - return PyHciLeAclConnection(handle, self.acl_stream, self.device, repr(peer), peer_type, repr(peer_resolvable), - repr(local_resolvable)) - - def incoming_le_connection_fails(self): - connection_complete = HciCaptures.LeConnectionCompleteCapture() - assertThat(self.le_event_stream).emitsNone(connection_complete, timeout=timedelta(seconds=5)) - - def add_device_to_resolving_list(self, peer_address_type, peer_address, peer_irk, local_irk): - self.send_command( - hci.LeAddDeviceToResolvingList(peer_identity_address_type=peer_address_type, - peer_identity_address=bluetooth.Address(peer_address), - peer_irk=peer_irk, - local_irk=local_irk)) - - def create_advertisement(self, - handle, - own_address: str, - properties=hci.LegacyAdvertisingEventProperties.ADV_IND, - min_interval=400, - max_interval=450, - channel_map=7, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - peer_address='00:00:00:00:00:00', - filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES, - tx_power=0xF8, - sid=1, - scan_request_notification=hci.Enable.DISABLED): - - self.send_command( - hci.LeSetExtendedAdvertisingParametersLegacy(advertising_handle=handle, - legacy_advertising_event_properties=properties, - primary_advertising_interval_min=min_interval, - primary_advertising_interval_max=max_interval, - primary_advertising_channel_map=channel_map, - own_address_type=own_address_type, - peer_address_type=peer_address_type, - peer_address=bluetooth.Address(peer_address), - advertising_filter_policy=filter_policy, - advertising_tx_power=tx_power, - advertising_sid=sid, - scan_request_notification_enable=scan_request_notification)) - - self.send_command( - hci.LeSetAdvertisingSetRandomAddress(advertising_handle=handle, - random_address=bluetooth.Address(own_address))) - - return PyHciAdvertisement(handle, self) diff --git a/system/blueberry/tests/gd/cert/py_le_acl_manager.py b/system/blueberry/tests/gd/cert/py_le_acl_manager.py deleted file mode 100644 index 94540a6b23..0000000000 --- a/system/blueberry/tests/gd/cert/py_le_acl_manager.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from google.protobuf import empty_pb2 as empty_proto - -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.event_stream import IEventStream -from blueberry.tests.gd.cert.captures import HciCaptures -from blueberry.tests.gd.cert.closable import Closable -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.truth import assertThat -from datetime import timedelta -from blueberry.facade.hci import le_acl_manager_facade_pb2 as le_acl_manager_facade -import hci_packets as hci - - -class PyLeAclManagerAclConnection(IEventStream, Closable): - - def __init__(self, le_acl_manager, address, remote_addr, remote_addr_type, handle, event_stream): - """ - An abstract representation for an LE ACL connection in GD certification test - :param le_acl_manager: The LeAclManager from this GD device - :param address: The local device address - :param remote_addr: Remote device address - :param remote_addr_type: Remote device address type - :param handle: Connection handle - :param event_stream: The connection event stream for this connection - """ - self.le_acl_manager = le_acl_manager - # todo enable filtering after sorting out handles - # self.our_acl_stream = FilteringEventStream(acl_stream, None) - self.handle = handle - self.connection_event_stream = event_stream - self.acl_stream = EventStream( - self.le_acl_manager.FetchAclData(le_acl_manager_facade.LeHandleMsg(handle=self.handle))) - self.remote_address = remote_addr - self.remote_address_type = remote_addr_type - self.own_address = address - self.disconnect_reason = None - - def close(self): - safeClose(self.connection_event_stream) - safeClose(self.acl_stream) - - def disconnect(self): - self.le_acl_manager.Disconnect(le_acl_manager_facade.LeHandleMsg(handle=self.handle)) - - def wait_for_disconnection_complete(self, timeout=timedelta(seconds=30)): - disconnection_complete = HciCaptures.DisconnectionCompleteCapture() - assertThat(self.connection_event_stream).emits(disconnection_complete, timeout=timeout) - self.disconnect_reason = disconnection_complete.get().reason - - def send(self, data): - self.le_acl_manager.SendAclData(le_acl_manager_facade.LeAclData(handle=self.handle, payload=bytes(data))) - - def get_event_queue(self): - return self.acl_stream.get_event_queue() - - -class PyLeAclManager(Closable): - - def __init__(self, device): - """ - LE ACL Manager for GD Certification test - :param device: The GD device - """ - self.le_acl_manager = device.hci_le_acl_manager - - self.incoming_connection_event_stream = None - self.outgoing_connection_event_streams = {} - self.active_connections = [] - self.next_token = 1 - - def close(self): - safeClose(self.incoming_connection_event_stream) - for v in self.outgoing_connection_event_streams.values(): - safeClose(v[0]) - for connection in self.active_connections: - safeClose(connection) - - def listen_for_incoming_connections(self): - assertThat(self.incoming_connection_event_stream).isNone() - self.incoming_connection_event_stream = EventStream( - self.le_acl_manager.FetchIncomingConnection(empty_proto.Empty())) - - def connect_to_remote(self, remote_addr): - token = self.initiate_connection(remote_addr) - return self.complete_outgoing_connection(token) - - def wait_for_connection(self): - self.listen_for_incoming_connections() - return self.complete_incoming_connection() - - def wait_for_connection_fail(self, token): - assertThat(self.outgoing_connection_event_streams[token]).isNotNone() - event_stream = self.outgoing_connection_event_streams[token][0] - connection_fail = HciCaptures.LeConnectionCompleteCapture() - assertThat(event_stream).emits(connection_fail, timeout=timedelta(seconds=35)) - complete = connection_fail.get() - assertThat(complete.status == hci.ErrorCode.CONNECTION_ACCEPT_TIMEOUT).isTrue() - - def cancel_connection(self, token): - assertThat(token in self.outgoing_connection_event_streams).isTrue() - pair = self.outgoing_connection_event_streams.pop(token) - safeClose(pair[0]) - self.le_acl_manager.CancelConnection(pair[1]) - - def initiate_connection(self, remote_addr, is_direct=True): - assertThat(self.next_token in self.outgoing_connection_event_streams).isFalse() - create_connection_msg = le_acl_manager_facade.CreateConnectionMsg(peer_address=remote_addr, is_direct=is_direct) - self.outgoing_connection_event_streams[self.next_token] = EventStream( - self.le_acl_manager.CreateConnection(create_connection_msg)), remote_addr - token = self.next_token - self.next_token += 1 - return token - - def is_on_background_list(self, remote_addr): - return self.le_acl_manager.IsOnBackgroundList( - le_acl_manager_facade.BackgroundRequestMsg(peer_address=remote_addr)) - - def remove_from_background_list(self, remote_addr): - self.le_acl_manager.RemoveFromBackgroundList( - le_acl_manager_facade.BackgroundRequestMsg(peer_address=remote_addr)) - - def complete_connection(self, event_stream): - connection_complete = HciCaptures.LeConnectionCompleteCapture() - assertThat(event_stream).emits(connection_complete) - complete = connection_complete.get() - handle = complete.connection_handle - remote = repr(complete.peer_address) - remote_address_type = complete.peer_address_type - if complete.subevent_code == hci.SubeventCode.ENHANCED_CONNECTION_COMPLETE: - address = complete.local_resolvable_private_address - else: - address = None - connection = PyLeAclManagerAclConnection(self.le_acl_manager, address, remote, remote_address_type, handle, - event_stream) - self.active_connections.append(connection) - return connection - - def complete_incoming_connection(self): - assertThat(self.incoming_connection_event_stream).isNotNone() - event_stream = self.incoming_connection_event_stream - self.incoming_connection_event_stream = None - return self.complete_connection(event_stream) - - def complete_outgoing_connection(self, token): - assertThat(self.outgoing_connection_event_streams[token]).isNotNone() - event_stream = self.outgoing_connection_event_streams.pop(token)[0] - return self.complete_connection(event_stream) diff --git a/system/blueberry/tests/gd/cert/test_decorators.py b/system/blueberry/tests/gd/cert/test_decorators.py deleted file mode 100644 index 453062e269..0000000000 --- a/system/blueberry/tests/gd/cert/test_decorators.py +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2017 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from mobly import signals - - -def test_info(predicate=None, **keyed_info): - """Adds info about test. - - Extra info to include about the test. This info will be available in the - test output. Note that if a key is given multiple times it will be added - as a list of all values. If multiples of these are stacked their results - will be merged. - - Example: - # This test will have a variable my_var - @test_info(my_var='THIS IS MY TEST') - def my_test(self): - return False - - Args: - predicate: A func to call that if false will skip adding this test - info. Function signature is bool(test_obj, args, kwargs) - **keyed_info: The key, value info to include in the extras for this - test. - """ - - def test_info_decorator(func): - return _TestInfoDecoratorFunc(func, predicate, keyed_info) - - return test_info_decorator - - -def __select_last(test_signals, _): - return test_signals[-1] - - -def repeated_test(num_passes, acceptable_failures=0, result_selector=__select_last): - """A decorator that runs a test case multiple times. - - This decorator can be used to run a test multiple times and aggregate the - data into a single test result. By setting `result_selector`, the user can - access the returned result of each run, allowing them to average results, - return the median, or gather and return standard deviation values. - - This decorator should be used on test cases, and should not be used on - static or class methods. The test case must take in an additional argument, - `attempt_number`, which returns the current attempt number, starting from - 1. - - Note that any TestSignal intended to abort or skip the test will take - abort or skip immediately. - - Args: - num_passes: The number of times the test needs to pass to report the - test case as passing. - acceptable_failures: The number of failures accepted. If the failures - exceeds this number, the test will stop repeating. The maximum - number of runs is `num_passes + acceptable_failures`. If the test - does fail, result_selector will still be called. - result_selector: A lambda that takes in the list of TestSignals and - returns the test signal to report the test case as. Note that the - list also contains any uncaught exceptions from the test execution. - """ - - def decorator(func): - if not func.__name__.startswith('test_'): - raise ValueError('Tests must start with "test_".') - - def test_wrapper(self): - num_failures = 0 - num_seen_passes = 0 - test_signals_received = [] - for i in range(num_passes + acceptable_failures): - try: - func(self, i + 1) - except (signals.TestFailure, signals.TestError, AssertionError) as signal: - test_signals_received.append(signal) - num_failures += 1 - except signals.TestPass as signal: - test_signals_received.append(signal) - num_seen_passes += 1 - except (signals.TestSignal, KeyboardInterrupt): - raise - except Exception as signal: - test_signals_received.append(signal) - num_failures += 1 - else: - num_seen_passes += 1 - test_signals_received.append( - signals.TestPass('Test iteration %s of %s passed without details.' % (i, func.__name__))) - - if num_failures > acceptable_failures: - break - elif num_seen_passes == num_passes: - break - else: - self.teardown_test() - self.setup_test() - - raise result_selector(test_signals_received, self) - - return test_wrapper - - return decorator - - -def test_tracker_info(uuid, extra_environment_info=None, predicate=None): - """Decorator for adding test tracker info to tests results. - - Will add test tracker info inside of Extras/test_tracker_info. - - Example: - # This test will be linked to test tracker uuid abcd - @test_tracker_info(uuid='abcd') - def my_test(self): - return False - - Args: - uuid: The uuid of the test case in test tracker. - extra_environment_info: Extra info about the test tracker environment. - predicate: A func that if false when called will ignore this info. - """ - return test_info(test_tracker_uuid=uuid, test_tracker_environment_info=extra_environment_info, predicate=predicate) - - -class _TestInfoDecoratorFunc(object): - """Object that acts as a function decorator test info.""" - - def __init__(self, func, predicate, keyed_info): - self.func = func - self.predicate = predicate - self.keyed_info = keyed_info - self.__name__ = func.__name__ - self.__doc__ = func.__doc__ - self.__module__ = func.__module__ - - def __get__(self, instance, owner): - """Called by Python to create a binding for an instance closure. - - When called by Python this object will create a special binding for - that instance. That binding will know how to interact with this - specific decorator. - """ - return _TestInfoBinding(self, instance) - - def __call__(self, *args, **kwargs): - """ - When called runs the underlying func and then attaches test info - to a signal. - """ - cause = None - try: - result = self.func(*args, **kwargs) - - if result or result is None: - new_signal = signals.TestPass('') - else: - new_signal = signals.TestFailure('') - except signals.TestSignal as signal: - new_signal = signal - except Exception as ex: - cause = ex - new_signal = signals.TestError(cause) - - if new_signal.extras is None: - new_signal.extras = {} - if not isinstance(new_signal.extras, dict): - raise ValueError('test_info can only append to signal data that has a dict as the extra value.') - - gathered_extras = self._gather_local_info(None, *args, **kwargs) - for k, v in gathered_extras.items(): - if k not in new_signal.extras: - new_signal.extras[k] = v - else: - if not isinstance(new_signal.extras[k], list): - new_signal.extras[k] = [new_signal.extras[k]] - - new_signal.extras[k].insert(0, v) - - raise new_signal from cause - - def gather(self, *args, **kwargs): - """ - Gathers the info from this decorator without invoking the underlying - function. This will also gather all child info if the underlying func - has that ability. - - Returns: A dictionary of info. - """ - if hasattr(self.func, 'gather'): - extras = self.func.gather(*args, **kwargs) - else: - extras = {} - - self._gather_local_info(extras, *args, **kwargs) - - return extras - - def _gather_local_info(self, gather_into, *args, **kwargs): - """Gathers info from this decorator and ignores children. - - Args: - gather_into: Gathers into a dictionary that already exists. - - Returns: The dictionary with gathered info in it. - """ - if gather_into is None: - extras = {} - else: - extras = gather_into - if not self.predicate or self.predicate(args, kwargs): - for k, v in self.keyed_info.items(): - if v and k not in extras: - extras[k] = v - elif v and k in extras: - if not isinstance(extras[k], list): - extras[k] = [extras[k]] - extras[k].insert(0, v) - - return extras - - -class _TestInfoBinding(object): - """ - When Python creates an instance of an object it creates a binding object - for each closure that contains what the instance variable should be when - called. This object is a similar binding for _TestInfoDecoratorFunc. - When Python tries to create a binding of a _TestInfoDecoratorFunc it - will return one of these objects to hold the instance for that closure. - """ - - def __init__(self, target, instance): - """ - Args: - target: The target for creating a binding to. - instance: The instance to bind the target with. - """ - self.target = target - self.instance = instance - self.__name__ = target.__name__ - - def __call__(self, *args, **kwargs): - """ - When this object is called it will call the target with the bound - instance. - """ - return self.target(self.instance, *args, **kwargs) - - def gather(self, *args, **kwargs): - """ - Will gather the target with the bound instance. - """ - return self.target.gather(self.instance, *args, **kwargs) diff --git a/system/blueberry/tests/gd/cert/tracelogger.py b/system/blueberry/tests/gd/cert/tracelogger.py deleted file mode 100644 index a4e5a2203c..0000000000 --- a/system/blueberry/tests/gd/cert/tracelogger.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2016 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import inspect -import os - - -class TraceLogger(object): - - def __init__(self, logger): - self._logger = logger - - @staticmethod - def _get_trace_info(level=1, offset=2): - # We want the stack frame above this and above the error/warning/info - inspect_stack = inspect.stack() - trace_info = '' - for i in range(level): - try: - stack_frames = inspect_stack[offset + i] - info = inspect.getframeinfo(stack_frames[0]) - trace_info = '%s[%s:%s:%s]' % (trace_info, os.path.basename(info.filename), info.function, info.lineno) - except IndexError: - break - return trace_info - - def _log_with(self, logging_lambda, trace_level, msg, *args, **kwargs): - trace_info = TraceLogger._get_trace_info(level=trace_level, offset=3) - logging_lambda('%s %s' % (msg, trace_info), *args, **kwargs) - - def exception(self, msg, *args, **kwargs): - self._log_with(self._logger.exception, 5, msg, *args, **kwargs) - - def debug(self, msg, *args, **kwargs): - self._log_with(self._logger.debug, 3, msg, *args, **kwargs) - - def error(self, msg, *args, **kwargs): - self._log_with(self._logger.error, 3, msg, *args, **kwargs) - - def warn(self, msg, *args, **kwargs): - self._log_with(self._logger.warn, 3, msg, *args, **kwargs) - - def warning(self, msg, *args, **kwargs): - self._log_with(self._logger.warning, 3, msg, *args, **kwargs) - - def info(self, msg, *args, **kwargs): - self._log_with(self._logger.info, 1, msg, *args, **kwargs) - - def __getattr__(self, name): - return getattr(self._logger, name) diff --git a/system/blueberry/tests/gd/cert/truth.py b/system/blueberry/tests/gd/cert/truth.py deleted file mode 100644 index e3133eb435..0000000000 --- a/system/blueberry/tests/gd/cert/truth.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from datetime import timedelta - -from mobly.asserts import assert_true -from mobly.asserts import assert_false -from mobly import signals - -from blueberry.tests.gd.cert.event_stream import IEventStream -from blueberry.tests.gd.cert.event_stream import NOT_FOR_YOU_assert_event_occurs -from blueberry.tests.gd.cert.event_stream import NOT_FOR_YOU_assert_all_events_occur -from blueberry.tests.gd.cert.event_stream import NOT_FOR_YOU_assert_none_matching -from blueberry.tests.gd.cert.event_stream import NOT_FOR_YOU_assert_none - - -class ObjectSubject(object): - - def __init__(self, value): - self._value = value - - def isEqualTo(self, other): - if self._value != other: - raise signals.TestFailure("Expected \"%s\" to be equal to \"%s\"" % (self._value, other), extras=None) - - def isNotEqualTo(self, other): - if self._value == other: - raise signals.TestFailure("Expected \"%s\" not to be equal to \"%s\"" % (self._value, other), extras=None) - - def isGreaterThan(self, other): - if self._value <= other: - raise signals.TestFailure("Expected \"%s\" to be greater than \"%s\"" % (self._value, other), extras=None) - - def isLessThan(self, other): - if self._value >= other: - raise signals.TestFailure("Expected \"%s\" to be less than \"%s\"" % (self._value, other), extras=None) - - def isNone(self): - if self._value is not None: - raise signals.TestFailure("Expected \"%s\" to be None" % self._value, extras=None) - - def isNotNone(self): - if self._value is None: - raise signals.TestFailure("Expected \"%s\" to not be None" % self._value, extras=None) - - -DEFAULT_TIMEOUT = timedelta(seconds=30) - - -class EventStreamSubject(ObjectSubject): - - def __init__(self, value): - super().__init__(value) - - def emits(self, *match_fns, at_least_times=1, timeout=DEFAULT_TIMEOUT): - if len(match_fns) == 0: - raise signals.TestFailure("Must specify a match function") - elif len(match_fns) == 1: - NOT_FOR_YOU_assert_event_occurs(self._value, match_fns[0], at_least_times=at_least_times, timeout=timeout) - return EventStreamContinuationSubject(self._value) - else: - return MultiMatchStreamSubject(self._value, match_fns, timeout) - - def emitsNone(self, *match_fns, timeout): - if len(match_fns) == 0: - NOT_FOR_YOU_assert_none(self._value, timeout=timeout) - return EventStreamContinuationSubject(self._value) - elif len(match_fns) == 1: - NOT_FOR_YOU_assert_none_matching(self._value, match_fns[0], timeout=timeout) - return EventStreamContinuationSubject(self._value) - else: - raise signals.TestFailure("Cannot specify multiple match functions") - - -class MultiMatchStreamSubject(object): - - def __init__(self, stream, match_fns, timeout): - self._stream = stream - self._match_fns = match_fns - self._timeout = timeout - - def inAnyOrder(self): - NOT_FOR_YOU_assert_all_events_occur(self._stream, self._match_fns, order_matters=False, timeout=self._timeout) - return EventStreamContinuationSubject(self._stream) - - def inOrder(self): - NOT_FOR_YOU_assert_all_events_occur(self._stream, self._match_fns, order_matters=True, timeout=self._timeout) - return EventStreamContinuationSubject(self._stream) - - -class EventStreamContinuationSubject(ObjectSubject): - - def __init__(self, value): - super().__init__(value) - - def then(self, *match_fns, at_least_times=1, timeout=DEFAULT_TIMEOUT): - if len(match_fns) == 0: - raise signals.TestFailure("Must specify a match function") - elif len(match_fns) == 1: - NOT_FOR_YOU_assert_event_occurs(self._value, match_fns[0], at_least_times=at_least_times, timeout=timeout) - return EventStreamContinuationSubject(self._value) - else: - return MultiMatchStreamSubject(self._value, match_fns, timeout) - - def thenNone(self, *match_fns, timeout): - if len(match_fns) == 0: - NOT_FOR_YOU_assert_none(self._value, timeout=timeout) - return EventStreamContinuationSubject(self._value) - elif len(match_fns) == 1: - NOT_FOR_YOU_assert_none_matching(self._value, match_fns[0], timeout=timeout) - return EventStreamContinuationSubject(self._value) - else: - raise signals.TestFailure("Cannot specify multiple match functions") - - -class BooleanSubject(ObjectSubject): - - def __init__(self, value): - super().__init__(value) - - def isTrue(self): - assert_true(self._value, "") - - def isFalse(self): - assert_false(self._value, "") - - -class TimeDeltaSubject(ObjectSubject): - - def __init__(self, value): - super().__init__(value) - - def isWithin(self, time_bound): - assert_true(self._value < time_bound, "") - - -def assertThat(subject): - if type(subject) is bool: - return BooleanSubject(subject) - elif isinstance(subject, IEventStream): - return EventStreamSubject(subject) - elif isinstance(subject, timedelta): - return TimeDeltaSubject(subject) - else: - return ObjectSubject(subject) diff --git a/system/blueberry/tests/gd/devices_config.yaml b/system/blueberry/tests/gd/devices_config.yaml deleted file mode 100644 index 2f8bbc82e0..0000000000 --- a/system/blueberry/tests/gd/devices_config.yaml +++ /dev/null @@ -1,44 +0,0 @@ -_description: Bluetooth cert testing -TestBeds: - - Name: AndroidDeviceCert - Controllers: - GdDevice: - - grpc_port: '8898' - grpc_root_server_port: '8896' - signal_port: '8894' - label: cert - serial_number: 'CERT' - name: Cert Device - cmd: - - "adb" - - "-s" - - "$(serial_number)" - - "shell" - - "ASAN_OPTIONS=detect_container_overflow=0" - - "/system/bin/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--btsnoop=/data/misc/bluetooth/logs/btsnoop_hci.log" - - "--btsnooz=/data/misc/bluetooth/logs/btsnooz_hci.log" - - "--btconfig=/data/misc/bluedroid/bt_config.conf" - - "--signal-port=$(signal_port)" - - grpc_port: '8899' - grpc_root_server_port: '8897' - signal_port: '8895' - label: dut - serial_number: 'DUT' - name: DUT Device - cmd: - - "adb" - - "-s" - - "$(serial_number)" - - "shell" - - "ASAN_OPTIONS=detect_container_overflow=0" - - "/system/bin/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--btsnoop=/data/misc/bluetooth/logs/btsnoop_hci.log" - - "--btsnooz=/data/misc/bluetooth/logs/btsnooz_hci.log" - - "--btconfig=/data/misc/bluedroid/bt_config.conf" - - "--signal-port=$(signal_port)" -logpath: "/tmp/logs"
\ No newline at end of file diff --git a/system/blueberry/tests/gd/gd_all_tests.py b/system/blueberry/tests/gd/gd_all_tests.py deleted file mode 100644 index 69fed0b1f0..0000000000 --- a/system/blueberry/tests/gd/gd_all_tests.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.cert_self_test import CertSelfTest -from blueberry.tests.gd.hal.simple_hal_test import SimpleHalTest -from blueberry.tests.gd.hci.acl_manager_test import AclManagerTest -from blueberry.tests.gd.hci.controller_test import ControllerTest -from blueberry.tests.gd.hci.direct_hci_test import DirectHciTest -from blueberry.tests.gd.hci.le_acl_manager_test import LeAclManagerTest -from blueberry.tests.gd.hci.le_advertising_manager_test import LeAdvertisingManagerTest -from blueberry.tests.gd.hci.le_scanning_manager_test import LeScanningManagerTest -from blueberry.tests.gd.hci.le_scanning_with_security_test import LeScanningWithSecurityTest -from blueberry.tests.gd.iso.le_iso_test import LeIsoTest -from blueberry.tests.gd.shim.shim_test import ShimTest -from blueberry.tests.gd.shim.stack_test import StackTest - -from mobly import suite_runner - -ALL_TESTS = { - CertSelfTest, SimpleHalTest, AclManagerTest, ControllerTest, DirectHciTest, LeAclManagerTest, - LeAdvertisingManagerTest, LeScanningManagerTest, LeScanningWithSecurityTest, LeIsoTest, - ShimTest, StackTest -} - -DISABLED_TESTS = set() - -ENABLED_TESTS = list(ALL_TESTS - DISABLED_TESTS) - -if __name__ == '__main__': - suite_runner.run_suite(ENABLED_TESTS) diff --git a/system/blueberry/tests/gd/gd_postsubmit_tests.py b/system/blueberry/tests/gd/gd_postsubmit_tests.py deleted file mode 100644 index 41cd09ee56..0000000000 --- a/system/blueberry/tests/gd/gd_postsubmit_tests.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.gd_all_tests import ALL_TESTS - -from mobly import suite_runner - -DISABLED_TESTS = set() - -POSTSUBMIT_TESTS = list(ALL_TESTS - DISABLED_TESTS) - -if __name__ == '__main__': - suite_runner.run_suite(POSTSUBMIT_TESTS) diff --git a/system/blueberry/tests/gd/gd_presubmit_tests.py b/system/blueberry/tests/gd/gd_presubmit_tests.py deleted file mode 100644 index 633d637c23..0000000000 --- a/system/blueberry/tests/gd/gd_presubmit_tests.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.gd_all_tests import ALL_TESTS - -from mobly import suite_runner - -# TODO(b/194723246): Investigate failures to re-activate the test class. -from blueberry.tests.gd.hci.le_scanning_manager_test import LeScanningManagerTest - -DISABLED_TESTS = {LeScanningManagerTest} - -PRESUBMIT_TESTS = list(ALL_TESTS - DISABLED_TESTS) - -if __name__ == '__main__': - suite_runner.run_suite(PRESUBMIT_TESTS) diff --git a/system/blueberry/tests/gd/gd_test_runner.py b/system/blueberry/tests/gd/gd_test_runner.py deleted file mode 100644 index cc2a1d2336..0000000000 --- a/system/blueberry/tests/gd/gd_test_runner.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.gd_all_tests import ALL_TESTS -from blueberry.tests.gd.gd_presubmit_tests import PRESUBMIT_TESTS -from blueberry.tests.gd.gd_postsubmit_tests import POSTSUBMIT_TESTS - -from mobly import suite_runner -import sys -import argparse -import pathlib - -DISABLED_TESTS = set() - -ENABLED_TESTS = list(ALL_TESTS - DISABLED_TESTS) - - -def main(): - """ - Local test runner that allows to specify list of tests to and customize - test config file location - """ - parser = argparse.ArgumentParser(description="Run local GD cert tests.") - parser.add_argument( - '-c', '--config', type=str, required=True, metavar='<PATH>', help='Path to the test configuration file.') - parser.add_argument( - '--tests', - '--test_case', - nargs='+', - type=str, - metavar='[ClassA[.test_a] ClassB[.test_b] ...]', - help='A list of test classes and optional tests to execute.') - parser.add_argument("--all_tests", "-A", type=bool, dest="all_tests", default=False, nargs="?") - parser.add_argument("--presubmit", type=bool, dest="presubmit", default=False, nargs="?") - parser.add_argument("--postsubmit", type=bool, dest="postsubmit", default=False, nargs="?") - args = parser.parse_args() - test_list = ALL_TESTS - if args.all_tests: - test_list = ALL_TESTS - elif args.presubmit: - test_list = PRESUBMIT_TESTS - elif args.postsubmit: - test_list = POSTSUBMIT_TESTS - # Do not pass this layer's cmd line argument to next layer - argv = ["--config", args.config] - if args.tests: - argv.append("--tests") - for test in args.tests: - argv.append(test) - - suite_runner.run_suite(test_list, argv=argv) - - -if __name__ == "__main__": - main() diff --git a/system/blueberry/tests/gd/hal/simple_hal_test.py b/system/blueberry/tests/gd/hal/simple_hal_test.py deleted file mode 100644 index 0a9fef101c..0000000000 --- a/system/blueberry/tests/gd/hal/simple_hal_test.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert.py_hal import PyHal -from blueberry.tests.gd.cert.matchers import HciMatchers -from blueberry.tests.gd.cert import gd_base_test -from blueberry.utils import bluetooth -from mobly import test_runner -import hci_packets as hci - -_GRPC_TIMEOUT = 10 - - -class SimpleHalTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HAL', cert_module='HAL') - - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - self.dut_hal = PyHal(self.dut) - self.cert_hal = PyHal(self.cert) - - self.dut_hal.reset() - self.cert_hal.reset() - - def teardown_test(self): - self.dut_hal.close() - self.cert_hal.close() - gd_base_test.GdBaseTestClass.teardown_test(self) - - def test_stream_events(self): - self.dut_hal.send_hci_command( - hci.LeAddDeviceToFilterAcceptList(address_type=hci.FilterAcceptListAddressType.RANDOM, - address=bluetooth.Address('0C:05:04:03:02:01'))) - assertThat(self.dut_hal.get_hci_event_stream()).emits( - HciMatchers.Exactly( - hci.LeAddDeviceToFilterAcceptListComplete(num_hci_command_packets=1, status=hci.ErrorCode.SUCCESS))) - - def test_loopback_hci_command(self): - self.dut_hal.send_hci_command(hci.WriteLoopbackMode(loopback_mode=hci.LoopbackMode.ENABLE_LOCAL)) - - command = hci.LeAddDeviceToFilterAcceptList(address_type=hci.FilterAcceptListAddressType.RANDOM, - address=bluetooth.Address('0C:05:04:03:02:01')) - self.dut_hal.send_hci_command(command) - - assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LoopbackOf(command.serialize())) - - def test_inquiry_from_dut(self): - self.cert_hal.send_hci_command(hci.WriteScanEnable(scan_enable=hci.ScanEnable.INQUIRY_AND_PAGE_SCAN)) - - self.dut_hal.send_hci_command(hci.Inquiry(lap=hci.Lap(lap=0x33), inquiry_length=0x30, num_responses=0xff)) - - assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'\x02\x0f' in packet.payload - # Expecting an HCI Event (code 0x02, length 0x0f) - ) - - def test_le_ad_scan_cert_advertises(self): - self.dut_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.dut_hal.unmask_le_event(hci.SubeventCode.EXTENDED_ADVERTISING_REPORT) - self.dut_hal.set_random_le_address('0D:05:04:03:02:01') - - self.dut_hal.set_scan_parameters() - self.dut_hal.start_scanning() - - advertisement = self.cert_hal.create_advertisement(0, - '0C:05:04:03:02:01', - min_interval=512, - max_interval=768, - peer_address='A6:A5:A4:A3:A2:A1', - tx_power=0x7f, - sid=1) - advertisement.set_data(b'Im_A_Cert') - advertisement.start() - - assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload) - - advertisement.stop() - - self.dut_hal.stop_scanning() - - def test_le_connection_dut_advertises(self): - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.cert_hal.set_random_le_address('0C:05:04:03:02:01') - self.cert_hal.initiate_le_connection('0D:05:04:03:02:01') - - # DUT Advertises - self.dut_hal.unmask_event(hci.EventCode.LE_META_EVENT) - advertisement = self.dut_hal.create_advertisement(0, '0D:05:04:03:02:01') - advertisement.set_data(b'Im_The_DUT') - advertisement.set_scan_response(b'Im_The_D') - advertisement.start() - - cert_acl = self.cert_hal.complete_le_connection() - dut_acl = self.dut_hal.complete_le_connection() - - dut_acl.send_first(b'Just SomeAclData') - cert_acl.send_first(b'Just SomeMoreAclData') - - assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload) - assertThat(self.dut_hal.get_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - - def test_le_filter_accept_list_connection_cert_advertises(self): - self.dut_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.dut_hal.set_random_le_address('0D:05:04:03:02:01') - self.dut_hal.add_to_filter_accept_list('0C:05:04:03:02:01') - self.dut_hal.initiate_le_connection_by_filter_accept_list('BA:D5:A4:A3:A2:A1') - - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - advertisement = self.cert_hal.create_advertisement(1, - '0C:05:04:03:02:01', - min_interval=512, - max_interval=768, - peer_address='A6:A5:A4:A3:A2:A1', - tx_power=0x7F, - sid=0) - advertisement.set_data(b'Im_A_Cert') - advertisement.start() - - assertThat(self.cert_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete()) - assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete()) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/acl_manager_test.py b/system/blueberry/tests/gd/hci/acl_manager_test.py deleted file mode 100644 index b01e0265e3..0000000000 --- a/system/blueberry/tests/gd/hci/acl_manager_test.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from blueberry.tests.gd.cert import gd_base_test -from blueberry.tests.gd.cert.py_hci import PyHci -from blueberry.tests.gd.cert.py_acl_manager import PyAclManager -from blueberry.tests.gd.cert.truth import assertThat -from datetime import timedelta -from blueberry.facade.neighbor import facade_pb2 as neighbor_facade -from mobly import test_runner -import hci_packets as hci - - -class AclManagerTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') - - # todo: move into GdBaseTestClass, based on modules inited - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - self.cert_hci = PyHci(self.cert, acl_streaming=True) - self.dut_acl_manager = PyAclManager(self.dut) - - def teardown_test(self): - self.cert_hci.close() - gd_base_test.GdBaseTestClass.teardown_test(self) - - def test_dut_connects(self): - self.cert_hci.enable_inquiry_and_page_scan() - cert_address = self.cert_hci.read_own_address() - - self.dut_acl_manager.initiate_connection(repr(cert_address)) - cert_acl = self.cert_hci.accept_connection() - with self.dut_acl_manager.complete_outgoing_connection() as dut_acl: - cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert') - dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT') - - assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload) - - def test_cert_connects(self): - dut_address = self.dut.hci_controller.GetMacAddressSimple() - self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True)) - - self.dut_acl_manager.listen_for_an_incoming_connection() - self.cert_hci.initiate_connection(dut_address) - with self.dut_acl_manager.complete_incoming_connection() as dut_acl: - cert_acl = self.cert_hci.complete_connection() - - dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT') - - cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert') - - assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload) - - def test_reject_broadcast(self): - dut_address = self.dut.hci_controller.GetMacAddressSimple() - self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True)) - - self.dut_acl_manager.listen_for_an_incoming_connection() - self.cert_hci.initiate_connection(dut_address) - with self.dut_acl_manager.complete_incoming_connection() as dut_acl: - cert_acl = self.cert_hci.complete_connection() - - cert_acl.send(hci.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE, - hci.BroadcastFlag.ACTIVE_PERIPHERAL_BROADCAST, - b'\x26\x00\x07\x00This is a Broadcast from the Cert') - assertThat(dut_acl).emitsNone(timeout=timedelta(seconds=0.5)) - - cert_acl.send(hci.PacketBoundaryFlag.FIRST_AUTOMATICALLY_FLUSHABLE, hci.BroadcastFlag.POINT_TO_POINT, - b'\x26\x00\x07\x00This is just SomeAclData from the Cert') - assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload) - - def test_cert_connects_disconnects(self): - dut_address = self.dut.hci_controller.GetMacAddressSimple() - self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True)) - - self.dut_acl_manager.listen_for_an_incoming_connection() - self.cert_hci.initiate_connection(dut_address) - with self.dut_acl_manager.complete_incoming_connection() as dut_acl: - cert_acl = self.cert_hci.complete_connection() - - dut_acl.send(b'\x29\x00\x07\x00This is just SomeMoreAclData from the DUT') - - cert_acl.send_first(b'\x26\x00\x07\x00This is just SomeAclData from the Cert') - - assertThat(cert_acl).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - assertThat(dut_acl).emits(lambda packet: b'SomeAclData' in packet.payload) - - dut_acl.disconnect(hci.DisconnectReason.REMOTE_USER_TERMINATED_CONNECTION) - dut_acl.wait_for_disconnection_complete() - - def test_recombination_l2cap_packet(self): - self.cert_hci.enable_inquiry_and_page_scan() - cert_address = self.cert_hci.read_own_address() - - self.dut_acl_manager.initiate_connection(repr(cert_address)) - cert_acl = self.cert_hci.accept_connection() - with self.dut_acl_manager.complete_outgoing_connection() as dut_acl: - cert_acl.send_first(b'\x06\x00\x07\x00Hello') - cert_acl.send_continuing(b'!') - cert_acl.send_first(b'\xe8\x03\x07\x00' + b'Hello' * 200) - - assertThat(dut_acl).emits(lambda packet: b'Hello!' in packet.payload, - lambda packet: b'Hello' * 200 in packet.payload).inOrder() - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/controller_test.py b/system/blueberry/tests/gd/hci/controller_test.py deleted file mode 100644 index 3177695110..0000000000 --- a/system/blueberry/tests/gd/hci/controller_test.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import time - -from blueberry.tests.gd.cert import gd_base_test -from blueberry.tests.gd.cert.truth import assertThat -from google.protobuf import empty_pb2 as empty_proto -from blueberry.facade.hci import controller_facade_pb2 as controller_facade -from mobly import test_runner -import hci_packets as hci - - -class ControllerTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') - - def test_get_addresses(self): - cert_address = self.cert.hci_controller.GetMacAddressSimple() - dut_address = self.dut.hci_controller.GetMacAddressSimple() - - assertThat(cert_address).isNotEqualTo(dut_address) - time.sleep(1) # This shouldn't be needed b/149120542 - - def test_write_local_name(self): - self.dut.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheDUT')) - self.cert.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheCert')) - cert_name = self.cert.hci_controller.GetLocalNameSimple() - dut_name = self.dut.hci_controller.GetLocalNameSimple() - - assertThat(dut_name).isEqualTo(b'ImTheDUT') - assertThat(cert_name).isEqualTo(b'ImTheCert') - - def test_extended_advertising_support(self): - extended_advertising_supported = self.dut.hci_controller.SupportsBleExtendedAdvertising(empty_proto.Empty()) - if extended_advertising_supported.supported: - number_of_sets = self.dut.hci_controller.GetLeNumberOfSupportedAdvertisingSets(empty_proto.Empty()) - assertThat(number_of_sets.value).isGreaterThan(5) # Android threshold for CTS - supported = self.dut.hci_controller.IsSupportedCommand( - controller_facade.OpCodeMsg(op_code=int(hci.OpCode.LE_SET_EXTENDED_ADVERTISING_PARAMETERS))) - assertThat(supported.supported).isEqualTo(True) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/direct_hci_test.py b/system/blueberry/tests/gd/hci/direct_hci_test.py deleted file mode 100644 index b3f6309e3d..0000000000 --- a/system/blueberry/tests/gd/hci/direct_hci_test.py +++ /dev/null @@ -1,324 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from blueberry.tests.gd.cert.captures import HalCaptures, HciCaptures -from blueberry.tests.gd.cert.matchers import HciMatchers -from blueberry.tests.gd.cert.py_hal import PyHal -from blueberry.tests.gd.cert.py_hci import PyHci -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert import gd_base_test -from blueberry.facade import common_pb2 as common -from mobly import test_runner - -import hci_packets as hci -from blueberry.utils import bluetooth - - -class DirectHciTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI', cert_module='HAL') - - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - self.dut_hci = PyHci(self.dut, acl_streaming=True) - self.cert_hal = PyHal(self.cert) - self.cert_hal.send_hci_command(hci.Reset()) - - def teardown_test(self): - self.dut_hci.close() - self.cert_hal.close() - gd_base_test.GdBaseTestClass.teardown_test(self) - - def enqueue_acl_data(self, handle, pb_flag, b_flag, data): - acl = hci.Acl(handle=handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.dut.hci.SendAcl(common.Data(payload=acl.serialize())) - - def test_local_hci_cmd_and_event(self): - # Loopback mode responds with ACL and SCO connection complete - self.dut_hci.register_for_events(hci.EventCode.LOOPBACK_COMMAND) - self.dut_hci.send_command(hci.WriteLoopbackMode(loopback_mode=hci.LoopbackMode.ENABLE_LOCAL)) - - self.dut_hci.send_command(hci.ReadLocalName()) - assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.LoopbackOf(hci.ReadLocalName().serialize())) - - def test_inquiry_from_dut(self): - self.dut_hci.register_for_events(hci.EventCode.INQUIRY_RESULT) - - self.cert_hal.enable_inquiry_and_page_scan() - self.dut_hci.send_command(hci.Inquiry(lap=hci.Lap(lap=0x33), inquiry_length=0x30, num_responses=0xff)) - assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.EventWithCode(hci.EventCode.INQUIRY_RESULT)) - - def test_le_ad_scan_cert_advertises(self): - self.dut_hci.register_for_le_events(hci.SubeventCode.EXTENDED_ADVERTISING_REPORT, - hci.SubeventCode.ADVERTISING_REPORT) - - # DUT Scans - self.dut_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0D:05:04:03:02:01'))) - - self.dut_hci.send_command( - hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL, - scanning_phys=1, - parameters=[ - hci.PhyScanParameters(le_scan_type=hci.LeScanType.ACTIVE, - le_scan_interval=6553, - le_scan_window=6553) - ])) - - self.dut_hci.send_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - - # CERT Advertises - advertising_handle = 0 - self.cert_hal.send_hci_command( - hci.LeSetExtendedAdvertisingParametersLegacy( - advertising_handle=advertising_handle, - legacy_advertising_event_properties=hci.LegacyAdvertisingEventProperties.ADV_IND, - primary_advertising_interval_min=512, - primary_advertising_interval_max=768, - primary_advertising_channel_map=7, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - peer_address=bluetooth.Address('A6:A5:A4:A3:A2:A1'), - advertising_filter_policy=hci.AdvertisingFilterPolicy.ALL_DEVICES, - advertising_tx_power=0xF7, - advertising_sid=1, - scan_request_notification_enable=hci.Enable.DISABLED)) - - self.cert_hal.send_hci_command( - hci.LeSetAdvertisingSetRandomAddress(advertising_handle=advertising_handle, - random_address=bluetooth.Address('0C:05:04:03:02:01'))) - - self.cert_hal.send_hci_command( - hci.LeSetExtendedAdvertisingData( - advertising_handle=advertising_handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - advertising_data=[hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(b'Im_A_Cert'))])) - - self.cert_hal.send_hci_command( - hci.LeSetExtendedScanResponseData( - advertising_handle=advertising_handle, - operation=hci.Operation.COMPLETE_ADVERTISEMENT, - fragment_preference=hci.FragmentPreference.CONTROLLER_SHOULD_NOT, - scan_response_data=[hci.GapData(data_type=hci.GapDataType.SHORTENED_LOCAL_NAME, data=list(b'Im_A_C'))])) - - self.cert_hal.send_hci_command( - hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.ENABLED, - enabled_sets=[ - hci.EnabledSet(advertising_handle=advertising_handle, - duration=0, - max_extended_advertising_events=0) - ])) - - assertThat(self.dut_hci.get_le_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload) - - self.cert_hal.send_hci_command( - hci.LeSetExtendedAdvertisingEnable(enable=hci.Enable.DISABLED, - enabled_sets=[ - hci.EnabledSet(advertising_handle=advertising_handle, - duration=0, - max_extended_advertising_events=0) - ])) - - self.dut_hci.send_command(hci.LeSetExtendedScanEnable(enable=hci.Enable.DISABLED)) - - def _verify_le_connection_complete(self): - cert_conn_complete_capture = HalCaptures.LeConnectionCompleteCapture() - assertThat(self.cert_hal.get_hci_event_stream()).emits(cert_conn_complete_capture) - cert_handle = cert_conn_complete_capture.get().connection_handle - - dut_conn_complete_capture = HciCaptures.LeConnectionCompleteCapture() - assertThat(self.dut_hci.get_le_event_stream()).emits(dut_conn_complete_capture) - dut_handle = dut_conn_complete_capture.get().connection_handle - - return (dut_handle, cert_handle) - - @staticmethod - def _create_phy_scan_params(): - return hci.LeCreateConnPhyScanParameters(scan_interval=0x60, - scan_window=0x30, - conn_interval_min=0x18, - conn_interval_max=0x28, - conn_latency=0, - supervision_timeout=0x1f4, - min_ce_length=0, - max_ce_length=0) - - def test_le_connection_dut_advertises(self): - self.dut_hci.register_for_le_events(hci.SubeventCode.CONNECTION_COMPLETE, - hci.SubeventCode.ADVERTISING_SET_TERMINATED, - hci.SubeventCode.READ_REMOTE_FEATURES_COMPLETE) - # Cert Connects - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.cert_hal.send_hci_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0C:05:04:03:02:01'))) - self.cert_hal.send_hci_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_PEER_ADDRESS, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - peer_address_type=hci.AddressType.RANDOM_DEVICE_ADDRESS, - peer_address=bluetooth.Address('0D:05:04:03:02:01'), - initiating_phys=1, - phy_scan_parameters=[self._create_phy_scan_params()])) - - advertisement = self.dut_hci.create_advertisement(0, '0D:05:04:03:02:01') - advertisement.set_data(b'Im_The_DUT') - advertisement.set_scan_response(b'Im_The_D') - advertisement.start() - - (dut_handle, cert_handle) = self._verify_le_connection_complete() - - self.dut_hci.send_command(hci.LeReadRemoteFeatures(connection_handle=dut_handle)) - assertThat(self.dut_hci.get_le_event_stream()).emits(lambda packet: packet.payload[0] == int( - hci.EventCode.LE_META_EVENT) and packet.payload[2] == int(hci.SubeventCode.READ_REMOTE_FEATURES_COMPLETE)) - - # Send ACL Data - self.enqueue_acl_data(dut_handle, hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, - hci.BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData')) - self.cert_hal.send_acl_first(cert_handle, bytes(b'Just SomeMoreAclData')) - - assertThat(self.cert_hal.get_acl_stream()).emits( - lambda packet: logging.debug(packet.payload) or b'SomeAclData' in packet.payload) - assertThat(self.dut_hci.get_raw_acl_stream()).emits( - lambda packet: logging.debug(packet.payload) or b'SomeMoreAclData' in packet.payload) - - def test_le_filter_accept_list_connection_cert_advertises(self): - # DUT Connects - self.dut_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0D:05:04:03:02:01'))) - self.dut_hci.send_command( - hci.LeAddDeviceToFilterAcceptList(address_type=hci.FilterAcceptListAddressType.RANDOM, - address=bluetooth.Address('0C:05:04:03:02:01'))) - self.dut_hci.send_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_FILTER_ACCEPT_LIST, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - initiating_phys=1, - phy_scan_parameters=[self._create_phy_scan_params()])) - - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - advertisement = self.cert_hal.create_advertisement(1, - '0C:05:04:03:02:01', - min_interval=512, - max_interval=768, - peer_address='A6:A5:A4:A3:A2:A1', - tx_power=0x7f, - sid=0) - advertisement.set_data(b'Im_A_Cert') - advertisement.start() - - # LeConnectionComplete - self._verify_le_connection_complete() - - def test_le_filter_accept_list_connection_cert_advertises_legacy(self): - # DUT Connects - self.dut_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0D:05:04:03:02:01'))) - self.dut_hci.send_command( - hci.LeAddDeviceToFilterAcceptList(address_type=hci.FilterAcceptListAddressType.RANDOM, - address=bluetooth.Address('0C:05:04:03:02:01'))) - self.dut_hci.send_command( - hci.LeExtendedCreateConnection(initiator_filter_policy=hci.InitiatorFilterPolicy.USE_FILTER_ACCEPT_LIST, - own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - initiating_phys=1, - phy_scan_parameters=[self._create_phy_scan_params()])) - - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.cert_hal.send_hci_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0C:05:04:03:02:01'))) - - advertisement = self.cert_hal.create_legacy_advertisement(min_interval=512, - max_interval=768, - peer_address='A6:A5:A4:A3:A2:A1') - advertisement.set_data(b'Im_A_Cert') - advertisement.start() - - # LeConnectionComplete - self._verify_le_connection_complete() - - def test_le_ad_scan_cert_advertises_legacy(self): - self.dut_hci.register_for_le_events(hci.SubeventCode.EXTENDED_ADVERTISING_REPORT, - hci.SubeventCode.ADVERTISING_REPORT) - - # DUT Scans - self.dut_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0D:05:04:03:02:01'))) - - self.dut_hci.send_command( - hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL, - scanning_phys=1, - parameters=[ - hci.PhyScanParameters(le_scan_type=hci.LeScanType.ACTIVE, - le_scan_interval=6553, - le_scan_window=6553) - ])) - - self.dut_hci.send_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - - self.cert_hal.unmask_event(hci.EventCode.LE_META_EVENT) - self.cert_hal.send_hci_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0C:05:04:03:02:01'))) - - advertisement = self.cert_hal.create_legacy_advertisement(min_interval=512, - max_interval=768, - peer_address='A6:A5:A4:A3:A2:A1') - advertisement.set_data(b'Im_A_Cert') - advertisement.start() - - assertThat(self.dut_hci.get_le_event_stream()).emits( - HciMatchers.LeAdvertisement(address='0C:05:04:03:02:01', data=b'Im_A_Cert')) - - def test_connection_dut_connects(self): - self.dut_hci.send_command(hci.WritePageTimeout(page_timeout=0x4000)) - - self.cert_hal.enable_inquiry_and_page_scan() - address = self.cert_hal.read_own_address() - - self.dut_hci.initiate_connection(address) - cert_acl = self.cert_hal.accept_connection() - dut_acl = self.dut_hci.complete_connection() - - # Send ACL Data - dut_acl.send_first(b'Just SomeAclData') - cert_acl.send_first(b'Just SomeMoreAclData') - - assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload) - assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - - def test_connection_cert_connects(self): - self.cert_hal.send_hci_command(hci.WritePageTimeout(page_timeout=0x4000)) - - self.dut_hci.enable_inquiry_and_page_scan() - address = self.dut_hci.read_own_address() - - self.cert_hal.initiate_connection(address) - dut_acl = self.dut_hci.accept_connection() - cert_acl = self.cert_hal.complete_connection() - - # Send ACL Data - dut_acl.send_first(b'This is just SomeAclData') - cert_acl.send_first(b'This is just SomeMoreAclData') - - assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload) - assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/le_acl_manager_test.py b/system/blueberry/tests/gd/hci/le_acl_manager_test.py deleted file mode 100644 index 51c01a5a1b..0000000000 --- a/system/blueberry/tests/gd/hci/le_acl_manager_test.py +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from blueberry.tests.gd.cert import gd_base_test -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert.py_hci import PyHci, PyHciAdvertisement -from blueberry.tests.gd.cert.py_le_acl_manager import PyLeAclManager -from blueberry.facade import common_pb2 as common -from blueberry.facade.hci import le_acl_manager_facade_pb2 as le_acl_manager_facade -from blueberry.facade.hci import le_advertising_manager_facade_pb2 as le_advertising_facade -from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade -from blueberry.facade.hci import hci_facade_pb2 as hci_facade -from mobly import test_runner -import hci_packets as hci - - -class LeAclManagerTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') - - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - self.cert_hci = PyHci(self.cert, acl_streaming=True) - self.dut_le_acl_manager = PyLeAclManager(self.dut) - self.cert_public_address = self.cert_hci.read_own_address() - self.dut_public_address = self.dut.hci_controller.GetMacAddressSimple().decode("utf-8") - self.dut_random_address = 'd0:05:04:03:02:01' - self.cert_random_address = 'c0:05:04:03:02:01' - - def teardown_test(self): - safeClose(self.dut_le_acl_manager) - self.cert_hci.close() - gd_base_test.GdBaseTestClass.teardown_test(self) - - def set_privacy_policy_static(self): - private_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(self.dut_random_address, "utf-8")), - type=common.RANDOM_DEVICE_ADDRESS)) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy) - - def register_for_event(self, event_code): - msg = hci_facade.EventRequest(code=int(event_code)) - self.cert.hci.RequestEvent(msg) - - def register_for_le_event(self, event_code): - msg = hci_facade.EventRequest(code=int(event_code)) - self.cert.hci.RequestLeSubevent(msg) - - def enqueue_hci_command(self, command): - cmd_bytes = bytes(command.serialize()) - cmd = common.Data(payload=cmd_bytes) - self.cert.hci.SendCommand(cmd) - - def enqueue_acl_data(self, handle, pb_flag, b_flag, data): - acl = hci.Acl(handle=handle, packet_boundary_flag=pb_flag, broadcast_flag=b_flag, payload=data) - self.cert.hci.SendAcl(common.Data(payload=acl.serialize())) - - def dut_connects(self): - # Cert Advertises - advertising_handle = 0 - py_hci_adv = PyHciAdvertisement(advertising_handle, self.cert_hci) - - self.cert_hci.create_advertisement( - advertising_handle, - self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, - ) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - dut_le_acl = self.dut_le_acl_manager.connect_to_remote( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS))) - - cert_le_acl = self.cert_hci.incoming_le_connection() - return dut_le_acl, cert_le_acl - - def cert_advertises_resolvable(self): - self.cert_hci.add_device_to_resolving_list(hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - self.dut_public_address, - b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f', - b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f') - - # Cert Advertises - advertising_handle = 0 - py_hci_adv = PyHciAdvertisement(advertising_handle, self.cert_hci) - - self.cert_hci.create_advertisement(advertising_handle, - self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, - own_address_type=hci.OwnAddressType.RESOLVABLE_OR_PUBLIC_ADDRESS, - peer_address=self.dut_public_address, - peer_address_type=hci.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - def dut_connects_cert_resolvable(self): - self.dut.hci_le_acl_manager.AddDeviceToResolvingList( - le_acl_manager_facade.IrkMsg( - peer=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=repr(self.cert_public_address).encode('utf-8')), - type=int(hci.AddressType.PUBLIC_DEVICE_ADDRESS)), - peer_irk=b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f', - local_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f', - )) - - dut_le_acl = self.dut_le_acl_manager.connect_to_remote( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=repr(self.cert_public_address).encode('utf-8')), - type=int(hci.AddressType.PUBLIC_DEVICE_ADDRESS))) - - cert_le_acl = self.cert_hci.incoming_le_connection() - return dut_le_acl, cert_le_acl - - def send_receive_and_check(self, dut_le_acl, cert_le_acl): - self.enqueue_acl_data(cert_le_acl.handle, hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, - hci.BroadcastFlag.POINT_TO_POINT, bytes(b'\x19\x00\x07\x00SomeAclData from the Cert')) - - dut_le_acl.send(b'\x1C\x00\x07\x00SomeMoreAclData from the DUT') - assertThat(cert_le_acl.our_acl_stream).emits(lambda packet: b'SomeMoreAclData' in packet.payload) - assertThat(dut_le_acl).emits(lambda packet: b'SomeAclData' in packet.payload) - - def test_dut_connects(self): - self.set_privacy_policy_static() - dut_le_acl, cert_le_acl = self.dut_connects() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isEqualTo(self.dut_random_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_dut_connects_resolvable_address(self): - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_RESOLVABLE_ADDRESS, - rotation_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f', - minimum_rotation_time=7 * 60 * 1000, - maximum_rotation_time=15 * 60 * 1000) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - dut_le_acl, cert_le_acl = self.dut_connects() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address) - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_dut_connects_resolvable_address_public(self): - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_RESOLVABLE_ADDRESS, - rotation_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f', - minimum_rotation_time=7 * 60 * 1000, - maximum_rotation_time=15 * 60 * 1000) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - self.cert_advertises_resolvable() - dut_le_acl, cert_le_acl = self.dut_connects_cert_resolvable() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address) - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(repr(self.cert_public_address)) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.PUBLIC_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_dut_connects_non_resolvable_address(self): - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_NON_RESOLVABLE_ADDRESS, - rotation_irk=b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f', - minimum_rotation_time=8 * 60 * 1000, - maximum_rotation_time=14 * 60 * 1000) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - dut_le_acl, cert_le_acl = self.dut_connects() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_public_address) - assertThat(cert_le_acl.peer).isNotEqualTo(self.dut_random_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_dut_connects_public_address(self): - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress( - le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS)) - dut_le_acl, cert_le_acl = self.dut_connects() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isEqualTo(self.dut_public_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.PUBLIC_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_dut_connects_public_address_cancelled(self): - # TODO (Add cancel) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress( - le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS)) - dut_le_acl, cert_le_acl = self.dut_connects() - - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isEqualTo(self.dut_public_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.PUBLIC_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_cert_connects(self): - self.set_privacy_policy_static() - self.dut_le_acl_manager.listen_for_incoming_connections() - - # DUT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, - peer_address=common.BluetoothAddress(address=bytes(b'A6:A5:A4:A3:A2:A1')), - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - - self.dut.hci_le_advertising_manager.CreateAdvertiser(request) - - # Cert Connects - self.cert_hci.set_random_le_address(self.cert_random_address) - self.cert_hci.initiate_le_connection(self.dut_random_address) - - # Cert gets ConnectionComplete with a handle and sends ACL data - cert_le_acl = self.cert_hci.incoming_le_connection() - - cert_le_acl.send(hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, hci.BroadcastFlag.POINT_TO_POINT, - b'\x19\x00\x07\x00SomeAclData from the Cert') - - # DUT gets a connection complete event and sends and receives - dut_le_acl = self.dut_le_acl_manager.complete_incoming_connection() - assertThat(cert_le_acl.handle).isNotNone() - assertThat(cert_le_acl.peer).isEqualTo(self.dut_random_address) - assertThat(cert_le_acl.peer_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - assertThat(dut_le_acl.handle).isNotNone() - assertThat(dut_le_acl.remote_address).isEqualTo(self.cert_random_address) - assertThat(dut_le_acl.remote_address_type).isEqualTo(hci.AddressType.RANDOM_DEVICE_ADDRESS) - - self.send_receive_and_check(dut_le_acl, cert_le_acl) - - def test_recombination_l2cap_packet(self): - self.set_privacy_policy_static() - dut_le_acl, cert_le_acl = self.dut_connects() - cert_handle = cert_le_acl.handle - self.enqueue_acl_data(cert_handle, hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, - hci.BroadcastFlag.POINT_TO_POINT, bytes(b'\x06\x00\x07\x00Hello')) - self.enqueue_acl_data(cert_handle, hci.PacketBoundaryFlag.CONTINUING_FRAGMENT, hci.BroadcastFlag.POINT_TO_POINT, - bytes(b'!')) - - assertThat(dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload) - - def test_background_connection(self): - self.set_privacy_policy_static() - - # Start background and direct connection - token_direct = self.dut_le_acl_manager.initiate_connection( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=bytes('0C:05:04:03:02:02', 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS))) - - token_background = self.dut_le_acl_manager.initiate_connection(remote_addr=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS)), - is_direct=False) - - # Wait for direct connection timeout - self.dut_le_acl_manager.wait_for_connection_fail(token_direct) - - # Cert Advertises - advertising_handle = 0 - - py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, 155, 165) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - # Check background connection complete - self.dut_le_acl_manager.complete_outgoing_connection(token_background) - - def skip_flaky_test_multiple_background_connections(self): - self.set_privacy_policy_static() - - # Start two background connections - token_1 = self.dut_le_acl_manager.initiate_connection(remote_addr=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS)), - is_direct=False) - - token_2 = self.dut_le_acl_manager.initiate_connection(remote_addr=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:02', 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS)), - is_direct=False) - - # Cert Advertises - advertising_handle = 0 - - py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, 155, 165) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - # First background connection completes - connection = self.dut_le_acl_manager.complete_outgoing_connection(token_1) - connection.close() - - # Cert Advertises again - advertising_handle = 0 - - py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, '0C:05:04:03:02:02', - hci.LegacyAdvertisingEventProperties.ADV_IND, 155, 165) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - # Second background connection completes - connection = self.dut_le_acl_manager.complete_outgoing_connection(token_2) - connection.close() - - def test_direct_connection(self): - self.set_privacy_policy_static() - - advertising_handle = 0 - py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, 155, 165) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - # Start direct connection - token = self.dut_le_acl_manager.initiate_connection(remote_addr=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS)), - is_direct=True) - self.dut_le_acl_manager.complete_outgoing_connection(token) - - def test_background_connection_list(self): - self.set_privacy_policy_static() - - # Start background connection - token_background = self.dut_le_acl_manager.initiate_connection(remote_addr=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS)), - is_direct=False) - - # Cert Advertises - advertising_handle = 0 - - py_hci_adv = self.cert_hci.create_advertisement(advertising_handle, self.cert_random_address, - hci.LegacyAdvertisingEventProperties.ADV_IND, 155, 165) - - py_hci_adv.set_data(b'Im_A_Cert') - py_hci_adv.set_scan_response(b'Im_A_C') - py_hci_adv.start() - - # Check background connection complete - self.dut_le_acl_manager.complete_outgoing_connection(token_background) - - msg = self.dut_le_acl_manager.is_on_background_list( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS))) - assertThat(msg.is_on_background_list).isEqualTo(True) - - self.dut_le_acl_manager.remove_from_background_list( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS))) - - msg = self.dut_le_acl_manager.is_on_background_list( - remote_addr=common.BluetoothAddressWithType(address=common.BluetoothAddress( - address=bytes(self.cert_random_address, 'utf8')), - type=int(hci.AddressType.RANDOM_DEVICE_ADDRESS))) - assertThat(msg.is_on_background_list).isEqualTo(False) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/le_advertising_manager_test.py b/system/blueberry/tests/gd/hci/le_advertising_manager_test.py deleted file mode 100644 index a157c981c1..0000000000 --- a/system/blueberry/tests/gd/hci/le_advertising_manager_test.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.matchers import AdvertisingMatchers -from blueberry.tests.gd.cert.py_hci import PyHci -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert import gd_base_test -from blueberry.facade import common_pb2 as common -from google.protobuf import empty_pb2 as empty_proto -from blueberry.facade.hci import controller_facade_pb2 as controller_facade -from blueberry.facade.hci import \ - le_advertising_manager_facade_pb2 as le_advertising_facade -from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade -from blueberry.facade.hci.le_advertising_manager_facade_pb2 import AdvertisingCallbackMsgType -from blueberry.facade.hci.le_advertising_manager_facade_pb2 import AdvertisingStatus - -from mobly import asserts, test_runner - -from blueberry.utils import bluetooth -import hci_packets as hci - - -class LeAdvertisingManagerTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') - - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - self.cert_hci = PyHci(self.cert, acl_streaming=True) - self.dut.callback_event_stream = EventStream( - self.dut.hci_le_advertising_manager.FetchCallbackEvents(empty_proto.Empty())) - self.dut.address_event_stream = EventStream( - self.dut.hci_le_advertising_manager.FetchAddressEvents(empty_proto.Empty())) - - def teardown_test(self): - self.cert_hci.close() - if self.dut.callback_event_stream is not None: - safeClose(self.dut.callback_event_stream) - else: - logging.info("DUT: Callback Event Stream is None!") - if self.dut.address_event_stream is not None: - safeClose(self.dut.address_event_stream) - else: - logging.info("DUT: address Event Stream is None!") - gd_base_test.GdBaseTestClass.teardown_test(self) - - def set_address_policy_with_static_address(self): - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'd0:05:04:03:02:01')), - type=common.RANDOM_DEVICE_ADDRESS), - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - - def create_advertiser(self): - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request) - return create_response - - def test_le_ad_scan_dut_advertises(self): - self.set_address_policy_with_static_address() - self.cert_hci.register_for_le_events(hci.SubeventCode.ADVERTISING_REPORT, - hci.SubeventCode.EXTENDED_ADVERTISING_REPORT) - - # CERT Scans - self.cert_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0C:05:04:03:02:01'))) - - self.cert_hci.send_command( - hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL, - scanning_phys=1, - parameters=[ - hci.PhyScanParameters(le_scan_type=hci.LeScanType.ACTIVE, - le_scan_interval=40, - le_scan_window=20) - ])) - - self.cert_hci.send_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - - create_response = self.create_advertiser() - - assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - self.cert_hci.send_command( - hci.LeSetScanEnable(le_scan_enable=hci.Enable.DISABLED, filter_duplicates=hci.Enable.DISABLED)) - - def test_extended_create_advertises(self): - self.set_address_policy_with_static_address() - self.cert_hci.register_for_le_events(hci.SubeventCode.ADVERTISING_REPORT, - hci.SubeventCode.EXTENDED_ADVERTISING_REPORT) - - # CERT Scans - self.cert_hci.send_command(hci.LeSetRandomAddress(random_address=bluetooth.Address('0C:05:04:03:02:01'))) - - self.cert_hci.send_command( - hci.LeSetExtendedScanParameters(own_address_type=hci.OwnAddressType.RANDOM_DEVICE_ADDRESS, - scanning_filter_policy=hci.LeScanningFilterPolicy.ACCEPT_ALL, - scanning_phys=1, - parameters=[ - hci.PhyScanParameters(le_scan_type=hci.LeScanType.ACTIVE, - le_scan_interval=40, - le_scan_window=20) - ])) - - self.cert_hci.send_command( - hci.LeSetExtendedScanEnable(enable=hci.Enable.ENABLED, - filter_duplicates=hci.FilterDuplicates.DISABLED, - duration=0, - period=0)) - - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - extended_config = le_advertising_facade.ExtendedAdvertisingConfig(advertising_config=config, - connectable=True, - scannable=False, - directed=False, - high_duty_directed_connectable=False, - legacy_pdus=True, - anonymous=False, - include_tx_power=True, - use_le_coded_phy=False, - secondary_max_skip=0x00, - secondary_advertising_phy=0x01, - sid=0x00, - enable_scan_request_notifications=0x00) - request = le_advertising_facade.ExtendedCreateAdvertiserRequest(config=extended_config) - create_response = self.dut.hci_le_advertising_manager.ExtendedCreateAdvertiser(request) - - assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - self.cert_hci.send_command( - hci.LeSetScanEnable(le_scan_enable=hci.Enable.DISABLED, filter_duplicates=hci.Enable.DISABLED)) - - def test_advertising_set_started_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.ADVERTISING_SET_STARTED, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS, - 0x00)) - - def test_enable_advertiser_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - enable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest( - advertiser_id=create_response.advertiser_id, enable=True) - self.dut.hci_le_advertising_manager.EnableAdvertiser(enable_advertiser_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.ADVERTISING_ENABLED, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS, - 0x01)) - - def test_disable_advertiser_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - disable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest( - advertiser_id=create_response.advertiser_id, enable=False) - self.dut.hci_le_advertising_manager.EnableAdvertiser(disable_advertiser_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.ADVERTISING_ENABLED, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS, - 0x00)) - - def test_set_advertising_data_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT2'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - - set_data_request = le_advertising_facade.SetDataRequest(advertiser_id=create_response.advertiser_id, - set_scan_rsp=False, - data=[gap_data]) - self.dut.hci_le_advertising_manager.SetData(set_data_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.ADVERTISING_DATA_SET, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS)) - - def test_set_scan_response_data_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - - set_data_request = le_advertising_facade.SetDataRequest(advertiser_id=create_response.advertiser_id, - set_scan_rsp=True, - data=[gap_data]) - self.dut.hci_le_advertising_manager.SetData(set_data_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.SCAN_RESPONSE_DATA_SET, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS)) - - def test_set_parameters_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - - # The Host shall not issue set parameters command when advertising is enabled - disable_advertiser_request = le_advertising_facade.EnableAdvertiserRequest( - advertiser_id=create_response.advertiser_id, enable=False) - self.dut.hci_le_advertising_manager.EnableAdvertiser(disable_advertiser_request) - - config = le_advertising_facade.AdvertisingConfig( - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - - set_parameters_request = le_advertising_facade.SetParametersRequest(advertiser_id=create_response.advertiser_id, - config=config) - self.dut.hci_le_advertising_manager.SetParameters(set_parameters_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.ADVERTISING_PARAMETERS_UPDATED, - create_response.advertiser_id, AdvertisingStatus.ADV_SUCCESS)) - - def test_set_periodic_parameters_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - - config = le_advertising_facade.PeriodicAdvertisingParameters( - min_interval=512, - max_interval=768, - advertising_property=le_advertising_facade.AdvertisingProperty.INCLUDE_TX_POWER) - - set_periodic_parameters_request = le_advertising_facade.SetPeriodicParametersRequest( - advertiser_id=create_response.advertiser_id, config=config) - self.dut.hci_le_advertising_manager.SetPeriodicParameters(set_periodic_parameters_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg( - AdvertisingCallbackMsgType.PERIODIC_ADVERTISING_PARAMETERS_UPDATED, create_response.advertiser_id)) - - def test_set_periodic_data_callback(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT2'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - - set_periodic_data_request = le_advertising_facade.SetPeriodicDataRequest( - advertiser_id=create_response.advertiser_id, data=[gap_data]) - self.dut.hci_le_advertising_manager.SetPeriodicData(set_periodic_data_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.PERIODIC_ADVERTISING_DATA_SET, - create_response.advertiser_id)) - - def test_enable_periodic_advertising_callback(self): - check_feature = self.dut.hci_controller.SupportsBlePeriodicAdvertising(empty_proto.Empty()) - if not check_feature.supported: - asserts.skip("Periodic advertising not supported.") - - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - enable_periodic_advertising_request = le_advertising_facade.EnablePeriodicAdvertisingRequest( - advertiser_id=create_response.advertiser_id, enable=True, include_adi=False) - self.dut.hci_le_advertising_manager.EnablePeriodicAdvertising(enable_periodic_advertising_request) - - assertThat(self.dut.callback_event_stream).emits( - AdvertisingMatchers.AdvertisingCallbackMsg(AdvertisingCallbackMsgType.PERIODIC_ADVERTISING_ENABLED, - create_response.advertiser_id)) - - def test_get_own_address(self): - self.set_address_policy_with_static_address() - create_response = self.create_advertiser() - get_own_address_request = le_advertising_facade.GetOwnAddressRequest( - advertiser_id=create_response.advertiser_id) - self.dut.hci_le_advertising_manager.GetOwnAddress(get_own_address_request) - address_with_type = common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'd0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS) - assertThat(self.dut.address_event_stream).emits( - AdvertisingMatchers.AddressMsg(AdvertisingCallbackMsgType.OWN_ADDRESS_READ, create_response.advertiser_id, - address_with_type)) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/le_extended_config.json b/system/blueberry/tests/gd/hci/le_extended_config.json deleted file mode 100644 index ae81c0af73..0000000000 --- a/system/blueberry/tests/gd/hci/le_extended_config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "supported_commands" : ["b3", "ff", "ef", "01", "ce", "fd", "81", "ff", "3f", "0f", - "40", "1e", "f0", "03", "e8", "02", "03", "d6", "81", "9f", - "1c", "00", "00", "00", "40", "f7", "ff", "a7", "0f", "20", - "00", "00", "4a", "80", "78", "ff", "ff", "ff", "00", "00",], -} diff --git a/system/blueberry/tests/gd/hci/le_extended_scanning_manager_test.py b/system/blueberry/tests/gd/hci/le_extended_scanning_manager_test.py deleted file mode 100644 index 890adde06d..0000000000 --- a/system/blueberry/tests/gd/hci/le_extended_scanning_manager_test.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from blueberry.tests.gd.cert import gd_base_test -from blueberry.tests.gd.hci.le_scanning_manager_test_blueberry import LeScanningManagerTestBase -from mobly import test_runner - - -class LeExtendedScanningManagerTest(gd_base_test.GdBaseTestClass, LeScanningManagerTestBase): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') - - def setup_test(self): - gd_base_test.GdBaseTestClass.set_controller_properties_path(self, - 'blueberry/tests/gd/hci/le_extended_config.json') - gd_base_test.GdBaseTestClass.setup_test(self) - LeScanningManagerTestBase.setup_test(self, self.cert, self.dut) - - def teardown_test(self): - LeScanningManagerTestBase.teardown_test(self) - gd_base_test.GdBaseTestClass.teardown_test(self) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/le_legacy_config.json b/system/blueberry/tests/gd/hci/le_legacy_config.json deleted file mode 100644 index 34e9c95a9f..0000000000 --- a/system/blueberry/tests/gd/hci/le_legacy_config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "supported_commands" : ["b3", "ff", "ef", "01", "ce", "fd", "81", "ff", "3f", "0f", - "40", "1e", "f0", "03", "e8", "02", "03", "d6", "81", "9f", - "1c", "00", "00", "00", "40", "f7", "ff", "a7", "0f", "20", - "00", "00", "4a", "80", "78", "0e", "00", "00", "00", "00",], -} diff --git a/system/blueberry/tests/gd/hci/le_scanning_manager_test.py b/system/blueberry/tests/gd/hci/le_scanning_manager_test.py deleted file mode 100644 index 0a4faea078..0000000000 --- a/system/blueberry/tests/gd/hci/le_scanning_manager_test.py +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.matchers import ScanningMatchers -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert import gd_base_test -from blueberry.facade import common_pb2 as common -from blueberry.facade import rootservice_pb2 as facade_rootservice -from google.protobuf import empty_pb2 as empty_proto -from blueberry.facade.hci import hci_facade_pb2 as hci_facade -from blueberry.facade.hci import le_scanning_manager_facade_pb2 as le_scanning_facade -from blueberry.facade.hci import le_advertising_manager_facade_pb2 as le_advertising_facade -from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade -from blueberry.facade.hci.le_scanning_manager_facade_pb2 import ScanningCallbackMsgType -from blueberry.facade.hci.le_scanning_manager_facade_pb2 import ScanningStatus -from mobly import test_runner -import hci_packets as hci - - -class LeScanningManagerTestBase(): - - def setup_test(self, cert, dut): - self.cert = cert - self.dut = dut - self.dut.callback_event_stream = EventStream( - self.dut.hci_le_scanning_manager.FetchCallbackEvents(empty_proto.Empty())) - self.dut.advertising_report_stream = EventStream( - self.dut.hci_le_scanning_manager.FetchAdvertisingReports(empty_proto.Empty())) - - def teardown_test(self): - if self.dut.callback_event_stream is not None: - safeClose(self.dut.callback_event_stream) - else: - logging.info("DUT: Callback Event Stream is None!") - if self.dut.advertising_report_stream is not None: - safeClose(self.dut.advertising_report_stream) - else: - logging.info("DUT: Advertising Report Stream is None!") - - def register_for_event(self, event_code): - msg = hci_facade.EventCodeMsg(code=int(event_code)) - self.cert.hci.RegisterEventHandler(msg) - - def register_for_le_event(self, event_code): - msg = hci_facade.LeSubeventCodeMsg(code=int(event_code)) - self.cert.hci.RegisterLeEventHandler(msg) - - def enqueue_hci_command(self, command, expect_complete): - cmd_bytes = bytes(command.serialize()) - cmd = common.Data(payload=cmd_bytes) - if (expect_complete): - self.cert.hci.EnqueueCommandWithComplete(cmd) - else: - self.cert.hci.EnqueueCommandWithStatus(cmd) - - def set_address_policy_with_static_address(self): - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), - type=common.RANDOM_DEVICE_ADDRESS), - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')), - type=common.RANDOM_DEVICE_ADDRESS), - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy) - - def test_le_ad_scan_dut_scans(self): - self.set_address_policy_with_static_address() - # CERT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_CERT!'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - gap_scan_name = hci.GapData(data_type=hci.GapDataType.SHORTENED_LOCAL_NAME, data=list(bytes(b'CERT!'))) - gap_scan_data = le_advertising_facade.GapDataMsg(data=gap_scan_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - scan_response=[gap_scan_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - - create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request) - - scan_request = le_scanning_facade.ScanRequest(start=True) - self.dut.hci_le_scanning_manager.Scan(scan_request) - - self.dut.advertising_report_stream.assert_event_occurs(lambda packet: b'Im_The_CERT' in packet.event) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - - def test_register_scanner_callback(self): - self.set_address_policy_with_static_address() - register_request = le_scanning_facade.RegisterScannerRequest(uuid=123) - self.dut.hci_le_scanning_manager.RegisterScanner(register_request) - assertThat(self.dut.callback_event_stream).emits( - ScanningMatchers.ScanningCallbackMsg(ScanningCallbackMsgType.SCANNER_REGISTERED, - ScanningStatus.SCAN_SUCCESS, 123)) - - def test_register_scanner_with_same_uuid(self): - self.set_address_policy_with_static_address() - register_request = le_scanning_facade.RegisterScannerRequest(uuid=123) - self.dut.hci_le_scanning_manager.RegisterScanner(register_request) - assertThat(self.dut.callback_event_stream).emits( - ScanningMatchers.ScanningCallbackMsg(ScanningCallbackMsgType.SCANNER_REGISTERED, - ScanningStatus.SCAN_SUCCESS, 123)) - self.dut.hci_le_scanning_manager.RegisterScanner(register_request) - assertThat(self.dut.callback_event_stream).emits( - ScanningMatchers.ScanningCallbackMsg(ScanningCallbackMsgType.SCANNER_REGISTERED, - ScanningStatus.SCAN_INTERNAL_ERROR, 123)) - - def test_set_scan_parameters_callback(self): - self.set_address_policy_with_static_address() - set_scan_parameters_request = le_scanning_facade.SetScanParametersRequest( - scanner_id=0x01, - scan_type=le_scanning_facade.LeScanType.ACTIVE, - scan_interval=0x10, - scan_window=0x04, - scan_phy=0x01) - self.dut.hci_le_scanning_manager.SetScanParameters(set_scan_parameters_request) - - assertThat(self.dut.callback_event_stream).emits( - ScanningMatchers.ScanningCallbackMsg(ScanningCallbackMsgType.SET_SCANNER_PARAMETER_COMPLETE, - ScanningStatus.SCAN_SUCCESS, 0x01)) - - def test_set_scan_parameters_with_invalid_parameter(self): - self.set_address_policy_with_static_address() - set_scan_parameters_request = le_scanning_facade.SetScanParametersRequest( - scanner_id=0x01, - scan_type=le_scanning_facade.LeScanType.ACTIVE, - scan_interval=0x00, - scan_window=0x00, - scan_phy=0x01) - self.dut.hci_le_scanning_manager.SetScanParameters(set_scan_parameters_request) - - assertThat(self.dut.callback_event_stream).emits( - ScanningMatchers.ScanningCallbackMsg(ScanningCallbackMsgType.SET_SCANNER_PARAMETER_COMPLETE, - ScanningStatus.SCAN_ILLEGAL_PARAMETER)) - - def test_active_scan(self): - self.set_address_policy_with_static_address() - # CERT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Scan response data'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - scan_response=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request) - - set_scan_parameters_request = le_scanning_facade.SetScanParametersRequest( - scanner_id=0x01, - scan_type=le_scanning_facade.LeScanType.ACTIVE, - scan_interval=0x10, - scan_window=0x04, - scan_phy=0x01) - self.dut.hci_le_scanning_manager.SetScanParameters(set_scan_parameters_request) - scan_request = le_scanning_facade.ScanRequest(start=True) - self.dut.hci_le_scanning_manager.Scan(scan_request) - - self.dut.advertising_report_stream.assert_event_occurs(lambda packet: b'Scan response data' in packet.event) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - - def test_passive_scan(self): - self.set_address_policy_with_static_address() - # CERT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Scan response data'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - scan_response=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request) - - set_scan_parameters_request = le_scanning_facade.SetScanParametersRequest( - scanner_id=0x01, - scan_type=le_scanning_facade.LeScanType.PASSIVE, - scan_interval=0x10, - scan_window=0x04, - scan_phy=0x01) - self.dut.hci_le_scanning_manager.SetScanParameters(set_scan_parameters_request) - scan_request = le_scanning_facade.ScanRequest(start=True) - self.dut.hci_le_scanning_manager.Scan(scan_request) - - self.dut.advertising_report_stream.assert_event_occurs_at_most( - lambda packet: b'Scan response data' in packet.event, 0) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - - -class LeScanningManagerTest(gd_base_test.GdBaseTestClass, LeScanningManagerTestBase): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') - - def setup_test(self): - gd_base_test.GdBaseTestClass.set_controller_properties_path(self, - 'blueberry/tests/gd/hci/le_legacy_config.json') - gd_base_test.GdBaseTestClass.setup_test(self) - LeScanningManagerTestBase.setup_test(self, self.cert, self.dut) - - def teardown_test(self): - LeScanningManagerTestBase.teardown_test(self) - gd_base_test.GdBaseTestClass.teardown_test(self) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/hci/le_scanning_with_security_test.py b/system/blueberry/tests/gd/hci/le_scanning_with_security_test.py deleted file mode 100644 index 79f4e3a762..0000000000 --- a/system/blueberry/tests/gd/hci/le_scanning_with_security_test.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 - 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. - -from blueberry.tests.gd.cert.event_stream import EventStream -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert import gd_base_test -from google.protobuf import empty_pb2 as empty_proto -from blueberry.facade.hci import hci_facade_pb2 as hci_facade -from blueberry.facade.hci import le_advertising_manager_facade_pb2 as le_advertising_facade -from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade -from blueberry.facade.hci import le_scanning_manager_facade_pb2 as le_scanning_facade -from blueberry.facade import common_pb2 as common -from mobly import test_runner -import hci_packets as hci - - -class LeScanningWithSecurityTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='HCI_INTERFACES') - - def register_for_event(self, event_code): - msg = hci_facade.EventCodeMsg(code=int(event_code)) - self.cert.hci.RegisterEventHandler(msg) - - def register_for_le_event(self, event_code): - msg = hci_facade.LeSubeventCodeMsg(code=int(event_code)) - self.cert.hci.RegisterLeEventHandler(msg) - - def enqueue_hci_command(self, command, expect_complete): - cmd_bytes = bytes(command.serialize()) - cmd = common.Data(command=cmd_bytes) - if (expect_complete): - self.cert.hci.EnqueueCommandWithComplete(cmd) - else: - self.cert.hci.EnqueueCommandWithStatus(cmd) - - def test_le_ad_scan_dut_scans(self): - """ - Verify that the IUT address policy is correctly initiated by SecurityManager, and we can start a scan. - """ - cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')), - type=common.RANDOM_DEVICE_ADDRESS), - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy) - with EventStream( - # DUT Scans - self.dut.hci_le_scanning_manager.FetchAdvertisingReports(empty_proto.Empty() - )) as advertising_event_stream: - - # CERT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_CERT!'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - gap_scan_name = hci.GapData(data_type=hci.GapDataType.SHORTENED_LOCAL_NAME, data=list(bytes(b'CERT!'))) - gap_scan_data = le_advertising_facade.GapDataMsg(data=gap_scan_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - scan_response=[gap_scan_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_RANDOM_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - - create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request) - - scan_request = le_scanning_facade.ScanRequest(start=True) - self.dut.hci_le_scanning_manager.Scan(scan_request) - - assertThat(advertising_event_stream).emits(lambda packet: b'Im_The_CERT' in packet.event) - - remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id) - self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/host_config.yaml b/system/blueberry/tests/gd/host_config.yaml deleted file mode 100644 index 15155db8d7..0000000000 --- a/system/blueberry/tests/gd/host_config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -_description: Bluetooth cert testing -TestBeds: - - Name: HostOnlyCert - Controllers: - rootcanal: - test_port: '6401' - hci_port: '6402' - link_layer_port: '6403' - GdDevice: - - grpc_port: '8998' - grpc_root_server_port: '8996' - signal_port: '8994' - label: cert - name: Cert Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" - - grpc_port: '8999' - grpc_root_server_port: '8997' - signal_port: '8995' - label: dut - name: DUT Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" -logpath: "/tmp/logs" diff --git a/system/blueberry/tests/gd/iso/cert_le_iso.py b/system/blueberry/tests/gd/iso/cert_le_iso.py deleted file mode 100644 index ee478e61e4..0000000000 --- a/system/blueberry/tests/gd/iso/cert_le_iso.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from blueberry.tests.gd.cert.closable import Closable -from blueberry.tests.gd.cert.py_le_iso import PyLeIso - - -class CertLeIso(Closable): - - def __init__(self, device): - self._device = device - self._le_iso = PyLeIso(device) - - def close(self): - logging.info("DUT: close") - self._le_iso.close() - - def le_set_cig_parameters(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy, - packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id, - max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, rtn_m_to_s, rtn_s_to_m): - return self._le_iso.le_set_cig_parameters( - cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id, max_sdu_m_to_s, max_sdu_s_to_m, - phy_m_to_s, phy_s_to_m, rtn_m_to_s, rtn_s_to_m) - - def le_set_cig_parameters_test(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, - iso_interval, peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_configs): - return self._le_iso.le_set_cig_parameters_test(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, - ft_s_to_m, iso_interval, peripherals_clock_accuracy, packing, - framing, max_transport_latency_m_to_s, - max_transport_latency_s_to_m, cis_configs) - - def wait_le_set_cig_parameters_complete(self): - return self._le_iso.wait_le_set_cig_parameters_complete() - - def le_cretate_cis(self, cis_and_acl_handle_array): - self._le_iso.le_create_cis(cis_and_acl_handle_array) - - def wait_le_cis_established(self): - return self._le_iso.wait_le_cis_established() diff --git a/system/blueberry/tests/gd/iso/le_iso_test.py b/system/blueberry/tests/gd/iso/le_iso_test.py deleted file mode 100644 index e5af99b314..0000000000 --- a/system/blueberry/tests/gd/iso/le_iso_test.py +++ /dev/null @@ -1,252 +0,0 @@ -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.closable import safeClose -from blueberry.tests.gd.cert.matchers import IsoMatchers -from blueberry.tests.gd.cert.metadata import metadata -from blueberry.tests.gd.cert.py_le_acl_manager import PyLeAclManager -from blueberry.tests.gd.cert.py_le_iso import PyLeIso -from blueberry.tests.gd.cert.py_le_iso import CisTestParameters -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.gd.cert import gd_base_test -from blueberry.tests.gd.iso.cert_le_iso import CertLeIso -from blueberry.facade import common_pb2 as common -from blueberry.facade.hci import controller_facade_pb2 as controller_facade -from blueberry.facade.hci import le_advertising_manager_facade_pb2 as le_advertising_facade -from blueberry.facade.hci import le_initiator_address_facade_pb2 as le_initiator_address_facade -from mobly import asserts -from mobly import test_runner -import hci_packets as hci - - -class LeIsoTest(gd_base_test.GdBaseTestClass): - - def setup_class(self): - gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') - - def setup_test(self): - gd_base_test.GdBaseTestClass.setup_test(self) - - self.dut_le_acl_manager = PyLeAclManager(self.dut) - self.cert_le_acl_manager = PyLeAclManager(self.cert) - - self.dut_address = common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS) - self.cert_address = common.BluetoothAddressWithType( - address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS) - dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=self.dut_address, - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy) - privacy_policy = le_initiator_address_facade.PrivacyPolicy( - address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS, - address_with_type=self.cert_address, - rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', - minimum_rotation_time=0, - maximum_rotation_time=0) - self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy) - - self.dut_iso = PyLeIso(self.dut) - self.cert_iso = CertLeIso(self.cert) - - def teardown_test(self): - self.dut_iso.close() - self.cert_iso.close() - - safeClose(self.dut_le_acl_manager) - safeClose(self.cert_le_acl_manager) - gd_base_test.GdBaseTestClass.teardown_test(self) - - #cert becomes central of connection, dut peripheral - def _setup_link_from_cert(self): - # DUT Advertises - gap_name = hci.GapData(data_type=hci.GapDataType.COMPLETE_LOCAL_NAME, data=list(bytes(b'Im_The_DUT'))) - gap_data = le_advertising_facade.GapDataMsg(data=gap_name.serialize()) - config = le_advertising_facade.AdvertisingConfig( - advertisement=[gap_data], - interval_min=512, - interval_max=768, - advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND, - own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS, - channel_map=7, - filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES) - request = le_advertising_facade.CreateAdvertiserRequest(config=config) - create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request) - self.dut_le_acl = self.dut_le_acl_manager.listen_for_incoming_connections() - self.cert_le_acl = self.cert_le_acl_manager.connect_to_remote(self.dut_address) - - def _setup_cis_from_cert(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy, - packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id, - max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, bn_m_to_s, bn_s_to_m): - - self.cert_iso.le_set_cig_parameters(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, - peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, - max_transport_latency_s_to_m, cis_id, max_sdu_m_to_s, max_sdu_s_to_m, - phy_m_to_s, phy_s_to_m, bn_m_to_s, bn_s_to_m) - - cis_handles = self.cert_iso.wait_le_set_cig_parameters_complete() - - cis_handle = cis_handles[0] - - acl_connection_handle = self.cert_le_acl.handle - self.cert_iso.le_cretate_cis([(cis_handle, acl_connection_handle)]) - dut_cis_stream = self.dut_iso.wait_le_cis_established() - cert_cis_stream = self.cert_iso.wait_le_cis_established() - return (dut_cis_stream, cert_cis_stream) - - def skip_if_iso_not_supported(self): - supported = self.dut.hci_controller.IsSupportedCommand( - controller_facade.OpCodeMsg(op_code=int(hci.OpCode.LE_SET_CIG_PARAMETERS))) - if (not supported.supported): - asserts.skip("Skipping this test. The chip doesn't support LE ISO") - - @metadata(pts_test_id="IAL/CIS/UNF/SLA/BV-01-C", - pts_test_name="connected isochronous stream, unframed data, peripheral role") - def test_iso_cis_unf_sla_bv_01_c(self): - self.skip_if_iso_not_supported() - """ - Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length. - """ - cig_id = 0x01 - sdu_interval_m_to_s = 0 - sdu_interval_s_to_m = 0x186a - peripherals_clock_accuracy = 0 - packing = 0 - framing = 0 - max_transport_latency_m_to_s = 0 - max_transport_latency_s_to_m = 7 - cis_id = 0x01 - max_sdu_m_to_s = 0 - max_sdu_s_to_m = 100 - phy_m_to_s = 0x02 - phy_s_to_m = 0x02 - bn_m_to_s = 0 - bn_s_to_m = 2 - - self._setup_link_from_cert() - (dut_cis_stream, - cert_cis_stream) = self._setup_cis_from_cert(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, - peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, - cis_id, max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, - bn_m_to_s, bn_s_to_m) - dut_cis_stream.send(b'abcdefgh' * 10) - assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10)) - - @metadata(pts_test_id="IAL/CIS/UNF/SLA/BV-25-C", - pts_test_name="connected isochronous stream, unframed data, peripheral role") - def test_iso_cis_unf_sla_bv_25_c(self): - self.skip_if_iso_not_supported() - """ - Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length. - """ - cig_id = 0x01 - sdu_interval_m_to_s = 0x7530 - sdu_interval_s_to_m = 0x7530 - peripherals_clock_accuracy = 0 - packing = 0 - framing = 0 - max_transport_latency_m_to_s = 30 - max_transport_latency_s_to_m = 30 - cis_id = 0x01 - max_sdu_m_to_s = 100 - max_sdu_s_to_m = 100 - phy_m_to_s = 0x02 - phy_s_to_m = 0x02 - bn_m_to_s = 3 - bn_s_to_m = 1 - - self._setup_link_from_cert() - (dut_cis_stream, - cert_cis_stream) = self._setup_cis_from_cert(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, - peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, - cis_id, max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, - bn_m_to_s, bn_s_to_m) - dut_cis_stream.send(b'abcdefgh' * 10) - assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10)) - - @metadata(pts_test_id="IAL/CIS/FRA/SLA/BV-03-C", - pts_test_name="connected isochronous stream, framed data, peripheral role") - def test_iso_cis_fra_sla_bv_03_c(self): - self.skip_if_iso_not_supported() - """ - Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length. - """ - cig_id = 0x01 - sdu_interval_m_to_s = 0x0000 - sdu_interval_s_to_m = 0x4e30 - peripherals_clock_accuracy = 0 - packing = 0 - framing = 1 - max_transport_latency_m_to_s = 0 - max_transport_latency_s_to_m = 21 - cis_id = 0x01 - max_sdu_m_to_s = 0 - max_sdu_s_to_m = 100 - phy_m_to_s = 0x02 - phy_s_to_m = 0x02 - bn_m_to_s = 0 - bn_s_to_m = 2 - - self._setup_link_from_cert() - (dut_cis_stream, - cert_cis_stream) = self._setup_cis_from_cert(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, - peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, - cis_id, max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, - bn_m_to_s, bn_s_to_m) - dut_cis_stream.send(b'abcdefgh' * 10) - assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10)) - - @metadata(pts_test_id="IAL/CIS/FRA/SLA/BV-26-C", - pts_test_name="connected isochronous stream, framed data, peripheral role") - def test_iso_cis_fra_sla_bv_26_c(self): - self.skip_if_iso_not_supported() - """ - Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length. - """ - cig_id = 0x01 - sdu_interval_m_to_s = 0x14D5 - sdu_interval_s_to_m = 0x14D5 - peripherals_clock_accuracy = 0 - packing = 0 - framing = 1 - max_transport_latency_m_to_s = 6 - max_transport_latency_s_to_m = 6 - cis_id = 0x01 - max_sdu_m_to_s = 100 - max_sdu_s_to_m = 100 - phy_m_to_s = 0x02 - phy_s_to_m = 0x02 - bn_m_to_s = 1 - bn_s_to_m = 1 - - self._setup_link_from_cert() - (dut_cis_stream, - cert_cis_stream) = self._setup_cis_from_cert(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, - peripherals_clock_accuracy, packing, framing, - max_transport_latency_m_to_s, max_transport_latency_s_to_m, - cis_id, max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, - bn_m_to_s, bn_s_to_m) - dut_cis_stream.send(b'abcdefgh' * 10) - assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10)) - - -if __name__ == '__main__': - test_runner.main() diff --git a/system/blueberry/tests/gd/rust_devices_config.yaml b/system/blueberry/tests/gd/rust_devices_config.yaml deleted file mode 100644 index 9414c52b59..0000000000 --- a/system/blueberry/tests/gd/rust_devices_config.yaml +++ /dev/null @@ -1,44 +0,0 @@ -_description: Bluetooth cert testing -TestBeds: - - Name: AndroidDeviceCert - Controllers: - GdDevice: - - grpc_port: '8898' - grpc_root_server_port: '8896' - signal_port: '8894' - label: cert - serial_number: 'CERT' - name: Cert Device - cmd: - - "adb" - - "-s" - - "$(serial_number)" - - "shell" - - "ASAN_OPTIONS=detect_container_overflow=0" - - "/system/bin/bluetooth_with_facades" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--btsnoop=/data/misc/bluetooth/logs/btsnoop_hci.log" - - "--btsnooz=/data/misc/bluetooth/logs/btsnooz_hci.log" - - "--btconfig=/data/misc/bluedroid/bt_config.conf" - - "--signal-port=$(signal_port)" - - grpc_port: '8899' - grpc_root_server_port: '8897' - signal_port: '8895' - label: dut - serial_number: 'DUT' - name: DUT Device - cmd: - - "adb" - - "-s" - - "$(serial_number)" - - "shell" - - "ASAN_OPTIONS=detect_container_overflow=0" - - "/system/bin/bluetooth_with_facades" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--btsnoop=/data/misc/bluetooth/logs/btsnoop_hci.log" - - "--btsnooz=/data/misc/bluetooth/logs/btsnooz_hci.log" - - "--btconfig=/data/misc/bluedroid/bt_config.conf" - - "--signal-port=$(signal_port)" -logpath: "/tmp/logs"
\ No newline at end of file diff --git a/system/blueberry/tests/gd/rust_host_config.yaml b/system/blueberry/tests/gd/rust_host_config.yaml deleted file mode 100644 index 527a1d11ce..0000000000 --- a/system/blueberry/tests/gd/rust_host_config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -_description: Bluetooth cert testing -TestBeds: - - Name: HostOnlyCert - Controllers: - rootcanal: - test_port: '6401' - hci_port: '6402' - link_layer_port: '6403' - GdDevice: - - grpc_port: '8998' - grpc_root_server_port: '8996' - signal_port: '8994' - label: cert - name: Cert Device - cmd: - - "$GD_ROOT/bluetooth_with_facades" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" - - grpc_port: '8999' - grpc_root_server_port: '8997' - signal_port: '8995' - label: dut - name: DUT Device - cmd: - - "$GD_ROOT/bluetooth_with_facades" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" -logpath: "/tmp/logs" diff --git a/system/blueberry/tests/topshim/adapter/adapter_test.py b/system/blueberry/tests/topshim/adapter/adapter_test.py deleted file mode 100644 index c2f1f1ee24..0000000000 --- a/system/blueberry/tests/topshim/adapter/adapter_test.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.topshim_base_test import TopshimBaseTest -from blueberry.tests.topshim.lib.adapter_client import AdapterClient - -from mobly import test_runner - - -class AdapterTest(TopshimBaseTest): - - def test_verify_adapter_started(self): - print("Adapter is verified when test starts") - - def test_enable_inquiry_scan(self): - status, discovery_mode = self.dut().enable_inquiry_scan() - assertThat(status).isEqualTo("Success") - assertThat(discovery_mode).isEqualTo("ConnectableDiscoverable") - - def test_enable_page_scan(self): - status, discovery_mode = self.dut().enable_page_scan() - assertThat(status).isEqualTo("Success") - assertThat(discovery_mode).isEqualTo("Connectable") - - def test_disable_page_scan(self): - status, discovery_mode = self.dut().disable_page_scan() - assertThat(status).isEqualTo("Success") - assertThat(discovery_mode).isEqualTo("None_") - - def test_set_local_io_caps(self): - status, caps = self.dut().set_local_io_caps(3) - assertThat(status).isEqualTo("Success") - assertThat(caps).isEqualTo("None_") - - def test_start_discovery(self): - state = self.dut().toggle_discovery(True) - assertThat(state).isEqualTo("Started") - # Reset device to not discovering. - self.dut().toggle_discovery(False) - - def test_cancel_discovery(self): - self.dut().toggle_discovery(True) - state = self.dut().toggle_discovery(False) - assertThat(state).isEqualTo("Stopped") - - def test_find_device_device_available(self): - self.dut().enable_inquiry_scan() - self.cert().enable_inquiry_scan() - self.dut().toggle_discovery(True) - device_addr = self.dut().find_device() - assertThat(device_addr).isNotNone() - # Reset DUT device discovering and scanning to None - self.dut().disable_page_scan() - self.dut().toggle_discovery(False) - # Reset CERT device to not discoverable - self.cert().disable_page_scan() - - -if __name__ == "__main__": - test_runner.main() diff --git a/system/blueberry/tests/topshim/hfp/hfp_test.py b/system/blueberry/tests/topshim/hfp/hfp_test.py deleted file mode 100644 index c8e8b95fdd..0000000000 --- a/system/blueberry/tests/topshim/hfp/hfp_test.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.topshim_base_test import TopshimBaseTest -from blueberry.tests.topshim.lib.topshim_device import TRANSPORT_CLASSIC - -from mobly import test_runner - - -class HfpTest(TopshimBaseTest): - - def setup_test(self): - super().setup_test() - # Pair dut and cert device. - self.dut().enable_inquiry_scan() - self.cert().enable_inquiry_scan() - self.dut().toggle_discovery(True) - self.__paired_device = self.dut().find_device() - self.dut().create_bond(address=self.__paired_device, transport=TRANSPORT_CLASSIC) - - def teardown_test(self): - super().teardown_test() - # Test teardown for dut and cert reset. - self.dut().toggle_discovery(False) - self.dut().disable_page_scan() - self.cert().disable_page_scan() - - def test_hfp_connect_with_bond(self): - state, _ = self.dut().start_slc(address=self.__paired_device) - assertThat(state).isEqualTo("Connecting") - state, _ = self.dut().wait_for_hfp_connection_state_change() - assertThat(state).isEqualTo("Connected") - state, conn_addr = self.dut().wait_for_hfp_connection_state_change() - assertThat(state).isEqualTo("SlcConnected") - assertThat(conn_addr).isEqualTo(self.__paired_device) - - #Extra steps to remove bonding to complete teardown. - self.dut().remove_bonded_device(self.__paired_device) - # This is required currently so that the HFP connection state change - # callback doesn't affect other tests. - self.dut().wait_for_hfp_connection_state_change() - - def test_hfp_disconnect_with_bond(self): - state, _ = self.dut().start_slc(address=self.__paired_device) - self.dut().wait_for_hfp_connection_state_change() # To connected - self.dut().wait_for_hfp_connection_state_change() # To SLC connected - - # Actual test for stopping SLC connection. - state, _ = self.dut().stop_slc(address=self.__paired_device) - assertThat(state).isEqualTo("Disconnecting") - state, _ = self.dut().wait_for_hfp_connection_state_change() - assertThat(state).isEqualTo("Disconnected") - #Extra steps to remove bonding to complete teardown. - self.dut().remove_bonded_device(self.__paired_device) - - -if __name__ == "__main__": - test_runner.main() diff --git a/system/blueberry/tests/topshim/lib/adapter_client.py b/system/blueberry/tests/topshim/lib/adapter_client.py deleted file mode 100644 index e85d379569..0000000000 --- a/system/blueberry/tests/topshim/lib/adapter_client.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import asyncio -import grpc - -from blueberry.facade.topshim import facade_pb2 -from blueberry.facade.topshim import facade_pb2_grpc -from blueberry.tests.topshim.lib.async_closable import AsyncClosable - -from google.protobuf import empty_pb2 as empty_proto - - -class AdapterClient(AsyncClosable): - """ - Wrapper gRPC interface to the Topshim/BTIF layer - """ - # Timeout for async wait - DEFAULT_TIMEOUT = 2 - __task_list = [] - __channel = None - __adapter_stub = None - __adapter_event_stream = None - - def __init__(self, port=8999): - self.__channel = grpc.aio.insecure_channel("localhost:%d" % port) - self.__adapter_stub = facade_pb2_grpc.AdapterServiceStub(self.__channel) - self.__adapter_event_stream = self.__adapter_stub.FetchEvents(facade_pb2.FetchEventsRequest()) - - async def close(self): - for task in self.__task_list: - if task.done() or task.cancelled(): - continue - task.cancel() - self.__task_list.clear() - await self.__channel.close() - - async def __get_next_event(self, event, future): - """Get the future of next event from the stream""" - while True: - e = await self.__adapter_event_stream.read() - - # Match event by some condition. - if e.event_type == event: - future.set_result(e.params) - break - else: - print("Got '%s'; expecting '%s'" % (e.event_type, event)) - print(e) - - async def _listen_for_event(self, event): - """Start fetching events""" - future = asyncio.get_running_loop().create_future() - task = asyncio.get_running_loop().create_task(self.__get_next_event(event, future)) - self.__task_list.append(task) - try: - await asyncio.wait_for(future, AdapterClient.DEFAULT_TIMEOUT) - except: - task.cancel() - print("Failed to get event", event) - return future - - async def _verify_adapter_started(self): - future = await self._listen_for_event(facade_pb2.EventType.ADAPTER_STATE) - params = future.result() - return params["state"].data[0] == "ON" - - async def toggle_stack(self, is_start=True): - """Enable/disable the stack""" - await self.__adapter_stub.ToggleStack(facade_pb2.ToggleStackRequest(start_stack=is_start)) - return await self._verify_adapter_started() - - async def enable_inquiry_scan(self): - """Enable inquiry scan (Required to make device connectable and discoverable by other devices)""" - await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_inquiry_scan=True)) - return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY) - - async def enable_page_scan(self): - """Enable page scan (might be used for A2dp sink to be discoverable)""" - await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_page_scan=True)) - return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY) - - async def disable_page_scan(self): - """Enable page scan (might be used for A2dp sink to be discoverable)""" - await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_page_scan=False)) - return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY) - - async def clear_event_filter(self): - await self.__adapter_stub.ClearEventFilter(empty_proto.Empty()) - - async def clear_event_mask(self): - await self.__adapter_stub.ClearEventMask(empty_proto.Empty()) - - async def clear_filter_accept_list(self): - await self.__adapter_stub.ClearFilterAcceptList(empty_proto.Empty()) - - async def disconnect_all_acls(self): - await self.__adapter_stub.DisconnectAllAcls(empty_proto.Empty()) - - async def le_rand(self): - await self.__adapter_stub.LeRand(empty_proto.Empty()) - future = await self._listen_for_event(facade_pb2.EventType.LE_RAND) - params = future.result() - return params["data"].data[0] - - async def restore_filter_accept_list(self): - await self.__adapter_stub.RestoreFilterAcceptList(empty_proto.Empty()) - - async def set_default_event_mask_except(self, mask, le_mask): - await self.__adapter_stub.SetDefaultEventMaskExcept( - facade_pb2.SetDefaultEventMaskExceptRequest(mask=mask, le_mask=le_mask)) - - async def set_event_filter_inquiry_result_all_devices(self): - await self.__adapter_stub.SetEventFilterInquiryResultAllDevices(empty_proto.Empty()) - - async def set_event_filter_connection_setup_all_devices(self): - await self.__adapter_stub.SetEventFilterConnectionSetupAllDevices(empty_proto.Empty()) - - async def allow_wake_by_hid(self): - await self.__adapter_stub.AllowWakeByHid(empty_proto.Empty()) - - async def set_local_io_caps(self, io_capability): - await self.__adapter_stub.SetLocalIoCaps(facade_pb2.SetLocalIoCapsRequest(io_capability=io_capability)) - return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY) - - async def toggle_discovery(self, is_start): - await self.__adapter_stub.ToggleDiscovery(facade_pb2.ToggleDiscoveryRequest(is_start=is_start)) - future = await self._listen_for_event(facade_pb2.EventType.DISCOVERY_STATE) - return future - - async def find_device(self): - return await self._listen_for_event(facade_pb2.EventType.DEVICE_FOUND) - - -class A2dpAutomationHelper(): - """Invoke gRPC on topshim for A2DP testing""" - - def __init__(self, port=8999): - self.__channel = grpc.insecure_channel("localhost:%d" % port) - self.media_stub = facade_pb2_grpc.MediaServiceStub(self.__channel) - - """Start A2dp source profile service""" - - def start_source(self): - self.media_stub.StartA2dp(facade_pb2.StartA2dpRequest(start_a2dp_source=True)) - - """Start A2dp sink profile service""" - - def start_sink(self): - self.media_stub.StartA2dp(facade_pb2.StartA2dpRequest(start_a2dp_sink=True)) - - """Initialize an A2dp connection from source to sink""" - - def source_connect_to_remote(self, address="11:22:33:44:55:66"): - self.media_stub.A2dpSourceConnect(facade_pb2.A2dpSourceConnectRequest(address=address)) diff --git a/system/blueberry/tests/topshim/lib/async_closable.py b/system/blueberry/tests/topshim/lib/async_closable.py deleted file mode 100644 index 5eea802d68..0000000000 --- a/system/blueberry/tests/topshim/lib/async_closable.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import asyncio -import time -from abc import ABC, abstractmethod -import logging - - -class AsyncClosable(ABC): - - async def __async_exit(self, type=None, value=None, traceback=None): - try: - return await self.close() - except Exception: - logging.warning("Failed to close or already closed") - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - asyncio.run_until_complete(self.__async_exit(type, value, traceback)) - return traceback is None - - def __del__(self): - asyncio.get_event_loop().run_until_complete(self.__async_exit()) - - @abstractmethod - async def close(self): - pass - - -async def asyncSafeClose(closable): - if closable is None: - logging.warn("Tried to close an object that is None") - return - await closable.close() diff --git a/system/blueberry/tests/topshim/lib/gatt_client.py b/system/blueberry/tests/topshim/lib/gatt_client.py deleted file mode 100644 index dcd0b8b7a9..0000000000 --- a/system/blueberry/tests/topshim/lib/gatt_client.py +++ /dev/null @@ -1,385 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import asyncio -import grpc - -from blueberry.facade.topshim import facade_pb2 -from blueberry.facade.topshim import facade_pb2_grpc -from blueberry.tests.topshim.lib.async_closable import AsyncClosable -from blueberry.tests.topshim.lib.async_closable import asyncSafeClose - -from google.protobuf import empty_pb2 as empty_proto - - -class GattClient(AsyncClosable): - """ - Wrapper gRPC interface to the GATT Service - """ - # Timeout for async wait - DEFAULT_TIMEOUT = 2 - __task_list = [] - __channel = None - __gatt_stub = None - __adapter_event_stream = None - - def __init__(self, port=8999): - self.__channel = grpc.aio.insecure_channel("localhost:%d" % port) - self.__gatt_stub = facade_pb2_grpc.GattServiceStub(self.__channel) - #self.__gatt_event_stream = self.__gatt_stub.FetchEvents(facade_pb2.FetchEventsRequest()) - - async def close(self): - """ - Terminate the current tasks - """ - for task in self.__task_list: - task.cancel() - task = None - self.__task_list.clear() - await self.__channel.close() - - async def register_advertiser(self): - """ - """ - await self.__gatt_stub.RegisterAdvertiser(empty_proto.Empty()) - - async def unregister_advertiser(self, advertiser_id): - """ - Stop advertising for advertiser id - """ - # TODO(optedoblivion): make message to pass advertiser id - await self.__gatt_stub.UnregisterAdvertiser(empty_proto.Empty()) - - async def get_own_address(self): - """ - """ - await self.__gatt_stub.GetOwnAddress(empty_proto.Empty()) - - async def set_parameters(self): - """ - """ - await self.__gatt_stub.SetParameters(empty_proto.Empty()) - - async def set_data(self): - """ - """ - await self.__gatt_stub.SetData(empty_proto.Empty()) - - async def advertising_enable(self): - """ - """ - await self.__gatt_stub.AdvertisingEnable(empty_proto.Empty()) - - async def advertising_disable(self): - """ - """ - await self.__gatt_stub.AdvertisingDisable(empty_proto.Empty()) - - async def set_periodic_advertising_parameters(self): - """ - """ - await self.__gatt_stub.SetPeriodicAdvertisingParameters(empty_proto.Empty()) - - async def set_periodic_advertising_data(self): - """ - """ - await self.__gatt_stub.SetPeriodicAdvertisingData(empty_proto.Empty()) - - async def set_periodic_advertising_enable(self): - """ - """ - await self.__gatt_stub.SetPeriodicAdvertisingEnable(empty_proto.Empty()) - - async def start_advertising(self): - """ - """ - await self.__gatt_stub.StartAdvertising(empty_proto.Empty()) - - async def start_advertising_set(self): - """ - Start advertising with the given parameters - """ - await self.__gatt_stub.StartAdvertisingSet(empty_proto.Empty()) - - async def register_scanner(self): - """ - """ - await self.__gatt_stub.RegisterScanner(empty_proto.Empty()) - - async def unregister_scanner(self): - """ - """ - await self.__gatt_stub.UnregisterScanner(empty_proto.Empty()) - - async def start_scan(self): - """ - """ - await self.__gatt_stub.StartScan(empty_proto.Empty()) - - async def stop_scan(self): - """ - """ - await self.__gatt_stub.StopScan(empty_proto.Empty()) - - async def scan_filter_setup(self): - """ - """ - await self.__gatt_stub.ScanFilterSetup(empty_proto.Empty()) - - async def scan_filter_add(self): - """ - """ - await self.__gatt_stub.ScanFilterAdd(empty_proto.Empty()) - - async def scan_filter_clear(self): - """ - """ - await self.__gatt_stub.ScanFilterClear(empty_proto.Empty()) - - async def scan_filter_enable(self): - """ - """ - await self.__gatt_stub.ScanFilterEnable(empty_proto.Empty()) - - async def scan_filter_disable(self): - """ - """ - await self.__gatt_stub.ScanFilterDisable(empty_proto.Empty()) - - async def set_scan_parameters(self): - """ - """ - await self.__gatt_stub.SetScanParameters(empty_proto.Empty()) - - async def batch_scan_config_storage(self): - """ - """ - await self.__gatt_stub.BatchScanConfigStorage(empty_proto.Empty()) - - async def batch_scan_enable(self): - """ - """ - await self.__gatt_stub.BatchScanEnable(empty_proto.Empty()) - - async def batch_scan_disable(self): - """ - """ - await self.__gatt_stub.BatchScanDisable(empty_proto.Empty()) - - async def batch_scan_read_reports(self): - """ - """ - await self.__gatt_stub.BatchScanReadReports(empty_proto.Empty()) - - async def start_sync(self): - """ - """ - await self.__gatt_stub.StartSync(empty_proto.Empty()) - - async def stop_sync(self): - """ - """ - await self.__gatt_stub.StopSync(empty_proto.Empty()) - - async def cancel_create_sync(self): - """ - """ - await self.__gatt_stub.CancelCreateSync(empty_proto.Empty()) - - async def transfer_sync(self): - """ - """ - await self.__gatt_stub.TransferSync(empty_proto.Empty()) - - async def transfer_set_info(self): - """ - """ - await self.__gatt_stub.TransferSetInfo(empty_proto.Empty()) - - async def sync_tx_parameters(self): - """ - """ - await self.__gatt_stub.SyncTxParameters(empty_proto.Empty()) - - async def register_client(self): - """ - """ - await self.__gatt_stub.RegisterClient(empty_proto.Empty()) - - async def unregister_client(self): - """ - """ - await self.__gatt_stub.UnregisterClient(empty_proto.Empty()) - - async def connect(self): - """ - """ - await self.__gatt_stub.Connect(empty_proto.Empty()) - - async def disconnect(self): - """ - """ - await self.__gatt_stub.Disconnect(empty_proto.Empty()) - - async def refresh(self): - """ - """ - await self.__gatt_stub.Refresh(empty_proto.Empty()) - - async def search_service(self): - """ - """ - await self.__gatt_stub.SearchService(empty_proto.Empty()) - - async def btif_gattc_discover_service_by_uuid(self): - """ - """ - await self.__gatt_stub.BtifGattcDiscoverServiceByUuid(empty_proto.Empty()) - - async def read_characteristic(self): - """ - """ - await self.__gatt_stub.ReadCharacteristic(empty_proto.Empty()) - - async def read_using_characteristic_uuid(self): - """ - """ - await self.__gatt_stub.ReadUsingCharacteristicUuid(empty_proto.Empty()) - - async def write_characteristic(self): - """ - """ - await self.__gatt_stub.WriteCharacteristic(empty_proto.Empty()) - - async def read_descriptor(self): - """ - """ - await self.__gatt_stub.ReadDescriptor(empty_proto.Empty()) - - async def write_descriptor(self): - """ - """ - await self.__gatt_stub.WriteDescriptor(empty_proto.Empty()) - - async def execute_write(self): - """ - """ - await self.__gatt_stub.ExecuteWrite(empty_proto.Empty()) - - async def register_for_notification(self): - """ - """ - await self.__gatt_stub.RegisterForNotification(empty_proto.Empty()) - - async def deregister_for_notification(self): - """ - """ - await self.__gatt_stub.DeregisterForNotification(empty_proto.Empty()) - - async def read_remote_rssi(self): - """ - """ - await self.__gatt_stub.ReadRemoteRssi(empty_proto.Empty()) - - async def get_device_type(self): - """ - """ - await self.__gatt_stub.GetDeviceType(empty_proto.Empty()) - - async def configure_mtu(self): - """ - """ - await self.__gatt_stub.ConfigureMtu(empty_proto.Empty()) - - async def conn_parameter_update(self): - """ - """ - await self.__gatt_stub.ConnParameterUpdate(empty_proto.Empty()) - - async def set_preferred_phy(self): - """ - """ - await self.__gatt_stub.SetPreferredPhy(empty_proto.Empty()) - - async def read_phy(self): - """ - """ - await self.__gatt_stub.ReadPhy(empty_proto.Empty()) - - async def test_command(self): - """ - """ - await self.__gatt_stub.TestCommand(empty_proto.Empty()) - - async def get_gatt_db(self): - """ - """ - await self.__gatt_stub.GetGattDb(empty_proto.Empty()) - - async def register_server(self): - """ - """ - await self.__gatt_stub.RegisterServer(empty_proto.Empty()) - - async def unregister_server(self): - """ - """ - await self.__gatt_stub.UnregisterServer(empty_proto.Empty()) - - async def connect(self): - """ - """ - await self.__gatt_stub.Connect(empty_proto.Empty()) - - async def disconnect(self): - """ - """ - await self.__gatt_stub.Disconnect(empty_proto.Empty()) - - async def add_service(self): - """ - """ - await self.__gatt_stub.AddService(empty_proto.Empty()) - - async def stop_service(self): - """ - """ - await self.__gatt_stub.StopService(empty_proto.Empty()) - - async def delete_service(self): - """ - """ - await self.__gatt_stub.DeleteService(empty_proto.Empty()) - - async def send_indication(self): - """ - """ - await self.__gatt_stub.SendIndication(empty_proto.Empty()) - - async def send_response(self): - """ - """ - await self.__gatt_stub.SendResponse(empty_proto.Empty()) - - async def set_preferred_phy(self): - """ - """ - await self.__gatt_stub.SetPreferredPhy(empty_proto.Empty()) - - async def read_phy(self): - """ - """ - await self.__gatt_stub.ReadPhy(empty_proto.Empty()) diff --git a/system/blueberry/tests/topshim/lib/hf_client_client.py b/system/blueberry/tests/topshim/lib/hf_client_client.py deleted file mode 100644 index 23d4832375..0000000000 --- a/system/blueberry/tests/topshim/lib/hf_client_client.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import asyncio -import grpc - -from blueberry.facade.topshim import facade_pb2 -from blueberry.facade.topshim import facade_pb2_grpc -from blueberry.tests.topshim.lib.async_closable import AsyncClosable - - -class HfClientClient(AsyncClosable): - """ - Wrapper gRPC interface to the HF Client Service - """ - __channel = None - __hf_client_stub = None - - def __init__(self, port=8999): - self.__channel = grpc.aio.insecure_channel("localhost:%d" % port) - self.__hf_client_stub = facade_pb2_grpc.HfClientServiceStub(self.__channel) - - async def close(self): - await self.__channel.close() - - async def start_slc(self, address): - """ - """ - await self.__hf_client_stub.StartSlc( - facade_pb2.StartSlcRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - - async def stop_slc(self, address): - """ - """ - await self.__hf_client_stub.StopSlc( - facade_pb2.StopSlcRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - - async def connect_audio(self, address): - """ - """ - await self.__hf_client_stub.ConnectAudio( - facade_pb2.ConnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - - async def disconnect_audio(self, address): - """ - """ - await self.__hf_client_stub.DisconnectAudio( - facade_pb2.DisconnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode()))) diff --git a/system/blueberry/tests/topshim/lib/hfp_client.py b/system/blueberry/tests/topshim/lib/hfp_client.py deleted file mode 100644 index c4bfd80f8a..0000000000 --- a/system/blueberry/tests/topshim/lib/hfp_client.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import asyncio -import grpc - -from blueberry.facade.topshim import facade_pb2 -from blueberry.facade.topshim import facade_pb2_grpc -from blueberry.tests.topshim.lib.async_closable import AsyncClosable - -from google.protobuf import empty_pb2 as empty_proto - - -class HfpClient(AsyncClosable): - """ - Wrapper gRPC interface to the HFP Service - """ - # Timeout for async wait - DEFAULT_TIMEOUT = 2 - __task_list = [] - __channel = None - __hfp_stub = None - __hfp_event_stream = None - - def __init__(self, port=8999): - self.__channel = grpc.aio.insecure_channel("localhost:%d" % port) - self.__hfp_stub = facade_pb2_grpc.HfpServiceStub(self.__channel) - self.__hfp_event_stream = self.__hfp_stub.FetchEvents(facade_pb2.FetchEventsRequest()) - - async def close(self): - """ - Terminate the current tasks. - """ - for task in self.__task_list: - task.cancel() - task = None - self.__task_list.clear() - await self.__channel.close() - - async def start_slc(self, address): - """ - """ - await self.__hfp_stub.StartSlc( - facade_pb2.StartSlcRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - return await self._listen_for_event(facade_pb2.EventType.HFP_CONNECTION_STATE) - - async def stop_slc(self, address): - """ - """ - await self.__hfp_stub.StopSlc( - facade_pb2.StopSlcRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - return await self._listen_for_event(facade_pb2.EventType.HFP_CONNECTION_STATE) - - async def connect_audio(self, address, is_sco_offload_enabled=False, disabled_codecs=0): - """ - """ - await self.__hfp_stub.ConnectAudio( - facade_pb2.ConnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode()), - is_sco_offload_enabled=is_sco_offload_enabled, - disabled_codecs=disabled_codecs)) - - async def disconnect_audio(self, address): - """ - """ - await self.__hfp_stub.DisconnectAudio( - facade_pb2.DisconnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode()))) - - async def set_volume(self, address, volume): - """ - """ - await self.__hfp_stub.DisconnectAudio( - facade_pb2.DisconnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode()), volume=volume)) - - async def wait_for_hfp_connection_state_change(self): - return await self._listen_for_event(facade_pb2.EventType.HFP_CONNECTION_STATE) - - async def __get_next_event(self, event, future): - """Get the future of next event from the stream""" - while True: - e = await self.__hfp_event_stream.read() - - # Match event by some condition. - if e.event_type == event: - future.set_result(e.data) - break - else: - print("Got '%s'; expecting '%s'" % (e.event_type, event)) - print(e) - - async def _listen_for_event(self, event): - """Start fetching events""" - future = asyncio.get_running_loop().create_future() - self.__task_list.append(asyncio.get_running_loop().create_task(self.__get_next_event(event, future))) - try: - await asyncio.wait_for(future, HfpClient.DEFAULT_TIMEOUT) - except: - print("Failed to get event", event) - return future diff --git a/system/blueberry/tests/topshim/lib/oob_data.py b/system/blueberry/tests/topshim/lib/oob_data.py deleted file mode 100644 index 0ec9ff7c4c..0000000000 --- a/system/blueberry/tests/topshim/lib/oob_data.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class OobData: - """ - Represents an Out of Band data set in a readible object - """ - - def __init__(self, is_valid, transport, byte_string_address, byte_string_c, byte_string_r): - """ - @param is_valid indicates whether the data was able to be parsed - @param transport LE or Classic - @param 7 octet byte string. Little Endian 6 byte address + 1 byte transport - @param byte_string_c 16 octet confirmation - @param byte_string_r 16 octet randomizer - """ - self.__is_valid = True if is_valid == "1" else False - self.__transport = int(transport) - self.__byte_string_address = byte_string_address - self.__byte_string_c = byte_string_c - self.__byte_string_r = byte_string_r - - def is_valid(self): - return self.__is_valid - - def transport(self): - return self.__transport - - def address(self): - return self.__byte_string_address - - def confirmation(self): - return self.__byte_string_c - - def randomizer(self): - return self.__byte_string_r diff --git a/system/blueberry/tests/topshim/lib/security_client.py b/system/blueberry/tests/topshim/lib/security_client.py deleted file mode 100644 index f22550e564..0000000000 --- a/system/blueberry/tests/topshim/lib/security_client.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import grpc - -from blueberry.facade.topshim import facade_pb2 -from blueberry.facade.topshim import facade_pb2_grpc -from blueberry.tests.topshim.lib.async_closable import AsyncClosable -from blueberry.tests.topshim.lib.async_closable import asyncSafeClose - -from google.protobuf import empty_pb2 as empty_proto - - -class SecurityClient(AsyncClosable): - """ - Wrapper gRPC interface to the GATT Service - """ - # Timeout for async wait - __task_list = [] - __channel = None - __security = None - __adapter = None - - def __init__(self, adapter, port=8999): - self.__channel = grpc.aio.insecure_channel("localhost:%d" % port) - self.__security = facade_pb2_grpc.SecurityServiceStub(self.__channel) - self.__adapter = adapter - - async def close(self): - """ - Terminate the current tasks - """ - for task in self.__task_list: - task.cancel() - task = None - self.__task_list.clear() - await self.__channel.close() - - async def create_bond(self, address, transport): - """ - Create a bonding entry for a given address with a particular transport type - """ - await self.__security.CreateBond(facade_pb2.CreateBondRequest(address=address, transport=transport)) - # State change to Bonding - await self.__adapter._listen_for_event(facade_pb2.EventType.BOND_STATE) - # State change to Bonded or None (based on success and failure) - return await self.__adapter._listen_for_event(facade_pb2.EventType.BOND_STATE) - - async def remove_bond(self, address): - """ - Removes a bonding entry for a given address - """ - await self.__security.RemoveBond(facade_pb2.RemoveBondRequest(address=address)) - - async def generate_local_oob_data(self, transport): - await self.__security.GenerateLocalOobData(facade_pb2.GenerateOobDataRequest(transport=transport)) - future = await self.__adapter._listen_for_event(facade_pb2.EventType.GENERATE_LOCAL_OOB_DATA) - return future diff --git a/system/blueberry/tests/topshim/lib/topshim_base_test.py b/system/blueberry/tests/topshim/lib/topshim_base_test.py deleted file mode 100644 index 00c9f03eaa..0000000000 --- a/system/blueberry/tests/topshim/lib/topshim_base_test.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2021 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import asyncio -import importlib -import logging -import os -import signal -import subprocess - -from blueberry.tests.gd.cert.async_subprocess_logger import AsyncSubprocessLogger -from blueberry.tests.gd.cert.context import get_current_context -from blueberry.tests.gd.cert.os_utils import get_gd_root -from blueberry.tests.gd.cert.os_utils import get_gd_root -from blueberry.tests.gd.cert.os_utils import read_crash_snippet_and_log_tail -from blueberry.tests.gd.cert.os_utils import is_subprocess_alive -from blueberry.tests.gd.cert.os_utils import make_ports_available -from blueberry.tests.gd.cert.os_utils import TerminalColor -from blueberry.tests.gd.cert.tracelogger import TraceLogger -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.adapter_client import AdapterClient -from blueberry.tests.topshim.lib.async_closable import asyncSafeClose -from blueberry.tests.topshim.lib.gatt_client import GattClient -from blueberry.tests.topshim.lib.hf_client_client import HfClientClient -from blueberry.tests.topshim.lib.hfp_client import HfpClient -from blueberry.tests.topshim.lib.security_client import SecurityClient -from blueberry.tests.topshim.lib.topshim_device import TopshimDevice - -from mobly import asserts -from mobly import base_test - -CONTROLLER_CONFIG_NAME = "GdDevice" - - -def _setup_class_core(verbose_mode, log_path_base, controller_configs): - info = {} - info['controller_configs'] = controller_configs - - # Start root-canal if needed - info['rootcanal_running'] = False - info['rootcanal_logpath'] = None - info['rootcanal_process'] = None - info['rootcanal_logger'] = None - if 'rootcanal' not in info['controller_configs']: - return - info['rootcanal_running'] = True - # Get root canal binary - rootcanal = os.path.join(get_gd_root(), "root-canal") - info['rootcanal'] = rootcanal - info['rootcanal_exist'] = os.path.isfile(rootcanal) - if not os.path.isfile(rootcanal): - return info - # Get root canal log - rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt') - info['rootcanal_logpath'] = rootcanal_logpath - # Make sure ports are available - rootcanal_config = info['controller_configs']['rootcanal'] - rootcanal_test_port = int(rootcanal_config.get("test_port", "6401")) - rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402")) - rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403")) - - info['make_rootcanal_ports_available'] = make_ports_available( - (rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)) - if not make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)): - return info - - # Start root canal process - rootcanal_cmd = [rootcanal, str(rootcanal_test_port), str(rootcanal_hci_port), str(rootcanal_link_layer_port)] - info['rootcanal_cmd'] = rootcanal_cmd - - rootcanal_process = subprocess.Popen(rootcanal_cmd, - cwd=get_gd_root(), - env=os.environ.copy(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - info['rootcanal_process'] = rootcanal_process - if rootcanal_process: - info['is_rootcanal_process_started'] = True - else: - info['is_rootcanal_process_started'] = False - return info - info['is_subprocess_alive'] = is_subprocess_alive(rootcanal_process) - if not is_subprocess_alive(rootcanal_process): - info['is_subprocess_alive'] = False - return info - - info['rootcanal_logger'] = AsyncSubprocessLogger(rootcanal_process, [rootcanal_logpath], - log_to_stdout=verbose_mode, - tag="rootcanal", - color=TerminalColor.MAGENTA) - - # Modify the device config to include the correct root-canal port - for gd_device_config in info['controller_configs'].get("GdDevice"): - gd_device_config["rootcanal_port"] = str(rootcanal_hci_port) - - return info - - -def _teardown_class_core(rootcanal_running, rootcanal_process, rootcanal_logger, subprocess_wait_timeout_seconds): - if rootcanal_running: - stop_signal = signal.SIGINT - rootcanal_process.send_signal(stop_signal) - try: - return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) - except subprocess.TimeoutExpired: - logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL") - stop_signal = signal.SIGKILL - rootcanal_process.kill() - try: - return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) - except subprocess.TimeoutExpired: - logging.error("Failed to kill root canal") - return_code = -65536 - if return_code != 0 and return_code != -stop_signal: - logging.error("rootcanal stopped with code: %d" % return_code) - rootcanal_logger.stop() - - -def dump_crashes_core(dut, cert, rootcanal_running, rootcanal_process, rootcanal_logpath): - dut_crash, dut_log_tail = dut.get_crash_snippet_and_log_tail() - cert_crash, cert_log_tail = cert.get_crash_snippet_and_log_tail() - rootcanal_crash = None - rootcanal_log_tail = None - if rootcanal_running and not is_subprocess_alive(rootcanal_process): - rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(rootcanal_logpath) - - crash_detail = "" - if dut_crash or cert_crash or rootcanal_crash: - if rootcanal_crash: - crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash - if dut_crash: - crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash - if cert_crash: - crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash - else: - if rootcanal_log_tail: - crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail - if dut_log_tail: - crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail - if cert_log_tail: - crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail - - return crash_detail - - -class TopshimBaseTest(base_test.BaseTestClass): - - __dut = None - __cert = None - - async def __setup_adapter(self): - dut_adapter = AdapterClient(port=self.dut_port) - cert_adapter = AdapterClient(port=self.cert_port) - started = await dut_adapter._verify_adapter_started() - assertThat(started).isTrue() - started = started and await cert_adapter._verify_adapter_started() - assertThat(started).isTrue() - self.__dut = TopshimDevice(dut_adapter, GattClient(port=self.dut_port), - SecurityClient(dut_adapter, port=self.dut_port), - HfpClient(port=self.dut_port), - HfClientClient(port=self.dut_port)) - self.__cert = TopshimDevice(cert_adapter, GattClient(port=self.cert_port), - SecurityClient(cert_adapter, port=self.cert_port), - HfpClient(port=self.cert_port), - HfClientClient(port=self.cert_port)) - return started - - async def __teardown_adapter(self): - await asyncSafeClose(self.__dut) - await asyncSafeClose(self.__cert) - - def dut(self): - """ - Get a handle on the DUT device - """ - return self.__dut - - def cert(self): - """ - Get a handle on the CERT device - """ - return self.__cert - - def setup_class(self): - """ - Configure rootcanal and setup test parameters - """ - self.log = TraceLogger(logging.getLogger()) - self.log_path_base = get_current_context().get_full_output_path() - self.verbose_mode = bool(self.user_params.get('verbose_mode', False)) - for config in self.controller_configs[CONTROLLER_CONFIG_NAME]: - config['verbose_mode'] = self.verbose_mode - - self.info = _setup_class_core(verbose_mode=self.verbose_mode, - log_path_base=self.log_path_base, - controller_configs=self.controller_configs) - self.rootcanal_running = self.info['rootcanal_running'] - self.rootcanal_logpath = self.info['rootcanal_logpath'] - self.rootcanal_process = self.info['rootcanal_process'] - self.rootcanal_logger = self.info['rootcanal_logger'] - - asserts.assert_true(self.info['rootcanal_exist'], "Root canal does not exist at %s" % self.info['rootcanal']) - asserts.assert_true(self.info['make_rootcanal_ports_available'], "Failed to make root canal ports available") - - self.log.debug("Running %s" % " ".join(self.info['rootcanal_cmd'])) - asserts.assert_true(self.info['is_rootcanal_process_started'], - msg="Cannot start root-canal at " + str(self.info['rootcanal'])) - asserts.assert_true(self.info['is_subprocess_alive'], msg="root-canal stopped immediately after running") - - self.controller_configs = self.info['controller_configs'] - - controllers = self.register_controller(importlib.import_module('blueberry.tests.topshim.lib.topshim_device')) - self.cert_port = controllers[0].grpc_port - self.dut_port = controllers[1].grpc_port - asyncio.set_event_loop(asyncio.new_event_loop()) - asyncio.get_event_loop().run_until_complete(self.__setup_adapter()) - - def teardown_class(self): - _teardown_class_core(rootcanal_running=self.rootcanal_running, - rootcanal_process=self.rootcanal_process, - rootcanal_logger=self.rootcanal_logger, - subprocess_wait_timeout_seconds=1) - asyncio.get_event_loop().run_until_complete(self.__teardown_adapter()) diff --git a/system/blueberry/tests/topshim/lib/topshim_device.py b/system/blueberry/tests/topshim/lib/topshim_device.py deleted file mode 100644 index b845f48174..0000000000 --- a/system/blueberry/tests/topshim/lib/topshim_device.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2019 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import asyncio -import logging - -from blueberry.tests.gd.cert.gd_device import GdHostOnlyDevice -from blueberry.tests.gd.cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME -from blueberry.tests.gd.cert.os_utils import get_gd_root -from blueberry.tests.topshim.lib.async_closable import AsyncClosable -from blueberry.tests.topshim.lib.async_closable import asyncSafeClose -from blueberry.tests.topshim.lib.oob_data import OobData -from blueberry.tests.topshim.lib.gatt_client import GattClient - - -def create(configs): - return get_instances_with_configs(configs) - - -def destroy(devices): - pass - - -def replace_vars_for_topshim(string, config): - serial_number = config.get("serial_number") - if serial_number is None: - serial_number = "" - rootcanal_port = config.get("rootcanal_port") - if rootcanal_port is None: - rootcanal_port = "" - if serial_number == "DUT" or serial_number == "CERT": - raise Exception("Did you forget to configure the serial number?") - # We run bt_topshim_facade instead of bluetooth_stack_with_facade - return string.replace("$GD_ROOT", get_gd_root()) \ - .replace("bluetooth_stack_with_facade", "bt_topshim_facade") \ - .replace("$(grpc_port)", config.get("grpc_port")) \ - .replace("$(grpc_root_server_port)", config.get("grpc_root_server_port")) \ - .replace("$(rootcanal_port)", rootcanal_port) \ - .replace("$(signal_port)", config.get("signal_port")) \ - .replace("$(serial_number)", serial_number) - - -def get_instances_with_configs(configs): - logging.info(configs) - devices = [] - for config in configs: - resolved_cmd = [] - for arg in config["cmd"]: - logging.debug(arg) - resolved_cmd.append(replace_vars_for_topshim(arg, config)) - verbose_mode = bool(config.get('verbose_mode', False)) - device = GdHostOnlyDevice(config["grpc_port"], "-1", config["signal_port"], resolved_cmd, config["label"], - MOBLY_CONTROLLER_CONFIG_NAME, config["name"], verbose_mode) - device.setup() - devices.append(device) - return devices - - -TRANSPORT_CLASSIC = 1 -TRANSPORT_LE = 2 - - -class TopshimDevice(AsyncClosable): - __adapter = None - __gatt = None - __security = None - __hfp = None - __hf_client = None - - async def __le_rand_wrapper(self, async_fn): - result = await async_fn - await self.__adapter.le_rand() - le_rand_future = await self.__adapter._listen_for_event(facade_pb2.EventType.LE_RAND) - return result - - def __post(self, async_fn): - return asyncio.get_event_loop().run_until_complete(async_fn) - - def __init__(self, adapter, gatt, security, hfp, hf_client): - self.__adapter = adapter - self.__gatt = gatt - self.__security = security - self.__hfp = hfp - self.__hf_client = hf_client - - async def close(self): - """ - Implement abstract method to close out any streams or jobs. - """ - await asyncSafeClose(self.__adapter) - await asyncSafeClose(self.__gatt) - await asyncSafeClose(self.__security) - await asyncSafeClose(self.__hfp) - await asyncSafeClose(self.__hf_client) - - def enable_inquiry_scan(self): - f = self.__post(self.__adapter.enable_inquiry_scan()) - return self.__post(self.__discovery_mode_waiter(f)) - - def enable_page_scan(self): - f = self.__post(self.__adapter.enable_page_scan()) - return self.__post(self.__discovery_mode_waiter(f)) - - def disable_page_scan(self): - f = self.__post(self.__adapter.disable_page_scan()) - return self.__post(self.__discovery_mode_waiter(f)) - - async def __discovery_mode_waiter(self, f): - params = await f - status, discovery_mode = params["status"].data[0], params["AdapterScanMode"].data[0] - return (status, discovery_mode) - - def start_advertising(self): - """ - Starts BLE Advertiser for the stack. - Assumes stack defaults. Which in our case would be RRPA - """ - self.__post(self.__gatt.advertising_enable()) - - def stop_advertising(self): - """ - Stop BLE Advertiser. - """ - self.__post(self.__gatt.advertising_disable()) - - def start_scanning(self): - pass - - def stop_scanning(self): - pass - - def clear_event_mask(self): - self.__post(self.__adapter.clear_event_mask()) - - def clear_event_filter(self): - self.__post(self.__adapter.clear_event_filter()) - - def clear_filter_accept_list(self): - self.__post(self.__adapter.clear_filter_accept_list()) - - def disconnect_all_acls(self): - self.__post(self.__adapter.disconnect_all_acls()) - - def allow_wake_by_hid(self): - self.__post(self.__adapter.allow_wake_by_hid()) - - def set_default_event_mask_except(self, mask, le_mask): - self.__post(self.__adapter.set_default_event_mask_except(mask, le_mask)) - - def set_event_filter_inquiry_result_all_devices(self): - self.__post(self.__adapter.set_event_filter_inquiry_result_all_devices()) - - def set_event_filter_connection_setup_all_devices(self): - self.__post(self.__adapter.set_event_filter_connection_setup_all_devices()) - - def le_rand(self): - self.__post(self.__adapter.le_rand()) - - def create_bond(self, address, transport=1): - """ - Create a bonding entry for a given address with a particular transport type. - """ - f = self.__post(self.__security.create_bond(address, transport)) - return self.__post(self.__bond_change_waiter(f)) - - def remove_bonded_device(self, address): - """ - Removes a bonding entry for a given address. - """ - self.__post(self.__security.remove_bond(address)) - - async def __bond_change_waiter(self, f): - params = await f - state, address = params["bond_state"].data[0], params["address"].data[0] - return (state, address) - - def generate_local_oob_data(self, transport=TRANSPORT_LE): - """ - Generate local Out of Band data - - @param transport TRANSPORT_CLASSIC or TRANSPORT_LE - - @return a future to await on fo the data - """ - f = self.__post(self.__security.generate_local_oob_data(transport)) - - async def waiter(f): - params = await f - return OobData(params["is_valid"].data[0], params["transport"].data[0], params["address"].data[0], - params["confirmation"].data[0], params["randomizer"].data[0]) - - return self.__post(waiter(f)) - - def set_local_io_caps(self, io_capability=0): - f = self.__post(self.__adapter.set_local_io_caps(io_capability)) - - async def waiter(f): - params = await f - status, io_caps = params["status"].data[0], params["LocalIoCaps"].data[0] - return (status, io_caps) - - return self.__post(waiter(f)) - - def toggle_discovery(self, is_start): - f = self.__post(self.__adapter.toggle_discovery(is_start)) - - async def waiter(f): - params = await f - return params["discovery_state"].data[0] - - return self.__post(waiter(f)) - - def find_device(self): - """ - Attempts to find discoverable devices when discovery is toggled on. - - @return a list of properties of found device. - """ - f = self.__post(self.__adapter.find_device()) - - async def waiter(f): - try: - params = await f - return params["BdAddr"].data[0] - except: - # The future `f` has a timeout after 2s post which it is cancelled. - print("No device was found. Timed out.") - return None - - return self.__post(waiter(f)) - - def start_slc(self, address): - f = self.__post(self.__hfp.start_slc(address)) - return self.__post(self.__hfp_connection_state_waiter(f)) - - def stop_slc(self, address): - f = self.__post(self.__hfp.stop_slc(address)) - return self.__post(self.__hfp_connection_state_waiter(f)) - - def wait_for_hfp_connection_state_change(self): - f = self.__post(self.__hfp.wait_for_hfp_connection_state_change()) - return self.__post(self.__hfp_connection_state_waiter(f)) - - async def __hfp_connection_state_waiter(self, f): - data = await f - data_list = data.split(", ") - state, address = data_list[0].strip(), data_list[1].strip() - return (state, address) diff --git a/system/blueberry/tests/topshim/power/suspend_test.py b/system/blueberry/tests/topshim/power/suspend_test.py deleted file mode 100644 index 4292c05459..0000000000 --- a/system/blueberry/tests/topshim/power/suspend_test.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.topshim_base_test import TopshimBaseTest -from blueberry.tests.topshim.lib.adapter_client import AdapterClient - -from mobly import test_runner - - -class SuspendTest(TopshimBaseTest): - - def __verify_no_wake_suspend(self): - # Start suspend work - self.dut().clear_event_mask() - self.dut().clear_event_filter() - self.dut().clear_filter_accept_list() - self.dut().stop_advertising() - self.dut().stop_scanning() - self.dut().disconnect_all_acls() - self.dut().le_rand() - - def __verify_no_wake_resume(self): - # Start resume work - self.dut().set_default_event_mask_except(0, 0) - self.dut().set_event_filter_inquiry_result_all_devices() - self.dut().set_event_filter_connection_setup_all_devices() - self.dut().le_rand() - - def __verify_wakeful_suspend(self, is_a2dp_connected): - self.dut().clear_event_mask() - self.dut().clear_event_filter() - self.dut().clear_filter_accept_list() - self.dut().stop_advertising() - self.dut().stop_scanning() - if is_a2dp_connected: - # self.media_server.disconnect_a2dp() - pass - self.dut().disconnect_all_acls() - self.dut().allow_wake_by_hid() - self.dut().le_rand() - - def __verify_wakeful_resume(self, was_a2dp_connected): - # Start resume work - self.dut().set_default_event_mask_except(0, 0) - self.dut().set_event_filter_inquiry_result_all_devices() - self.dut().set_event_filter_connection_setup_all_devices() - if was_a2dp_connected: - # restore filter accept list? - self.dut().restore_filter_accept_list() - # reconnect a2dp - # self.media_server.reconnect_last_a2dp() - # self.gatt.restart_all_previous_advertising() - self.dut().start_advertising() - self.dut().le_rand() - - def test_no_wake_suspend(self): - self.__verify_no_wake_suspend() - - def test_no_wake_resume(self): - self.__verify_no_wake_resume() - - def test_no_wake_suspend_then_resume(self): - self.__verify_no_wake_suspend() - self.__verify_no_wake_resume() - - def test_no_wake_suspend_then_resume_then_suspend(self): - self.__verify_no_wake_suspend() - self.__verify_no_wake_resume() - self.__verify_no_wake_suspend() - - def test_wakeful_suspend_no_a2dp(self): - self.__verify_wakeful_suspend(False) - - def test_wakeful_resume_no_a2dp(self): - self.__verify_wakeful_resume(False) - - def test_wakeful_suspend_then_resume_no_a2dp(self): - self.__verify_wakeful_suspend(False) - self.__verify_wakeful_resume(False) - - def test_wakeful_suspend_then_resume_then_suspend_no_a2dp(self): - self.__verify_wakeful_suspend(False) - self.__verify_wakeful_resume(False) - self.__verify_wakeful_suspend(False) - - -if __name__ == "__main__": - test_runner.main() diff --git a/system/blueberry/tests/topshim/security/classic_security_test.py b/system/blueberry/tests/topshim/security/classic_security_test.py deleted file mode 100644 index b0bfab469d..0000000000 --- a/system/blueberry/tests/topshim/security/classic_security_test.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.adapter_client import AdapterClient -from blueberry.tests.topshim.lib.topshim_base_test import TopshimBaseTest -from blueberry.tests.topshim.lib.topshim_device import TRANSPORT_CLASSIC - -from mobly import test_runner - - -class ClassicSecurityTest(TopshimBaseTest): - - def test_create_classic_bond(self): - self.dut().enable_inquiry_scan() - self.cert().enable_inquiry_scan() - self.dut().toggle_discovery(True) - address = self.dut().find_device() - state, conn_addr = self.dut().create_bond(address=address, transport=TRANSPORT_CLASSIC) - assertThat(state).isEqualTo("Bonded") - assertThat(conn_addr).isEqualTo(address) - - -if __name__ == "__main__": - test_runner.main() diff --git a/system/blueberry/tests/topshim/security/le_security_test.py b/system/blueberry/tests/topshim/security/le_security_test.py deleted file mode 100644 index 307c3ec429..0000000000 --- a/system/blueberry/tests/topshim/security/le_security_test.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.gd.cert.truth import assertThat -from blueberry.tests.topshim.lib.adapter_client import AdapterClient -from blueberry.tests.topshim.lib.topshim_base_test import TopshimBaseTest -from blueberry.tests.topshim.lib.topshim_device import TRANSPORT_CLASSIC, TRANSPORT_LE - -from mobly import test_runner - - -class LeSecurityTest(TopshimBaseTest): - - DUMMY_ADDRESS = "01:02:03:04:05:06" - - def test_remove_bond_with_no_bonded_devices(self): - self.dut().remove_bonded_device(self.DUMMY_ADDRESS) - self.dut().le_rand() - - def test_generate_local_oob_data(self): - oob_data = self.dut().generate_local_oob_data(TRANSPORT_CLASSIC) - assertThat(oob_data.is_valid()).isTrue() - oob_data = self.dut().generate_local_oob_data(TRANSPORT_LE) - assertThat(oob_data.is_valid()).isTrue() - - -if __name__ == "__main__": - test_runner.main() diff --git a/system/blueberry/tests/topshim/topshim_host_config.yaml b/system/blueberry/tests/topshim/topshim_host_config.yaml deleted file mode 100644 index 15155db8d7..0000000000 --- a/system/blueberry/tests/topshim/topshim_host_config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -_description: Bluetooth cert testing -TestBeds: - - Name: HostOnlyCert - Controllers: - rootcanal: - test_port: '6401' - hci_port: '6402' - link_layer_port: '6403' - GdDevice: - - grpc_port: '8998' - grpc_root_server_port: '8996' - signal_port: '8994' - label: cert - name: Cert Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" - - grpc_port: '8999' - grpc_root_server_port: '8997' - signal_port: '8995' - label: dut - name: DUT Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" -logpath: "/tmp/logs" diff --git a/system/blueberry/tests/topshim/topshim_test_runner.py b/system/blueberry/tests/topshim/topshim_test_runner.py deleted file mode 100644 index 115e078f97..0000000000 --- a/system/blueberry/tests/topshim/topshim_test_runner.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2022 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from blueberry.tests.topshim.adapter.adapter_test import AdapterTest -from blueberry.tests.topshim.hfp.hfp_test import HfpTest -from blueberry.tests.topshim.power.suspend_test import SuspendTest -from blueberry.tests.topshim.security.classic_security_test import ClassicSecurityTest -from blueberry.tests.topshim.security.le_security_test import LeSecurityTest - -from mobly import suite_runner -import argparse - -ALL_TESTS = [AdapterTest, ClassicSecurityTest, HfpTest, LeSecurityTest, SuspendTest] - - -def main(): - """ - Local test runner that allows to specify list of tests to and customize - test config file location - """ - parser = argparse.ArgumentParser(description="Run local Topshim to Topshim tests.") - parser.add_argument('-c', - '--config', - type=str, - required=True, - metavar='<PATH>', - help='Path to the test configuration file.') - parser.add_argument('--tests', - '--test_case', - nargs='+', - type=str, - metavar='[ClassA[.test_a] ClassB[.test_b] ...]', - help='A list of test classes and optional tests to execute.') - parser.add_argument("--all_tests", "-A", type=bool, dest="all_tests", default=False, nargs="?") - parser.add_argument("--presubmit", type=bool, dest="presubmit", default=False, nargs="?") - parser.add_argument("--postsubmit", type=bool, dest="postsubmit", default=False, nargs="?") - args = parser.parse_args() - test_list = ALL_TESTS - if args.all_tests: - test_list = ALL_TESTS - elif args.presubmit: - test_list = ALL_TESTS - elif args.postsubmit: - test_list = ALL_TESTS - # Do not pass this layer's cmd line argument to next layer - argv = ["--config", args.config] - if args.tests: - argv.append("--tests") - for test in args.tests: - argv.append(test) - - suite_runner.run_suite(test_list, argv=argv) - - -if __name__ == "__main__": - main() diff --git a/system/bta/Android.bp b/system/bta/Android.bp index f99f437bd4..efa1d25fd1 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -27,9 +27,6 @@ cc_defaults { "packages/modules/Bluetooth/system/udrv/include", ], header_libs: ["libbluetooth_headers"], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], } filegroup { @@ -238,7 +235,6 @@ cc_library_static { cc_test { name: "net_test_bta", defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_bta_defaults", "latest_android_hardware_audio_common_ndk_static", "latest_android_hardware_bluetooth_audio_ndk_static", @@ -288,9 +284,6 @@ cc_test { "test/bta_sdp_test.cc", "test/bta_sec_test.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], shared_libs: [ "libaconfig_storage_read_api_cc", "libcrypto", @@ -376,9 +369,6 @@ cc_test { "test/gatt/database_builder_test.cc", "test/gatt/database_test.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], shared_libs: [ "libaconfig_storage_read_api_cc", "libbase", @@ -438,6 +428,7 @@ cc_test { "libbluetooth_core_rs_bridge", "libbluetooth_crypto_toolbox", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-audio-hal-interface", "libbt-bta", @@ -466,9 +457,6 @@ cc_test { "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":TestCommonMockFunctions", ":TestFakeOsi", @@ -647,6 +635,7 @@ cc_test { srcs: [ ":TestCommonMockFunctions", ":TestMockStackBtmInterface", + ":TestMockStackGatt", "gatt/database.cc", "gatt/database_builder.cc", "test/common/bta_gatt_api_mock.cc", @@ -670,6 +659,7 @@ cc_test { static_libs: [ "aics", "bluetooth_flags_c_lib_for_test", + "libaconfig_storage_read_api_cc", "libbluetooth-types", "libbluetooth_crypto_toolbox", "libbluetooth_gd", @@ -764,7 +754,6 @@ cc_test { test_suites: ["general-tests"], defaults: [ "aconfig_lib_cc_shared_link.defaults", - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -811,7 +800,6 @@ cc_test { ":audio_set_scenarios_json", ], generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", "LeAudioSetConfigSchemas_h", ], shared_libs: [ @@ -823,6 +811,7 @@ cc_test { ], static_libs: [ "bluetooth_flags_c_lib_for_test", + "libaconfig_storage_read_api_cc", "libbluetooth-types", "libbluetooth_crypto_toolbox", "libbluetooth_gd", @@ -848,7 +837,6 @@ cc_test { name: "bluetooth_le_audio_test", test_suites: ["general-tests"], defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -921,7 +909,6 @@ cc_test { ":audio_set_scenarios_json", ], generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", "LeAudioSetConfigSchemas_h", ], shared_libs: [ @@ -959,7 +946,6 @@ cc_test { name: "bluetooth_le_audio_client_test", test_suites: ["general-tests"], defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_bta_defaults", "mts_defaults", ], @@ -979,6 +965,7 @@ cc_test { ":TestMockMainShimEntry", ":TestMockStackBtmInterface", ":TestMockStackBtmIso", + ":TestMockStackGatt", ":TestMockStackL2cap", ":TestStubOsi", "gatt/database.cc", @@ -1111,6 +1098,7 @@ cc_test { "le_audio/mock_codec_manager.cc", ], shared_libs: [ + "libaconfig_storage_read_api_cc", "libbase", "libcrypto", "liblog", @@ -1149,7 +1137,6 @@ cc_test { test_suites: ["general-tests"], defaults: [ "aconfig_lib_cc_shared_link.defaults", - "bluetooth_flatbuffer_bundler_defaults", "fluoride_bta_defaults", "mts_defaults", ], @@ -1181,6 +1168,7 @@ cc_test { "le_audio/mock_codec_manager.cc", ], shared_libs: [ + "libaconfig_storage_read_api_cc", "libbase", "libbinder_ndk", "libcrypto", @@ -1262,6 +1250,7 @@ cc_test { ], static_libs: [ "bluetooth_flags_c_lib_for_test", + "libaconfig_storage_read_api_cc", "libbluetooth-types", "libbluetooth_crypto_toolbox", "libbluetooth_gd", @@ -1346,9 +1335,6 @@ cc_test { "libgmock", "libosi", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], sanitize: { cfi: true, scs: true, @@ -1417,9 +1403,6 @@ cc_test { "libgmock", "libosi", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], sanitize: { cfi: true, scs: true, diff --git a/system/bta/BUILD.gn b/system/bta/BUILD.gn index 928359ab22..425289338b 100644 --- a/system/bta/BUILD.gn +++ b/system/bta/BUILD.gn @@ -14,8 +14,7 @@ # limitations under the License. # -import("//bt/system/gd/dumpsys/bundler/bundler.gni") -import("//common-mk/flatbuffer.gni") +import("//bt/system/bta/bundler.gni") static_library("bta") { sources = [ diff --git a/system/bta/ag/bta_ag_act.cc b/system/bta/ag/bta_ag_act.cc index 11a9de4c48..063cb4c18d 100644 --- a/system/bta/ag/bta_ag_act.cc +++ b/system/bta/ag/bta_ag_act.cc @@ -403,6 +403,7 @@ void bta_ag_rfc_close(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& /* data */) { int i, num_active_conn = 0; /* reinitialize stuff */ + p_scb->clip[0] = 0; p_scb->conn_service = 0; p_scb->peer_features = 0; p_scb->masked_features = p_scb->features; diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h index ce9ff6298d..06caf10be9 100644 --- a/system/bta/ag/bta_ag_int.h +++ b/system/bta/ag/bta_ag_int.h @@ -161,9 +161,7 @@ typedef struct { typedef struct { tBTA_AG_RES result; tBTA_AG_RES_DATA data; - std::string ToString() const { - return base::StringPrintf("result:%s", bta_ag_result_text(result).c_str()); - } + std::string ToString() const { return std::format("result:{}", bta_ag_result_text(result)); } } tBTA_AG_API_RESULT; /* data type for BTA_AG_API_SETCODEC_EVT */ @@ -240,6 +238,17 @@ typedef enum { BTA_AG_SCO_APTX_SWB_SETTINGS_UNKNOWN = 0xFFFF, } tBTA_AG_SCO_APTX_SWB_SETTINGS; +namespace std { +template <> +struct formatter<tBTA_AG_SCO_MSBC_SETTINGS> : enum_formatter<tBTA_AG_SCO_MSBC_SETTINGS> {}; +template <> +struct formatter<tBTA_AG_SCO_LC3_SETTINGS> : enum_formatter<tBTA_AG_SCO_LC3_SETTINGS> {}; +template <> +struct formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> : enum_formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> {}; +template <> +struct formatter<tBTA_AG_SCO> : enum_formatter<tBTA_AG_SCO> {}; +} // namespace std + /* state machine states */ typedef enum { BTA_AG_INIT_ST, BTA_AG_OPENING_ST, BTA_AG_OPEN_ST, BTA_AG_CLOSING_ST } tBTA_AG_STATE; @@ -314,12 +323,11 @@ struct tBTA_AG_SCB { HF indicators */ std::string ToString() const { - return base::StringPrintf( - "codec_updated=%d, codec_fallback=%d, nrec=%d" - "sco_codec=%d, peer_codec=%d, msbc_settings=%d, lc3_settings=%d, " - "device=%s", + return std::format( + "codec_updated={}, codec_fallback={}, nrec={}sco_codec={}, peer_codec={}, " + "msbc_settings={}, lc3_settings={}, device={}", codec_updated, codec_fallback, nrec_enabled, sco_codec, peer_codecs, - codec_msbc_settings, codec_lc3_settings, ADDRESS_TO_LOGGABLE_CSTR(peer_addr)); + codec_msbc_settings, codec_lc3_settings, peer_addr); } }; @@ -476,11 +484,4 @@ bool bta_ag_is_sco_managed_by_audio(); */ void bta_ag_stream_suspended(); -namespace std { -template <> -struct formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> : enum_formatter<tBTA_AG_SCO_APTX_SWB_SETTINGS> {}; -template <> -struct formatter<tBTA_AG_SCO> : enum_formatter<tBTA_AG_SCO> {}; -} // namespace std - #endif /* BTA_AG_INT_H */ diff --git a/system/bta/ag/bta_ag_rfc.cc b/system/bta/ag/bta_ag_rfc.cc index 1943492759..c96b867c71 100644 --- a/system/bta/ag/bta_ag_rfc.cc +++ b/system/bta/ag/bta_ag_rfc.cc @@ -215,11 +215,10 @@ static void bta_ag_port_cback_6(uint32_t code, uint16_t port_handle) { static void bta_ag_setup_port(tBTA_AG_SCB* p_scb, uint16_t handle) { int port_callback_index = bta_ag_scb_to_idx(p_scb) - 1; log::assert_that(port_callback_index >= 0, "invalid callback index, handle={}, bd_addr={}", - handle, ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); + handle, p_scb->peer_addr); log::assert_that(port_callback_index < static_cast<int>(sizeof(bta_ag_port_cback_tbl) / sizeof(bta_ag_port_cback_tbl[0])), - "callback index out of bound, handle={}, bd_addr={}", handle, - ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); + "callback index out of bound, handle={}, bd_addr={}", handle, p_scb->peer_addr); if (PORT_SetEventMaskAndCallback(handle, BTA_AG_PORT_EV_MASK, bta_ag_port_cback_tbl[port_callback_index]) != PORT_SUCCESS) { log::warn("Unable to set RFCOMM event and callback mask peer:{} handle:{}", p_scb->peer_addr, @@ -245,16 +244,16 @@ void bta_ag_start_servers(tBTA_AG_SCB* p_scb, tBTA_SERVICE_MASK services) { int management_callback_index = bta_ag_scb_to_idx(p_scb) - 1; log::assert_that(management_callback_index >= 0, "invalid callback index, services=0x{:x}, bd_addr={}", services, - ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); + p_scb->peer_addr); log::assert_that( management_callback_index < static_cast<int>(sizeof(bta_ag_mgmt_cback_tbl) / sizeof(bta_ag_mgmt_cback_tbl[0])), "callback index out of bound, services=0x{:x}, bd_addr={}", services, - ADDRESS_TO_LOGGABLE_STR(p_scb->peer_addr)); + p_scb->peer_addr); int status = RFCOMM_CreateConnectionWithSecurity( bta_ag_uuid[i], bta_ag_cb.profile[i].scn, true, BTA_AG_MTU, RawAddress::kAny, &(p_scb->serv_handle[i]), bta_ag_mgmt_cback_tbl[management_callback_index], - BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, RfcommCfgInfo{}); if (status == PORT_SUCCESS) { bta_ag_setup_port(p_scb, p_scb->serv_handle[i]); } else { @@ -331,7 +330,7 @@ void bta_ag_rfc_do_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { int status = RFCOMM_CreateConnectionWithSecurity( bta_ag_uuid[p_scb->conn_service], p_scb->peer_scn, false, BTA_AG_MTU, p_scb->peer_addr, &(p_scb->conn_handle), bta_ag_mgmt_cback_tbl[management_callback_index], - BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, RfcommCfgInfo{}); log::verbose("p_scb=0x{}, conn_handle={}, mgmt_cback_index={}, status={}", std::format_ptr(p_scb), p_scb->conn_handle, management_callback_index, status); if (status == PORT_SUCCESS) { diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc index 7c4d8a04c1..8cb73a4402 100644 --- a/system/bta/av/bta_av_aact.cc +++ b/system/bta/av/bta_av_aact.cc @@ -26,7 +26,6 @@ #define LOG_TAG "bluetooth-a2dp" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -65,6 +64,7 @@ #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "osi/include/list.h" +#include "osi/include/osi.h" // UINT_TO_PTR PTR_TO_UINT #include "osi/include/properties.h" #include "sdpdefs.h" #include "stack/include/a2dp_ext.h" @@ -114,6 +114,13 @@ constexpr char kBtmLogTag[] = "A2DP"; /* ACL quota we are letting FW use for A2DP Offload Tx. */ #define BTA_AV_A2DP_OFFLOAD_XMIT_QUOTA 4 +/* Time to wait for open from SNK when signaling is initiated from SNK. */ +/* If not, we abort and try to initiate the connection as SRC. */ +#ifndef BTA_AV_ACCEPT_OPEN_TIMEOUT_MS +#define BTA_AV_ACCEPT_OPEN_TIMEOUT_MS (2 * 1000) /* 2 seconds */ +#endif + +static void bta_av_accept_open_timer_cback(void* data); static void bta_av_offload_codec_builder(tBTA_AV_SCB* p_scb, tBT_A2DP_OFFLOAD* p_a2dp_offload); /* state machine states */ @@ -866,6 +873,9 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->avrc_ct_timer); alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + alarm_cancel(p_scb->accept_open_timer); + } /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION vendor_get_interface()->send_command( @@ -1009,7 +1019,9 @@ void bta_av_disconnect_req(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->link_signalling_timer); alarm_cancel(p_scb->accept_signalling_timer); alarm_cancel(p_scb->avrc_ct_timer); - + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + alarm_cancel(p_scb->accept_open_timer); + } // conn_lcb is the index bitmask of all used LCBs, and since LCB and SCB use // the same index, it should be safe to use SCB index here. if ((bta_av_cb.conn_lcb & (1 << p_scb->hdi)) != 0) { @@ -1105,61 +1117,25 @@ void bta_av_setconfig_rsp(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { p_scb->SetAvdtpVersion(AVDT_VERSION_1_3); } - if (com::android::bluetooth::flags::avdt_discover_seps_as_acceptor()) { - if (btif_av_src_sink_coexist_enabled()) { - if (local_sep == AVDT_TSEP_SRC) { - /* Make sure UUID has been initialized... */ - /* if local sep is source, uuid_int should be source */ - p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SOURCE; - } else { - p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SINK; - } - } else if (p_scb->uuid_int == 0) { - p_scb->uuid_int = p_scb->open_api.uuid; - } - bta_av_discover_req(p_scb, NULL); - } else { - p_scb->num_seps = 1; - if (A2DP_GetCodecType(p_scb->cfg.codec_info) == A2DP_MEDIA_CT_SBC) { - /* if SBC is used by the SNK as INT, discover req is not sent in - * bta_av_config_ind. - * call disc_res now */ - /* this is called in A2DP SRC path only, In case of SINK we don't need it - */ - if (local_sep == AVDT_TSEP_SRC) { - p_scb->p_cos->disc_res(p_scb->hndl, p_scb->PeerAddress(), p_scb->num_seps, - p_scb->num_seps, 0, UUID_SERVCLASS_AUDIO_SOURCE); - } + if (btif_av_src_sink_coexist_enabled()) { + if (local_sep == AVDT_TSEP_SRC) { + /* Make sure UUID has been initialized... */ + /* if local sep is source, uuid_int should be source */ + p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SOURCE; } else { - /* we do not know the peer device and it is using non-SBC codec - * we need to know all the SEPs on SNK */ - if (p_scb->uuid_int == 0) { - p_scb->uuid_int = p_scb->open_api.uuid; - } - bta_av_discover_req(p_scb, NULL); - return; + p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SINK; } - - /* only in case of local sep as SRC we need to look for other SEPs, In case - * of SINK we don't */ - if (btif_av_src_sink_coexist_enabled()) { - if (local_sep == AVDT_TSEP_SRC) { - /* Make sure UUID has been initialized... */ - /* if local sep is source, uuid_int should be source */ - p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SOURCE; - bta_av_next_getcap(p_scb, p_data); - } else { - p_scb->uuid_int = UUID_SERVCLASS_AUDIO_SINK; - } - } else { - if (local_sep == AVDT_TSEP_SRC) { - /* Make sure UUID has been initialized... */ - if (p_scb->uuid_int == 0) { - p_scb->uuid_int = p_scb->open_api.uuid; - } - bta_av_next_getcap(p_scb, p_data); - } + } else if (p_scb->uuid_int == 0) { + p_scb->uuid_int = p_scb->open_api.uuid; + } + bta_av_discover_req(p_scb, NULL); + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + // Set timer to initiate stream opening if peer doesn't + if (!p_scb->accept_open_timer) { + p_scb->accept_open_timer = alarm_new("accept_open_timer"); } + alarm_set_on_mloop(p_scb->accept_open_timer, BTA_AV_ACCEPT_OPEN_TIMEOUT_MS, + bta_av_accept_open_timer_cback, UINT_TO_PTR(p_scb->hdi)); } } } @@ -1180,6 +1156,9 @@ void bta_av_str_opened(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { log::verbose("peer {} bta_handle: 0x{:x}", p_scb->PeerAddress(), p_scb->hndl); + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + alarm_cancel(p_scb->accept_open_timer); + } msg.hdr.layer_specific = p_scb->hndl; msg.is_up = true; msg.peer_addr = p_scb->PeerAddress(); @@ -1782,11 +1761,6 @@ void bta_av_setconfig_rej(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { log::info("sep_idx={} avdt_handle={} bta_handle=0x{:x} err_code=0x{:x}", p_scb->sep_idx, p_scb->avdt_handle, p_scb->hndl, err_code); - if (!com::android::bluetooth::flags::avdtp_error_codes()) { - bta_av_adjust_seps_idx(p_scb, avdt_handle); - err_code = AVDT_ERR_UNSUP_CFG; - } - // The error code might not be set when the configuration is rejected // based on the current AVDTP state. if (err_code == AVDT_SUCCESS) { @@ -1795,13 +1769,25 @@ void bta_av_setconfig_rej(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { AVDT_ConfigRsp(avdt_handle, p_scb->avdt_label, err_code, 0); - tBTA_AV bta_av_data = { - .reject = - { - .bd_addr = p_data->str_msg.bd_addr, - .hndl = p_scb->hndl, - }, - }; + tBTA_AV bta_av_data; + + if (com::android::bluetooth::flags::bta_av_setconfig_rej_type_confusion()) { + bta_av_data = { + .reject = + { + .bd_addr = p_scb->PeerAddress(), + .hndl = p_scb->hndl, + }, + }; + } else { + bta_av_data = { + .reject = + { + .bd_addr = p_data->str_msg.bd_addr, + .hndl = p_scb->hndl, + }, + }; + } (*bta_av_cb.p_cback)(BTA_AV_REJECT_EVT, &bta_av_data); } @@ -2036,8 +2022,8 @@ void bta_av_reconfig(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { log::debug("Reconfig codec: {}", A2DP_CodecInfoString(p_rcfg->codec_info)); BTM_LogHistory(kBtmLogTag, p_scb->PeerAddress(), "Codec reconfig", - base::StringPrintf("%s => %s", A2DP_CodecName(p_scb->cfg.codec_info), - A2DP_CodecName(p_rcfg->codec_info))); + std::format("{} => {}", A2DP_CodecName(p_scb->cfg.codec_info), + A2DP_CodecName(p_rcfg->codec_info))); p_cfg->num_protect = p_rcfg->num_protect; memcpy(p_cfg->codec_info, p_rcfg->codec_info, AVDT_CODEC_SIZE); @@ -3011,8 +2997,14 @@ void bta_av_open_at_inc(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) { /* API open will be handled at timeout if SNK did not start signalling. */ /* API open will be ignored if SNK starts signalling. */ } else { - /* SNK did not start signalling, API was called N seconds timeout. */ + /* SNK did not start signalling or failed to complete the AVDT configuration in time. */ + /* API was called N seconds timeout. */ /* We need to switch to INIT state and start opening connection. */ + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + // Reset peer device + bta_av_cco_close(p_scb, p_data); + alarm_cancel(p_scb->avrc_ct_timer); + } p_scb->coll_mask = 0; bta_av_set_scb_sst_init(p_scb); @@ -3381,3 +3373,33 @@ void bta_av_api_set_peer_sep(tBTA_AV_DATA* p_data) { } } } + +/******************************************************************************* + * + * Function bta_av_accept_open_timer_cback + * + * Description Process the timeout when SRC is accepting connection + * and SNK did not open the stream. + * + * Returns void + * + ******************************************************************************/ +static void bta_av_accept_open_timer_cback(void* data) { + uint32_t hdi = PTR_TO_UINT(data); + tBTA_AV_SCB* p_scb = NULL; + if (hdi < BTA_AV_NUM_STRS) { + p_scb = bta_av_cb.p_scb[hdi]; + } + if (p_scb == nullptr) { + log::error("SCB not found for index {}", hdi); + return; + } + + /* Abort the current connection */ + AVDT_AbortReq(p_scb->avdt_handle); + + /* Try connecting and opening as initiator with event: BTA_AV_API_OPEN_EVT */ + tBTA_AV_API_OPEN* p_buf = (tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN)); + memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN)); + bta_sys_sendmsg(p_buf); +}
\ No newline at end of file diff --git a/system/bta/av/bta_av_act.cc b/system/bta/av/bta_av_act.cc index 32bcf56a1a..d45d63fd77 100644 --- a/system/bta/av/bta_av_act.cc +++ b/system/bta/av/bta_av_act.cc @@ -26,6 +26,7 @@ #define LOG_TAG "bluetooth-a2dp" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <cstddef> #include <cstdint> @@ -1408,7 +1409,10 @@ void bta_av_disable(tBTA_AV_CB* p_cb, tBTA_AV_DATA* /* p_data */) { p_cb->p_scb[xx]->link_signalling_timer = NULL; alarm_free(p_cb->p_scb[xx]->accept_signalling_timer); p_cb->p_scb[xx]->accept_signalling_timer = NULL; - + if (com::android::bluetooth::flags::avdt_handle_signaling_on_peer_failure()) { + alarm_free(p_cb->p_scb[xx]->accept_open_timer); + p_cb->p_scb[xx]->accept_open_timer = NULL; + } hdr.layer_specific = xx + 1; bta_av_api_deregister((tBTA_AV_DATA*)&hdr); disabling_in_progress = true; diff --git a/system/bta/av/bta_av_int.h b/system/bta/av/bta_av_int.h index c9c176c445..abd79be555 100644 --- a/system/bta/av/bta_av_int.h +++ b/system/bta/av/bta_av_int.h @@ -492,6 +492,7 @@ public: alarm_t* avrc_ct_timer; /* delay timer for AVRC CT */ alarm_t* link_signalling_timer; alarm_t* accept_signalling_timer; /* timer to monitor signalling when accepting */ + alarm_t* accept_open_timer; /* timer to monitor AVDT open when accepting */ uint16_t l2c_cid; /* L2CAP channel ID */ uint16_t stream_mtu; /* MTU of stream */ uint8_t media_type; /* Media type: AVDT_MEDIA_TYPE_* */ diff --git a/system/bta/av/bta_av_main.cc b/system/bta/av/bta_av_main.cc index 45a4ffd251..c9276a6564 100644 --- a/system/bta/av/bta_av_main.cc +++ b/system/bta/av/bta_av_main.cc @@ -1472,7 +1472,8 @@ void bta_debug_av_dump(int fd) { if (lcb.addr.IsEmpty()) { continue; } - dprintf(fd, "\n Link control block: %zu peer: %s\n", i, ADDRESS_TO_LOGGABLE_CSTR(lcb.addr)); + dprintf(fd, "\n Link control block: %zu peer: %s\n", i, + lcb.addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " Connected stream handle mask: 0x%x\n", lcb.conn_msk); dprintf(fd, " Index(+1) to LCB: %d\n", lcb.lidx); } @@ -1484,7 +1485,8 @@ void bta_debug_av_dump(int fd) { if (p_scb->PeerAddress().IsEmpty()) { continue; } - dprintf(fd, "\n BTA ID: %zu peer: %s\n", i, ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress())); + dprintf(fd, "\n BTA ID: %zu peer: %s\n", i, + p_scb->PeerAddress().ToRedactedStringForLogging().c_str()); dprintf(fd, " SDP discovery started: %s\n", p_scb->sdp_discovery_started ? "true" : "false"); for (size_t j = 0; j < BTAV_A2DP_CODEC_INDEX_MAX; j++) { const tBTA_AV_SEP& sep = p_scb->seps[j]; @@ -1497,11 +1499,13 @@ void bta_debug_av_dump(int fd) { dprintf(fd, " Codec: %s\n", A2DP_CodecName(sep.codec_info)); } dprintf(fd, " BTA info tag: %d\n", p_scb->q_tag); - dprintf(fd, " API Open peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(p_scb->q_info.open.bd_addr)); + dprintf(fd, " API Open peer: %s\n", + p_scb->q_info.open.bd_addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " Use AVRCP: %s\n", p_scb->q_info.open.use_rc ? "true" : "false"); dprintf(fd, " Switch result: %d\n", p_scb->q_info.open.switch_res); dprintf(fd, " Initiator UUID: 0x%x\n", p_scb->q_info.open.uuid); - dprintf(fd, " Saved API Open peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(p_scb->open_api.bd_addr)); + dprintf(fd, " Saved API Open peer: %s\n", + p_scb->open_api.bd_addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " Use AVRCP: %s\n", p_scb->open_api.use_rc ? "true" : "false"); dprintf(fd, " Switch result: %d\n", p_scb->open_api.switch_res); dprintf(fd, " Initiator UUID: 0x%x\n", p_scb->open_api.uuid); @@ -1509,6 +1513,8 @@ void bta_debug_av_dump(int fd) { alarm_is_scheduled(p_scb->link_signalling_timer) ? "Scheduled" : "Not scheduled"); dprintf(fd, " Accept signalling timer: %s\n", alarm_is_scheduled(p_scb->accept_signalling_timer) ? "Scheduled" : "Not scheduled"); + dprintf(fd, " Accept open timer: %s\n", + alarm_is_scheduled(p_scb->accept_open_timer) ? "Scheduled" : "Not scheduled"); // TODO: Print p_scb->sep_info[], cfg, avrc_ct_timer, current_codec ? dprintf(fd, " L2CAP Channel ID: %d\n", p_scb->l2c_cid); dprintf(fd, " Stream MTU: %d\n", p_scb->stream_mtu); diff --git a/system/gd/dumpsys/bundler/bundler.gni b/system/bta/bundler.gni index 2942bbee1d..2942bbee1d 100644 --- a/system/gd/dumpsys/bundler/bundler.gni +++ b/system/bta/bundler.gni diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc index d38a232111..aba46a68c6 100644 --- a/system/bta/csis/csis_client.cc +++ b/system/bta/csis/csis_client.cc @@ -752,14 +752,15 @@ public: for (auto& device : devices_) { if (!g->IsDeviceInTheGroup(device)) { if (device->GetExpectedGroupIdMember() == g->GetGroupId()) { - stream << " == candidate addr: " << ADDRESS_TO_LOGGABLE_STR(device->addr) + stream << " == candidate addr: " << device->addr.ToRedactedStringForLogging() << "\n"; } continue; } - stream << " == addr: " << ADDRESS_TO_LOGGABLE_STR(device->addr) << " ==\n" - << " csis instance: data:" << "\n"; + stream << " == addr: " << device->addr.ToRedactedStringForLogging() << " ==\n" + << " csis instance: data:" + << "\n"; auto instance = device->GetCsisInstanceByGroupId(g->GetGroupId()); if (!instance) { diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc index ec3ca882bc..7a931a6248 100644 --- a/system/bta/dm/bta_dm_act.cc +++ b/system/bta/dm/bta_dm_act.cc @@ -269,6 +269,13 @@ void BTA_dm_on_hw_on() { } } + if (com::android::bluetooth::flags::socket_settings_api()) { + /* Read low power processor offload features */ + if (bta_dm_acl_cb.p_acl_cback) { + bta_dm_acl_cb.p_acl_cback(BTA_DM_LPP_OFFLOAD_FEATURES_READ, NULL); + } + } + btm_ble_scanner_init(); // Synchronize with the controller before continuing @@ -1664,9 +1671,7 @@ static void bta_ble_energy_info_cmpl(tBTM_BLE_TX_TIME_MS tx_time, tBTM_BLE_RX_TI tBTM_CONTRL_STATE ctrl_state = BTM_CONTRL_UNKNOWN; if (BTA_SUCCESS == st) { - ctrl_state = com::android::bluetooth::flags::bt_system_context_report() - ? bta_dm_obtain_system_context() - : bta_dm_pm_obtain_controller_state(); + ctrl_state = bta_dm_obtain_system_context(); } if (bta_dm_cb.p_energy_info_cback) { diff --git a/system/bta/dm/bta_dm_device_search.cc b/system/bta/dm/bta_dm_device_search.cc index 6f5691d050..b11200a074 100644 --- a/system/bta/dm/bta_dm_device_search.cc +++ b/system/bta/dm/bta_dm_device_search.cc @@ -19,7 +19,6 @@ #include "bta/dm/bta_dm_device_search.h" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <stddef.h> @@ -340,11 +339,10 @@ static void bta_dm_inq_cmpl() { } static void bta_dm_remote_name_cmpl(const tBTA_DM_REMOTE_NAME& remote_name_msg) { - BTM_LogHistory( - kBtmLogTag, remote_name_msg.bd_addr, "Remote name completed", - base::StringPrintf("status:%s state:%s name:\"%s\"", - hci_status_code_text(remote_name_msg.hci_status).c_str(), - bta_dm_state_text(bta_dm_search_get_state()).c_str(), + BTM_LogHistory(kBtmLogTag, remote_name_msg.bd_addr, "Remote name completed", + std::format("status:{} state:{} name:\"{}\"", + hci_status_code_text(remote_name_msg.hci_status), + bta_dm_state_text(bta_dm_search_get_state()), PRIVATE_NAME(reinterpret_cast<char const*>(remote_name_msg.bd_name)))); tBTM_INQ_INFO* p_btm_inq_info = @@ -518,7 +516,7 @@ static void bta_dm_discover_name(const RawAddress& remote_bd_addr) { (!bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name)))) { if (bta_dm_read_remote_device_name(bta_dm_search_cb.peer_bdaddr, transport)) { BTM_LogHistory(kBtmLogTag, bta_dm_search_cb.peer_bdaddr, "Read remote name", - base::StringPrintf("Transport:%s", bt_transport_text(transport).c_str())); + std::format("Transport:{}", bt_transport_text(transport))); return; } else { log::error("Unable to start read remote device name"); @@ -754,8 +752,7 @@ std::string EpochMillisToString(uint64_t time_ms) { struct tm tm; localtime_r(&time_sec, &tm); std::string s = bluetooth::common::StringFormatTime(kTimeFormatString, tm); - return base::StringPrintf("%s.%03u", s.c_str(), - static_cast<unsigned int>(time_ms % MillisPerSecond)); + return std::format("{}.{:03}", s, time_ms % MillisPerSecond); } } // namespace @@ -764,8 +761,7 @@ struct tSEARCH_STATE_HISTORY { const tBTA_DM_DEVICE_SEARCH_STATE state; const tBTA_DM_DEV_SEARCH_EVT event; std::string ToString() const { - return base::StringPrintf("state:%25s event:%s", bta_dm_state_text(state).c_str(), - bta_dm_event_text(event).c_str()); + return std::format("state:{:25s} event:{}", bta_dm_state_text(state), bta_dm_event_text(event)); } }; diff --git a/system/bta/dm/bta_dm_device_search_int.h b/system/bta/dm/bta_dm_device_search_int.h index 28e3fdabd8..35d41f1d2c 100644 --- a/system/bta/dm/bta_dm_device_search_int.h +++ b/system/bta/dm/bta_dm_device_search_int.h @@ -16,7 +16,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <string> diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc index f5d59d10de..2580e3a2da 100644 --- a/system/bta/dm/bta_dm_disc.cc +++ b/system/bta/dm/bta_dm_disc.cc @@ -19,7 +19,6 @@ #include "bta/dm/bta_dm_disc.h" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -399,7 +398,7 @@ static void bta_dm_discover_services(tBTA_DM_API_DISCOVER& discover) { } BTM_LogHistory(kBtmLogTag, bd_addr, "Discovery started ", - base::StringPrintf("Transport:%s", bt_transport_text(transport).c_str())); + std::format("Transport:{}", bt_transport_text(transport))); if (transport == BT_TRANSPORT_LE) { if (bta_dm_discovery_cb.transports & BT_TRANSPORT_LE) { @@ -735,8 +734,7 @@ std::string EpochMillisToString(uint64_t time_ms) { struct tm tm; localtime_r(&time_sec, &tm); std::string s = bluetooth::common::StringFormatTime(kTimeFormatString, tm); - return base::StringPrintf("%s.%03u", s.c_str(), - static_cast<unsigned int>(time_ms % MillisPerSecond)); + return std::format("{}.{:03}", s, time_ms % MillisPerSecond); } } // namespace @@ -745,8 +743,7 @@ struct tDISCOVERY_STATE_HISTORY { const tBTA_DM_SERVICE_DISCOVERY_STATE state; const tBTA_DM_DISC_EVT event; std::string ToString() const { - return base::StringPrintf("state:%25s event:%s", bta_dm_state_text(state).c_str(), - bta_dm_event_text(event).c_str()); + return std::format("state:{:25s} event:{}", bta_dm_state_text(state), bta_dm_event_text(event)); } }; diff --git a/system/bta/dm/bta_dm_disc_int.h b/system/bta/dm/bta_dm_disc_int.h index 6c89f895fd..4714af997a 100644 --- a/system/bta/dm/bta_dm_disc_int.h +++ b/system/bta/dm/bta_dm_disc_int.h @@ -16,7 +16,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <queue> diff --git a/system/bta/dm/bta_dm_disc_sdp.cc b/system/bta/dm/bta_dm_disc_sdp.cc index 588e7afe96..dd467e210b 100644 --- a/system/bta/dm/bta_dm_disc_sdp.cc +++ b/system/bta/dm/bta_dm_disc_sdp.cc @@ -17,7 +17,6 @@ #define LOG_TAG "bt_bta_dm" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -257,9 +256,9 @@ void bta_dm_sdp_result(tSDP_STATUS sdp_result, tBTA_DM_SDP_STATE* sdp_state) { /* callbacks */ /* start next bd_addr if necessary */ BTM_LogHistory(kBtmLogTag, sdp_state->bd_addr, "Discovery completed", - base::StringPrintf("Result:%s services_found:0x%x service_index:0x%d", - sdp_result_text(sdp_result).c_str(), - sdp_state->services_found, sdp_state->service_index)); + std::format("Result:{} services_found:0x{:x} service_index:0x{}", + sdp_result_text(sdp_result), sdp_state->services_found, + sdp_state->service_index)); // Copy the raw_data to the discovery result structure if (p_sdp_db != NULL && p_sdp_db->raw_used != 0 && p_sdp_db->raw_data != NULL) { @@ -284,7 +283,7 @@ void bta_dm_sdp_result(tSDP_STATUS sdp_result, tBTA_DM_SDP_STATE* sdp_state) { bta_dm_sdp_finished(sdp_state->bd_addr, result, uuid_list, gatt_uuids); } else { BTM_LogHistory(kBtmLogTag, sdp_state->bd_addr, "Discovery failed", - base::StringPrintf("Result:%s", sdp_result_text(sdp_result).c_str())); + std::format("Result:{}", sdp_result_text(sdp_result))); log::error("SDP connection failed {}", sdp_status_text(sdp_result)); /* not able to connect go to next device */ diff --git a/system/bta/dm/bta_dm_gatt_client.cc b/system/bta/dm/bta_dm_gatt_client.cc index 3963f4b225..a78ed4ca33 100644 --- a/system/bta/dm/bta_dm_gatt_client.cc +++ b/system/bta/dm/bta_dm_gatt_client.cc @@ -18,7 +18,6 @@ #include <base/functional/bind.h> #include <base/functional/callback.h> -#include <base/strings/stringprintf.h> #include <cstdint> #include <string> @@ -44,52 +43,47 @@ std::string EpochMillisToString(uint64_t time_ms) { struct tm tm; localtime_r(&time_sec, &tm); std::string s = bluetooth::common::StringFormatTime(kTimeFormatString, tm); - return base::StringPrintf("%s.%03u", s.c_str(), - static_cast<unsigned int>(time_ms % MillisPerSecond)); + return std::format("{}.{:03}", s, time_ms % MillisPerSecond); } } // namespace gatt_interface_t default_gatt_interface = { .BTA_GATTC_CancelOpen = [](tGATT_IF client_if, const RawAddress& remote_bda, bool is_direct) { - gatt_history_.Push(base::StringPrintf( - "%-32s bd_addr:%s client_if:%hu is_direct:%c", "GATTC_CancelOpen", - ADDRESS_TO_LOGGABLE_CSTR(remote_bda), static_cast<uint16_t>(client_if), - (is_direct) ? 'T' : 'F')); + gatt_history_.Push(std::format("{:<32s} bd_addr:{} client_if:{} is_direct:{:c}", + "GATTC_CancelOpen", remote_bda, client_if, + is_direct ? 'T' : 'F')); BTA_GATTC_CancelOpen(client_if, remote_bda, is_direct); }, .BTA_GATTC_Refresh = [](const RawAddress& remote_bda) { - gatt_history_.Push(base::StringPrintf("%-32s bd_addr:%s", "GATTC_Refresh", - ADDRESS_TO_LOGGABLE_CSTR(remote_bda))); + gatt_history_.Push( + std::format("{:<32s} bd_addr:{}", "GATTC_Refresh", remote_bda)); BTA_GATTC_Refresh(remote_bda); }, .BTA_GATTC_GetGattDb = [](tCONN_ID conn_id, uint16_t start_handle, uint16_t end_handle, btgatt_db_element_t** db, int* count) { - gatt_history_.Push(base::StringPrintf( - "%-32s conn_id:%hu start_handle:%hu end:handle:%hu", "GATTC_GetGattDb", - static_cast<uint16_t>(conn_id), start_handle, end_handle)); + gatt_history_.Push(std::format("{:<32s} conn_id:{} start_handle:{} end:handle:{}", + "GATTC_GetGattDb", conn_id, start_handle, + end_handle)); BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, db, count); }, .BTA_GATTC_AppRegister = [](tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb, bool eatt_support) { - gatt_history_.Push(base::StringPrintf("%-32s eatt_support:%c", - "GATTC_AppRegister", - (eatt_support) ? 'T' : 'F')); + gatt_history_.Push(std::format("{:<32s} eatt_support:{:c}", "GATTC_AppRegister", + eatt_support ? 'T' : 'F')); BTA_GATTC_AppRegister(p_client_cb, cb, eatt_support); }, .BTA_GATTC_Close = [](tCONN_ID conn_id) { - gatt_history_.Push(base::StringPrintf("%-32s conn_id:%hu", "GATTC_Close", - static_cast<uint16_t>(conn_id))); + gatt_history_.Push(std::format("{:<32s} conn_id:{}", "GATTC_Close", conn_id)); BTA_GATTC_Close(conn_id); }, .BTA_GATTC_ServiceSearchRequest = [](tCONN_ID conn_id, const bluetooth::Uuid* p_srvc_uuid) { - gatt_history_.Push(base::StringPrintf("%-32s conn_id:%hu", - "GATTC_ServiceSearchRequest", - static_cast<uint16_t>(conn_id))); + gatt_history_.Push( + std::format("{:<32s} conn_id:{}", "GATTC_ServiceSearchRequest", conn_id)); if (p_srvc_uuid) { BTA_GATTC_ServiceSearchRequest(conn_id, *p_srvc_uuid); } else { @@ -99,10 +93,10 @@ gatt_interface_t default_gatt_interface = { .BTA_GATTC_Open = [](tGATT_IF client_if, const RawAddress& remote_bda, tBTM_BLE_CONN_TYPE connection_type, bool opportunistic, uint16_t preferred_mtu) { - gatt_history_.Push(base::StringPrintf( - "%-32s bd_addr:%s client_if:%hu type:0x%x opportunistic:%c", "GATTC_Open", - ADDRESS_TO_LOGGABLE_CSTR(remote_bda), static_cast<uint16_t>(client_if), - connection_type, (opportunistic) ? 'T' : 'F')); + gatt_history_.Push(std::format( + "{:<32s} bd_addr:{} client_if:{} type:0x{:x} opportunistic:{:c}", + "GATTC_Open", remote_bda, client_if, connection_type, + opportunistic ? 'T' : 'F')); BTA_GATTC_Open(client_if, remote_bda, BLE_ADDR_PUBLIC, connection_type, BT_TRANSPORT_LE, opportunistic, LE_PHY_1M, preferred_mtu); }, diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h index e79facd403..b5357bb06b 100644 --- a/system/bta/dm/bta_dm_int.h +++ b/system/bta/dm/bta_dm_int.h @@ -24,7 +24,6 @@ #ifndef BTA_DM_INT_H #define BTA_DM_INT_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -79,7 +78,7 @@ inline std::string device_info_text(tBTA_DM_DEV_INFO info) { ":set_sniff", ":int_sniff", ":acp_sniff", ":unused", ":use_ssr", ":av_active", }; - std::string s = base::StringPrintf("0x%02x", info); + std::string s = std::format("0x{:02x}", info); if (info == BTA_DM_DI_NONE) { return s + std::string(":none"); } @@ -181,10 +180,8 @@ typedef struct { bool new_request; std::string ToString() const { - return base::StringPrintf("peer:%s sys_name:%s app_id:%hhu state:%s new_request:%s", - ADDRESS_TO_LOGGABLE_CSTR(peer_bdaddr), BtaIdSysText(id).c_str(), - app_id, bta_sys_conn_status_text(state).c_str(), - new_request ? "true" : "false"); + return std::format("peer:{} sys_name:{} app_id:{} state:{} new_request:{}", peer_bdaddr, + BtaIdSysText(id), app_id, bta_sys_conn_status_text(state), new_request); } } tBTA_DM_SRVCS; diff --git a/system/bta/dm/bta_dm_main.cc b/system/bta/dm/bta_dm_main.cc index 966fb85d57..249c0d0431 100644 --- a/system/bta/dm/bta_dm_main.cc +++ b/system/bta/dm/bta_dm_main.cc @@ -21,7 +21,6 @@ * This is the main implementation file for the BTA device manager. * ******************************************************************************/ -#include <base/strings/stringprintf.h> #include <stddef.h> #include "bta/dm/bta_dm_device_search.h" diff --git a/system/bta/dm/bta_dm_sec_api.cc b/system/bta/dm/bta_dm_sec_api.cc index f46bd40197..4d38a9680f 100644 --- a/system/bta/dm/bta_dm_sec_api.cc +++ b/system/bta/dm/bta_dm_sec_api.cc @@ -119,7 +119,12 @@ void BTA_DmAddDevice(RawAddress bd_addr, DEV_CLASS dev_class, LinkKey link_key, /** This function removes a device from the security database list of peer * device. It manages unpairing even while connected */ tBTA_STATUS BTA_DmRemoveDevice(const RawAddress& bd_addr) { - bta_dm_remove_device(bd_addr); + if (!com::android::bluetooth::flags::remove_device_in_main_thread()) { + bta_dm_remove_device(bd_addr); + return BTA_SUCCESS; + } + + do_in_main_thread(base::BindOnce(bta_dm_remove_device, bd_addr)); return BTA_SUCCESS; } diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc index 37ecf8f23c..9f5ad701c7 100644 --- a/system/bta/gatt/bta_gattc_act.cc +++ b/system/bta/gatt/bta_gattc_act.cc @@ -26,7 +26,6 @@ #define LOG_TAG "bt_bta_gattc" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -49,7 +48,6 @@ // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" -using base::StringPrintf; using bluetooth::Uuid; using namespace bluetooth; @@ -766,9 +764,8 @@ void bta_gattc_disc_close(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data log::verbose("Discovery cancel conn_id=0x{:x}", p_clcb->bta_conn_id); if (p_clcb->disc_active || - (com::android::bluetooth::flags::gatt_rediscover_on_canceled() && - (p_clcb->request_during_discovery == BTA_GATTC_DISCOVER_REQ_READ_DB_HASH || - p_clcb->request_during_discovery == BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG))) { + (p_clcb->request_during_discovery == BTA_GATTC_DISCOVER_REQ_READ_DB_HASH || + p_clcb->request_during_discovery == BTA_GATTC_DISCOVER_REQ_READ_DB_HASH_FOR_SVC_CHG)) { bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_ERROR); } else { p_clcb->state = BTA_GATTC_CONN_ST; diff --git a/system/bta/gatt/bta_gattc_cache.cc b/system/bta/gatt/bta_gattc_cache.cc index 600cfa9edc..ce7deeeb9e 100644 --- a/system/bta/gatt/bta_gattc_cache.cc +++ b/system/bta/gatt/bta_gattc_cache.cc @@ -28,7 +28,6 @@ #include <base/functional/bind.h> #include <base/functional/callback.h> #include <base/strings/string_number_conversions.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -55,7 +54,6 @@ using namespace bluetooth::legacy::stack::sdp; using namespace bluetooth; -using base::StringPrintf; using bluetooth::Uuid; using gatt::Characteristic; using gatt::Database; diff --git a/system/bta/gatt/bta_gattc_int.h b/system/bta/gatt/bta_gattc_int.h index 1bfd741348..4ceaff4062 100644 --- a/system/bta/gatt/bta_gattc_int.h +++ b/system/bta/gatt/bta_gattc_int.h @@ -505,7 +505,7 @@ inline std::string bta_clcb_state_text(const tBTA_GATTC_STATE& state) { CASE_RETURN_TEXT(BTA_GATTC_CONN_ST); CASE_RETURN_TEXT(BTA_GATTC_DISCOVER_ST); default: - return base::StringPrintf("UNKNOWN[%hhu]", state); + return std::format("UNKNOWN[{}]", static_cast<int>(state)); } } @@ -517,7 +517,7 @@ inline std::string bta_server_state_text(const tBTA_GATTC_SERV_STATE& state) { CASE_RETURN_TEXT(BTA_GATTC_SERV_DISC); CASE_RETURN_TEXT(BTA_GATTC_SERV_DISC_ACT); default: - return base::StringPrintf("UNKNOWN[%hhu]", state); + return std::format("UNKNOWN[{}]", static_cast<int>(state)); } } @@ -528,7 +528,7 @@ inline std::string bta_gattc_state_text(const tBTA_GATTC_CB_STATE& state) { CASE_RETURN_TEXT(BTA_GATTC_STATE_ENABLED); CASE_RETURN_TEXT(BTA_GATTC_STATE_DISABLING); default: - return base::StringPrintf("UNKNOWN[%hhu]", state); + return std::format("UNKNOWN[{}]", static_cast<int>(state)); } } diff --git a/system/bta/gatt/bta_gattc_main.cc b/system/bta/gatt/bta_gattc_main.cc index b4afcd542a..bf85b58697 100644 --- a/system/bta/gatt/bta_gattc_main.cc +++ b/system/bta/gatt/bta_gattc_main.cc @@ -23,14 +23,12 @@ ******************************************************************************/ #define LOG_TAG "bta_gattc_main" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include "bta/gatt/bta_gattc_int.h" #include "internal_include/bt_target.h" #include "stack/include/bt_hdr.h" -using base::StringPrintf; using namespace bluetooth; /***************************************************************************** diff --git a/system/bta/gatt/bta_gattc_queue.cc b/system/bta/gatt/bta_gattc_queue.cc index 40d99a7fb5..9afd9b3de7 100644 --- a/system/bta/gatt/bta_gattc_queue.cc +++ b/system/bta/gatt/bta_gattc_queue.cc @@ -145,51 +145,6 @@ void BtaGattQueue::gatt_read_multi_op_finished(tCONN_ID conn_id, tGATT_STATUS st } } -void BtaGattQueue::gatt_read_multi_op_simulate(tCONN_ID conn_id, tGATT_STATUS status, - uint16_t handle, uint16_t len, uint8_t* value, - void* data_read) { - gatt_read_multi_simulate_op_data* data = (gatt_read_multi_simulate_op_data*)data_read; - - log::verbose("conn_id: 0x{:x} handle: 0x{:x} status: 0x{:x} len: {}", conn_id, handle, status, - len); - if (status == GATT_SUCCESS && ((data->values_end + 2 + len) < MAX_ATT_MTU)) { - data->values[data->values_end] = (len & 0x00ff); - data->values[data->values_end + 1] = (len & 0xff00) >> 8; - data->values_end += 2; - - // concatenate all read values together - std::copy(value, value + len, data->values.data() + data->values_end); - data->values_end += len; - - if (data->read_index < data->handles.num_attr - 1) { - // grab next handle and read it - data->read_index++; - uint16_t next_handle = data->handles.handles[data->read_index]; - - BTA_GATTC_ReadCharacteristic(conn_id, next_handle, GATT_AUTH_REQ_NONE, - gatt_read_multi_op_simulate, data_read); - return; - } - } - - // all handles read, or bad status, or values too long - GATT_READ_MULTI_OP_CB tmp_cb = data->cb; - void* tmp_cb_data = data->cb_data; - - std::array<uint8_t, MAX_ATT_MTU> value_copy = data->values; - uint16_t value_len = data->values_end; - auto handles = data->handles; - - osi_free(data_read); - - mark_as_not_executing(conn_id); - gatt_execute_next_op(conn_id); - - if (tmp_cb) { - tmp_cb(conn_id, status, handles, value_len, value_copy.data(), tmp_cb_data); - } -} - void BtaGattQueue::gatt_execute_next_op(tCONN_ID conn_id) { log::verbose("conn_id=0x{:x}", conn_id); if (gatt_op_queue.empty()) { @@ -255,28 +210,6 @@ void BtaGattQueue::gatt_execute_next_op(tCONN_ID conn_id) { data->cb_data = op.read_cb_data; BTA_GATTC_ReadMultiple(conn_id, op.handles, true, GATT_AUTH_REQ_NONE, gatt_read_multi_op_finished, data); - } else { - /* This file contains just queue, and simulating reads should rather live in BTA or - * stack/gatt. However, placing this logic in layers below would be significantly harder. - * Having it here is a good balance - it's easy to add, and the API we expose to apps is same - * as if it was in layers below. - */ - log::verbose("EATT not supported, simulating read multi. conn_id: 0x{:x} num_handles: {}", - conn_id, op.handles.num_attr); - gatt_read_multi_simulate_op_data* data = (gatt_read_multi_simulate_op_data*)osi_malloc( - sizeof(gatt_read_multi_simulate_op_data)); - data->cb = op.read_multi_cb; - data->cb_data = op.read_cb_data; - - std::fill(data->values.begin(), data->values.end(), 0); - data->values_end = 0; - - data->handles = op.handles; - data->read_index = 0; - uint16_t handle = data->handles.handles[data->read_index]; - - BTA_GATTC_ReadCharacteristic(conn_id, handle, GATT_AUTH_REQ_NONE, gatt_read_multi_op_simulate, - data); } } @@ -333,11 +266,15 @@ void BtaGattQueue::ConfigureMtu(tCONN_ID conn_id, uint16_t mtu) { gatt_execute_next_op(conn_id); } -void BtaGattQueue::ReadMultiCharacteristic(tCONN_ID conn_id, tBTA_GATTC_MULTI& handles, +bool BtaGattQueue::ReadMultiCharacteristic(tCONN_ID conn_id, tBTA_GATTC_MULTI& handles, GATT_READ_MULTI_OP_CB cb, void* cb_data) { + if (!gatt_profile_get_eatt_support_by_conn_id(conn_id)) { + return false; + } gatt_op_queue[conn_id].push_back({.type = GATT_READ_MULTI, .handles = handles, .read_multi_cb = cb, .read_cb_data = cb_data}); gatt_execute_next_op(conn_id); + return true; } diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc index 36bbe69407..f1cbab7aee 100644 --- a/system/bta/gatt/bta_gattc_utils.cc +++ b/system/bta/gatt/bta_gattc_utils.cc @@ -920,7 +920,7 @@ void bta_gatt_client_dump(int fd) { tBTA_GATTC_CONN* p_conn_track = &bta_gattc_cb.conn_track[i]; if (p_conn_track->in_use) { entry_count++; - stream << " address: " << ADDRESS_TO_LOGGABLE_STR(p_conn_track->remote_bda); + stream << " address: " << p_conn_track->remote_bda.ToRedactedStringForLogging(); stream << "\n"; } } @@ -934,7 +934,7 @@ void bta_gatt_client_dump(int fd) { continue; } entry_count++; - stream << " address: " << ADDRESS_TO_LOGGABLE_STR(p_bg_track->remote_bda) + stream << " address: " << p_bg_track->remote_bda.ToRedactedStringForLogging() << " cif_mask: " << loghex(p_bg_track->cif_mask); stream << "\n"; } @@ -974,7 +974,7 @@ void bta_gatt_client_dump(int fd) { } entry_count++; stream << " conn_id: " << loghex(p_clcb->bta_conn_id) - << " address: " << ADDRESS_TO_LOGGABLE_STR(p_clcb->bda) + << " address: " << p_clcb->bda.ToRedactedStringForLogging() << " transport: " << bt_transport_text(p_clcb->transport) << " state: " << bta_clcb_state_text(p_clcb->state); stream << "\n"; @@ -988,7 +988,7 @@ void bta_gatt_client_dump(int fd) { } entry_count++; stream << " conn_id: " << loghex(p_clcb->bta_conn_id) - << " address: " << ADDRESS_TO_LOGGABLE_STR(p_clcb->bda) + << " address: " << p_clcb->bda.ToRedactedStringForLogging() << " transport: " << bt_transport_text(p_clcb->transport) << " state: " << bta_clcb_state_text(p_clcb->state); stream << "\n"; @@ -1004,7 +1004,7 @@ void bta_gatt_client_dump(int fd) { continue; } entry_count++; - stream << " server_address: " << ADDRESS_TO_LOGGABLE_STR(p_known_server->server_bda) + stream << " server_address: " << p_known_server->server_bda.ToRedactedStringForLogging() << " mtu: " << p_known_server->mtu << " blocked_conn_id: " << loghex(p_known_server->blocked_conn_id) << " num_clcb: " << +p_known_server->num_clcb diff --git a/system/bta/gatt/bta_gatts_act.cc b/system/bta/gatt/bta_gatts_act.cc index c9c4427f51..b48b0cb5d1 100644 --- a/system/bta/gatt/bta_gatts_act.cc +++ b/system/bta/gatt/bta_gatts_act.cc @@ -415,15 +415,9 @@ void bta_gatts_open(tBTA_GATTS_CB* /* p_cb */, tBTA_GATTS_DATA* p_msg) { p_rcb = bta_gatts_find_app_rcb_by_app_if(p_msg->api_open.server_if); if (p_rcb != NULL) { /* should always get the connection ID */ - bool success = false; - if (com::android::bluetooth::flags::ble_gatt_server_use_address_type_in_connection()) { - success = GATT_Connect(p_rcb->gatt_if, p_msg->api_open.remote_bda, - p_msg->api_open.remote_addr_type, p_msg->api_open.connection_type, - p_msg->api_open.transport, false, LE_PHY_1M, 0); - } else { - success = GATT_Connect(p_rcb->gatt_if, p_msg->api_open.remote_bda, - p_msg->api_open.connection_type, p_msg->api_open.transport, false); - } + bool success = GATT_Connect(p_rcb->gatt_if, p_msg->api_open.remote_bda, + p_msg->api_open.remote_addr_type, p_msg->api_open.connection_type, + p_msg->api_open.transport, false, LE_PHY_1M, 0); if (success) { status = GATT_SUCCESS; diff --git a/system/bta/groups/groups.cc b/system/bta/groups/groups.cc index 42b271537a..2390a2793e 100644 --- a/system/bta/groups/groups.cc +++ b/system/bta/groups/groups.cc @@ -383,7 +383,7 @@ std::ostream& operator<<(std::ostream& out, bluetooth::groups::DeviceGroup const << " Uuid: " << group.group_uuid_ << std::endl; out << " Devices:\n"; for (auto const& addr : group.devices_) { - out << " " << ADDRESS_TO_LOGGABLE_STR(addr) << std::endl; + out << " " << addr.ToRedactedStringForLogging() << std::endl; } return out; } diff --git a/system/bta/has/has_client_test.cc b/system/bta/has/has_client_test.cc index 322c387153..9c98258601 100644 --- a/system/bta/has/has_client_test.cc +++ b/system/bta/has/has_client_test.cc @@ -71,6 +71,7 @@ using ::bluetooth::le_audio::has::HasPreset; using ::testing::_; using ::testing::AnyNumber; +using ::testing::AtLeast; using ::testing::DoAll; using ::testing::DoDefault; using ::testing::Invoke; @@ -1242,7 +1243,7 @@ TEST_F(HasClientTest, test_disconnect_connected_without_hap_connect_only_request TestConnect(test_address); EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1); - EXPECT_CALL(gatt_queue, Clean(1)).Times(1); + EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1)); TestDisconnect(test_address, 1); } @@ -1257,7 +1258,7 @@ TEST_F(HasClientTest, test_disconnect_connected) { TestConnect(test_address); EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1); - EXPECT_CALL(gatt_queue, Clean(1)).Times(1); + EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1)); TestDisconnect(test_address, 1); } @@ -1632,7 +1633,7 @@ TEST_F(HasClientTest, test_cp_not_usable_read_all_presets) { OnDeviceAvailable(test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded | bluetooth::has::kFeatureBitWritablePresets | bluetooth::has::kFeatureBitDynamicPresets)); - EXPECT_CALL(gatt_queue, Clean(1)).Times(1); + EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1)); TestConnect(test_address); } diff --git a/system/bta/has/has_ctp.cc b/system/bta/has/has_ctp.cc index e9b45ca845..dbe41442d5 100644 --- a/system/bta/has/has_ctp.cc +++ b/system/bta/has/has_ctp.cc @@ -236,7 +236,7 @@ std::ostream& operator<<(std::ostream& out, const HasCtpOp& op) { if (std::holds_alternative<int>(op.addr_or_group)) { out << "\"group_id\": " << std::get<int>(op.addr_or_group); } else if (std::holds_alternative<RawAddress>(op.addr_or_group)) { - out << "\"address\": \"" << ADDRESS_TO_LOGGABLE_STR(std::get<RawAddress>(op.addr_or_group)) + out << "\"address\": \"" << std::get<RawAddress>(op.addr_or_group).ToRedactedStringForLogging() << "\""; } else { out << "\"bad value\""; diff --git a/system/bta/hearing_aid/hearing_aid.cc b/system/bta/hearing_aid/hearing_aid.cc index e27b420c8e..340e08d992 100644 --- a/system/bta/hearing_aid/hearing_aid.cc +++ b/system/bta/hearing_aid/hearing_aid.cc @@ -1070,9 +1070,7 @@ public: /// The L2CAP will automatically reconnect the LE-ACL link on /// disconnection when there is a pending channel request, /// which invalidates all encryption checks performed here. - com::android::bluetooth::flags::asha_encrypted_l2c_coc() - ? BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT - : BTM_SEC_NONE, + BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT, HearingAidImpl::GapCallbackStatic, BT_TRANSPORT_LE); if (gap_handle == GAP_INVALID_HANDLE) { @@ -1692,10 +1690,11 @@ public: const struct AudioStats* stats = &device.audio_stats; if (stats->rssi_history.size() <= 0) { - dprintf(fd, " No RSSI history for %s:\n", ADDRESS_TO_LOGGABLE_CSTR(device.address)); + dprintf(fd, " No RSSI history for %s:\n", + device.address.ToRedactedStringForLogging().c_str()); return; } - dprintf(fd, " RSSI history for %s:\n", ADDRESS_TO_LOGGABLE_CSTR(device.address)); + dprintf(fd, " RSSI history for %s:\n", device.address.ToRedactedStringForLogging().c_str()); dprintf(fd, " Time of RSSI 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9\n"); for (auto& rssi_logs : stats->rssi_history) { diff --git a/system/bta/hf_client/bta_hf_client_rfc.cc b/system/bta/hf_client/bta_hf_client_rfc.cc index 42e8a3c96c..6e9655ac38 100644 --- a/system/bta/hf_client/bta_hf_client_rfc.cc +++ b/system/bta/hf_client/bta_hf_client_rfc.cc @@ -188,7 +188,7 @@ void bta_hf_client_start_server() { port_status = RFCOMM_CreateConnectionWithSecurity( UUID_SERVCLASS_HF_HANDSFREE, bta_hf_client_cb_arr.scn, true, BTA_HF_CLIENT_MTU, RawAddress::kAny, &(bta_hf_client_cb_arr.serv_handle), bta_hf_client_mgmt_cback, - BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, RfcommCfgInfo{}); log::verbose("started rfcomm server with handle {}", bta_hf_client_cb_arr.serv_handle); @@ -240,10 +240,10 @@ void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA* p_data) { return; } - if (RFCOMM_CreateConnectionWithSecurity(UUID_SERVCLASS_HF_HANDSFREE, client_cb->peer_scn, false, - BTA_HF_CLIENT_MTU, client_cb->peer_addr, - &(client_cb->conn_handle), bta_hf_client_mgmt_cback, - BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT) == PORT_SUCCESS) { + if (RFCOMM_CreateConnectionWithSecurity( + UUID_SERVCLASS_HF_HANDSFREE, client_cb->peer_scn, false, BTA_HF_CLIENT_MTU, + client_cb->peer_addr, &(client_cb->conn_handle), bta_hf_client_mgmt_cback, + BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, RfcommCfgInfo{}) == PORT_SUCCESS) { bta_hf_client_setup_port(client_cb->conn_handle); log::verbose("bta_hf_client_rfc_do_open : conn_handle = {}", client_cb->conn_handle); } else { diff --git a/system/bta/hf_client/bta_hf_client_sdp.cc b/system/bta/hf_client/bta_hf_client_sdp.cc index 81721799f4..6e2585a6b8 100644 --- a/system/bta/hf_client/bta_hf_client_sdp.cc +++ b/system/bta/hf_client/bta_hf_client_sdp.cc @@ -25,6 +25,7 @@ ******************************************************************************/ #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <cstddef> #include <cstdint> @@ -351,6 +352,19 @@ void bta_hf_client_do_disc(tBTA_HF_CLIENT_CB* client_cb) { uuid_list[0] = Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE); } + /* If we already have a non-null discovery database at this point, we can get + * into a race condition leading to UAF once this connection is closed. + * This should only happen with malicious modifications to a client. */ + if (com::android::bluetooth::flags::btsec_check_valid_discovery_database() && + client_cb->p_disc_db != NULL) { + log::error("Tried to set up a HF client with a preexisting discovery database."); + client_cb->p_disc_db = NULL; + // We manually set the state here because it's possible to call this from an + // OPEN state, in which case the discovery fail event will be ignored. + client_cb->state = 0; // BTA_HF_CLIENT_INIT_ST + return; + } + /* allocate buffer for sdp database */ client_cb->p_disc_db = (tSDP_DISCOVERY_DB*)osi_malloc(BT_DEFAULT_BUFFER_SIZE); diff --git a/system/bta/hh/bta_hh_act.cc b/system/bta/hh/bta_hh_act.cc index 116e572518..01a7e1652e 100644 --- a/system/bta/hh/bta_hh_act.cc +++ b/system/bta/hh/bta_hh_act.cc @@ -578,9 +578,8 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) { conn.app_id = p_cb->app_id; BTM_LogHistory(kBtmLogTag, p_cb->link_spec.addrt.bda, "Opened", - base::StringPrintf("%s initiator:%s", - bt_transport_text(p_cb->link_spec.transport).c_str(), - (p_cb->incoming_conn) ? "remote" : "local")); + std::format("{} initiator:{}", bt_transport_text(p_cb->link_spec.transport), + (p_cb->incoming_conn) ? "remote" : "local")); if (p_cb->link_spec.transport != BT_TRANSPORT_LE) { /* inform role manager */ @@ -843,14 +842,12 @@ void bta_hh_close_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) { disc_dat.handle = p_cb->hid_handle; disc_dat.status = to_bta_hh_status(p_data->hid_cback.data); - std::string overlay_fail = base::StringPrintf( - "%s %s %s", (l2cap_conn_fail) ? "l2cap_conn_fail" : "", - (l2cap_req_fail) ? "l2cap_req_fail" : "", (l2cap_cfg_fail) ? "l2cap_cfg_fail" : ""); - BTM_LogHistory( - kBtmLogTag, p_cb->link_spec.addrt.bda, "Closed", - base::StringPrintf("%s reason %s %s", + BTM_LogHistory(kBtmLogTag, p_cb->link_spec.addrt.bda, "Closed", + std::format("{} reason {} {} {} {}", (p_cb->link_spec.transport == BT_TRANSPORT_LE) ? "le" : "classic", - hid_status_text(hid_status).c_str(), overlay_fail.c_str())); + hid_status_text(hid_status), l2cap_conn_fail ? "l2cap_conn_fail" : "", + l2cap_req_fail ? "l2cap_req_fail" : "", + l2cap_cfg_fail ? "l2cap_cfg_fail" : "")); /* inform role manager */ bta_sys_conn_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda); diff --git a/system/bta/hh/bta_hh_le.cc b/system/bta/hh/bta_hh_le.cc index dab8a1d738..1f288716f7 100644 --- a/system/bta/hh/bta_hh_le.cc +++ b/system/bta/hh/bta_hh_le.cc @@ -1716,10 +1716,9 @@ static void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY* p_data) { void bta_hh_le_open_fail(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) { const tBTA_HH_LE_CLOSE* le_close = &p_data->le_close; - BTM_LogHistory( - kBtmLogTag, p_cb->link_spec.addrt.bda, "Open failed", - base::StringPrintf("%s reason %s", bt_transport_text(p_cb->link_spec.transport).c_str(), - gatt_disconnection_reason_text(le_close->reason).c_str())); + BTM_LogHistory(kBtmLogTag, p_cb->link_spec.addrt.bda, "Open failed", + std::format("{} reason {}", bt_transport_text(p_cb->link_spec.transport), + gatt_disconnection_reason_text(le_close->reason))); log::warn("Open failed for device:{}", p_cb->link_spec.addrt.bda); /* open failure in the middle of service discovery, clear all services */ @@ -1763,10 +1762,9 @@ void bta_hh_le_open_fail(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) { void bta_hh_gatt_close(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) { const tBTA_HH_LE_CLOSE* le_close = &p_data->le_close; - BTM_LogHistory( - kBtmLogTag, p_cb->link_spec.addrt.bda, "Closed", - base::StringPrintf("%s reason %s", bt_transport_text(p_cb->link_spec.transport).c_str(), - gatt_disconnection_reason_text(le_close->reason).c_str())); + BTM_LogHistory(kBtmLogTag, p_cb->link_spec.addrt.bda, "Closed", + std::format("{} reason {}", bt_transport_text(p_cb->link_spec.transport), + gatt_disconnection_reason_text(le_close->reason))); /* deregister all notification */ bta_hh_le_deregister_input_notif(p_cb); diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h index 90f1508c50..60ab9b36a0 100644 --- a/system/bta/include/bta_api.h +++ b/system/bta/include/bta_api.h @@ -26,7 +26,6 @@ #define BTA_API_H #include <base/functional/callback.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -124,7 +123,7 @@ inline std::string preferred_role_text(const tBTA_PREF_ROLES& role) { CASE_RETURN_TEXT(BTA_CENTRAL_ROLE_ONLY); CASE_RETURN_TEXT(BTA_PERIPHERAL_ROLE_ONLY); default: - return base::StringPrintf("UNKNOWN[%hhu]", role); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(role)); } } @@ -157,10 +156,11 @@ typedef struct { typedef uint8_t tBTA_DM_BLE_RSSI_ALERT_TYPE; typedef enum : uint8_t { - BTA_DM_LINK_UP_EVT = 5, /* Connection UP event */ - BTA_DM_LINK_DOWN_EVT = 6, /* Connection DOWN event */ - BTA_DM_LE_FEATURES_READ = 27, /* Controller specific LE features are read */ - BTA_DM_LINK_UP_FAILED_EVT = 34, /* Create connection failed event */ + BTA_DM_LINK_UP_EVT = 5, /* Connection UP event */ + BTA_DM_LINK_DOWN_EVT = 6, /* Connection DOWN event */ + BTA_DM_LE_FEATURES_READ = 27, /* Controller specific LE features are read */ + BTA_DM_LPP_OFFLOAD_FEATURES_READ = 28, /* Low power processor offload features are read */ + BTA_DM_LINK_UP_FAILED_EVT = 34, /* Create connection failed event */ } tBTA_DM_ACL_EVT; /* Structure associated with BTA_DM_LINK_UP_EVT */ @@ -216,7 +216,7 @@ inline std::string bta_dm_search_evt_text(const tBTA_DM_SEARCH_EVT& event) { CASE_RETURN_TEXT(BTA_DM_NAME_READ_EVT); CASE_RETURN_TEXT(BTA_DM_OBSERVE_CMPL_EVT); default: - return base::StringPrintf("UNKNOWN[%hhu]", event); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(event)); } } diff --git a/system/bta/include/bta_api_data_types.h b/system/bta/include/bta_api_data_types.h index 5b4cb3f27b..70b00d664c 100644 --- a/system/bta/include/bta_api_data_types.h +++ b/system/bta/include/bta_api_data_types.h @@ -18,7 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -51,7 +50,7 @@ inline std::string bta_status_text(const tBTA_STATUS& status) { CASE_RETURN_TEXT(BTA_NO_RESOURCES); CASE_RETURN_TEXT(BTA_WRONG_MODE); default: - return base::StringPrintf("UNKNOWN[%d]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } diff --git a/system/bta/include/bta_gatt_api.h b/system/bta/include/bta_gatt_api.h index 8a8a91de9e..6e93821e4a 100644 --- a/system/bta/include/bta_gatt_api.h +++ b/system/bta/include/bta_gatt_api.h @@ -26,7 +26,6 @@ #define BTA_GATT_API_H #include <base/functional/callback_forward.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <list> @@ -95,7 +94,7 @@ inline std::string gatt_client_event_text(const tBTA_GATTC_EVT& event) { CASE_RETURN_TEXT(BTA_GATTC_CONN_UPDATE_EVT); CASE_RETURN_TEXT(BTA_GATTC_SUBRATE_CHG_EVT); default: - return base::StringPrintf("UNKNOWN[%hhu]", event); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(event)); } } @@ -322,7 +321,7 @@ inline std::string gatt_server_event_text(const tBTA_GATTS_EVT& event) { CASE_RETURN_TEXT(BTA_GATTS_CONN_UPDATE_EVT); CASE_RETURN_TEXT(BTA_GATTS_SUBRATE_CHG_EVT); default: - return base::StringPrintf("UNKNOWN[%hhu]", event); + return std::format("UNKNOWN[{}]", event); } } diff --git a/system/bta/include/bta_gatt_queue.h b/system/bta/include/bta_gatt_queue.h index 0f01f053cf..c530004f3d 100644 --- a/system/bta/include/bta_gatt_queue.h +++ b/system/bta/include/bta_gatt_queue.h @@ -44,11 +44,12 @@ public: static void WriteDescriptor(tCONN_ID conn_id, uint16_t handle, std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb, void* cb_data); static void ConfigureMtu(tCONN_ID conn_id, uint16_t mtu); - /* This method uses "Read Multiple Variable Length Characteristic Values". - * If EATT is not enabled on remote, it would send multiple regular Characteristic Reads, and - * concatenate their values into Length Value Tuple List + /* This method queues "Read Multiple Variable Length Characteristic Values". + * Remote must support this method when it supports EATT. + * Returns true when remote supports EATT and operation was successfully queued. + * Returns false when remote doesn't support EATT and operation was not scheduled. */ - static void ReadMultiCharacteristic(tCONN_ID conn_id, tBTA_GATTC_MULTI& p_read_multi, + static bool ReadMultiCharacteristic(tCONN_ID conn_id, tBTA_GATTC_MULTI& p_read_multi, GATT_READ_MULTI_OP_CB cb, void* cb_data); /* Holds pending GATT operations */ @@ -80,8 +81,6 @@ private: static void gatt_read_multi_op_finished(tCONN_ID conn_id, tGATT_STATUS status, tBTA_GATTC_MULTI& handle, uint16_t len, uint8_t* value, void* data); - static void gatt_read_multi_op_simulate(tCONN_ID conn_id, tGATT_STATUS status, uint16_t handle, - uint16_t len, uint8_t* value, void* data_read); // maps connection id to operations waiting for execution static std::unordered_map<tCONN_ID, std::list<gatt_operation>> gatt_op_queue; // contain connection ids that currently execute operations diff --git a/system/bta/include/bta_hh_api.h b/system/bta/include/bta_hh_api.h index 976c7d2bd8..b18bba5e60 100644 --- a/system/bta/include/bta_hh_api.h +++ b/system/bta/include/bta_hh_api.h @@ -18,7 +18,6 @@ #ifndef BTA_HH_API_H #define BTA_HH_API_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -249,7 +248,7 @@ typedef struct { uint8_t hid_handle; std::string ToString() const { - return base::StringPrintf("%04x::%04x::%04x", vendor_id, product_id, version); + return std::format("{:04x}::{:04x}::{:04x}", vendor_id, product_id, version); } } tBTA_HH_DEV_DSCP_INFO; diff --git a/system/bta/include/bta_jv_api.h b/system/bta/include/bta_jv_api.h index 4b43cbc485..8978ab00c2 100644 --- a/system/bta/include/bta_jv_api.h +++ b/system/bta/include/bta_jv_api.h @@ -33,6 +33,7 @@ #include "internal_include/bt_target.h" #include "stack/include/bt_hdr.h" #include "stack/include/l2cap_types.h" +#include "stack/include/port_api.h" #include "stack/include/rfcdefs.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" @@ -231,7 +232,7 @@ inline std::string bta_jv_event_text(const tBTA_JV_EVT& event) { CASE_RETURN_TEXT(BTA_JV_RFCOMM_SRV_OPEN_EVT); CASE_RETURN_TEXT(BTA_JV_MAX_EVT); default: - return base::StringPrintf("UNKNOWN[%hu]", event); + return std::format("UNKNOWN[{}]", static_cast<uint16_t>(event)); } } @@ -260,6 +261,11 @@ typedef struct { int32_t tx_mtu; /* The transmit MTU */ uint16_t local_cid; /* The local CID */ uint16_t remote_cid; /* The remote CID */ + uint16_t local_coc_mps; /* The local COC MPS */ + uint16_t remote_coc_mps; /* The remote COC MPS */ + uint16_t local_coc_credit; /* The local COC credit */ + uint16_t remote_coc_credit; /* The remote COC credit */ + uint16_t acl_handle; /* The ACL handle */ } tBTA_JV_L2CAP_OPEN; /* data associated with BTA_JV_L2CAP_OPEN_EVT for LE sockets */ @@ -323,9 +329,19 @@ typedef struct { /* data associated with BTA_JV_RFCOMM_OPEN_EVT */ typedef struct { - tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ - uint32_t handle; /* The connection handle */ - RawAddress rem_bda; /* The peer address */ + tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */ + uint32_t handle; /* The connection handle */ + RawAddress rem_bda; /* The peer address */ + uint16_t rx_mtu; /* The receive (local) L2CAP MTU */ + uint16_t tx_mtu; /* The transmit (remote) L2CAP MTU */ + uint16_t local_credit; /* The local RFCOMM credit */ + uint16_t remote_credit; /* The remote RFCOMM credit */ + uint16_t local_cid; /* The local L2CAP CID */ + uint16_t remote_cid; /* The remote L2CAP CID */ + uint16_t dlci; /* DLCI */ + uint16_t max_frame_size; /* The max frame size for RFCOMM */ + uint16_t acl_handle; /* The ACL handle */ + bool mux_initiator; /* Is the initiator of the RFCOMM multiplexer control channel */ } tBTA_JV_RFCOMM_OPEN; /* data associated with BTA_JV_RFCOMM_SRV_OPEN_EVT */ typedef struct { @@ -333,6 +349,16 @@ typedef struct { uint32_t handle; /* The connection handle */ uint32_t new_listen_handle; /* The new listen handle */ RawAddress rem_bda; /* The peer address */ + uint16_t rx_mtu; /* The receive (local) L2CAP MTU */ + uint16_t tx_mtu; /* The transmit (remote) L2CAP MTU */ + uint16_t local_credit; /* The local RFCOMM credit */ + uint16_t remote_credit; /* The remote RFCOMM credit */ + uint16_t local_cid; /* The local L2CAP CID */ + uint16_t remote_cid; /* The remote L2CAP CID */ + uint16_t dlci; /* DLCI */ + uint16_t max_frame_size; /* The max frame size for RFCOMM */ + uint16_t acl_handle; /* The ACL handle */ + bool mux_initiator; /* Is the initiator of the RFCOMM multiplexer control channel */ } tBTA_JV_RFCOMM_SRV_OPEN; /* data associated with BTA_JV_RFCOMM_CLOSE_EVT */ @@ -672,7 +698,7 @@ tBTA_JV_STATUS BTA_JvL2capWrite(uint32_t handle, uint32_t req_id, BT_HDR* msg, u ******************************************************************************/ tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr, tBTA_JV_RFCOMM_CBACK* p_cback, - uint32_t rfcomm_slot_id); + uint32_t rfcomm_slot_id, RfcommCfgInfo cfg); /******************************************************************************* * @@ -702,7 +728,8 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t handle, uint32_t rfcomm_slot_id); * ******************************************************************************/ tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id); + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg); /******************************************************************************* * diff --git a/system/bta/include/bta_ras_api.h b/system/bta/include/bta_ras_api.h index 8757c1382c..ee6eccec4f 100644 --- a/system/bta/include/bta_ras_api.h +++ b/system/bta/include/bta_ras_api.h @@ -38,6 +38,7 @@ public: const RawAddress& address, const std::vector<VendorSpecificCharacteristic>& vendor_specific_reply) = 0; virtual void OnRasServerConnected(const RawAddress& identity_address) = 0; + virtual void OnMtuChangedFromServer(const RawAddress& address, uint16_t mtu) = 0; virtual void OnRasServerDisconnected(const RawAddress& identity_address) = 0; }; @@ -67,6 +68,7 @@ public: virtual void OnWriteVendorSpecificReplyComplete(const RawAddress& address, bool success) = 0; virtual void OnRemoteData(const RawAddress& address, const std::vector<uint8_t>& data) = 0; virtual void OnRemoteDataTimeout(const RawAddress& address) = 0; + virtual void OnMtuChangedFromClient(const RawAddress& address, uint16_t mtu) = 0; }; class RasClient { diff --git a/system/bta/include/bta_sdp_api.h b/system/bta/include/bta_sdp_api.h index 5eaf66bfb8..6a7c7832c8 100644 --- a/system/bta/include/bta_sdp_api.h +++ b/system/bta/include/bta_sdp_api.h @@ -24,8 +24,6 @@ #ifndef BTA_SDP_API_H #define BTA_SDP_API_H -#include <base/strings/stringprintf.h> - #include <cstdint> #include <string> @@ -50,7 +48,7 @@ inline std::string bta_sdp_status_text(const tBTA_SDP_STATUS& status) { CASE_RETURN_TEXT(BTA_SDP_FAILURE); CASE_RETURN_TEXT(BTA_SDP_BUSY); default: - return base::StringPrintf("UNKNOWN[%d]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } diff --git a/system/bta/include/bta_sec_api.h b/system/bta/include/bta_sec_api.h index a0f431f754..82d3029c91 100644 --- a/system/bta/include/bta_sec_api.h +++ b/system/bta/include/bta_sec_api.h @@ -19,7 +19,6 @@ #pragma once #include <base/functional/callback.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> diff --git a/system/bta/jv/bta_jv_act.cc b/system/bta/jv/bta_jv_act.cc index 9d4715269c..7b6fa38ca8 100644 --- a/system/bta/jv/bta_jv_act.cc +++ b/system/bta/jv/bta_jv_act.cc @@ -982,11 +982,31 @@ static void bta_jv_l2cap_client_cback(uint16_t gap_handle, uint16_t event, tGAP_ switch (event) { case GAP_EVT_CONN_OPENED: - evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); - evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); - if (data != nullptr) { - evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid; - evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid; + if (!com::android::bluetooth::flags::socket_settings_api() || + !GAP_IsTransportLe(gap_handle)) { + evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); + evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); + if (data != nullptr) { + evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid; + evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid; + } + } else { + uint16_t remote_mtu, local_mps, remote_mps, local_credit, remote_credit; + uint16_t local_cid, remote_cid, acl_handle; + evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); + if (GAP_GetLeChannelInfo(gap_handle, &remote_mtu, &local_mps, &remote_mps, &local_credit, + &remote_credit, &local_cid, &remote_cid, + &acl_handle) != PORT_SUCCESS) { + log::warn("Unable to get GAP channel info handle:{}", gap_handle); + } + evt_data.l2c_open.tx_mtu = remote_mtu; + evt_data.l2c_open.local_coc_mps = local_mps; + evt_data.l2c_open.remote_coc_mps = remote_mps; + evt_data.l2c_open.local_coc_credit = local_credit; + evt_data.l2c_open.remote_coc_credit = remote_credit; + evt_data.l2c_open.local_cid = local_cid; + evt_data.l2c_open.remote_cid = remote_cid; + evt_data.l2c_open.acl_handle = acl_handle; } p_cb->state = BTA_JV_ST_CL_OPEN; p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id); @@ -1142,11 +1162,31 @@ static void bta_jv_l2cap_server_cback(uint16_t gap_handle, uint16_t event, tGAP_ switch (event) { case GAP_EVT_CONN_OPENED: - evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); - evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); - if (data != nullptr) { - evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid; - evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid; + if (!com::android::bluetooth::flags::socket_settings_api() || + !GAP_IsTransportLe(gap_handle)) { + evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); + evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle); + if (data != nullptr) { + evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid; + evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid; + } + } else { + uint16_t remote_mtu, local_mps, remote_mps, local_credit, remote_credit; + uint16_t local_cid, remote_cid, acl_handle; + evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle); + if (GAP_GetLeChannelInfo(gap_handle, &remote_mtu, &local_mps, &remote_mps, &local_credit, + &remote_credit, &local_cid, &remote_cid, + &acl_handle) != PORT_SUCCESS) { + log::warn("Unable to get GAP channel info handle:{}", gap_handle); + } + evt_data.l2c_open.tx_mtu = remote_mtu; + evt_data.l2c_open.local_coc_mps = local_mps; + evt_data.l2c_open.remote_coc_mps = remote_mps; + evt_data.l2c_open.local_coc_credit = local_credit; + evt_data.l2c_open.remote_coc_credit = remote_credit; + evt_data.l2c_open.local_cid = local_cid; + evt_data.l2c_open.remote_cid = remote_cid; + evt_data.l2c_open.acl_handle = acl_handle; } p_cb->state = BTA_JV_ST_SR_OPEN; p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id); @@ -1401,6 +1441,16 @@ static void bta_jv_port_mgmt_cl_cback(const tPORT_RESULT code, uint16_t port_han .rem_bda = rem_bda, }, }; + if (com::android::bluetooth::flags::socket_settings_api()) { + if (PORT_GetChannelInfo(port_handle, &evt_data.rfc_open.rx_mtu, &evt_data.rfc_open.tx_mtu, + &evt_data.rfc_open.local_credit, &evt_data.rfc_open.remote_credit, + &evt_data.rfc_open.local_cid, &evt_data.rfc_open.remote_cid, + &evt_data.rfc_open.dlci, &evt_data.rfc_open.max_frame_size, + &evt_data.rfc_open.acl_handle, + &evt_data.rfc_open.mux_initiator) != PORT_SUCCESS) { + log::warn("Unable to get RFCOMM channel info peer:{} handle:{}", rem_bda, port_handle); + } + } p_pcb->state = BTA_JV_ST_CL_OPEN; p_cb->p_cback(BTA_JV_RFCOMM_OPEN_EVT, &evt_data, p_pcb->rfcomm_slot_id); } else { @@ -1461,7 +1511,8 @@ static void bta_jv_port_event_cl_cback(uint32_t code, uint16_t port_handle) { /* Client initiates an RFCOMM connection */ void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id) { + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg) { uint16_t handle = 0; uint32_t event_mask = BTA_JV_RFC_EV_MASK; PortSettings port_settings; @@ -1485,9 +1536,9 @@ void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddre 0); } - if (RFCOMM_CreateConnectionWithSecurity(UUID_SERVCLASS_SERIAL_PORT, remote_scn, false, - BTA_JV_DEF_RFC_MTU, peer_bd_addr, &handle, - bta_jv_port_mgmt_cl_cback, sec_mask) != PORT_SUCCESS) { + if (RFCOMM_CreateConnectionWithSecurity( + UUID_SERVCLASS_SERIAL_PORT, remote_scn, false, BTA_JV_DEF_RFC_MTU, peer_bd_addr, + &handle, bta_jv_port_mgmt_cl_cback, sec_mask, cfg) != PORT_SUCCESS) { log::error("RFCOMM_CreateConnection failed"); bta_jv.rfc_cl_init.status = tBTA_JV_STATUS::FAILURE; } else { @@ -1608,6 +1659,17 @@ static void bta_jv_port_mgmt_sr_cback(const tPORT_RESULT code, uint16_t port_han evt_data.rfc_srv_open.handle = p_pcb->handle; evt_data.rfc_srv_open.status = tBTA_JV_STATUS::SUCCESS; evt_data.rfc_srv_open.rem_bda = rem_bda; + if (com::android::bluetooth::flags::socket_settings_api()) { + if (PORT_GetChannelInfo(port_handle, &evt_data.rfc_srv_open.rx_mtu, + &evt_data.rfc_srv_open.tx_mtu, &evt_data.rfc_srv_open.local_credit, + &evt_data.rfc_srv_open.remote_credit, + &evt_data.rfc_srv_open.local_cid, &evt_data.rfc_srv_open.remote_cid, + &evt_data.rfc_srv_open.dlci, &evt_data.rfc_srv_open.max_frame_size, + &evt_data.rfc_srv_open.acl_handle, + &evt_data.rfc_srv_open.mux_initiator) != PORT_SUCCESS) { + log::warn("Unable to get RFCOMM channel info peer:{} handle:{}", rem_bda, port_handle); + } + } tBTA_JV_PCB* p_pcb_new_listen = bta_jv_add_rfc_port(p_cb, p_pcb); if (p_pcb_new_listen) { evt_data.rfc_srv_open.new_listen_handle = p_pcb_new_listen->handle; @@ -1735,9 +1797,10 @@ static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb log::error("RFCOMM_CreateConnection failed: invalid port_handle"); } - if (RFCOMM_CreateConnectionWithSecurity( - p_cb->sec_id, p_cb->scn, true, BTA_JV_DEF_RFC_MTU, RawAddress::kAny, - &(p_cb->rfc_hdl[si]), bta_jv_port_mgmt_sr_cback, sec_mask) == PORT_SUCCESS) { + if (RFCOMM_CreateConnectionWithSecurity(p_cb->sec_id, p_cb->scn, true, BTA_JV_DEF_RFC_MTU, + RawAddress::kAny, &(p_cb->rfc_hdl[si]), + bta_jv_port_mgmt_sr_cback, sec_mask, + RfcommCfgInfo{}) == PORT_SUCCESS) { p_cb->curr_sess++; p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[si] - 1]; p_pcb->state = BTA_JV_ST_SR_LISTEN; @@ -1781,7 +1844,8 @@ static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb /* waits for an RFCOMM client to connect */ void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id) { + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg) { uint16_t handle = 0; uint32_t event_mask = BTA_JV_RFC_EV_MASK; PortSettings port_settings; @@ -1795,7 +1859,7 @@ void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t ma do { if (RFCOMM_CreateConnectionWithSecurity(0, local_scn, true, BTA_JV_DEF_RFC_MTU, RawAddress::kAny, &handle, bta_jv_port_mgmt_sr_cback, - sec_mask) != PORT_SUCCESS) { + sec_mask, cfg) != PORT_SUCCESS) { log::error("RFCOMM_CreateConnection failed"); break; } diff --git a/system/bta/jv/bta_jv_api.cc b/system/bta/jv/bta_jv_api.cc index 1b5e715384..bf240a9834 100644 --- a/system/bta/jv/bta_jv_api.cc +++ b/system/bta/jv/bta_jv_api.cc @@ -415,7 +415,7 @@ tBTA_JV_STATUS BTA_JvL2capWrite(uint32_t handle, uint32_t req_id, BT_HDR* msg, u ******************************************************************************/ tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr, tBTA_JV_RFCOMM_CBACK* p_cback, - uint32_t rfcomm_slot_id) { + uint32_t rfcomm_slot_id, RfcommCfgInfo cfg) { log::verbose("remote_scn:{}, peer_bd_addr:{}, rfcomm_slot_id:{}", remote_scn, peer_bd_addr, rfcomm_slot_id); @@ -424,7 +424,7 @@ tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC sec_mask, uint8_t remote_scn, } do_in_main_thread(Bind(&bta_jv_rfcomm_connect, sec_mask, remote_scn, peer_bd_addr, p_cback, - rfcomm_slot_id)); + rfcomm_slot_id, cfg)); return tBTA_JV_STATUS::SUCCESS; } @@ -469,7 +469,8 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t handle, uint32_t rfcomm_slot_id) { * ******************************************************************************/ tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id) { + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg) { log::verbose("local_scn:{}, rfcomm_slot_id:{}", local_scn, rfcomm_slot_id); if (p_cback == NULL) { @@ -485,7 +486,7 @@ tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC sec_mask, uint8_t local_scn, uin } do_in_main_thread(Bind(&bta_jv_rfcomm_start_server, sec_mask, local_scn, max_session, p_cback, - rfcomm_slot_id)); + rfcomm_slot_id, cfg)); return tBTA_JV_STATUS::SUCCESS; } diff --git a/system/bta/jv/bta_jv_int.h b/system/bta/jv/bta_jv_int.h index f7aa7d106f..49ccd976b0 100644 --- a/system/bta/jv/bta_jv_int.h +++ b/system/bta/jv/bta_jv_int.h @@ -165,10 +165,12 @@ void bta_jv_l2cap_stop_server(uint16_t local_psm, uint32_t l2cap_socket_id); void bta_jv_l2cap_write(uint32_t handle, uint32_t req_id, BT_HDR* msg, uint32_t user_id, tBTA_JV_L2C_CB* p_cb); void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id); + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg); void bta_jv_rfcomm_close(uint32_t handle, uint32_t rfcomm_slot_id); void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session, - tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id); + tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id, + RfcommCfgInfo cfg); void bta_jv_rfcomm_stop_server(uint32_t handle, uint32_t rfcomm_slot_id); void bta_jv_rfcomm_write(uint32_t handle, uint32_t req_id, tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb); diff --git a/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc b/system/bta/le_audio/audio_hal_client/audio_source_hal_client.cc index 58e2bb08c1..ee85db11e4 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 @@ -227,19 +227,12 @@ void SourceImpl::SendAudioData() { sStats.media_read_last_underflow_us = bluetooth::common::time_get_os_boottime_us(); } - if (com::android::bluetooth::flags::leaudio_hal_client_asrc()) { - auto asrc_buffers = asrc_->Run(data); - - std::lock_guard<std::mutex> guard(audioSourceCallbacksMutex_); - for (auto buffer : asrc_buffers) { - if (audioSourceCallbacks_ != nullptr) { - audioSourceCallbacks_->OnAudioDataReady(*buffer); - } - } - } else { - std::lock_guard<std::mutex> guard(audioSourceCallbacksMutex_); + auto asrc_buffers = asrc_->Run(data); + + std::lock_guard<std::mutex> guard(audioSourceCallbacksMutex_); + for (auto buffer : asrc_buffers) { if (audioSourceCallbacks_ != nullptr) { - audioSourceCallbacks_->OnAudioDataReady(data); + audioSourceCallbacks_->OnAudioDataReady(*buffer); } } } @@ -267,11 +260,9 @@ bool SourceImpl::InitAudioSinkThread() { void SourceImpl::StartAudioTicks() { wakelock_acquire(); - if (com::android::bluetooth::flags::leaudio_hal_client_asrc()) { - asrc_ = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( - worker_thread_, source_codec_config_.num_channels, source_codec_config_.sample_rate, - source_codec_config_.bits_per_sample, source_codec_config_.data_interval_us); - } + asrc_ = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( + worker_thread_, source_codec_config_.num_channels, source_codec_config_.sample_rate, + source_codec_config_.bits_per_sample, source_codec_config_.data_interval_us); audio_timer_.SchedulePeriodic( worker_thread_->GetWeakPtr(), FROM_HERE, base::BindRepeating(&SourceImpl::SendAudioData, weak_factory_.GetWeakPtr()), diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 9c17be4088..0ce7da4f25 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -86,6 +86,7 @@ #include "osi/include/osi.h" #include "osi/include/properties.h" #include "stack/btm/btm_sec.h" +#include "stack/gatt/gatt_int.h" #include "stack/include/bt_types.h" #include "stack/include/btm_client_interface.h" #include "stack/include/btm_status.h" @@ -234,6 +235,120 @@ LeAudioGroupStateMachine::Callbacks* stateMachineCallbacks; DeviceGroupsCallbacks* device_group_callbacks; LeAudioIsoDataCallback* iso_data_callback; +class StreamSpeedTracker { +public: + StreamSpeedTracker(void) + : is_started_(false), + group_id_(bluetooth::groups::kGroupUnknown), + num_of_devices_(0), + context_type_(LeAudioContextType::UNSPECIFIED), + reconfig_start_ts_(0), + setup_start_ts_(0), + total_time_(0), + reconfig_time_(0), + stream_setup_time_(0) {} + + void Init(int group_id, LeAudioContextType context_type, int num_of_devices) { + Reset(); + group_id_ = group_id; + context_type_ = context_type; + num_of_devices_ = num_of_devices; + log::verbose("StreamSpeedTracker group_id: {}, context: {} #{}", group_id_, + ToString(context_type_), num_of_devices); + } + + void Reset(void) { + log::verbose("StreamSpeedTracker group_id: {}", group_id_); + is_started_ = false; + group_id_ = bluetooth::groups::kGroupUnknown; + reconfig_start_ts_ = setup_start_ts_ = total_time_ = reconfig_time_ = stream_setup_time_ = + num_of_devices_ = 0; + context_type_ = LeAudioContextType::UNSPECIFIED; + } + + void ReconfigStarted(void) { + log::verbose("StreamSpeedTracker group_id: {}", group_id_); + reconfig_time_ = 0; + is_started_ = true; + reconfig_start_ts_ = bluetooth::common::time_get_os_boottime_us(); + } + + void StartStream(void) { + log::verbose("StreamSpeedTracker group_id: {}", group_id_); + setup_start_ts_ = bluetooth::common::time_get_os_boottime_us(); + is_started_ = true; + } + + void ReconfigurationComplete(void) { + reconfig_time_ = (bluetooth::common::time_get_os_boottime_us() - reconfig_start_ts_) / 1000; + log::verbose("StreamSpeedTracker group_id: {}, {} reconfig time {} ms", group_id_, + ToString(context_type_), reconfig_time_); + } + + void StreamCreated(void) { + stream_setup_time_ = (bluetooth::common::time_get_os_boottime_us() - setup_start_ts_) / 1000; + log::verbose("StreamSpeedTracker group_id: {}, {} stream create time {} ms", group_id_, + ToString(context_type_), stream_setup_time_); + } + + void StopStreamSetup(void) { + is_started_ = false; + uint64_t start_ts = reconfig_time_ != 0 ? reconfig_start_ts_ : setup_start_ts_; + total_time_ = (bluetooth::common::time_get_os_boottime_us() - start_ts) / 1000; + clock_gettime(CLOCK_REALTIME, &end_ts_); + log::verbose("StreamSpeedTracker group_id: {}, {} setup time {} ms", group_id_, + ToString(context_type_), total_time_); + } + + bool IsStarted(void) { + if (is_started_) { + log::verbose("StreamSpeedTracker group_id: {}, {} is_started_: {} ", group_id_, + ToString(context_type_), is_started_); + } else { + log::verbose("StreamSpeedTracker not started "); + } + return is_started_; + } + + void Dump(std::stringstream& stream) { + char ts[20]; + std::strftime(ts, sizeof(ts), "%T", std::gmtime(&end_ts_.tv_sec)); + + if (total_time_ < 900) { + stream << "[ 🌟 "; + } else if (total_time_ < 1500) { + stream << "[ 🌤 "; + } else if (total_time_ < 2500) { + stream << "[ 🌧 "; + } else { + stream << "[ ❗ "; + } + + stream << ts << ": Gid: " << group_id_ << "(#" << num_of_devices_ << "), " << context_type_ + << ", "; + auto hal_idle = total_time_ - stream_setup_time_ - reconfig_time_; + if (reconfig_time_ != 0) { + stream << "t:" << total_time_ << "ms (r:" << reconfig_time_ << "/s:" << stream_setup_time_ + << "/hal:" << hal_idle << ")"; + } else { + stream << "t:" << total_time_ << "ms (hal:" << hal_idle << ")"; + } + stream << "]"; + } + +private: + bool is_started_; + int group_id_; + int num_of_devices_; + LeAudioContextType context_type_; + struct timespec end_ts_; + uint64_t reconfig_start_ts_; + uint64_t setup_start_ts_; + uint64_t total_time_; + uint64_t reconfig_time_; + uint64_t stream_setup_time_; +}; + /* * Coordinatet Set Identification Profile (CSIP) based on CSIP 1.0 * and Coordinatet Set Identification Service (CSIS) 1.0 @@ -282,8 +397,6 @@ public: configuration_context_type_(LeAudioContextType::UNINITIALIZED), in_call_metadata_context_types_({.sink = AudioContexts(), .source = AudioContexts()}), local_metadata_context_types_({.sink = AudioContexts(), .source = AudioContexts()}), - stream_setup_start_timestamp_(0), - stream_setup_end_timestamp_(0), audio_receiver_state_(AudioState::IDLE), audio_sender_state_(AudioState::IDLE), in_call_(false), @@ -505,6 +618,8 @@ public: void AseInitialStateReadRequest(LeAudioDevice* leAudioDevice) { int ases_num = leAudioDevice->ases_.size(); + bool is_eatt_supported = gatt_profile_get_eatt_support_by_conn_id(leAudioDevice->conn_id_); + void* notify_flag_ptr = NULL; tBTA_GATTC_MULTI multi_read{}; @@ -518,7 +633,7 @@ public: notify_flag_ptr = INT_TO_PTR(leAudioDevice->notify_connected_after_read_); } - if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) { + if (!com::android::bluetooth::flags::le_ase_read_multiple_variable() || !is_eatt_supported) { BtaGattQueue::ReadCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ases_[i].hdls.val_hdl, OnGattReadRspStatic, notify_flag_ptr); @@ -690,7 +805,8 @@ public: leAudioDevice = group->GetNextActiveDevice(leAudioDevice); } while (leAudioDevice); - if (recovery) { + if (recovery && !group->NumOfConnected()) { + log::info("All devices disconnected, group becomes inactive"); /* Both devices will be disconnected soon. Notify upper layer that group * is inactive */ groupSetAndNotifyInactive(); @@ -1031,7 +1147,7 @@ public: return groupStateMachine_->ConfigureStream(group, configuration_context_type_, remote_contexts, ccids); } else if (!group_is_streaming) { - stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); + speed_start_setup(group->group_id_, configuration_context_type, group->NumOfConnected()); } /* If assistant have some connected delegators that needs to be informed @@ -1176,7 +1292,7 @@ public: pre_configuration_context_type_ = previous_context_type; group->SetPendingConfiguration(); groupStateMachine_->StopStream(group); - stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); + speed_start_setup(group->group_id_, configuration_context_type_, group->NumOfConnected(), true); } void SetInCall(bool in_call) override { @@ -1896,11 +2012,26 @@ public: dev->SetConnectionState(DeviceConnectState::DISCONNECTED); } } + + /* If group is Streaming or is in transition for Streaming - lets stop it + * and mark device to disconnect when stream is closed + */ if (group->IsStreaming() || !group->IsReleasingOrIdle()) { + log::debug("group_id {} needs to stop streaming before {} disconnection", + group->group_id_, leAudioDevice->address_); leAudioDevice->closing_stream_for_disconnection_ = true; groupStateMachine_->StopStream(group); return; } + + if (group->IsReleasing()) { + log::debug("group_id {} needs to stop streaming before {} disconnection", + group->group_id_, leAudioDevice->address_); + /* Stream is releasing, wait till it is completed and then disconnect ACL. */ + leAudioDevice->closing_stream_for_disconnection_ = true; + return; + } + force_acl_disconnect &= group->IsEnabled(); } @@ -2427,7 +2558,9 @@ public: } void ReadMustHaveAttributesOnReconnect(LeAudioDevice* leAudioDevice) { - log::verbose("{}", leAudioDevice->address_); + bool is_eatt_supported = gatt_profile_get_eatt_support_by_conn_id(leAudioDevice->conn_id_); + + log::verbose("{}, eatt supported {}", leAudioDevice->address_, is_eatt_supported); /* Here we read * 1) ASCS Control Point CCC descriptor in order to validate proper * behavior of remote device which should store CCC values for bonded device. @@ -2436,7 +2569,7 @@ public: * it can change very often which, as we observed, might lead to not being sent by * remote devices */ - if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) { + if (!com::android::bluetooth::flags::le_ase_read_multiple_variable() || !is_eatt_supported) { BtaGattQueue::ReadCharacteristic(leAudioDevice->conn_id_, leAudioDevice->audio_avail_hdls_.val_hdl, OnGattReadRspStatic, NULL); @@ -3396,7 +3529,7 @@ public: group->group_id_); scheduleAttachDeviceToTheStream(leAudioDevice->address_); } else { - stream_setup_start_timestamp_ = bluetooth::common::time_get_os_boottime_us(); + speed_start_setup(group->group_id_, configuration_context_type_, 1); } } @@ -4059,9 +4192,10 @@ public: dprintf(fd, " Codec location: UNKNOWN\n"); } - stream << " Start time: "; - for (auto t : stream_start_history_queue_) { - stream << static_cast<int>(t) << " ms, "; + stream << " Stream creation speed: "; + for (auto t : stream_speed_history_) { + t.Dump(stream); + stream << ","; } stream << "\n"; printCurrentStreamConfiguration(stream); @@ -4268,10 +4402,21 @@ public: return; } + /* Group should not be resumed if: + * - configured context type is not allowed + * - updated metadata contains only not allowed context types + */ if (!group->GetAllowedContextMask(bluetooth::le_audio::types::kLeAudioDirectionSink) + .test_all(local_metadata_context_types_.source) || + !group->GetAllowedContextMask(bluetooth::le_audio::types::kLeAudioDirectionSink) .test(configuration_context_type_)) { - log::warn("Block source resume request context type: {}", - ToHexString(configuration_context_type_)); + log::warn( + "Block source resume request context types: {}, allowed context mask: {}, " + "configured: {}", + ToString(local_metadata_context_types_.source), + ToString(group->GetAllowedContextMask( + bluetooth::le_audio::types::kLeAudioDirectionSink)), + ToString(configuration_context_type_)); CancelLocalAudioSourceStreamingRequest(); return; } @@ -4545,10 +4690,21 @@ public: return; } + /* Group should not be resumed if: + * - configured context type is not allowed + * - updated metadata contains only not allowed context types + */ if (!group->GetAllowedContextMask(bluetooth::le_audio::types::kLeAudioDirectionSource) + .test_all(local_metadata_context_types_.sink) || + !group->GetAllowedContextMask(bluetooth::le_audio::types::kLeAudioDirectionSource) .test(configuration_context_type_)) { - log::warn("Block sink resume request context type: {}", - ToHexString(configuration_context_type_)); + log::warn( + "Block sink resume request context types: {} vs allowed context mask: {}, " + "configured: {}", + ToString(local_metadata_context_types_.sink), + ToString(group->GetAllowedContextMask( + bluetooth::le_audio::types::kLeAudioDirectionSource)), + ToString(configuration_context_type_)); CancelLocalAudioSourceStreamingRequest(); return; } @@ -4777,6 +4933,30 @@ public: return true; } + bool StopStreamIfUpdatedContextIsNoLongerSupporteded(uint8_t direction, LeAudioDeviceGroup* group, + AudioContexts local_contexts) { + AudioContexts allowed_contexts = group->GetAllowedContextMask(direction); + + /* Stream should be suspended if: + * - updated metadata is only not allowed + * - there is no metadata (cleared) but configuration is for not allowed context + */ + if (group->IsStreaming() && !allowed_contexts.test_any(local_contexts) && + !(allowed_contexts.test(configuration_context_type_) && local_contexts.none())) { + /* SuspendForReconfiguration and ReconfigurationComplete is a workaround method to let Audio + * Framework know that session is suspended. Strem resume would be handled from + * suspended session context with stopped group. + */ + SuspendedForReconfiguration(); + ReconfigurationComplete(direction); + GroupStop(active_group_id_); + + return true; + } + + return false; + } + void OnLocalAudioSourceMetadataUpdate( const std::vector<struct playback_track_metadata_v7>& source_metadata, DsaMode dsa_mode) { if (active_group_id_ == bluetooth::groups::kGroupUnknown) { @@ -4812,6 +4992,23 @@ public: /* Set the remote sink metadata context from the playback tracks metadata */ local_metadata_context_types_.source = GetAudioContextsFromSourceMetadata(source_metadata); + /* Check if stream should be suspended due to reamaining only not allowed contexts in metadata + * or configured context. + */ + if (com::android::bluetooth::flags::leaudio_stop_updated_to_not_available_context_stream() && + StopStreamIfUpdatedContextIsNoLongerSupporteded( + bluetooth::le_audio::types::kLeAudioDirectionSink, group, + local_metadata_context_types_.source)) { + log::info( + "Updated source metadata contexts are not allowed context types: {} | configured: {} " + "vs allowed context mask: {}", + ToString(local_metadata_context_types_.source), ToString(configuration_context_type_), + ToString(group->GetAllowedContextMask( + bluetooth::le_audio::types::kLeAudioDirectionSink))); + + return; + } + local_metadata_context_types_.source = ChooseMetadataContextType(local_metadata_context_types_.source); @@ -4962,6 +5159,22 @@ public: /* Set remote source metadata context from the recording tracks metadata */ local_metadata_context_types_.sink = GetAudioContextsFromSinkMetadata(sink_metadata); + /* Check if stream should be suspended due to only reamaining not allowed contexts in metadata + * or configured context. + */ + if (com::android::bluetooth::flags::leaudio_stop_updated_to_not_available_context_stream() && + StopStreamIfUpdatedContextIsNoLongerSupporteded( + bluetooth::le_audio::types::kLeAudioDirectionSource, group, + local_metadata_context_types_.sink)) { + log::info( + "Updated sink metadata contexts are not allowed context types: {} | configured: {} " + "vs allowed context mask: {}", + ToString(local_metadata_context_types_.sink), ToString(configuration_context_type_), + ToString(group->GetAllowedContextMask( + bluetooth::le_audio::types::kLeAudioDirectionSource))); + return; + } + local_metadata_context_types_.sink = ChooseMetadataContextType(local_metadata_context_types_.sink); @@ -5589,21 +5802,50 @@ public: } } - void take_stream_time(void) { - if (stream_setup_start_timestamp_ == 0) { + void speed_start_setup(int group_id, LeAudioContextType context_type, int num_of_connected, + bool is_reconfig = false) { + log::verbose("is_started {} is_reconfig {} num_of_connected {}", speed_tracker_.IsStarted(), + is_reconfig, num_of_connected); + if (!speed_tracker_.IsStarted()) { + speed_tracker_.Init(group_id, context_type, num_of_connected); + } + if (is_reconfig) { + speed_tracker_.ReconfigStarted(); + } else { + speed_tracker_.StartStream(); + } + } + + void speed_stop_reconfig(void) { + log::verbose(""); + if (!speed_tracker_.IsStarted()) { return; } + speed_tracker_.ReconfigurationComplete(); + } - if (stream_start_history_queue_.size() == 10) { - stream_start_history_queue_.pop_back(); + void speed_stream_created() { + log::verbose(""); + if (!speed_tracker_.IsStarted()) { + return; } - stream_setup_end_timestamp_ = bluetooth::common::time_get_os_boottime_us(); - stream_start_history_queue_.emplace_front( - (stream_setup_end_timestamp_ - stream_setup_start_timestamp_) / 1000); + speed_tracker_.StreamCreated(); + } + + void speed_stop_setup() { + log::verbose(""); + if (!speed_tracker_.IsStarted()) { + return; + } - stream_setup_end_timestamp_ = 0; - stream_setup_start_timestamp_ = 0; + if (stream_speed_history_.size() == 10) { + stream_speed_history_.pop_back(); + } + + speed_tracker_.StopStreamSetup(); + stream_speed_history_.emplace_front(speed_tracker_); + speed_tracker_.Reset(); } void notifyGroupStreamStatus(int group_id, GroupStreamStatus groupStreamStatus) { @@ -5658,6 +5900,7 @@ public: */ CancelStreamingRequest(); ReconfigurationComplete(previously_active_directions); + speed_stop_reconfig(); } void OnStateMachineStatusReportCb(int group_id, GroupStreamStatus status) { @@ -5676,8 +5919,7 @@ public: return; } - take_stream_time(); - + speed_stream_created(); bluetooth::le_audio::MetricsCollector::Get()->OnStreamStarted(active_group_id_, configuration_context_type_); @@ -5699,6 +5941,7 @@ public: * Just stop streaming */ log::warn("Stopping stream for group {} as AF not interested.", group_id); + speed_stop_setup(); groupStateMachine_->StopStream(group); return; } @@ -5713,6 +5956,7 @@ public: "reconfigure to {}", ToString(group->GetConfigurationContextType()), ToString(configuration_context_type_)); + speed_stop_setup(); initReconfiguration(group, group->GetConfigurationContextType()); return; } @@ -5733,11 +5977,12 @@ public: if (audio_receiver_state_ == AudioState::READY_TO_START) { StartReceivingAudio(group_id); } + + speed_stop_setup(); break; } case GroupStreamStatus::SUSPENDED: - stream_setup_end_timestamp_ = 0; - stream_setup_start_timestamp_ = 0; + speed_tracker_.Reset(); /** Stop Audio but don't release all the Audio resources */ SuspendAudio(); break; @@ -5811,8 +6056,7 @@ public: } } - stream_setup_end_timestamp_ = 0; - stream_setup_start_timestamp_ = 0; + speed_tracker_.Reset(); CancelStreamingRequest(); if (group) { @@ -5879,9 +6123,8 @@ private: "persist.bluetooth.leaudio.allow.multiple.contexts"; BidirectionalPair<AudioContexts> in_call_metadata_context_types_; BidirectionalPair<AudioContexts> local_metadata_context_types_; - uint64_t stream_setup_start_timestamp_; - uint64_t stream_setup_end_timestamp_; - std::deque<uint64_t> stream_start_history_queue_; + StreamSpeedTracker speed_tracker_; + std::deque<StreamSpeedTracker> stream_speed_history_; /* Microphone (s) */ AudioState audio_receiver_state_; diff --git a/system/bta/le_audio/device_groups.cc b/system/bta/le_audio/device_groups.cc index 192f3e9058..c3fd37b36d 100644 --- a/system/bta/le_audio/device_groups.cc +++ b/system/bta/le_audio/device_groups.cc @@ -1138,6 +1138,10 @@ bool LeAudioDeviceGroup::IsReleasingOrIdle(void) const { !in_transition_); } +bool LeAudioDeviceGroup::IsReleasing(void) const { + return (target_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) && in_transition_; +} + bool LeAudioDeviceGroup::IsGroupStreamReady(void) const { bool is_device_ready = false; @@ -2259,7 +2263,7 @@ void LeAudioDeviceGroup::Dump(std::stringstream& stream, int active_group_id) co stream << "\n\t cis id: " << static_cast<int>(cis.id) << ",\ttype: " << static_cast<int>(cis.type) << ",\tconn_handle: " << static_cast<int>(cis.conn_handle) - << ",\taddr: " << ADDRESS_TO_LOGGABLE_STR(cis.addr); + << ",\taddr: " << cis.addr.ToRedactedStringForLogging(); } } stream << "\n"; diff --git a/system/bta/le_audio/device_groups.h b/system/bta/le_audio/device_groups.h index 40a87b0a71..f0fcb17125 100644 --- a/system/bta/le_audio/device_groups.h +++ b/system/bta/le_audio/device_groups.h @@ -112,6 +112,7 @@ public: notify_streaming_when_cises_are_ready_(false), audio_directions_(0), dsa_({DsaMode::DISABLED, false}), + asymmetric_phy_for_unidirectional_cis_supported(true), is_enabled_(true), transport_latency_mtos_us_(0), transport_latency_stom_us_(0), @@ -142,8 +143,6 @@ public: is_output_preference_le_audio = true; is_duplex_preference_le_audio = true; #endif - asymmetric_phy_for_unidirectional_cis_supported = - com::android::bluetooth::flags::asymmetric_phy_for_unidirectional_cis(); } ~LeAudioDeviceGroup(void); @@ -407,6 +406,7 @@ public: } bool IsStreaming(void) const; bool IsReleasingOrIdle(void) const; + bool IsReleasing(void) const; void PrintDebugState(void) const; void Dump(std::stringstream& stream, int active_group_id) const; diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc index e30ecbd379..bb38daa805 100644 --- a/system/bta/le_audio/devices.cc +++ b/system/bta/le_audio/devices.cc @@ -1033,7 +1033,7 @@ void LeAudioDevice::DumpPacsDebugState(std::stringstream& stream, } void LeAudioDevice::DumpPacsDebugState(std::stringstream& stream) { - stream << " ● Device PACS, address: " << ADDRESS_TO_LOGGABLE_STR(address_) << "\n"; + stream << " ● Device PACS, address: " << address_.ToRedactedStringForLogging() << "\n"; stream << "\t == Sink PACs:\n"; DumpPacsDebugState(stream, snk_pacs_); stream << "\t == Source PACs:\n"; @@ -1060,7 +1060,7 @@ void LeAudioDevice::Dump(std::stringstream& stream) { std::string snk_location = locationToString(snk_audio_locations_.to_ulong()); std::string src_location = locationToString(src_audio_locations_.to_ulong()); - stream << " ● Device address: " << ADDRESS_TO_LOGGABLE_STR(address_) << ", " + stream << " ● Device address: " << address_.ToRedactedStringForLogging() << ", " << connection_state_ << ", conn_id: " << (conn_id_ == GATT_INVALID_CONN_ID ? "-1" : std::to_string(conn_id_)) << ", acl_handle: " << std::to_string(acl_handle) << ", snk_location: " << snk_location @@ -1387,9 +1387,14 @@ void LeAudioDevices::Cleanup(tGATT_IF client_if) { continue; } - if (connection_state == DeviceConnectState::CONNECTING_AUTOCONNECT) { - BTA_GATTC_CancelOpen(client_if, device->address_, false); - } else { + // For connecting or connected device always remove background connect + BTA_GATTC_CancelOpen(client_if, device->address_, false); + + if (connection_state == DeviceConnectState::CONNECTING_BY_USER) { + // When connecting by user, remove direct connect + BTA_GATTC_CancelOpen(client_if, device->address_, true); + } else if (connection_state != DeviceConnectState::CONNECTING_AUTOCONNECT) { + // If connected, close the connection BtaGattQueue::Clean(device->conn_id_); BTA_GATTC_Close(device->conn_id_); device->DisconnectAcl(); diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index fe66085fa3..2891a9a8d4 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -492,6 +492,12 @@ protected: base::Unretained(this->gatt_callback), event_data)); } + void TriggerDisconnectionFromApp(const RawAddress& address) { + do_in_main_thread(base::BindOnce( + [](LeAudioClient* client, const RawAddress& address) { client->Disconnect(address); }, + LeAudioClient::Get(), address)); + } + void InjectDisconnectedEvent(uint16_t conn_id, tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST) { ASSERT_NE(conn_id, GATT_INVALID_CONN_ID); @@ -1434,9 +1440,15 @@ protected: // Inject the state group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); - group->SetState(group->GetTargetState()); + group->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING); state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::RELEASING); + if (stay_at_releasing_stop_stream) { + log::info("StopStream {} -> stay in Releasing state", group->group_id_); + return; + } + group->SetState(group->GetTargetState()); + do_in_main_thread(base::BindOnce( [](bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* cb, int group_id) { cb->StatusReportCb(group_id, GroupStreamStatus::IDLE); @@ -1477,6 +1489,7 @@ protected: SetUpMockCodecManager(codec_location); stay_at_qos_config_in_start_stream = false; + stay_at_releasing_stop_stream = false; available_snk_context_types_ = 0xffff; available_src_context_types_ = 0xffff; @@ -2699,6 +2712,7 @@ protected: bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks_; std::map<int, LeAudioDeviceGroup*> streaming_groups; bool stay_at_qos_config_in_start_stream = false; + bool stay_at_releasing_stop_stream = false; bool attach_to_stream_scheduled = false; @@ -2970,6 +2984,102 @@ TEST_F(UnicastTestNoInit, InitializeNoHal_2_1) { "disable LE Audio Profile, or update your HAL"); } +TEST_F(UnicastTest, CleanupWhenUserConnecting) { + const RawAddress test_address0 = GetTestAddress(0); + uint16_t conn_id = 1; + SetSampleDatabaseEarbudsValid(1, test_address0, codec_spec_conf::kLeAudioLocationStereo, + codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, + default_channel_cnt, 0x0004, + /* source sample freq 16khz */ true, /*add_csis*/ + true, /*add_cas*/ + true, /*add_pacs*/ + default_ase_cnt /*add_ascs*/); + + /* Remove default action on the direct connect */ + ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_DIRECT_CONNECTION, _)).WillByDefault(Return()); + ConnectLeAudio(test_address0, false, false); + + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false)).Times(1); + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, true)).Times(1); + EXPECT_CALL(mock_btm_interface_, AclDisconnectFromHandle(conn_id, _)).Times(0); + EXPECT_CALL(mock_gatt_interface_, Close(_)).Times(0); + + LeAudioClient::Cleanup(); + SyncOnMainLoop(); + + Mock::VerifyAndClearExpectations(&mock_gatt_interface_); +} + +TEST_F(UnicastTest, CleanupWhenAutoConnecting) { + const RawAddress test_address0 = GetTestAddress(0); + uint16_t conn_id = 1; + SetSampleDatabaseEarbudsValid(1, test_address0, codec_spec_conf::kLeAudioLocationStereo, + codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, + default_channel_cnt, 0x0004, + /* source sample freq 16khz */ true, /*add_csis*/ + true, /*add_cas*/ + true, /*add_pacs*/ + default_ase_cnt /*add_ascs*/); + + log::info("Connect device"); + ConnectLeAudio(test_address0); + + /* Remove default action on the autoconnect */ + ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _)) + .WillByDefault(Return()); + + EXPECT_CALL(mock_audio_hal_client_callbacks_, + OnConnectionState(ConnectionState::DISCONNECTED, test_address0)) + .Times(1); + /* Make sure when remote device disconnects us, TA is used */ + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, _)).Times(1); + EXPECT_CALL(mock_gatt_interface_, + Open(gatt_if, test_address0, BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _)) + .Times(1); + + InjectDisconnectedEvent(1, GATT_CONN_TERMINATE_PEER_USER); + SyncOnMainLoop(); + + Mock::VerifyAndClearExpectations(&mock_gatt_interface_); + + log::info("Device is in auto connect"); + + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false)).Times(1); + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, true)).Times(0); + EXPECT_CALL(mock_btm_interface_, AclDisconnectFromHandle(conn_id, _)).Times(0); + EXPECT_CALL(mock_gatt_interface_, Close(_)).Times(0); + + LeAudioClient::Cleanup(); + SyncOnMainLoop(); + + Mock::VerifyAndClearExpectations(&mock_gatt_interface_); +} + +TEST_F(UnicastTest, CleanupWhenConnected) { + const RawAddress test_address0 = GetTestAddress(0); + uint16_t conn_id = 1; + SetSampleDatabaseEarbudsValid(1, test_address0, codec_spec_conf::kLeAudioLocationStereo, + codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, + default_channel_cnt, 0x0004, + /* source sample freq 16khz */ true, /*add_csis*/ + true, /*add_cas*/ + true, /*add_pacs*/ + default_ase_cnt /*add_ascs*/); + + log::info("Connect device"); + ConnectLeAudio(test_address0); + + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false)).Times(1); + EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, true)).Times(0); + EXPECT_CALL(mock_btm_interface_, AclDisconnectFromHandle(conn_id, _)).Times(1); + EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1); + + LeAudioClient::Cleanup(); + SyncOnMainLoop(); + + Mock::VerifyAndClearExpectations(&mock_gatt_interface_); +} + TEST_F(UnicastTest, ConnectAndSetupPhy) { const RawAddress test_address0 = GetTestAddress(0); uint16_t conn_id = 1; @@ -5960,6 +6070,63 @@ TEST_F(UnicastTest, DisconnecteWhileAlmostStreaming) { ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); } +TEST_F(UnicastTest, DisconnecteWhileAlmostStreaming_twoDevices) { + const RawAddress test_address0 = GetTestAddress(0); + const RawAddress test_address1 = GetTestAddress(1); + int group_id = 5; + + TestSetupRemoteDevices(group_id); + SyncOnMainLoop(); + + EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(1); + + 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(); + + ASSERT_NE(0lu, streaming_groups.count(group_id)); + auto group_inject = streaming_groups.at(group_id); + + // This shall be called once only when first device from the group is disconnecting. + EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1); + + EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::DISCONNECTED, _)) + .Times(0); + + // Do not got to IDLE state imidiatelly. + stay_at_releasing_stop_stream = true; + + log::info("First of all disconnect: {}", test_address0); + TriggerDisconnectionFromApp(test_address0); + SyncOnMainLoop(); + + log::info("Secondly disconnect: {}", test_address1); + TriggerDisconnectionFromApp(test_address1); + SyncOnMainLoop(); + + Mock::VerifyAndClearExpectations(&mock_state_machine_); + Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); + + EXPECT_CALL(mock_audio_hal_client_callbacks_, + OnConnectionState(ConnectionState::DISCONNECTED, test_address0)) + .Times(1); + EXPECT_CALL(mock_audio_hal_client_callbacks_, + OnConnectionState(ConnectionState::DISCONNECTED, test_address1)) + .Times(1); + + do_in_main_thread(base::BindOnce( + [](bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* cb, int group_id) { + cb->StatusReportCb(group_id, GroupStreamStatus::IDLE); + }, + state_machine_callbacks_, group_id)); + SyncOnMainLoop(); + Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); +} + TEST_F(UnicastTest, EarbudsTwsStyleStreaming) { const RawAddress test_address0 = GetTestAddress(0); int group_id = bluetooth::groups::kGroupUnknown; @@ -9856,6 +10023,71 @@ TEST_F(UnicastTest, TwoEarbudsStreamingProfileDisconnectStreamStopTimeout) { ASSERT_NE(device->GetConnectionState(), DeviceConnectState::DISCONNECTING_AND_RECOVER); } +TEST_F(UnicastTest, TwoEarbudsStreamingProfileDisconnectForSingleEarbudStreamStopTimeout) { + uint8_t group_size = 2; + int group_id = 2; + + // Report working CSIS + ON_CALL(mock_csis_client_module_, IsCsisClientRunning()).WillByDefault(Return(true)); + + ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id)) + .WillByDefault(Invoke([&](int /*group_id*/) { return group_size; })); + + // First earbud + const RawAddress test_address0 = GetTestAddress(0); + ConnectCsisDevice(test_address0, 1 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontLeft, + codec_spec_conf::kLeAudioLocationFrontLeft, group_size, group_id, 1 /* rank*/); + + // Second earbud + const RawAddress test_address1 = GetTestAddress(1); + ConnectCsisDevice(test_address1, 2 /*conn_id*/, codec_spec_conf::kLeAudioLocationFrontRight, + codec_spec_conf::kLeAudioLocationFrontRight, group_size, group_id, 2 /* rank*/, + true /*connect_through_csis*/); + + // 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()->GroupSetActive(group_id); + SyncOnMainLoop(); + + /* Use StartStream to generate situation when target state is IDLE, one device is streaming and + * other one reached IDLE state. + */ + ON_CALL(mock_state_machine_, StartStream(_, _, _, _)) + .WillByDefault([this](LeAudioDeviceGroup* group, + types::LeAudioContextType /* context_type */, + types::BidirectionalPair< + types::AudioContexts> /* metadata_context_types */, + types::BidirectionalPair<std::vector<uint8_t>> /* ccid_lists */) { + group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE); + auto group_state = group->GetState(); + LeAudioDevice* device = group->GetFirstDevice(); + for (auto& ase : device->ases_) { + ase.state = types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING; + ase.active = true; + } + device = group->GetNextDevice(device); + for (auto& ase : device->ases_) { + ase.state = types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE; + ase.active = false; + } + + state_machine_callbacks_->OnStateTransitionTimeout(group->group_id_); + + return true; + }); + + // Do not accept direct connect, but expect it to arrive. + ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_DIRECT_CONNECTION, _)).WillByDefault(Return()); + + EXPECT_CALL(mock_btm_interface_, AclDisconnectFromHandle(_, _)).Times(1); + EXPECT_CALL(mock_audio_hal_client_callbacks_, OnGroupStatus(group_id, GroupStatus::INACTIVE)) + .Times(0); + StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id, AUDIO_SOURCE_INVALID, false, + false); + SyncOnMainLoop(); +} + TEST_F(UnicastTest, EarbudsWithStereoSinkMonoSourceSupporting32kHz) { const RawAddress test_address0 = GetTestAddress(0); int group_id = 0; @@ -12777,4 +13009,65 @@ TEST_F(UnicastTest, CodecFrameBlocks2) { ASSERT_EQ(codec_manager_stream_params.sink.codec_frames_blocks_per_sdu, max_codec_frames_per_sdu); } +TEST_F(UnicastTestHandoverMode, UpdateMetadataToNotAllowedContexts) { + com::android::bluetooth::flags::provider_->leaudio_stop_updated_to_not_available_context_stream( + true); + const RawAddress test_address0 = GetTestAddress(0); + int group_id = bluetooth::groups::kGroupUnknown; + + available_snk_context_types_ = + (types::LeAudioContextType::RINGTONE | types::LeAudioContextType::CONVERSATIONAL | + types::LeAudioContextType::UNSPECIFIED | types::LeAudioContextType::MEDIA | + types::LeAudioContextType::SOUNDEFFECTS) + .value(); + available_src_context_types_ = available_snk_context_types_; + supported_snk_context_types_ = types::kLeAudioContextAllTypes.value(); + supported_src_context_types_ = + (types::kLeAudioContextAllRemoteSource | types::LeAudioContextType::UNSPECIFIED).value(); + /* Don't allow SOUNDEFFECTS context type to be streamed */ + int allowed_context_types = + (types::LeAudioContextType::RINGTONE | types::LeAudioContextType::CONVERSATIONAL | + types::LeAudioContextType::UNSPECIFIED | types::LeAudioContextType::MEDIA) + .value(); + + SetSampleDatabaseEarbudsValid(1, test_address0, codec_spec_conf::kLeAudioLocationStereo, + codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, + default_channel_cnt, 0x0004, 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); + + EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _, _)).Times(1); + types::BidirectionalPair<types::AudioContexts> metadata = {.sink = types::AudioContexts(), + .source = types::AudioContexts()}; + EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::MEDIA, _, _)).Times(1); + + LeAudioClient::Get()->GroupSetActive(group_id); + SyncOnMainLoop(); + + /* Set the same allowed context mask for sink and source */ + LeAudioClient::Get()->SetGroupAllowedContextMask(group_id, allowed_context_types, + allowed_context_types); + + StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_UNKNOWN, group_id, AUDIO_SOURCE_INVALID, + false, false); + + SyncOnMainLoop(); + Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_); + Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_); + + /* Expect stream to be stopped when not allowed context would be updated in metadata */ + EXPECT_CALL(mock_state_machine_, StopStream(_)); + + UpdateLocalSourceMetadata(AUDIO_USAGE_ASSISTANCE_SONIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN, true); +} + } // namespace bluetooth::le_audio diff --git a/system/bta/le_audio/le_audio_health_status.cc b/system/bta/le_audio/le_audio_health_status.cc index 6b811e1174..b0729a6f9c 100644 --- a/system/bta/le_audio/le_audio_health_status.cc +++ b/system/bta/le_audio/le_audio_health_status.cc @@ -221,7 +221,8 @@ private: void dumpsys_dev(int fd, const device_stats& dev) { std::stringstream stream; - stream << "\n " << ADDRESS_TO_LOGGABLE_STR(dev.address_) << ": " << dev.latest_recommendation_ + stream << "\n " << dev.address_.ToRedactedStringForLogging() << ": " + << dev.latest_recommendation_ << (dev.is_valid_service_ ? " service: OK" : " service : NOK") << (dev.is_valid_group_member_ ? " csis: OK" : " csis : NOK"); diff --git a/system/bta/le_audio/le_audio_log_history.cc b/system/bta/le_audio/le_audio_log_history.cc index a53e4944ea..d6848f3f79 100644 --- a/system/bta/le_audio/le_audio_log_history.cc +++ b/system/bta/le_audio/le_audio_log_history.cc @@ -113,7 +113,7 @@ private: history_->Push("%-*s GID %-3d %-*s: %-22s %s", kMaxLogHistoryTagLength, tag.substr(0, kMaxLogHistoryTagLength).c_str(), group_id, kMaxLogHistoryMsgLength, msg.substr(0, kMaxLogHistoryMsgLength).c_str(), - ADDRESS_TO_LOGGABLE_CSTR(addr), extra.c_str()); + addr.ToRedactedStringForLogging().c_str(), extra.c_str()); } std::shared_ptr<TimestampedStringCircularBuffer> history_{nullptr}; diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index cc59c4dad7..8a82e4a5e1 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -923,13 +923,21 @@ public: } uint8_t data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci; + bluetooth::le_audio::types::LeAudioCodecId codec = { + .coding_format = bluetooth::hci::kIsoCodingFormatTransparent, + .vendor_company_id = 0x0000, + .vendor_codec_id = 0x0000}; log::info("DSA mode used: {}", static_cast<int>(group->dsa_.mode)); switch (group->dsa_.mode) { case DsaMode::ISO_HW: data_path_id = bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault; + if (!com::android::bluetooth::flags::dsa_hw_transparent_codec()) { + codec = bluetooth::le_audio::types::kLeAudioCodecHeadtracking; + } break; case DsaMode::ISO_SW: data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci; + codec = bluetooth::le_audio::types::kLeAudioCodecHeadtracking; break; default: log::warn("Unexpected DsaMode: {}", static_cast<int>(group->dsa_.mode)); @@ -951,11 +959,9 @@ public: bluetooth::hci::iso_manager::iso_data_path_params param = { .data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut, .data_path_id = data_path_id, - .codec_id_format = bluetooth::le_audio::types::kLeAudioCodecHeadtracking.coding_format, - .codec_id_company = - bluetooth::le_audio::types::kLeAudioCodecHeadtracking.vendor_company_id, - .codec_id_vendor = - bluetooth::le_audio::types::kLeAudioCodecHeadtracking.vendor_codec_id, + .codec_id_format = codec.coding_format, + .codec_id_company = codec.vendor_company_id, + .codec_id_vendor = codec.vendor_codec_id, .controller_delay = 0x00000000, .codec_conf = std::vector<uint8_t>(), }; @@ -1070,8 +1076,7 @@ public: log::assert_that(ase != nullptr, "shouldn't be called without an active ASE, device {}, " "group id: {}, cis handle 0x{:04x}", - ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), event->cig_id, - event->cis_conn_hdl); + leAudioDevice->address_, event->cig_id, event->cis_conn_hdl); PrepareAndSendReceiverStartReady(leAudioDevice, ase); } @@ -1080,8 +1085,8 @@ public: tGATT_WRITE_TYPE write_type = GATT_WRITE_NO_RSP; if (value.size() > (leAudioDevice->mtu_ - 3)) { - log::warn("{}, using long write procedure ({} > {})", leAudioDevice->address_, - static_cast<int>(value.size()), leAudioDevice->mtu_ - 3); + log::warn("{}, using long write procedure ({} > {})", leAudioDevice->address_, value.size(), + leAudioDevice->mtu_ - 3); /* Note, that this type is actually LONG WRITE. * Meaning all the Prepare Writes plus Execute is handled in the stack @@ -2151,7 +2156,8 @@ private: /* Last node configured, process group to codec configured state */ group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED); - if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) { + if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING || + group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) { if (group->cig.GetState() == CigState::CREATED) { /* It can happen on the earbuds switch scenario. When one device * is getting remove while other is adding to the stream and CIG is diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc index 7593bdeaf5..7fc2985b28 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -7370,6 +7370,68 @@ TEST_F(StateMachineTest, StartStreamAfterConfigureToQoS) { testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); } +TEST_F(StateMachineTest, StartStreamAfterConfigureToQoS_ConfigurationCaching) { + const auto context_type = kContextTypeMedia; + const auto leaudio_group_id = 6; + const auto num_devices = 2; + + ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid); + + // Prepare multiple fake connected devices in a group + auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices); + ASSERT_EQ(group->Size(), num_devices); + + PrepareConfigureCodecHandler(group, 0, true); + PrepareConfigureQosHandler(group); + PrepareEnableHandler(group); + PrepareDisableHandler(group); + PrepareReleaseHandler(group); + + InjectInitialConfiguredNotification(group); + + auto* leAudioDevice = group->GetFirstDevice(); + auto expected_devices_written = 0; + while (leAudioDevice) { + /* Three Writes: + * 1. Codec configure + * 2: Codec QoS + * 3: Enabling + */ + EXPECT_CALL(gatt_queue, + WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _, + GATT_WRITE_NO_RSP, _, _)) + .Times(3); + expected_devices_written++; + leAudioDevice = group->GetNextDevice(leAudioDevice); + } + ASSERT_EQ(expected_devices_written, num_devices); + + // Validate GroupStreamStatus + EXPECT_CALL(mock_callbacks_, + StatusReportCb(leaudio_group_id, + bluetooth::le_audio::GroupStreamStatus::CONFIGURED_BY_USER)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->ConfigureStream(group, context_type, + {.sink = types::AudioContexts(context_type), + .source = types::AudioContexts(context_type)}, + {.sink = {}, .source = {}}, true); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); + ASSERT_EQ(1, get_func_call_count("alarm_cancel")); + + // Validate GroupStreamStatus + EXPECT_CALL(mock_callbacks_, + StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING)); + + // Start the configuration and stream Media content + LeAudioGroupStateMachine::Get()->StartStream(group, context_type, + {.sink = types::AudioContexts(context_type), + .source = types::AudioContexts(context_type)}); + + testing::Mock::VerifyAndClearExpectations(&mock_callbacks_); +} + TEST_F(StateMachineTest, StopStreamAfterConfigureToQoS) { const auto context_type = kContextTypeMedia; const auto leaudio_group_id = 6; diff --git a/system/bta/ras/ras_client.cc b/system/bta/ras/ras_client.cc index 172645f00e..6dcf00c377 100644 --- a/system/bta/ras/ras_client.cc +++ b/system/bta/ras/ras_client.cc @@ -40,6 +40,7 @@ #include "stack/include/bt_types.h" #include "stack/include/btm_ble_addr.h" #include "stack/include/gap_api.h" +#include "stack/include/l2cap_interface.h" #include "stack/include/main_thread.h" #include "types/ble_address_with_type.h" #include "types/bluetooth/uuid.h" @@ -68,6 +69,7 @@ class RasClientImpl : public bluetooth::ras::RasClient { static constexpr uint16_t kFollowingSegmentTimeoutMs = 1000; static constexpr uint16_t kRangingDataReadyTimeoutMs = 5000; static constexpr uint16_t kInvalidConnInterval = 0; // valid value is from 0x0006 to 0x0C0 + static constexpr uint16_t kMinimumRasMtu = 247; // 4.1 Maximum transmission unit of RAP 1.0 public: struct GattReadCallbackData { @@ -108,8 +110,13 @@ public: RangingType ranging_type_ = RANGING_TYPE_NONE; TimeoutType timeout_type_ = TIMEOUT_NONE; uint16_t conn_interval_ = kInvalidConnInterval; + uint16_t mtu = kDefaultGattMtu; const gatt::Characteristic* FindCharacteristicByUuid(Uuid uuid) { + if (service_ == nullptr) { + log::error("Can't find Ranging Service"); + return nullptr; + } for (auto& characteristic : service_->characteristics) { if (characteristic.uuid == uuid) { return &characteristic; @@ -117,6 +124,7 @@ public: } return nullptr; } + const gatt::Characteristic* FindCharacteristicByHandle(uint16_t handle) { for (auto& characteristic : service_->characteristics) { if (characteristic.value_handle == handle) { @@ -230,6 +238,9 @@ public: case BTA_GATTC_SEARCH_CMPL_EVT: { OnGattServiceSearchComplete(p_data->search_cmpl); } break; + case BTA_GATTC_CFG_MTU_EVT: { + OnGattConfigMtu(p_data->cfg_mtu); + } break; case BTA_GATTC_NOTIF_EVT: { OnGattNotification(p_data->notify); } break; @@ -247,9 +258,13 @@ public: log::debug("no ongoing measurement, skip"); return; } - tracker->conn_interval_ = evt.interval; - log::info("conn interval is updated as {}", evt.interval); - callbacks_->OnConnIntervalUpdated(tracker->address_for_cs_, tracker->conn_interval_); + if (tracker->conn_interval_ != evt.interval) { + tracker->conn_interval_ = evt.interval; + log::info("conn interval is updated as {}", evt.interval); + callbacks_->OnConnIntervalUpdated(tracker->address_for_cs_, tracker->conn_interval_); + } else { + log::debug("conn interval was not updated"); + } } void OnGattConnected(const tBTA_GATTC_OPEN& evt) { @@ -275,6 +290,9 @@ public: } tracker->conn_id_ = evt.conn_id; tracker->is_connected_ = true; + tracker->conn_interval_ = + bluetooth::stack::l2cap::get_interface().L2CA_GetBleConnInterval(tracker->address_); + log::debug("The initial conn interval {}", tracker->conn_interval_); log::info("Search service"); BTA_GATTC_ServiceSearchRequest(tracker->conn_id_, kRangingService); } @@ -310,6 +328,9 @@ public: break; } } + // config mtu anyway, if it had been configured by others, it can get the current mtu. + log::info("config the MTU size as RAP minimum value {}", kMinimumRasMtu); + BTA_GATTC_ConfigureMTU(evt.conn_id, kMinimumRasMtu); if (tracker->service_search_complete_) { log::info("Service search already completed, ignore"); @@ -362,6 +383,20 @@ public: } } + void OnGattConfigMtu(const tBTA_GATTC_CFG_MTU& evt) { + if (evt.status != GATT_SUCCESS) { + log::warn("Failed to config the MTU size:{}", evt.mtu); + return; + } + // the MTU is always 517 since android 14 + log::info("conn_id=0x{:04x}, status:{}, mtu:{}", evt.conn_id, evt.status, evt.mtu); + auto tracker = FindTrackerByHandle(evt.conn_id); + if (tracker != nullptr) { + tracker->mtu = evt.mtu; + callbacks_->OnMtuChangedFromClient(tracker->address_for_cs_, evt.mtu); + } + } + bool UseCachedData(std::shared_ptr<RasTracker> tracker) { auto cached_data = cached_data_.find(tracker->address_); if (cached_data == cached_data_.end()) { diff --git a/system/bta/ras/ras_server.cc b/system/bta/ras/ras_server.cc index 006c1bce5d..9daf6e646e 100644 --- a/system/bta/ras/ras_server.cc +++ b/system/bta/ras/ras_server.cc @@ -85,6 +85,7 @@ public: PendingWriteResponse pending_write_response_; uint16_t last_ready_procedure_ = 0; uint16_t last_overwritten_procedure_ = 0; + uint16_t mtu = kDefaultGattMtu; }; void Initialize() override { @@ -210,6 +211,9 @@ public: case BTA_GATTS_DISCONNECT_EVT: { OnGattDisconnect(p_data); } break; + case BTA_GATTS_MTU_EVT: { + OnGattMtuChanged(p_data->req_data); + } break; case BTA_GATTS_REG_EVT: { OnGattServerRegister(p_data); } break; @@ -250,6 +254,19 @@ public: callbacks_->OnRasServerConnected(identity_address); } + void OnGattMtuChanged(const tBTA_GATTS_REQ& req_data) { + auto remote_bda = req_data.remote_bda; + log::info("mtu is changed as {}", req_data.p_data->mtu); + auto it = trackers_.find(remote_bda); + if (it != trackers_.end()) { + it->second.mtu = req_data.p_data->mtu; + + tBLE_ADDR_TYPE address_type = BLE_ADDR_PUBLIC_ID; + btm_random_pseudo_to_identity_addr(&remote_bda, &address_type); + callbacks_->OnMtuChangedFromServer(remote_bda, it->second.mtu); + } + } + void OnGattDisconnect(tBTA_GATTS* p_data) { auto remote_bda = p_data->conn.remote_bda; log::info("Address: {}, conn_id:{}", remote_bda, p_data->conn.conn_id); @@ -322,7 +339,7 @@ public: btgatt_db_element_t ras_control_point; ras_control_point.uuid = kRasControlPointCharacteristic; ras_control_point.type = BTGATT_DB_CHARACTERISTIC; - ras_control_point.properties = GATT_CHAR_PROP_BIT_WRITE | GATT_CHAR_PROP_BIT_INDICATE; + ras_control_point.properties = GATT_CHAR_PROP_BIT_WRITE_NR | GATT_CHAR_PROP_BIT_INDICATE; ras_control_point.permissions = GATT_PERM_WRITE_ENCRYPTED | key_mask; service.push_back(ras_control_point); service.push_back(ccc_descriptor); diff --git a/system/bta/ras/ras_types.h b/system/bta/ras/ras_types.h index 30e935a9fb..2e860bc2fc 100644 --- a/system/bta/ras/ras_types.h +++ b/system/bta/ras/ras_types.h @@ -32,6 +32,7 @@ static const uint16_t kRasControlPointCharacteristic16bit = 0x2C17; static const uint16_t kRasRangingDataReadyCharacteristic16bit = 0x2C18; static const uint16_t kRasRangingDataOverWrittenCharacteristic16bit = 0x2C19; static const uint16_t kClientCharacteristicConfiguration16bit = 0x2902; +static const uint16_t kDefaultGattMtu = 23; static const bluetooth::Uuid kRangingService = bluetooth::Uuid::From16Bit(kRangingService16Bit); static const bluetooth::Uuid kRasFeaturesCharacteristic = diff --git a/system/bta/sys/bta_sys.h b/system/bta/sys/bta_sys.h index e1a9e5e803..d02e1ff0bd 100644 --- a/system/bta/sys/bta_sys.h +++ b/system/bta/sys/bta_sys.h @@ -24,7 +24,6 @@ #ifndef BTA_SYS_H #define BTA_SYS_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <chrono> @@ -140,7 +139,7 @@ inline std::string BtaIdSysText(const tBTA_SYS_ID& sys_id) { CASE_RETURN_TEXT(BTA_ID_SDP); CASE_RETURN_TEXT(BTA_ID_BLUETOOTH_MAX); default: - return base::StringPrintf("Unknown[%hhu]", sys_id); + return std::format("Unknown[{}]", static_cast<uint8_t>(sys_id)); } } diff --git a/system/bta/test/common/bta_gatt_queue_mock.cc b/system/bta/test/common/bta_gatt_queue_mock.cc index 9c0d048a71..d4b3b9b0a2 100644 --- a/system/bta/test/common/bta_gatt_queue_mock.cc +++ b/system/bta/test/common/bta_gatt_queue_mock.cc @@ -49,8 +49,9 @@ void BtaGattQueue::ConfigureMtu(uint16_t conn_id, uint16_t mtu) { gatt_queue->ConfigureMtu(conn_id, mtu); } -void BtaGattQueue::ReadMultiCharacteristic(uint16_t conn_id, tBTA_GATTC_MULTI& p_read_multi, +bool BtaGattQueue::ReadMultiCharacteristic(uint16_t conn_id, tBTA_GATTC_MULTI& p_read_multi, GATT_READ_MULTI_OP_CB cb, void* cb_data) { bluetooth::log::assert_that(gatt_queue, "Mock GATT queue not set!"); gatt_queue->ReadMultiCharacteristic(conn_id, p_read_multi, cb, cb_data); + return true; } diff --git a/system/bta/vc/device.cc b/system/bta/vc/device.cc index d1d9611a23..a0c41029df 100644 --- a/system/bta/vc/device.cc +++ b/system/bta/vc/device.cc @@ -36,6 +36,7 @@ #include "gatt/database.h" #include "gattdefs.h" #include "stack/btm/btm_sec.h" +#include "stack/gatt/gatt_int.h" #include "stack/include/bt_types.h" #include "stack/include/gatt_api.h" #include "types/bluetooth/uuid.h" @@ -421,48 +422,67 @@ void VolumeControlDevice::EnqueueRemainingRequests(tGATT_IF /*gatt_if*/, GATT_READ_OP_CB chrc_read_cb, GATT_READ_MULTI_OP_CB chrc_multi_read_cb, GATT_WRITE_OP_CB /*cccd_write_cb*/) { - std::vector<uint16_t> handles_to_read; + const auto is_eatt_supported = gatt_profile_get_eatt_support_by_conn_id(connection_id); - for (auto const& input : audio_inputs.volume_audio_inputs) { - handles_to_read.push_back(input.state_handle); - handles_to_read.push_back(input.gain_setting_handle); - handles_to_read.push_back(input.type_handle); - handles_to_read.push_back(input.status_handle); - handles_to_read.push_back(input.description_handle); - } + /* List of handles to the attributes having known and fixed-size values to read using the + * ATT_READ_MULTIPLE_REQ. The `.second` component contains 1 octet for the length + the actual + * attribute value length, exactly as in the received HCI packet for ATT_READ_MULTIPLE_RSP. + * We use this to make sure the request response will fit the current MTU size. + */ + std::list<std::pair<uint16_t, size_t>> handles_to_read; + + /* Variable-length attributes - always read using the regular read requests to automatically + * handle truncation in the GATT layer if MTU is to small to fit even a single complete value. + */ + std::vector<uint16_t> handles_to_read_variable_length; for (auto const& offset : audio_offsets.volume_offsets) { - handles_to_read.push_back(offset.state_handle); - handles_to_read.push_back(offset.audio_location_handle); - handles_to_read.push_back(offset.audio_descr_handle); + handles_to_read.push_back(std::make_pair(offset.state_handle, 4)); + handles_to_read.push_back(std::make_pair(offset.audio_location_handle, 5)); + handles_to_read_variable_length.push_back(offset.audio_descr_handle); } - log::debug("{}, number of handles={}", address, handles_to_read.size()); + for (auto const& input : audio_inputs.volume_audio_inputs) { + handles_to_read.push_back(std::make_pair(input.state_handle, 5)); + handles_to_read.push_back(std::make_pair(input.gain_setting_handle, 4)); + handles_to_read.push_back(std::make_pair(input.type_handle, 2)); + handles_to_read.push_back(std::make_pair(input.status_handle, 2)); + handles_to_read_variable_length.push_back(input.description_handle); + } + + log::debug("{}, number of fixed-size attribute handles={}", address, handles_to_read.size()); + log::debug("{}, number of variable-size attribute handles={}", address, + handles_to_read_variable_length.size()); + + if (com::android::bluetooth::flags::le_ase_read_multiple_variable() && is_eatt_supported) { + const size_t payload_limit = this->mtu_ - 1; + + auto pair_it = handles_to_read.begin(); + while (pair_it != handles_to_read.end()) { + tBTA_GATTC_MULTI multi_read{.num_attr = 0}; + size_t size_limit = 0; + + // Send at once just enough attributes to stay below the MTU size limit for the response + while ((pair_it != handles_to_read.end()) && (size_limit + pair_it->second < payload_limit) && + (multi_read.num_attr < GATT_MAX_READ_MULTI_HANDLES)) { + multi_read.handles[multi_read.num_attr] = pair_it->first; + size_limit += pair_it->second; + ++multi_read.num_attr; + ++pair_it; + } - if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) { - for (auto const& handle : handles_to_read) { + log::debug{"{}, calling multi-read with {} attributes, {} left", address, multi_read.num_attr, + std::distance(pair_it, handles_to_read.end())}; + BtaGattQueue::ReadMultiCharacteristic(connection_id, multi_read, chrc_multi_read_cb, nullptr); + } + } else { + for (auto const& [handle, _] : handles_to_read) { BtaGattQueue::ReadCharacteristic(connection_id, handle, chrc_read_cb, nullptr); } - return; } - size_t sent_cnt = 0; - - while (sent_cnt < handles_to_read.size()) { - tBTA_GATTC_MULTI multi_read{}; - size_t remain_cnt = (handles_to_read.size() - sent_cnt); - - multi_read.num_attr = - remain_cnt > GATT_MAX_READ_MULTI_HANDLES ? GATT_MAX_READ_MULTI_HANDLES : remain_cnt; - - auto handles_begin = handles_to_read.begin() + sent_cnt; - std::copy(handles_begin, handles_begin + multi_read.num_attr, multi_read.handles); - - sent_cnt += multi_read.num_attr; - log::debug{"{}, calling multi with {} attributes, sent_cnt {} ", address, multi_read.num_attr, - sent_cnt}; - - BtaGattQueue::ReadMultiCharacteristic(connection_id, multi_read, chrc_multi_read_cb, nullptr); + for (auto const& handle : handles_to_read_variable_length) { + BtaGattQueue::ReadCharacteristic(connection_id, handle, chrc_read_cb, nullptr); } } diff --git a/system/bta/vc/devices.h b/system/bta/vc/devices.h index 19f2bd5650..4c0a105ee8 100644 --- a/system/bta/vc/devices.h +++ b/system/bta/vc/devices.h @@ -25,7 +25,6 @@ #include "bta/include/bta_gatt_api.h" #include "bta/vc/types.h" -#include "common/interfaces/ILoggable.h" #include "os/logging/log_adapter.h" #include "types/raw_address.h" @@ -33,7 +32,7 @@ namespace bluetooth { namespace vc { namespace internal { -class VolumeControlDevice : public bluetooth::common::IRedactableLoggable { +class VolumeControlDevice { public: RawAddress address; @@ -48,6 +47,7 @@ public: uint8_t flags; tCONN_ID connection_id; + uint16_t mtu_ = GATT_DEF_BLE_MTU_SIZE; /* Volume Control Service */ uint16_t volume_state_handle; @@ -81,15 +81,13 @@ public: ~VolumeControlDevice() = default; - std::string ToStringForLogging() const override { return address.ToStringForLogging(); } + std::string ToStringForLogging() const { return address.ToStringForLogging(); } - std::string ToRedactedStringForLogging() const override { - return address.ToRedactedStringForLogging(); - } + std::string ToRedactedStringForLogging() const { return address.ToRedactedStringForLogging(); } void DebugDump(int fd) { std::stringstream stream; - stream << " == device address: " << ADDRESS_TO_LOGGABLE_STR(address) << " == \n"; + stream << " == device address: " << address.ToRedactedStringForLogging() << " == \n"; if (connection_id == GATT_INVALID_CONN_ID) { stream << " Not connected\n"; diff --git a/system/bta/vc/devices_test.cc b/system/bta/vc/devices_test.cc index fea281c701..15dd527e2b 100644 --- a/system/bta/vc/devices_test.cc +++ b/system/bta/vc/devices_test.cc @@ -692,25 +692,39 @@ TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests_multiread) { tGATT_IF gatt_if = 0x0001; std::vector<uint8_t> register_for_notification_data({0x01, 0x00}); + // The amount of attributes read at once is limited by the MTU size - 1 (here 22) tBTA_GATTC_MULTI expected_to_read_part_1 = { - .num_attr = 10, + .num_attr = 4, .handles = {0x0022 /* audio input state 1 */, 0x0025 /* gain setting properties 1 */, - 0x0027 /* audio input type 1 */, 0x0029 /* audio input status 1 */, - 0x002e /* audio input description 1 */, 0x0042 /* audio input state 2 */, - 0x0045 /* gain setting properties 2 */, 0x0047 /* audio input type 2 */, - 0x0049 /* audio input status 2 */, 0x004e /* audio input description 2 */}, + 0x0027 /* audio input type 1 */, 0x0029 /* audio input status 1 */}, }; tBTA_GATTC_MULTI expected_to_read_part_2 = { - .num_attr = 6, - .handles = {0x0062 /* audio output state 1 */, 0x0065 /* audio output location 1 */, - 0x0069 /* audio output description 1 */, 0x0082 /* audio output state 1 */, - 0x0085 /* audio output location 1 */, - 0x008a /* audio output description 1 */}, + .num_attr = 5, + .handles = {0x0042 /* audio input state 2 */, 0x0045 /* gain setting properties 2 */, + 0x0047 /* audio input type 2 */, 0x0049 /* audio input status 2 */, + 0x0062 /* audio output state 1 */}, }; + tBTA_GATTC_MULTI expected_to_read_part_3 = { + .num_attr = 3, + .handles = {0x0065 /* audio output location 1 */, 0x0082 /* audio output state 1 */, + 0x0085 /* audio output location 1 */}, + }; + + uint16_t expected_audio_input_description_1 = 0x002e; + uint16_t expected_audio_input_description_2 = 0x004e; + uint16_t expected_audio_output_description_1 = 0x0069; + uint16_t expected_audio_output_description_2 = 0x008a; + tBTA_GATTC_MULTI received_to_read_part_1{}; tBTA_GATTC_MULTI received_to_read_part_2{}; + tBTA_GATTC_MULTI received_to_read_part_3{}; + + uint16_t audio_input_description_1 = 0; + uint16_t audio_input_description_2 = 0; + uint16_t audio_output_description_1 = 0; + uint16_t audio_output_description_2 = 0; { testing::InSequence s; @@ -719,6 +733,16 @@ TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests_multiread) { .WillOnce(SaveArg<1>(&received_to_read_part_1)); EXPECT_CALL(gatt_queue, ReadMultiCharacteristic(_, _, _, _)) .WillOnce(SaveArg<1>(&received_to_read_part_2)); + EXPECT_CALL(gatt_queue, ReadMultiCharacteristic(_, _, _, _)) + .WillOnce(SaveArg<1>(&received_to_read_part_3)); + EXPECT_CALL(gatt_queue, ReadCharacteristic(_, _, _, _)) + .WillOnce(SaveArg<1>(&audio_output_description_1)); + EXPECT_CALL(gatt_queue, ReadCharacteristic(_, _, _, _)) + .WillOnce(SaveArg<1>(&audio_output_description_2)); + EXPECT_CALL(gatt_queue, ReadCharacteristic(_, _, _, _)) + .WillOnce(SaveArg<1>(&audio_input_description_1)); + EXPECT_CALL(gatt_queue, ReadCharacteristic(_, _, _, _)) + .WillOnce(SaveArg<1>(&audio_input_description_2)); } EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, GATT_WRITE, _, _)).Times(0); EXPECT_CALL(gatt_interface, RegisterForNotifications(_, _, _)).Times(0); @@ -738,6 +762,12 @@ TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests_multiread) { ASSERT_EQ(expected_to_read_part_1.num_attr, received_to_read_part_1.num_attr); ASSERT_EQ(expected_to_read_part_2.num_attr, received_to_read_part_2.num_attr); + ASSERT_EQ(expected_to_read_part_3.num_attr, received_to_read_part_3.num_attr); + + EXPECT_EQ(expected_audio_input_description_1, audio_input_description_1); + EXPECT_EQ(expected_audio_input_description_2, audio_input_description_2); + EXPECT_EQ(expected_audio_output_description_1, audio_output_description_1); + EXPECT_EQ(expected_audio_output_description_2, audio_output_description_2); } TEST_F(VolumeControlDeviceTest, test_check_link_encrypted) { diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc index cf9fee3387..c381f1210a 100644 --- a/system/bta/vc/vc.cc +++ b/system/bta/vc/vc.cc @@ -176,7 +176,7 @@ public: } void OnGattConnected(tGATT_STATUS status, tCONN_ID connection_id, tGATT_IF /*client_if*/, - RawAddress address, tBT_TRANSPORT transport, uint16_t /*mtu*/) { + RawAddress address, tBT_TRANSPORT transport, uint16_t mtu) { bluetooth::log::info("{}, conn_id=0x{:04x}, transport={}, status={}(0x{:02x})", address, connection_id, bt_transport_text(transport), gatt_status_text(status), status); @@ -202,6 +202,7 @@ public: } device->connection_id = connection_id; + device->mtu_ = mtu; /* Make sure to remove device from background connect. * It will be added back if needed, when device got disconnected @@ -275,6 +276,15 @@ public: ClearDeviceInformationAndStartSearch(device); } + void OnMtuChanged(tCONN_ID conn_id, uint16_t mtu) { + VolumeControlDevice* device = volume_control_devices_.FindByConnId(conn_id); + if (!device) { + bluetooth::log::error("Skipping unknown device conn_id: {}", conn_id); + return; + } + device->mtu_ = mtu; + } + void OnServiceDiscDoneEvent(const RawAddress& address) { VolumeControlDevice* device = volume_control_devices_.FindByAddress(address); if (!device) { @@ -1461,6 +1471,7 @@ private: device->Disconnect(gatt_if_); RemoveDeviceFromOperationList(device->address); + device->mtu_ = GATT_DEF_BLE_MTU_SIZE; if (notify) { callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, device->address); @@ -1545,6 +1556,10 @@ private: OnServiceChangeEvent(p_data->service_changed.remote_bda); break; + case BTA_GATTC_CFG_MTU_EVT: + OnMtuChanged(p_data->cfg_mtu.conn_id, p_data->cfg_mtu.mtu); + break; + case BTA_GATTC_SRVC_DISC_DONE_EVT: OnServiceDiscDoneEvent(p_data->service_discovery_done.remote_bda); break; @@ -1599,14 +1614,13 @@ private: instance->OnCharacteristicValueChanged(conn_id, status, hdl, len, ptr, ((index == (handles.num_attr - 1)) ? data : nullptr), false); - position += len + 2; /* skip the length of data */ index++; } - if (handles.num_attr - 1 != index) { + if (handles.num_attr != index) { bluetooth::log::warn("Attempted to read {} handles, but received just {} values", - +handles.num_attr, index + 1); + +handles.num_attr, index); } } }; diff --git a/system/btcore/src/hal_util.cc b/system/btcore/src/hal_util.cc index 5c32ed6208..5acc399730 100644 --- a/system/btcore/src/hal_util.cc +++ b/system/btcore/src/hal_util.cc @@ -18,12 +18,9 @@ #include "btcore/include/hal_util.h" -#include <base/strings/stringprintf.h> #include <dlfcn.h> #include <hardware/bluetooth.h> -using base::StringPrintf; - extern bt_interface_t bluetoothInterface; int hal_util_load_bt_library(const bt_interface_t** interface) { diff --git a/system/btif/Android.bp b/system/btif/Android.bp index d442e0c2cf..75494590fd 100644 --- a/system/btif/Android.bp +++ b/system/btif/Android.bp @@ -123,9 +123,6 @@ cc_library_static { "src/btif_rc.cc", "src/btif_vc.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], local_include_dirs: [ "include", ], @@ -201,6 +198,7 @@ cc_library_static { "src/btif_sdp.cc", "src/btif_sdp_server.cc", "src/btif_sock.cc", + "src/btif_sock_hal.cc", "src/btif_sock_l2cap.cc", "src/btif_sock_logging.cc", "src/btif_sock_rfc.cc", @@ -213,9 +211,6 @@ cc_library_static { "src/btif_util.cc", "src/stack_manager.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], target: { android: { whole_static_libs: [ @@ -308,7 +303,6 @@ cc_test { "bluetooth_flags_c_lib_for_test", "libFraunhoferAAC", "libbase", - "libbluetooth-dumpsys", "libbluetooth-for-tests", "libbluetooth-types", "libbluetooth_core_rs", @@ -390,9 +384,6 @@ cc_test { "test/btif_profile_queue_test.cc", ], header_libs: ["libbluetooth_headers"], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], static_libs: [ "libbluetooth-types", "libbluetooth_gd", @@ -426,9 +417,6 @@ cc_test { "test/btif_avrcp_audio_track_test.cc", ], header_libs: ["libbluetooth_headers"], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], static_libs: [ "libbluetooth-types", "libbluetooth_log", @@ -516,6 +504,7 @@ cc_test { static_libs: [ "libbluetooth-types", "libbluetooth_core_rs_bridge", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-platform-protos-lite", "libchrome", @@ -574,10 +563,6 @@ cc_test { ":TestMockUdrv", "test/btif_hh_test.cc", ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], header_libs: ["libbluetooth_headers"], aidl: { libs: ["bluetooth_constants"], @@ -607,6 +592,7 @@ cc_test { "libbluetooth_core_rs_bridge", "libbluetooth_crypto_toolbox", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-audio-hal-interface", "libbt-common", @@ -699,10 +685,6 @@ cc_test { ":TestMockUdrv", "test/btif_core_test.cc", ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], header_libs: ["libbluetooth_headers"], aidl: { libs: ["bluetooth_constants"], diff --git a/system/btif/BUILD.gn b/system/btif/BUILD.gn index 519237997a..38b8fa9c6a 100644 --- a/system/btif/BUILD.gn +++ b/system/btif/BUILD.gn @@ -75,6 +75,7 @@ static_library("btif") { "src/btif_sdp.cc", "src/btif_sdp_server.cc", "src/btif_sock.cc", + "src/btif_sock_hal.cc", "src/btif_sock_l2cap.cc", "src/btif_sock_logging.cc", "src/btif_sock_rfc.cc", diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc index d589607124..c9dd6e7b74 100644 --- a/system/btif/co/bta_av_co.cc +++ b/system/btif/co/bta_av_co.cc @@ -720,8 +720,7 @@ void BtaAvCo::SaveCodec(const RawAddress& peer_address, const uint8_t* new_codec void BtaAvCo::GetPeerEncoderParameters(const RawAddress& peer_address, tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params) { uint16_t min_mtu = 0xFFFF; - log::assert_that(p_peer_params != nullptr, "Peer address {}", - ADDRESS_TO_LOGGABLE_STR(peer_address)); + log::assert_that(p_peer_params != nullptr, "Peer address {}", peer_address); std::lock_guard<std::recursive_mutex> lock(peer_cache_->codec_lock_); @@ -1001,11 +1000,11 @@ void BtaAvCo::DebugDump(int fd) { dprintf(fd, "\nA2DP Peers State:\n"); dprintf(fd, " Source: active peer: %s\n", (bta_av_source_state_.getActivePeer() != nullptr) - ? ADDRESS_TO_LOGGABLE_CSTR(bta_av_source_state_.getActivePeer()->addr) + ? bta_av_source_state_.getActivePeer()->addr.ToRedactedStringForLogging().c_str() : "null"); dprintf(fd, " Sink: active peer: %s\n", (bta_av_sink_state_.getActivePeer() != nullptr) - ? ADDRESS_TO_LOGGABLE_CSTR(bta_av_sink_state_.getActivePeer()->addr) + ? bta_av_sink_state_.getActivePeer()->addr.ToRedactedStringForLogging().c_str() : "null"); for (size_t i = 0; i < BTA_AV_CO_NUM_ELEMENTS(peer_cache_->peers_); i++) { @@ -1013,7 +1012,7 @@ void BtaAvCo::DebugDump(int fd) { if (peer.addr.IsEmpty()) { continue; } - dprintf(fd, " Peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(peer.addr)); + dprintf(fd, " Peer: %s\n", peer.addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " Number of sinks: %u\n", peer.num_sinks); dprintf(fd, " Number of sources: %u\n", peer.num_sources); dprintf(fd, " Number of SEPs: %u\n", peer.num_seps); diff --git a/system/btif/include/btif_hh.h b/system/btif/include/btif_hh.h index c55ddedec2..5ab2399b31 100644 --- a/system/btif/include/btif_hh.h +++ b/system/btif/include/btif_hh.h @@ -19,7 +19,6 @@ #ifndef BTIF_HH_H #define BTIF_HH_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <hardware/bluetooth.h> #include <hardware/bt_hh.h> @@ -74,7 +73,7 @@ inline std::string btif_hh_status_text(const BTIF_HH_STATUS& status) { CASE_RETURN_TEXT(BTIF_HH_ENABLED); CASE_RETURN_TEXT(BTIF_HH_DISABLING); default: - return base::StringPrintf("UNKNOWN[%u]", status); + return std::format("UNKNOWN[{}]", static_cast<unsigned>(status)); } } @@ -133,7 +132,7 @@ typedef struct { btif_hh_added_device_t added_devices[BTIF_HH_MAX_ADDED_DEV]; bool service_dereg_active; - std::list<tAclLinkSpec> pending_connections; + std::list<tAclLinkSpec> new_connection_requests; } btif_hh_cb_t; /******************************************************************************* diff --git a/system/gd/dumpsys/dumpsys_test_data.h b/system/btif/include/btif_sock_hal.h index 763d702f48..9588110920 100644 --- a/system/gd/dumpsys/dumpsys_test_data.h +++ b/system/btif/include/btif_sock_hal.h @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,7 @@ */ #pragma once -#include <string> -namespace testing { -const std::string& GetBundledSchemaData(); -} // namespace testing +#include <hardware/bluetooth.h> + +bt_status_t btsock_hal_init(); diff --git a/system/btif/include/btif_sock_l2cap.h b/system/btif/include/btif_sock_l2cap.h index c08ef532d7..3edcd35e32 100644 --- a/system/btif/include/btif_sock_l2cap.h +++ b/system/btif/include/btif_sock_l2cap.h @@ -22,20 +22,26 @@ #define BTIF_SOCK_L2CAP_H #include <hardware/bluetooth.h> +#include <hardware/bt_sock.h> #include "btif_uid.h" #include "types/raw_address.h" bt_status_t btsock_l2cap_init(int handle, uid_set_t* set); bt_status_t btsock_l2cap_cleanup(); -bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, - int app_uid); +bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, int app_uid, + btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size); bt_status_t btsock_l2cap_connect(const RawAddress* bd_addr, int channel, int* sock_fd, int flags, - int app_uid); + int app_uid, btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size); void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id); void on_l2cap_psm_assigned(int id, int psm); bt_status_t btsock_l2cap_disconnect(const RawAddress* bd_addr); bt_status_t btsock_l2cap_get_l2cap_local_cid(bluetooth::Uuid& conn_uuid, uint16_t* cid); bt_status_t btsock_l2cap_get_l2cap_remote_cid(bluetooth::Uuid& conn_uuid, uint16_t* cid); +bool btsock_l2cap_in_use(uint64_t socket_id); +void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success); +void on_btsocket_l2cap_close(uint64_t socket_id); #endif diff --git a/system/btif/include/btif_sock_rfc.h b/system/btif/include/btif_sock_rfc.h index a7b21aab52..eb5f22b82d 100644 --- a/system/btif/include/btif_sock_rfc.h +++ b/system/btif/include/btif_sock_rfc.h @@ -28,6 +28,8 @@ #define BTIF_SOCK_RFC_H #include "btif_uid.h" +#include "hardware/bluetooth.h" +#include "hardware/bt_sock.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" bt_status_t btsock_rfc_init(int handle, uid_set_t* set); @@ -36,10 +38,17 @@ bt_status_t btsock_rfc_control_req(uint8_t dlci, const RawAddress& bd_addr, uint uint8_t break_signal, uint8_t discard_buffers, uint8_t break_signal_seq, bool fc); bt_status_t btsock_rfc_listen(const char* name, const bluetooth::Uuid* uuid, int channel, - int* sock_fd, int flags, int app_uid); + int* sock_fd, int flags, int app_uid, btsock_data_path_t data_path, + const char* socket_name, uint64_t hub_id, uint64_t endpoint_id, + int max_rx_packet_size); bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const bluetooth::Uuid* uuid, int channel, - int* sock_fd, int flags, int app_uid); + int* sock_fd, int flags, int app_uid, btsock_data_path_t data_path, + const char* socket_name, uint64_t hub_id, uint64_t endpoint_id, + int max_rx_packet_size); void btsock_rfc_signaled(int fd, int flags, uint32_t user_id); bt_status_t btsock_rfc_disconnect(const RawAddress* bd_addr); +bool btsock_rfc_in_use(uint64_t socket_id); +void on_btsocket_rfc_opened_complete(uint64_t socket_id, bool success); +void on_btsocket_rfc_close(uint64_t socket_id); #endif diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc index 77a492f68d..3abbb1122c 100644 --- a/system/btif/src/bluetooth.cc +++ b/system/btif/src/bluetooth.cc @@ -877,7 +877,7 @@ static int set_event_filter_connection_setup_all_devices() { return BT_STATUS_SUCCESS; } -static void dump(int fd, const char** arguments) { +static void dump(int fd, const char** /*arguments*/) { log::debug("Started bluetooth dumpsys"); btif_debug_conn_dump(fd); btif_debug_bond_event_dump(fd); @@ -911,7 +911,7 @@ static void dump(int fd, const char** arguments) { DumpsysRecord(fd); L2CA_Dumpsys(fd); DumpsysBtm(fd); - bluetooth::shim::Dump(fd, arguments); + bluetooth::shim::Dump(fd); power_telemetry::GetInstance().Dumpsys(fd); log::debug("Finished bluetooth dumpsys"); } diff --git a/system/btif/src/btif_a2dp.cc b/system/btif/src/btif_a2dp.cc index c572a34e2b..d643d199a2 100644 --- a/system/btif/src/btif_a2dp.cc +++ b/system/btif/src/btif_a2dp.cc @@ -148,14 +148,7 @@ void btif_a2dp_on_offload_started(const RawAddress& peer_addr, tBTA_AV_STATUS st if (btif_av_is_a2dp_offload_running()) { if (ack != Status::SUCCESS && btif_av_stream_started_ready(A2dpType::kSource)) { log::error("peer {} offload start failed", peer_addr); - if (com::android::bluetooth::flags::stop_on_offload_fail()) { - btif_av_stream_stop(peer_addr); - } else { - // Offload request will return with failure from btif_av sm if - // suspend is triggered for remote start. Disconnect only if SoC - // returned failure for offload VSC - btif_av_source_disconnect(peer_addr); - } + btif_av_stream_stop(peer_addr); } } diff --git a/system/btif/src/btif_a2dp_sink.cc b/system/btif/src/btif_a2dp_sink.cc index 2d214b0521..02b92621ed 100644 --- a/system/btif/src/btif_a2dp_sink.cc +++ b/system/btif/src/btif_a2dp_sink.cc @@ -540,7 +540,6 @@ static void btif_a2dp_sink_audio_handle_stop_decoding() { } static void btif_decode_alarm_cb(void* /* context */) { - LockGuard lock(g_mutex); btif_a2dp_sink_cb.worker_thread.DoInThread(FROM_HERE, base::BindOnce(btif_a2dp_sink_avk_handle_timer)); } diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc index a7ba4fa0dc..d58e3de696 100644 --- a/system/btif/src/btif_a2dp_source.cc +++ b/system/btif/src/btif_a2dp_source.cc @@ -356,8 +356,7 @@ class A2dpStreamCallbacks : public bluetooth::audio::a2dp::StreamCallbacks { } // Check if LE Audio is currently active. - if (com::android::bluetooth::flags::a2dp_check_lea_iso_channel() && - hci::IsoManager::GetInstance()->GetNumberOfActiveIso() > 0) { + if (hci::IsoManager::GetInstance()->GetNumberOfActiveIso() > 0) { log::error("unable to start stream: LEA is active"); return Status::FAILURE; } @@ -575,8 +574,13 @@ void btif_a2dp_source_shutdown(std::promise<void> shutdown_complete_promise) { /* Make sure no channels are restarted while shutting down */ btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateShuttingDown); - local_thread()->DoInThread(FROM_HERE, base::BindOnce(&btif_a2dp_source_shutdown_delayed, - std::move(shutdown_complete_promise))); + // TODO(b/374166531) Remove the check for get_main_thread. + if (local_thread() != get_main_thread()) { + local_thread()->DoInThread(FROM_HERE, base::BindOnce(&btif_a2dp_source_shutdown_delayed, + std::move(shutdown_complete_promise))); + } else { + btif_a2dp_source_shutdown_delayed(std::move(shutdown_complete_promise)); + } } static void btif_a2dp_source_shutdown_delayed(std::promise<void> shutdown_complete_promise) { diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc index d40586461d..5a09cf2581 100644 --- a/system/btif/src/btif_av.cc +++ b/system/btif/src/btif_av.cc @@ -21,7 +21,6 @@ #include "btif/include/btif_av.h" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <frameworks/proto_logging/stats/enums/bluetooth/a2dp/enums.pb.h> @@ -1076,7 +1075,7 @@ std::string BtifAvPeer::FlagsToString() const { result = "None"; } - return base::StringPrintf("0x%x(%s)", flags_, result.c_str()); + return std::format("0x{:x}({})", flags_, result); } bt_status_t BtifAvPeer::Init() { @@ -2810,7 +2809,7 @@ static void btif_av_source_initiate_av_open_timer_timeout(void* data) { BtifAvPeer* peer = reinterpret_cast<BtifAvPeer*>(data); bool device_connected = false; - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay() && is_new_avrcp_enabled()) { + if (is_new_avrcp_enabled()) { // check if device is connected if (bluetooth::avrcp::AvrcpService::Get() != nullptr) { device_connected = @@ -3992,7 +3991,7 @@ static void btif_debug_av_peer_dump(int fd, const BtifAvPeer& peer) { break; } - dprintf(fd, " Peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(peer.PeerAddress())); + dprintf(fd, " Peer: %s\n", peer.PeerAddress().ToRedactedStringForLogging().c_str()); dprintf(fd, " Connected: %s\n", peer.IsConnected() ? "true" : "false"); dprintf(fd, " Streaming: %s\n", peer.IsStreaming() ? "true" : "false"); dprintf(fd, " SEP: %d(%s)\n", peer.PeerSep(), (peer.IsSource()) ? "Source" : "Sink"); @@ -4018,7 +4017,8 @@ static void btif_debug_av_source_dump(int fd) { if (!enabled) { return; } - dprintf(fd, " Active peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(btif_av_source.ActivePeer())); + dprintf(fd, " Active peer: %s\n", + btif_av_source.ActivePeer().ToRedactedStringForLogging().c_str()); dprintf(fd, " Peers:\n"); btif_av_source.DumpPeersInfo(fd); } @@ -4030,7 +4030,8 @@ static void btif_debug_av_sink_dump(int fd) { if (!enabled) { return; } - dprintf(fd, " Active peer: %s\n", ADDRESS_TO_LOGGABLE_CSTR(btif_av_sink.ActivePeer())); + dprintf(fd, " Active peer: %s\n", + btif_av_sink.ActivePeer().ToRedactedStringForLogging().c_str()); dprintf(fd, " Peers:\n"); btif_av_sink.DumpPeersInfo(fd); } diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc index 73fe810e23..9164e53ef7 100644 --- a/system/btif/src/btif_core.cc +++ b/system/btif/src/btif_core.cc @@ -53,6 +53,7 @@ #include "device/include/device_iot_config.h" #include "hci/controller_interface.h" #include "internal_include/bt_target.h" +#include "lpp/lpp_offload_interface.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" #include "osi/include/allocator.h" @@ -510,6 +511,16 @@ void btif_get_adapter_property(bt_property_type_t type) { log::verbose("Don't support Dynamic Audio Buffer"); } } + } else if (prop.type == BT_PROPERTY_LPP_OFFLOAD_FEATURES) { + bt_lpp_offload_features_t lpp_offload_features; + hal::SocketCapabilities socket_offload_capabilities = + bluetooth::shim::GetLppOffloadManager()->GetSocketCapabilities(); + lpp_offload_features.number_of_supported_offloaded_le_coc_sockets = + socket_offload_capabilities.le_coc_capabilities.number_of_supported_sockets; + lpp_offload_features.number_of_supported_offloaded_rfcomm_sockets = + socket_offload_capabilities.rfcomm_capabilities.number_of_supported_sockets; + prop.len = sizeof(bt_lpp_offload_features_t); + memcpy(prop.val, &lpp_offload_features, prop.len); } else { status = btif_storage_get_adapter_property(&prop); } diff --git a/system/btif/src/btif_debug_conn.cc b/system/btif/src/btif_debug_conn.cc index ef2843baf3..935758a876 100644 --- a/system/btif/src/btif_debug_conn.cc +++ b/system/btif/src/btif_debug_conn.cc @@ -90,7 +90,7 @@ void btif_debug_conn_dump(int fd) { while (connection_events[dump_event].ts) { conn_event_t* evt = &connection_events[dump_event]; dprintf(fd, " %s %s %s", format_ts(evt->ts, ts_buffer, sizeof(ts_buffer)), - format_state(evt->state), ADDRESS_TO_LOGGABLE_CSTR(evt->bda)); + format_state(evt->state), evt->bda.ToRedactedStringForLogging().c_str()); if (evt->state == BTIF_DEBUG_DISCONNECTED) { dprintf(fd, " reason=%d", evt->disconnect_reason); } diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc index 8ed9face97..670889006b 100644 --- a/system/btif/src/btif_dm.cc +++ b/system/btif/src/btif_dm.cc @@ -31,7 +31,6 @@ #include "btif_dm.h" #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <hardware/bluetooth.h> @@ -69,6 +68,7 @@ #include "hci/le_rand_callback.h" #include "internal_include/bt_target.h" #include "internal_include/stack_config.h" +#include "main/shim/acl_api.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" #include "main/shim/le_advertising_manager.h" @@ -231,7 +231,7 @@ typedef struct { #define BTA_SERVICE_ID_TO_SERVICE_MASK(id) (1 << (id)) -#define MAX_BTIF_BOND_EVENT_ENTRIES 15 +#define MAX_BTIF_BOND_EVENT_ENTRIES 31 #define MAX_NUM_DEVICES_IN_EIR_UUID_CACHE 128 @@ -551,10 +551,8 @@ static void bond_state_changed(bt_status_t status, const RawAddress& bd_addr, if (!pairing_cb.static_bdaddr.IsEmpty()) { BTM_LogHistory( kBtmLogTagCallback, bd_addr, "Bond state changed", - base::StringPrintf( - "Crosskey bt_status:%s bond_state:%u reason:%s", - bt_status_text(status).c_str(), state, - hci_reason_code_text(to_hci_reason_code(pairing_cb.fail_reason)).c_str())); + std::format("Crosskey bt_status:{} bond_state:{} reason:{}", bt_status_text(status), + state, hci_reason_code_text(to_hci_reason_code(pairing_cb.fail_reason)))); GetInterfaceToProfiles()->events->invoke_bond_state_changed_cb(status, bd_addr, state, pairing_cb.fail_reason); } @@ -581,11 +579,9 @@ static void bond_state_changed(bt_status_t status, const RawAddress& bd_addr, log::error("Fail to save metric id for device:{}", bd_addr); } } - BTM_LogHistory( - kBtmLogTagCallback, bd_addr, "Bond state changed", - base::StringPrintf( - "bt_status:%s bond_state:%u reason:%s", bt_status_text(status).c_str(), state, - hci_reason_code_text(to_hci_reason_code(pairing_cb.fail_reason)).c_str())); + BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Bond state changed", + std::format("bt_status:{} bond_state:{} reason:{}", bt_status_text(status), state, + hci_reason_code_text(to_hci_reason_code(pairing_cb.fail_reason)))); GetInterfaceToProfiles()->events->invoke_bond_state_changed_cb(status, bd_addr, state, pairing_cb.fail_reason); @@ -974,9 +970,9 @@ static void btif_dm_pin_req_evt(tBTA_DM_PIN_REQ* p_pin_req) { } } BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Pin request", - base::StringPrintf("name:\"%s\" min16:%c", - PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name)), - (p_pin_req->min_16_digit) ? 'T' : 'F')); + std::format("name:\"{}\" min16:{:c}", + PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name)), + (p_pin_req->min_16_digit) ? 'T' : 'F')); GetInterfaceToProfiles()->events->invoke_pin_request_cb(bd_addr, bd_name, cod, p_pin_req->min_16_digit); } @@ -1047,12 +1043,11 @@ static void btif_dm_ssp_cfm_req_evt(tBTA_DM_SP_CFM_REQ* p_ssp_cfm_req) { pairing_cb.sdp_attempts = 0; BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Ssp request", - base::StringPrintf("just_works:%c pin:%u", (p_ssp_cfm_req->just_works) ? 'T' : 'F', - p_ssp_cfm_req->num_val)); + std::format("just_works:{:c} pin:{}", (p_ssp_cfm_req->just_works) ? 'T' : 'F', + p_ssp_cfm_req->num_val)); GetInterfaceToProfiles()->events->invoke_ssp_request_cb( bd_addr, - (p_ssp_cfm_req->just_works ? BT_SSP_VARIANT_CONSENT - : BT_SSP_VARIANT_PASSKEY_CONFIRMATION), + p_ssp_cfm_req->just_works ? BT_SSP_VARIANT_CONSENT : BT_SSP_VARIANT_PASSKEY_CONFIRMATION, p_ssp_cfm_req->num_val); } @@ -1078,7 +1073,7 @@ static void btif_dm_ssp_key_notif_evt(tBTA_DM_SP_KEY_NOTIF* p_ssp_key_notif) { pairing_cb.is_ssp = true; BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Ssp request", - base::StringPrintf("passkey:%u", p_ssp_key_notif->passkey)); + std::format("passkey:{}", p_ssp_key_notif->passkey)); GetInterfaceToProfiles()->events->invoke_ssp_request_cb( bd_addr, BT_SSP_VARIANT_PASSKEY_NOTIFICATION, p_ssp_key_notif->passkey); } @@ -1209,9 +1204,8 @@ static void btif_dm_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) { if (is_crosskey) { // If bonding occurred due to cross-key pairing, send address // consolidate callback - BTM_LogHistory( - kBtmLogTagCallback, bd_addr, "Consolidate", - base::StringPrintf(" <=> %s", ADDRESS_TO_LOGGABLE_CSTR(pairing_cb.bd_addr))); + BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Consolidate", + std::format("<=> {}", pairing_cb.bd_addr)); GetInterfaceToProfiles()->events->invoke_address_consolidate_cb(pairing_cb.bd_addr, bd_addr); } else { @@ -1683,9 +1677,8 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, const tBTA_STATUS bta_status = result; BTM_LogHistory(kBtmLogTagSdp, bd_addr, "Discovered services", - base::StringPrintf("bta_status:%s sdp_uuids:%zu eir_uuids:%zu", - bta_status_text(bta_status).c_str(), uuids_param.size(), - num_eir_uuids)); + std::format("bta_status:{} sdp_uuids:{} eir_uuids:{}", bta_status_text(bta_status), + uuids_param.size(), num_eir_uuids)); if (!uuids_param.empty() || num_eir_uuids != 0) { /* Also write this to the NVRAM */ @@ -1967,6 +1960,22 @@ void BTIF_dm_enable() { pairing_cb = {}; pairing_cb.bond_type = BOND_TYPE_PERSISTENT; + /* Bluetooth Core Specification version 5.4 + * 7.8.5 LE Set Advertising Parameters command + * 7.8.53 LE Set Extended Advertising Parameters command + * 7.8.64 LE Set Extended Scan Parameters command + * 7.8.12 LE Create Connection command + * 7.8.66 LE Extended Create Connection command + * Set all-zero set to resolving list to make controller generate RPA for + * un-direct (broadcast) advertising RPA */ + if (bluetooth::shim::GetController()->IsRpaGenerationSupported()) { + log::info("Support RPA offload, set all-zero set in resolving list"); + tBLE_BD_ADDR all_zero_address_with_type = {0}; + const Octet16 all_zero_peer_irk = {0}; + bluetooth::shim::ACL_AddToAddressResolution(all_zero_address_with_type, all_zero_peer_irk, + ble_local_key_cb.id_keys.irk); + } + // Enable address consolidation. btif_storage_load_le_devices(); @@ -2257,6 +2266,9 @@ void btif_dm_acl_evt(tBTA_DM_ACL_EVT event, tBTA_DM_ACL* p_data) { case BTA_DM_LE_FEATURES_READ: btif_get_adapter_property(BT_PROPERTY_LOCAL_LE_FEATURES); break; + case BTA_DM_LPP_OFFLOAD_FEATURES_READ: + btif_get_adapter_property(BT_PROPERTY_LPP_OFFLOAD_FEATURES); + break; default: { log::error("Unexpected tBTA_DM_ACL_EVT:{}", event); @@ -2313,9 +2325,9 @@ static void bta_energy_info_cb(tBTM_BLE_TX_TIME_MS tx_time, tBTM_BLE_RX_TIME_MS void btif_dm_start_discovery(void) { log::verbose("start device discover/inquiry"); - BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Device discovery", - base::StringPrintf("is_request_queued:%c", - bta_dm_is_search_request_queued() ? 'T' : 'F')); + BTM_LogHistory( + kBtmLogTag, RawAddress::kEmpty, "Device discovery", + std::format("is_request_queued:{:c}", bta_dm_is_search_request_queued() ? 'T' : 'F')); /* no race here because we're guaranteed to be in the main thread */ if (bta_dm_is_search_request_queued()) { @@ -2357,7 +2369,7 @@ void btif_dm_create_bond(const RawAddress bd_addr, tBT_TRANSPORT transport) { log::verbose("bd_addr={}, transport={}", bd_addr, transport); BTM_LogHistory(kBtmLogTag, bd_addr, "Create bond", - base::StringPrintf("transport:%s", bt_transport_text(transport).c_str())); + std::format("transport:{}", bt_transport_text(transport))); btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_CREATE_BOND, pairing_cb.state); @@ -2379,7 +2391,7 @@ void btif_dm_create_bond_le(const RawAddress bd_addr, tBLE_ADDR_TYPE addr_type) .bda = bd_addr, }; BTM_LogHistory(kBtmLogTag, ble_bd_addr, "Create bond", - base::StringPrintf("transport:%s", bt_transport_text(BT_TRANSPORT_LE).c_str())); + std::format("transport:{}", bt_transport_text(BT_TRANSPORT_LE))); btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_CREATE_BOND, pairing_cb.state); @@ -2418,8 +2430,8 @@ void btif_dm_create_bond_out_of_band(const RawAddress bd_addr, tBT_TRANSPORT tra } BTM_LogHistory(kBtmLogTag, bd_addr, "Create bond", - base::StringPrintf("transport:%s oob:%s", bt_transport_text(transport).c_str(), - btm_oob_data_text(oob_cb.data_present).c_str())); + std::format("transport:{} oob:{}", bt_transport_text(transport), + btm_oob_data_text(oob_cb.data_present))); uint8_t empty[] = {0, 0, 0, 0, 0, 0, 0}; switch (transport) { @@ -2628,12 +2640,11 @@ void btif_dm_pin_reply(const RawAddress bd_addr, uint8_t accept, uint8_t pin_len ******************************************************************************/ void btif_dm_ssp_reply(const RawAddress bd_addr, bt_ssp_variant_t variant, uint8_t accept) { log::verbose("accept={}", accept); - BTM_LogHistory( - kBtmLogTag, bd_addr, "Ssp reply", - base::StringPrintf("originator:%s variant:%d accept:%c le:%c numeric_comparison:%c", - (pairing_cb.is_local_initiated) ? "local" : "remote", variant, - (accept) ? 'Y' : 'N', (pairing_cb.is_le_only) ? 'T' : 'F', - (pairing_cb.is_le_nc) ? 'T' : 'F')); + BTM_LogHistory(kBtmLogTag, bd_addr, "Ssp reply", + std::format("originator:{} variant:{} accept:{:c} le:{:c} numeric_comparison:{:c}", + pairing_cb.is_local_initiated ? "local" : "remote", variant, + accept ? 'Y' : 'N', pairing_cb.is_le_only ? 'T' : 'F', + pairing_cb.is_le_nc ? 'T' : 'F')); if (pairing_cb.is_le_only) { if (pairing_cb.is_le_nc) { BTA_DmBleConfirmReply(bd_addr, accept); @@ -2816,7 +2827,7 @@ void btif_dm_get_remote_services(RawAddress remote_addr, const tBT_TRANSPORT tra log::verbose("transport={}, remote_addr={}", bt_transport_text(transport), remote_addr); BTM_LogHistory(kBtmLogTag, remote_addr, "Service discovery", - base::StringPrintf("transport:%s", bt_transport_text(transport).c_str())); + std::format("transport:{}", bt_transport_text(transport))); BTA_DmDiscover(remote_addr, service_discovery_callbacks{ @@ -3070,7 +3081,7 @@ static void id_status_callback(tBT_TRANSPORT transport, bool is_valid, const Oct auto advertiser = bluetooth::shim::get_ble_advertiser_instance(); ::AdvertiseParameters parameters{}; - parameters.advertising_event_properties = 0x0045 /* connectable, discoverable, tx power */; + parameters.advertising_event_properties = 0x0041 /* connectable, tx power */; parameters.min_interval = 0xa0; // 100 ms parameters.max_interval = 0x500; // 800 ms parameters.channel_map = 0x7; // Use all the channels @@ -3079,6 +3090,7 @@ static void id_status_callback(tBT_TRANSPORT transport, bool is_valid, const Oct parameters.secondary_advertising_phy = 2; parameters.scan_request_notification_enable = 0; parameters.own_address_type = BLE_ADDR_RANDOM; + parameters.discoverable = true; std::vector<uint8_t> advertisement{0x02, 0x01 /* Flags */, 0x02 /* Connectable */}; std::vector<uint8_t> scan_data{}; @@ -3233,7 +3245,7 @@ static void btif_dm_ble_passkey_notif_evt(tBTA_DM_SP_KEY_NOTIF* p_ssp_key_notif) pairing_cb.is_ssp = false; BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Ssp request", - base::StringPrintf("passkey:%u", p_ssp_key_notif->passkey)); + std::format("passkey:{}", p_ssp_key_notif->passkey)); GetInterfaceToProfiles()->events->invoke_ssp_request_cb( bd_addr, BT_SSP_VARIANT_PASSKEY_NOTIFICATION, p_ssp_key_notif->passkey); @@ -3363,16 +3375,20 @@ static void btif_dm_ble_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) { break; case BTA_DM_AUTH_SMP_CONN_TOUT: { - if (!p_auth_cmpl->is_ctkd && btm_sec_is_a_bonded_dev(bd_addr)) { + bool during_bonding = + (bd_addr == pairing_cb.bd_addr || bd_addr == pairing_cb.static_bdaddr); + + if (during_bonding || p_auth_cmpl->is_ctkd || !btm_sec_is_a_bonded_dev(bd_addr)) { + log::info("Removing ble bonding keys on SMP_CONN_TOUT during_bonding: {}, is_ctkd: {}", + during_bonding, p_auth_cmpl->is_ctkd); + btif_dm_remove_ble_bonding_keys(); + status = BT_STATUS_AUTH_FAILURE; + } else { log::warn("Bonded device addr={}, timed out - will not remove the keys", bd_addr); // Don't send state change to upper layers - otherwise Java think we // unbonded, and will disconnect HID profile. return; } - log::info("Removing ble bonding keys on SMP_CONN_TOUT during crosskey: {}", - p_auth_cmpl->is_ctkd); - btif_dm_remove_ble_bonding_keys(); - status = BT_STATUS_AUTH_FAILURE; break; } case BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT: @@ -3548,9 +3564,9 @@ static void btif_dm_ble_passkey_req_evt(tBTA_DM_PIN_REQ* p_pin_req) { cod = COD_UNCLASSIFIED; - BTM_LogHistory(kBtmLogTagCallback, bd_addr, "PIN request", - base::StringPrintf("name:'%s'", - PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name)))); + BTM_LogHistory( + kBtmLogTagCallback, bd_addr, "PIN request", + std::format("name:'{}'", PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name)))); GetInterfaceToProfiles()->events->invoke_pin_request_cb(bd_addr, bd_name, cod, false); } @@ -3570,7 +3586,7 @@ static void btif_dm_ble_key_nc_req_evt(tBTA_DM_SP_KEY_NOTIF* p_notif_req) { pairing_cb.is_le_nc = true; BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Ssp request", - base::StringPrintf("passkey:%u", p_notif_req->passkey)); + std::format("passkey:{}", p_notif_req->passkey)); GetInterfaceToProfiles()->events->invoke_ssp_request_cb( bd_addr, BT_SSP_VARIANT_PASSKEY_CONFIRMATION, p_notif_req->passkey); @@ -3828,8 +3844,8 @@ void btif_debug_bond_event_dump(int fd) { break; } - dprintf(fd, " %s %s %s %s\n", eventtime, ADDRESS_TO_LOGGABLE_CSTR(event->bd_addr), - func_name, bond_state); + dprintf(fd, " %s %s %s %s\n", eventtime, + event->bd_addr.ToRedactedStringForLogging().c_str(), func_name, bond_state); } } diff --git a/system/btif/src/btif_gatt_server.cc b/system/btif/src/btif_gatt_server.cc index 558148e427..5af90205ba 100644 --- a/system/btif/src/btif_gatt_server.cc +++ b/system/btif/src/btif_gatt_server.cc @@ -292,46 +292,6 @@ static bt_status_t btif_gatts_unregister_app(int server_if) { return do_in_jni_thread(Bind(&BTA_GATTS_AppDeregister, server_if)); } -static void btif_gatts_open_impl(int server_if, const RawAddress& address, bool is_direct, - tBT_TRANSPORT transport) { - // Ensure device is in inquiry database - tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC; - int device_type = 0; - - if (btif_get_address_type(address, &addr_type) && btif_get_device_type(address, &device_type) && - device_type != BT_DEVICE_TYPE_BREDR) { - BTA_DmAddBleDevice(address, addr_type, device_type); - } - - // Determine transport - if (transport == BT_TRANSPORT_AUTO) { - switch (device_type) { - case BT_DEVICE_TYPE_BREDR: - transport = BT_TRANSPORT_BR_EDR; - break; - - case BT_DEVICE_TYPE_BLE: - transport = BT_TRANSPORT_LE; - break; - - case BT_DEVICE_TYPE_DUMO: - transport = BT_TRANSPORT_BR_EDR; - break; - - default: - log::error("Unknown device type {}", DeviceTypeText(device_type)); - // transport must not be AUTO for finding control blocks. Use LE for backward compatibility. - transport = BT_TRANSPORT_LE; - break; - } - } - - // Connect! - BTA_GATTS_Open(server_if, address, BLE_ADDR_PUBLIC, is_direct, transport); -} - -// Used instead of btif_gatts_open_impl if the flag -// ble_gatt_server_use_address_type_in_connection is enabled. static void btif_gatts_open_impl_use_address_type(int server_if, const RawAddress& address, tBLE_ADDR_TYPE addr_type, bool is_direct, tBT_TRANSPORT transport) { @@ -369,13 +329,8 @@ static bt_status_t btif_gatts_open(int server_if, const RawAddress& bd_addr, uin bool is_direct, int transport) { CHECK_BTGATT_INIT(); - if (com::android::bluetooth::flags::ble_gatt_server_use_address_type_in_connection()) { - return do_in_jni_thread(Bind(&btif_gatts_open_impl_use_address_type, server_if, bd_addr, - addr_type, is_direct, to_bt_transport(transport))); - } else { - return do_in_jni_thread( - Bind(&btif_gatts_open_impl, server_if, bd_addr, is_direct, to_bt_transport(transport))); - } + return do_in_jni_thread(Bind(&btif_gatts_open_impl_use_address_type, server_if, bd_addr, + addr_type, is_direct, to_bt_transport(transport))); } static void btif_gatts_close_impl(int server_if, const RawAddress& address, int conn_id) { diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc index 8edc70ab3d..bd607ea142 100644 --- a/system/btif/src/btif_hf.cc +++ b/system/btif/src/btif_hf.cc @@ -1416,9 +1416,8 @@ bt_status_t HeadsetInterface::PhoneStateChange(int num_active, int num_held, { std::string cell_number(number); BTM_LogHistory(kBtmLogTag, raw_address, "Call Incoming", - base::StringPrintf("number:%s", PRIVATE_CELL(cell_number))); + std::format("number:{}", PRIVATE_CELL(cell_number))); } - // base::StringPrintf("number:%s", PRIVATE_CELL(number))); break; case BTHF_CALL_STATE_DIALING: if (!(num_active + num_held) && is_active_device(*bd_addr)) { diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc index 1a9855ef31..70631dbf2f 100644 --- a/system/btif/src/btif_hh.cc +++ b/system/btif/src/btif_hh.cc @@ -571,8 +571,9 @@ static void hh_open_handler(tBTA_HH_CONN& conn) { dev_status = p_dev->dev_status; } - if (std::find(btif_hh_cb.pending_connections.begin(), btif_hh_cb.pending_connections.end(), - conn.link_spec) != btif_hh_cb.pending_connections.end()) { + if (std::find(btif_hh_cb.new_connection_requests.begin(), + btif_hh_cb.new_connection_requests.end(), + conn.link_spec) != btif_hh_cb.new_connection_requests.end()) { log::verbose("Device connection was pending for: {}, status: {}", conn.link_spec, btif_hh_status_text(btif_hh_cb.status)); dev_status = BTHH_CONN_STATE_CONNECTING; @@ -602,7 +603,7 @@ static void hh_open_handler(tBTA_HH_CONN& conn) { BTHH_STATE_UPDATE(conn.link_spec, BTHH_CONN_STATE_CONNECTING); } - btif_hh_cb.pending_connections.remove(conn.link_spec); + btif_hh_cb.new_connection_requests.remove(conn.link_spec); if (conn.status != BTA_HH_OK) { btif_dm_hh_open_failed(&conn.link_spec.addrt.bda); @@ -1036,7 +1037,7 @@ bt_status_t btif_hh_virtual_unplug(const tAclLinkSpec& link_spec) { if (btif_hh_find_dev_by_link_spec(link_spec) != nullptr || btif_hh_find_added_dev(link_spec) != nullptr) { // Remove pending connection if address matches - btif_hh_cb.pending_connections.remove_if( + btif_hh_cb.new_connection_requests.remove_if( [link_spec](auto ls) { return ls.addrt.bda == link_spec.addrt.bda; }); btif_hh_remove_device(link_spec); @@ -1047,7 +1048,7 @@ bt_status_t btif_hh_virtual_unplug(const tAclLinkSpec& link_spec) { // Abort outgoing initial connection attempt bool pending_connection = false; - for (auto ls : btif_hh_cb.pending_connections) { + for (auto ls : btif_hh_cb.new_connection_requests) { if (ls.addrt.bda == link_spec.addrt.bda) { pending_connection = true; break; @@ -1055,7 +1056,7 @@ bt_status_t btif_hh_virtual_unplug(const tAclLinkSpec& link_spec) { } if (pending_connection) { - btif_hh_cb.pending_connections.remove_if( + btif_hh_cb.new_connection_requests.remove_if( [link_spec](auto ls) { return ls.addrt.bda == link_spec.addrt.bda; }); /* need to notify up-layer device is disconnected to avoid @@ -1120,11 +1121,16 @@ bt_status_t btif_hh_connect(const tAclLinkSpec& link_spec) { p_dev->dev_status = BTHH_CONN_STATE_CONNECTING; } + // Add the new connection to the pending list + if (!com::android::bluetooth::flags::pending_hid_connection_cancellation() || + added_dev == nullptr) { + btif_hh_cb.new_connection_requests.push_back(link_spec); + } + /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways sending this request from host, for subsequent user initiated connection. If the remote is not in pagescan mode, we will do 2 retries to connect before giving up */ - btif_hh_cb.pending_connections.push_back(link_spec); BTA_HhOpen(link_spec); do_in_jni_thread(base::Bind( @@ -1580,7 +1586,7 @@ static bt_status_t connect(RawAddress* bd_addr, tBLE_ADDR_TYPE addr_type, tBT_TR BTHH_LOG_LINK(link_spec); if (!com::android::bluetooth::flags::initiate_multiple_hid_connections() && - !btif_hh_cb.pending_connections.empty()) { + !btif_hh_cb.new_connection_requests.empty()) { log::warn("HH status = {}", btif_hh_status_text(btif_hh_cb.status)); return BT_STATUS_BUSY; } else if (btif_hh_cb.status == BTIF_HH_DISABLED || btif_hh_cb.status == BTIF_HH_DISABLING) { @@ -1645,12 +1651,16 @@ static bt_status_t disconnect(RawAddress* bd_addr, tBLE_ADDR_TYPE addr_type, log::warn("Device {} already not connected, state: {}", p_dev->link_spec, bthh_connection_state_text(p_dev->dev_status)); p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED; + + if (com::android::bluetooth::flags::pending_hid_connection_cancellation()) { + btif_hh_cb.new_connection_requests.remove(link_spec); + } return BT_STATUS_DONE; } else if (com::android::bluetooth::flags::initiate_multiple_hid_connections() && - std::find(btif_hh_cb.pending_connections.begin(), - btif_hh_cb.pending_connections.end(), - link_spec) != btif_hh_cb.pending_connections.end()) { - btif_hh_cb.pending_connections.remove(link_spec); + std::find(btif_hh_cb.new_connection_requests.begin(), + btif_hh_cb.new_connection_requests.end(), + link_spec) != btif_hh_cb.new_connection_requests.end()) { + btif_hh_cb.new_connection_requests.remove(link_spec); log::info("Pending connection cancelled {}", link_spec); return BT_STATUS_SUCCESS; } @@ -1688,7 +1698,7 @@ static bt_status_t virtual_unplug(RawAddress* bd_addr, tBLE_ADDR_TYPE addr_type, btif_hh_device_t* p_dev = btif_hh_find_dev_by_link_spec(link_spec); if (com::android::bluetooth::flags::remove_input_device_on_vup()) { bool pending_connection = false; - for (auto ls : btif_hh_cb.pending_connections) { + for (auto ls : btif_hh_cb.new_connection_requests) { if (ls.addrt.bda == link_spec.addrt.bda) { pending_connection = true; break; @@ -2098,7 +2108,7 @@ static void cleanup(void) { btif_hh_cb.service_dereg_active = FALSE; btif_disable_service(BTA_HID_SERVICE_ID); } - btif_hh_cb.pending_connections.clear(); + btif_hh_cb.new_connection_requests.clear(); for (i = 0; i < BTIF_HH_MAX_HID; i++) { p_dev = &btif_hh_cb.devices[i]; int fd = (com::android::bluetooth::flags::hid_report_queuing() ? p_dev->internal_send_fd @@ -2184,7 +2194,7 @@ void DumpsysHid(int fd) { LOG_DUMPSYS(fd, "status:%s num_devices:%u", btif_hh_status_text(btif_hh_cb.status).c_str(), btif_hh_cb.device_num); LOG_DUMPSYS(fd, "status:%s", btif_hh_status_text(btif_hh_cb.status).c_str()); - for (auto link_spec : btif_hh_cb.pending_connections) { + for (auto link_spec : btif_hh_cb.new_connection_requests) { LOG_DUMPSYS(fd, "Pending connection: %s", link_spec.ToRedactedStringForLogging().c_str()); } for (unsigned i = 0; i < BTIF_HH_MAX_HID; i++) { diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc index 5b97f454b6..72c4bdd690 100644 --- a/system/btif/src/btif_rc.cc +++ b/system/btif/src/btif_rc.cc @@ -5544,7 +5544,7 @@ void btif_debug_rc_dump(int fd) { for (int i = 0; i < BTIF_RC_NUM_CONN; ++i) { btif_rc_device_cb_t* p_dev = &btif_rc_cb.rc_multi_cb[i]; if (p_dev->rc_state != BTRC_CONNECTION_STATE_DISCONNECTED) { - dprintf(fd, " %s:\n", ADDRESS_TO_LOGGABLE_CSTR(p_dev->rc_addr)); + dprintf(fd, " %s:\n", p_dev->rc_addr.ToRedactedStringForLogging().c_str()); rc_transaction_set_t* transaction_set = &(p_dev->transaction_set); std::unique_lock<std::recursive_mutex> lock(transaction_set->label_lock); diff --git a/system/btif/src/btif_sock.cc b/system/btif/src/btif_sock.cc index 04b43dda0e..d58326e571 100644 --- a/system/btif/src/btif_sock.cc +++ b/system/btif/src/btif_sock.cc @@ -22,12 +22,14 @@ #include <base/functional/callback.h> #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <hardware/bluetooth.h> #include <hardware/bt_sock.h> #include <atomic> #include "bta/include/bta_api.h" +#include "btif_sock_hal.h" #include "btif_sock_l2cap.h" #include "btif_sock_logging.h" #include "btif_sock_rfc.h" @@ -43,9 +45,13 @@ using bluetooth::Uuid; using namespace bluetooth; static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, const Uuid* uuid, - int channel, int* sock_fd, int flags, int app_uid); + int channel, int* sock_fd, int flags, int app_uid, + btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size); static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, const Uuid* uuid, - int channel, int* sock_fd, int flags, int app_uid); + int channel, int* sock_fd, int flags, int app_uid, + btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size); static void btsock_request_max_tx_data_length(const RawAddress& bd_addr); static bt_status_t btsock_control_req(uint8_t dlci, const RawAddress& bd_addr, uint8_t modem_signal, uint8_t break_signal, uint8_t discard_buffers, @@ -112,6 +118,13 @@ bt_status_t btif_sock_init(uid_set_t* uid_set) { goto error; } + if (com::android::bluetooth::flags::socket_settings_api()) { + status = btsock_hal_init(); + if (status != BT_STATUS_SUCCESS) { + log::warn("error initializing socket hal: {}", status); + } + } + return BT_STATUS_SUCCESS; error: @@ -148,7 +161,8 @@ static bt_status_t btsock_control_req(uint8_t dlci, const RawAddress& bd_addr, u static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, const Uuid* service_uuid, int channel, int* sock_fd, int flags, - int app_uid) { + int app_uid, btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) { if ((flags & BTSOCK_FLAG_NO_SDP) == 0) { log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL"); } @@ -158,20 +172,25 @@ static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, log::info( "Attempting listen for socket connections for device: {}, type: {}, " - "channel: {}, app_uid: {}", - RawAddress::kEmpty, type, channel, app_uid); + "channel: {}, app_uid: {}, data_path: {}, hub_id: {}, endpoint_id: {}, " + "max_rx_packet_size: {}", + RawAddress::kEmpty, type, channel, app_uid, data_path, hub_id, endpoint_id, + max_rx_packet_size); btif_sock_connection_logger(RawAddress::kEmpty, 0, type, SOCKET_CONNECTION_STATE_LISTENING, SOCKET_ROLE_LISTEN, app_uid, channel, 0, 0, service_name); switch (type) { case BTSOCK_RFCOMM: - status = btsock_rfc_listen(service_name, service_uuid, channel, sock_fd, flags, app_uid); + status = btsock_rfc_listen(service_name, service_uuid, channel, sock_fd, flags, app_uid, + data_path, socket_name, hub_id, endpoint_id, max_rx_packet_size); break; case BTSOCK_L2CAP: - status = btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid); + status = btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid, data_path, + socket_name, hub_id, endpoint_id, max_rx_packet_size); break; case BTSOCK_L2CAP_LE: status = btsock_l2cap_listen(service_name, channel, sock_fd, flags | BTSOCK_FLAG_LE_COC, - app_uid); + app_uid, data_path, socket_name, hub_id, endpoint_id, + max_rx_packet_size); break; case BTSOCK_SCO: status = btsock_sco_listen(sock_fd, flags); @@ -194,14 +213,16 @@ static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, } static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, const Uuid* uuid, - int channel, int* sock_fd, int flags, int app_uid) { + int channel, int* sock_fd, int flags, int app_uid, + btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) { log::assert_that(bd_addr != NULL, "assert failed: bd_addr != NULL"); log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL"); log::info( "Attempting socket connection for device: {}, type: {}, channel: {}, " - "app_uid: {}", - *bd_addr, type, channel, app_uid); + "app_uid: {}, data_path: {}, hub_id: {}, endpoint_id: {}, max_rx_packet_size: {}", + *bd_addr, type, channel, app_uid, data_path, hub_id, endpoint_id, max_rx_packet_size); *sock_fd = INVALID_FD; bt_status_t status = BT_STATUS_SOCKET_ERROR; @@ -211,15 +232,18 @@ static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, uuid ? uuid->ToString().c_str() : ""); switch (type) { case BTSOCK_RFCOMM: - status = btsock_rfc_connect(bd_addr, uuid, channel, sock_fd, flags, app_uid); + status = btsock_rfc_connect(bd_addr, uuid, channel, sock_fd, flags, app_uid, data_path, + socket_name, hub_id, endpoint_id, max_rx_packet_size); break; case BTSOCK_L2CAP: - status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid); + status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid, data_path, + socket_name, hub_id, endpoint_id, max_rx_packet_size); break; case BTSOCK_L2CAP_LE: - status = btsock_l2cap_connect(bd_addr, channel, sock_fd, (flags | BTSOCK_FLAG_LE_COC), - app_uid); + status = + btsock_l2cap_connect(bd_addr, channel, sock_fd, (flags | BTSOCK_FLAG_LE_COC), app_uid, + data_path, socket_name, hub_id, endpoint_id, max_rx_packet_size); break; case BTSOCK_SCO: status = btsock_sco_connect(bd_addr, sock_fd, flags); diff --git a/system/btif/src/btif_sock_hal.cc b/system/btif/src/btif_sock_hal.cc new file mode 100644 index 0000000000..ce820833d0 --- /dev/null +++ b/system/btif/src/btif_sock_hal.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "bt_btif_sock_hal" + +#include "btif/include/btif_sock_hal.h" + +#include "btif/include/btif_sock_l2cap.h" +#include "btif/include/btif_sock_rfc.h" +#include "lpp/lpp_offload_interface.h" +#include "main/shim/entry.h" +#include "stack/include/main_thread.h" + +using namespace bluetooth; + +class BtifSocketHalCallback : public hal::SocketHalCallback { +public: + void SocketOpenedComplete(uint64_t socket_id, hal::SocketStatus status) const override { + log::info("socket_id: {}, status: {}", socket_id, static_cast<int>(status)); + if (btsock_l2cap_in_use(socket_id)) { + do_in_main_thread(base::BindOnce(on_btsocket_l2cap_opened_complete, socket_id, + (status == hal::SocketStatus::SUCCESS))); + } else if (btsock_rfc_in_use(socket_id)) { + do_in_main_thread(base::BindOnce(on_btsocket_rfc_opened_complete, socket_id, + (status == hal::SocketStatus::SUCCESS))); + } else { + log::error("Unable to find socket with socket_id:{}", socket_id); + } + } + + void SocketClose(uint64_t socket_id) const override { + log::info("socket_id: {}", socket_id); + if (btsock_l2cap_in_use(socket_id)) { + do_in_main_thread(base::BindOnce(on_btsocket_l2cap_close, socket_id)); + } else if (btsock_rfc_in_use(socket_id)) { + do_in_main_thread(base::BindOnce(on_btsocket_rfc_close, socket_id)); + } else { + log::error("Unable to find socket with socket_id:{}", socket_id); + } + } +}; + +static BtifSocketHalCallback btif_socket_hal_cb; + +bt_status_t btsock_hal_init() { + log::info(""); + auto lpp_offload_manager_interface = bluetooth::shim::GetLppOffloadManager(); + if (lpp_offload_manager_interface == nullptr) { + log::warn("GetLppOffloadManager() returned nullptr!"); + return BT_STATUS_FAIL; + } + if (!lpp_offload_manager_interface->RegisterSocketHalCallback(&btif_socket_hal_cb)) { + log::warn("RegisterSocketHalCallback() failed!"); + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; +} diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc index 3aa7162b78..2a8e4e4f9a 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -16,11 +16,11 @@ */ #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> -#include <com_android_bluetooth_flags.h> #include <cstdint> #include <cstring> @@ -36,6 +36,8 @@ #include "gd/os/rand.h" #include "include/hardware/bluetooth.h" #include "internal_include/bt_target.h" +#include "lpp/lpp_offload_interface.h" +#include "main/shim/entry.h" #include "osi/include/allocator.h" #include "osi/include/osi.h" #include "stack/include/bt_hdr.h" @@ -65,6 +67,7 @@ typedef struct l2cap_socket { int channel; // PSM int our_fd; // fd from our side int app_fd; // fd from app's side + int listen_fd; // listen socket fd from our side unsigned bytes_buffered; struct packet* first_packet; // fist packet to be delivered to app @@ -84,9 +87,18 @@ typedef struct l2cap_socket { uint16_t local_cid; // The local CID uint16_t remote_cid; // The remote CID Uuid conn_uuid; // The connection uuid + uint64_t socket_id; // Socket ID in connected state + btsock_data_path_t data_path; // socket data path + char socket_name[128]; // descriptive socket name + uint64_t hub_id; // ID of the hub to which the end point belongs + uint64_t endpoint_id; // ID of the hub end point + bool is_accepting; // is app accepting on server socket? } l2cap_socket; static void btsock_l2cap_server_listen(l2cap_socket* sock); +static uint64_t btif_l2cap_sock_generate_socket_id(); +static void on_cl_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock); +static void on_srv_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock); static std::mutex state_lock; @@ -98,18 +110,13 @@ static int pth = -1; static void btsock_l2cap_cbk(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t l2cap_socket_id); /* TODO: Consider to remove this buffer, as we have a buffer in l2cap as well, - * and we risk - * a buffer overflow with this implementation if the socket data is not - * read from - * JAVA for a while. In such a case we should use flow control to tell the - * sender to - * back off. - * BUT remember we need to avoid blocking the BTA task execution - hence - * we cannot - * directly write to the socket. - * we should be able to change to store the data pointer here, and just - * wait - * confirming the l2cap_ind until we have more space in the buffer. */ + * and we risk a buffer overflow with this implementation if the socket data is not + * read from JAVA for a while. In such a case we should use flow control to tell the + * sender to back off. + * BUT remember we need to avoid blocking the BTA task execution - hence + * we cannot directly write to the socket. We should be able to change to store the + * data pointer here, and just wait confirming the l2cap_ind until we have more space + * in the buffer. */ /* returns false if none - caller must free "data" memory when done with it */ static char packet_get_head_l(l2cap_socket* sock, uint8_t** data, uint32_t* len) { @@ -246,13 +253,19 @@ static void btsock_l2cap_free_l(l2cap_socket* sock) { log::info( "Disconnected L2CAP connection for device: {}, channel: {}, app_uid: {}, " - "id: {}, is_le: {}", - sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc); + "id: {}, is_le: {}, socket_id: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id); btif_sock_connection_logger( sock->addr, sock->id, sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, SOCKET_CONNECTION_STATE_DISCONNECTED, sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, sock->app_uid, sock->channel, sock->tx_bytes, sock->rx_bytes, sock->name); + if (com::android::bluetooth::flags::socket_settings_api()) { + if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD && !sock->server && + sock->socket_id != 0) { + bluetooth::shim::GetLppOffloadManager()->SocketClosed(sock->socket_id); + } + } if (sock->next) { sock->next->prev = sock->prev; } @@ -321,10 +334,10 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad } #if TARGET_FLOSS - //Changed socket type to SOCK_STREAM to address a platform issue on FLOSS. - //This is a workaround and not the recommended approach. - //SOCK_SEQPACKET is preferred for L2CAP LE CoC channels because it preserves L2CAP - //packet boundaries, ensuring message integrity. + // Changed socket type to SOCK_STREAM to address a platform issue on FLOSS. + // This is a workaround and not the recommended approach. + // SOCK_SEQPACKET is preferred for L2CAP LE CoC channels because it preserves L2CAP + // packet boundaries, ensuring message integrity. sock_type = SOCK_STREAM; #endif if (socketpair(AF_LOCAL, sock_type, 0, fds)) { @@ -334,12 +347,19 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad sock->our_fd = fds[0]; sock->app_fd = fds[1]; + sock->listen_fd = -1; sock->security = security; sock->server = is_server; sock->connected = false; sock->handle = 0; sock->server_psm_sent = false; sock->app_uid = -1; + sock->conn_uuid = Uuid::kEmpty; + sock->socket_id = 0; + sock->data_path = BTSOCK_DATA_PATH_NO_OFFLOAD; + sock->hub_id = 0; + sock->endpoint_id = 0; + sock->is_accepting = false; if (name) { strncpy(sock->name, name, sizeof(sock->name) - 1); @@ -370,7 +390,7 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad while (t && t->id != sock->id) { t = t->next; } - if (!t && sock->id) { /* non-zeor handle is unique -> we're done */ + if (!t && sock->id) { /* non-zero handle is unique -> we're done */ break; } /* if we're here, we found a duplicate */ @@ -446,7 +466,7 @@ static uint64_t uuid_msb(const Uuid& uuid) { static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, int status, int send_fd, uint16_t rx_mtu, uint16_t tx_mtu, - const Uuid& conn_uuid) { + const Uuid& conn_uuid, uint64_t socket_id) { sock_connect_signal_t cs; cs.size = sizeof(cs); cs.bd_addr = *addr; @@ -456,6 +476,7 @@ static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, cs.max_tx_packet_size = tx_mtu; cs.conn_uuid_lsb = uuid_lsb(conn_uuid); cs.conn_uuid_msb = uuid_msb(conn_uuid); + cs.socket_id = socket_id; if (send_fd != -1) { if (sock_send_fd(fd, (const uint8_t*)&cs, sizeof(cs), send_fd) == sizeof(cs)) { return true; @@ -531,7 +552,7 @@ static void on_cl_l2cap_init(tBTA_JV_L2CAP_CL_INIT* p_init, uint32_t id) { * will be a clone of the sock representing the BluetoothServerSocket. * */ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) { - // std::mutex locked by caller + // state_lock taken by caller l2cap_socket* accept_rs = btsock_l2cap_alloc_l(sock->name, &p_open->rem_bda, false, 0); accept_rs->connected = true; accept_rs->security = sock->security; @@ -541,10 +562,22 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock->handle = -1; /* We should no longer associate this handle with the server socket */ accept_rs->is_le_coc = sock->is_le_coc; accept_rs->tx_mtu = sock->tx_mtu = p_open->tx_mtu; + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349374 + accept_rs->rx_mtu = sock->rx_mtu; + } accept_rs->local_cid = p_open->local_cid; accept_rs->remote_cid = p_open->remote_cid; + // TODO(b/342012881) Remove connection uuid when offload socket API is landed. Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>()); accept_rs->conn_uuid = uuid; + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349374 + accept_rs->socket_id = btif_l2cap_sock_generate_socket_id(); + accept_rs->data_path = sock->data_path; + strncpy(accept_rs->socket_name, sock->socket_name, sizeof(accept_rs->socket_name) - 1); + accept_rs->socket_name[sizeof(accept_rs->socket_name) - 1] = '\0'; + accept_rs->hub_id = sock->hub_id; + accept_rs->endpoint_id = sock->endpoint_id; + } /* Swap IDs to hand over the GAP connection to the accepted socket, and start a new server on the newly create socket ID. */ @@ -554,8 +587,9 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* log::info( "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, " - "id: {}, is_le: {}", - sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc); + "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, accept_rs->socket_id, + accept_rs->rx_mtu); btif_sock_connection_logger(accept_rs->addr, accept_rs->id, accept_rs->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, SOCKET_CONNECTION_STATE_CONNECTED, @@ -566,11 +600,15 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id); btsock_thread_add_fd(pth, accept_rs->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, accept_rs->id); send_app_connect_signal(sock->our_fd, &accept_rs->addr, sock->channel, 0, accept_rs->app_fd, - sock->rx_mtu, p_open->tx_mtu, accept_rs->conn_uuid); + sock->rx_mtu, p_open->tx_mtu, accept_rs->conn_uuid, accept_rs->socket_id); accept_rs->app_fd = -1; // The fd is closed after sent to app in send_app_connect_signal() // But for some reason we still leak a FD - either the server socket // one or the accept socket one. btsock_l2cap_server_listen(sock); + // start monitoring the socketpair to get call back when app is accepting on server socket + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349375 + btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id); + } } static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) { @@ -578,8 +616,12 @@ static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock->tx_mtu = p_open->tx_mtu; sock->local_cid = p_open->local_cid; sock->remote_cid = p_open->remote_cid; + // TODO(b/342012881) Remove connection uuid when offload socket API is landed. Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>()); sock->conn_uuid = uuid; + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349374 + sock->socket_id = btif_l2cap_sock_generate_socket_id(); + } if (!send_app_psm_or_chan_l(sock)) { log::error("Unable to send l2cap socket to application socket_id:{}", sock->id); @@ -587,15 +629,16 @@ static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* } if (!send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, sock->rx_mtu, - p_open->tx_mtu, sock->conn_uuid)) { + p_open->tx_mtu, sock->conn_uuid, sock->socket_id)) { log::error("Unable to connect l2cap socket to application socket_id:{}", sock->id); return; } log::info( "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, " - "id: {}, is_le: {}", - sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc); + "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id, + sock->rx_mtu); btif_sock_connection_logger(sock->addr, sock->id, sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, SOCKET_CONNECTION_STATE_CONNECTED, @@ -621,10 +664,19 @@ static void on_l2cap_connect(tBTA_JV* p_data, uint32_t id) { sock->tx_mtu = le_open->tx_mtu; if (psm_open->status == tBTA_JV_STATUS::SUCCESS) { - if (!sock->server) { - on_cl_l2cap_psm_connect_l(psm_open, sock); + if (!com::android::bluetooth::flags::socket_settings_api() || // Added with aosp/3349378 + sock->data_path == BTSOCK_DATA_PATH_NO_OFFLOAD) { + if (!sock->server) { + on_cl_l2cap_psm_connect_l(psm_open, sock); + } else { + on_srv_l2cap_psm_connect_l(psm_open, sock); + } } else { - on_srv_l2cap_psm_connect_l(psm_open, sock); + if (!sock->server) { + on_cl_l2cap_psm_connect_offload_l(psm_open, sock); + } else { + on_srv_l2cap_psm_connect_offload_l(psm_open, sock); + } } } else { log::error("Unable to open socket after receiving connection socket_id:{}", sock->id); @@ -825,6 +877,15 @@ static void btsock_l2cap_server_listen(l2cap_socket* sock) { /* Setup ETM settings: mtu will be set below */ std::unique_ptr<tL2CAP_CFG_INFO> cfg = std::make_unique<tL2CAP_CFG_INFO>( tL2CAP_CFG_INFO{.fcr_present = true, .fcr = kDefaultErtmOptions}); + /* For hardware offload data path, host stack sets the initial credits to 0. The offload stack + * should send initial credits to peer device through L2CAP signaling command when the data path + * is switched successfully. */ + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349376 + if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg->init_credit_present = true; + cfg->init_credit = 0; + } + } std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info; if (!sock->is_le_coc) { @@ -835,9 +896,38 @@ static void btsock_l2cap_server_listen(l2cap_socket* sock) { sock->rx_mtu, std::move(cfg), btsock_l2cap_cbk, sock->id); } +/* + * Determine the local MTU for the offloaded L2CAP connection. + * + * The local MTU is selected as the minimum of: + * - The socket hal's offload capabilities (socket_cap.leCocCapabilities.mtu) + * - The application's requested maximum RX packet size (app_max_rx_packet_size) + * + * However, the MTU must be at least the minimum required by the L2CAP LE + * specification (L2CAP_SDU_LENGTH_LE_MIN). + */ + +static bool btsock_l2cap_get_offload_mtu(uint16_t* rx_mtu, uint16_t app_max_rx_packet_size) { + hal::SocketCapabilities socket_cap = + bluetooth::shim::GetLppOffloadManager()->GetSocketCapabilities(); + if (!socket_cap.le_coc_capabilities.number_of_supported_sockets) { + return false; + } + /* Socket HAL client has already verified that the MTU is in a valid range. */ + uint16_t mtu = static_cast<uint16_t>(socket_cap.le_coc_capabilities.mtu); + mtu = std::min(mtu, app_max_rx_packet_size); + if (mtu < L2CAP_SDU_LENGTH_LE_MIN) { + mtu = L2CAP_SDU_LENGTH_LE_MIN; + } + *rx_mtu = mtu; + return true; +} + static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAddress* addr, int channel, int* sock_fd, int flags, char listen, - int app_uid) { + int app_uid, btsock_data_path_t data_path, + const char* socket_name, uint64_t hub_id, + uint64_t endpoint_id, int max_rx_packet_size) { if (!is_inited()) { return BT_STATUS_NOT_READY; } @@ -875,11 +965,31 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd sock->channel = channel; sock->app_uid = app_uid; sock->is_le_coc = is_le_coc; - sock->rx_mtu = is_le_coc ? L2CAP_SDU_LENGTH_LE_MAX : L2CAP_SDU_LENGTH_MAX; + if (com::android::bluetooth::flags::socket_settings_api() && // Added with aosp/3349377 + data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + if (!btsock_l2cap_get_offload_mtu(&sock->rx_mtu, static_cast<uint16_t>(max_rx_packet_size))) { + return BT_STATUS_UNSUPPORTED; + } + } else { + sock->rx_mtu = is_le_coc ? L2CAP_SDU_LENGTH_LE_MAX : L2CAP_SDU_LENGTH_MAX; + } + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349374 + sock->data_path = data_path; + if (socket_name) { + strncpy(sock->socket_name, socket_name, sizeof(sock->socket_name) - 1); + sock->socket_name[sizeof(sock->socket_name) - 1] = '\0'; + } + sock->hub_id = hub_id; + sock->endpoint_id = endpoint_id; + } /* "role" is never initialized in rfcomm code */ if (listen) { btsock_l2cap_server_listen(sock); + // start monitoring the socketpair to get call back when app is accepting on server socket + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349375 + btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id); + } } else { tBTA_JV_CONN_TYPE connection_type = sock->is_le_coc ? tBTA_JV_CONN_TYPE::L2CAP_LE : tBTA_JV_CONN_TYPE::L2CAP; @@ -887,6 +997,15 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd /* Setup ETM settings: mtu will be set below */ std::unique_ptr<tL2CAP_CFG_INFO> cfg = std::make_unique<tL2CAP_CFG_INFO>( tL2CAP_CFG_INFO{.fcr_present = true, .fcr = kDefaultErtmOptions}); + /* For hardware offload data path, host stack sets the initial credits to 0. The offload stack + * should send initial credits to peer device through L2CAP signaling command when the data path + * is switched successfully. */ + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349376 + if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg->init_credit_present = true; + cfg->init_credit = 0; + } + } std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info; if (!sock->is_le_coc) { @@ -909,14 +1028,19 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd return BT_STATUS_SUCCESS; } -bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, - int app_uid) { - return btsock_l2cap_listen_or_connect(name, NULL, channel, sock_fd, flags, 1, app_uid); +bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, int app_uid, + btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) { + return btsock_l2cap_listen_or_connect(name, NULL, channel, sock_fd, flags, 1, app_uid, data_path, + socket_name, hub_id, endpoint_id, max_rx_packet_size); } bt_status_t btsock_l2cap_connect(const RawAddress* bd_addr, int channel, int* sock_fd, int flags, - int app_uid) { - return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags, 0, app_uid); + int app_uid, btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) { + return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags, 0, app_uid, + data_path, socket_name, hub_id, endpoint_id, + max_rx_packet_size); } /* return true if we have more to send and should wait for user readiness, false @@ -962,7 +1086,111 @@ inline uint8_t* get_l2cap_sdu_start_ptr(BT_HDR* msg) { return (uint8_t*)(msg) + BT_HDR_SIZE + msg->offset; } +// state_lock taken by caller +bool btsock_l2cap_read_signaled_on_connected_socket(int fd, int flags, uint32_t user_id, + l2cap_socket* sock) { + if (!sock->connected) { + return false; + } + int size = 0; + bool ioctl_success = ioctl(sock->our_fd, FIONREAD, &size) == 0; + if (!(flags & SOCK_THREAD_FD_EXCEPTION) || (ioctl_success && size)) { + /* FIONREAD return number of bytes that are immediately available for + reading, might be bigger than awaiting packet. + + BluetoothSocket.write(...) guarantees that any packet send to this + socket is broken into pieces no bigger than MTU bytes (as requested + by BT spec). */ + size = std::min(size, (int)sock->tx_mtu); + + BT_HDR* buffer = malloc_l2cap_buf(size); + /* The socket is created with SOCK_SEQPACKET, hence we read one message + * at the time. */ + ssize_t count; + OSI_NO_INTR(count = recv(fd, get_l2cap_sdu_start_ptr(buffer), size, + MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC)); + if (count > sock->tx_mtu) { + /* This can't happen thanks to check in BluetoothSocket.java but leave + * this in case this socket is ever used anywhere else*/ + log::error("recv more than MTU. Data will be lost: {}", count); + count = sock->tx_mtu; + } + + /* When multiple packets smaller than MTU are flushed to the socket, the + size of the single packet read could be smaller than the ioctl + reported total size of awaiting packets. Hence, we adjust the buffer + length. */ + buffer->len = count; + + // will take care of freeing buffer + BTA_JvL2capWrite(sock->handle, PTR_TO_UINT(buffer), buffer, user_id); + } + return true; +} + +// state_lock taken by caller +bool btsock_l2cap_read_signaled_on_listen_socket(int fd, int /* flags */, uint32_t /* user_id */, + l2cap_socket* sock) { + int size = 0; + bool ioctl_success = ioctl(sock->our_fd, FIONREAD, &size) == 0; + if (ioctl_success && size) { + sock_accept_signal_t accept_signal = {}; + ssize_t count; + OSI_NO_INTR(count = recv(fd, reinterpret_cast<uint8_t*>(&accept_signal), sizeof(accept_signal), + MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC)); + if (count != sizeof(accept_signal) || count != accept_signal.size) { + log::error("Unexpected count: {}, sizeof(accept_signal): {}, accept_signal.size: {}", count, + sizeof(accept_signal), accept_signal.size); + return false; + } + sock->is_accepting = accept_signal.is_accepting; + log::info("Server socket: {}, is_accepting: {}", sock->id, sock->is_accepting); + } + return true; +} + +void btsock_l2cap_signaled_flagged(int fd, int flags, uint32_t user_id) { + char drop_it = false; + + /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to + * hold the lock. */ + std::unique_lock<std::mutex> lock(state_lock); + l2cap_socket* sock = btsock_l2cap_find_by_id_l(user_id); + if (!sock) { + return; + } + if (flags & SOCK_THREAD_FD_RD) { + if (!sock->server) { + // app sending data on connection socket + if (!btsock_l2cap_read_signaled_on_connected_socket(fd, flags, user_id, sock)) { + drop_it = true; + } + } else { + // app sending signal on listen socket + if (!btsock_l2cap_read_signaled_on_listen_socket(fd, flags, user_id, sock)) { + drop_it = true; + } + } + } + if (flags & SOCK_THREAD_FD_WR) { + // app is ready to receive more data, tell stack to enable the data flow + if (flush_incoming_que_on_wr_signal_l(sock) && sock->connected) { + btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_WR, sock->id); + } + } + if (drop_it || (flags & SOCK_THREAD_FD_EXCEPTION)) { + int size = 0; + if (drop_it || ioctl(sock->our_fd, FIONREAD, &size) != 0 || size == 0) { + btsock_l2cap_free_l(sock); + } + } +} + void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id) { + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349375 + btsock_l2cap_signaled_flagged(fd, flags, user_id); + return; + } char drop_it = false; /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to @@ -1074,3 +1302,203 @@ bt_status_t btsock_l2cap_get_l2cap_remote_cid(Uuid& conn_uuid, uint16_t* cid) { *cid = sock->remote_cid; return BT_STATUS_SUCCESS; } + +// TODO(b/380189525): Replace the randomized socket ID with static counter when we don't have +// security concerns about using static counter. +static uint64_t btif_l2cap_sock_generate_socket_id() { + uint64_t socket_id; + do { + socket_id = bluetooth::os::GenerateRandomUint64(); + } while (!socket_id); + return socket_id; +} + +/* only call with state_lock taken */ +static l2cap_socket* btsock_l2cap_find_by_socket_id_l(uint64_t socket_id) { + l2cap_socket* sock = socks; + + while (sock) { + if (sock->socket_id == socket_id) { + return sock; + } + sock = sock->next; + } + + return nullptr; +} + +bool btsock_l2cap_in_use(uint64_t socket_id) { + std::unique_lock<std::mutex> lock(state_lock); + return btsock_l2cap_find_by_socket_id_l(socket_id) != nullptr; +} + +void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success) { + l2cap_socket* sock; + + std::unique_lock<std::mutex> lock(state_lock); + sock = btsock_l2cap_find_by_socket_id_l(socket_id); + if (!sock) { + log::error("Unable to find l2cap socket with socket_id:{}", socket_id); + return; + } + if (!success) { + log::error("L2CAP opened complete failed with socket_id:{}", socket_id); + btsock_l2cap_free_l(sock); + return; + } + // If the socket was accepted from listen socket, use listen_fd. + if (sock->listen_fd != -1) { + send_app_connect_signal(sock->listen_fd, &sock->addr, sock->channel, 0, sock->app_fd, + sock->rx_mtu, sock->tx_mtu, sock->conn_uuid, sock->socket_id); + // The fd is closed after sent to app in send_app_connect_signal() + sock->app_fd = -1; + } else { + if (!send_app_psm_or_chan_l(sock)) { + log::error("Unable to send l2cap socket to application socket_id:{}", sock->id); + return; + } + if (!send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, sock->rx_mtu, + sock->tx_mtu, sock->conn_uuid, sock->socket_id)) { + log::error("Unable to connect l2cap socket to application socket_id:{}", sock->id); + return; + } + + log::info( + "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, id: {}, " + "is_le: {}, socket_id: {}, rx_mtu: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id, + sock->rx_mtu); + btif_sock_connection_logger(sock->addr, sock->id, + sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, + SOCKET_CONNECTION_STATE_CONNECTED, + sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, + sock->app_uid, sock->channel, 0, 0, sock->name); + + log::info("Connected l2cap socket socket_id:{}", sock->id); + sock->connected = true; + } +} + +void on_btsocket_l2cap_close(uint64_t socket_id) { + l2cap_socket* sock; + + std::unique_lock<std::mutex> lock(state_lock); + sock = btsock_l2cap_find_by_socket_id_l(socket_id); + if (!sock) { + log::error("Unable to find l2cap socket with socket_id:{}", socket_id); + return; + } + log::info("L2CAP close request for socket_id:{}", socket_id); + btsock_l2cap_free_l(sock); +} + +static void on_cl_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) { + sock->addr = p_open->rem_bda; + sock->tx_mtu = p_open->tx_mtu; + sock->local_cid = p_open->local_cid; + sock->remote_cid = p_open->remote_cid; + // TODO(b/342012881) Remove connection uuid when offload socket API is landed. + Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>()); + sock->conn_uuid = uuid; + sock->socket_id = btif_l2cap_sock_generate_socket_id(); + + log::info( + "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, " + "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id, + sock->rx_mtu); + btif_sock_connection_logger(sock->addr, sock->id, + sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, + SOCKET_CONNECTION_STATE_CONNECTED, + sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, + sock->app_uid, sock->channel, 0, 0, sock->name); + + bluetooth::hal::SocketContext socket_context = { + .socket_id = sock->socket_id, + .name = sock->socket_name, + .acl_connection_handle = p_open->acl_handle, + .channel_info = bluetooth::hal::LeCocChannelInfo( + p_open->local_cid, p_open->remote_cid, static_cast<uint16_t>(sock->channel), + sock->rx_mtu, sock->tx_mtu, p_open->local_coc_mps, p_open->remote_coc_mps, + p_open->local_coc_credit, p_open->remote_coc_credit), + .endpoint_info.hub_id = sock->hub_id, + .endpoint_info.endpoint_id = sock->endpoint_id, + }; + if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) { + log::warn("L2CAP socket opened failed. Disconnect the incoming connection."); + btsock_l2cap_free_l(sock); + } else { + log::info( + "L2CAP socket opened successful. Will send connect signal in " + "on_btsocket_l2cap_opened_complete() asynchronously."); + } +} + +static void on_srv_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) { + // std::mutex locked by caller + l2cap_socket* accept_rs = btsock_l2cap_alloc_l(sock->name, &p_open->rem_bda, false, 0); + accept_rs->connected = true; + accept_rs->security = sock->security; + accept_rs->channel = sock->channel; + accept_rs->handle = sock->handle; + accept_rs->app_uid = sock->app_uid; + sock->handle = -1; /* We should no longer associate this handle with the server socket */ + accept_rs->is_le_coc = sock->is_le_coc; + accept_rs->tx_mtu = sock->tx_mtu = p_open->tx_mtu; + accept_rs->rx_mtu = sock->rx_mtu; + accept_rs->local_cid = p_open->local_cid; + accept_rs->remote_cid = p_open->remote_cid; + // TODO(b/342012881) Remove connection uuid when offload socket API is landed. + Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>()); + accept_rs->conn_uuid = uuid; + accept_rs->socket_id = btif_l2cap_sock_generate_socket_id(); + accept_rs->data_path = sock->data_path; + strncpy(accept_rs->socket_name, sock->socket_name, sizeof(accept_rs->socket_name) - 1); + accept_rs->socket_name[sizeof(accept_rs->socket_name) - 1] = '\0'; + accept_rs->hub_id = sock->hub_id; + accept_rs->endpoint_id = sock->endpoint_id; + accept_rs->listen_fd = sock->our_fd; + + /* Swap IDs to hand over the GAP connection to the accepted socket, and start + a new server on the newly create socket ID. */ + uint32_t new_listen_id = accept_rs->id; + accept_rs->id = sock->id; + sock->id = new_listen_id; + + log::info( + "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, " + "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}", + sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, accept_rs->socket_id, + accept_rs->rx_mtu); + btif_sock_connection_logger(accept_rs->addr, accept_rs->id, + accept_rs->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP, + SOCKET_CONNECTION_STATE_CONNECTED, + accept_rs->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, + accept_rs->app_uid, accept_rs->channel, 0, 0, accept_rs->name); + + bluetooth::hal::SocketContext socket_context = { + .socket_id = accept_rs->socket_id, + .name = accept_rs->socket_name, + .acl_connection_handle = p_open->acl_handle, + .channel_info = bluetooth::hal::LeCocChannelInfo( + p_open->local_cid, p_open->remote_cid, static_cast<uint16_t>(accept_rs->channel), + accept_rs->rx_mtu, accept_rs->tx_mtu, p_open->local_coc_mps, + p_open->remote_coc_mps, p_open->local_coc_credit, p_open->remote_coc_credit), + .endpoint_info.hub_id = accept_rs->hub_id, + .endpoint_info.endpoint_id = accept_rs->endpoint_id, + }; + if (!sock->is_accepting) { + log::warn("Server socket is not accepting. Disconnect the incoming connection."); + btsock_l2cap_free_l(accept_rs); + } else if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) { + log::warn("L2CAP socket opened failed. Disconnect the incoming connection."); + btsock_l2cap_free_l(accept_rs); + } else { + log::info("L2CAP socket opened successful. Will send connect signal in async callback."); + } + // start monitor the socket + btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id); + btsock_l2cap_server_listen(sock); + // start monitoring the socketpair to get call back when app is accepting on server socket + btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id); +} diff --git a/system/btif/src/btif_sock_logging.cc b/system/btif/src/btif_sock_logging.cc index 9ff6980a63..338a47f2f1 100644 --- a/system/btif/src/btif_sock_logging.cc +++ b/system/btif/src/btif_sock_logging.cc @@ -163,7 +163,8 @@ void SockConnectionEvent::dump(const int fd) { } dprintf(fd, " %s\t%s\t%s \t%s \t%d \t%s\t%s\n", eventtime, - ADDRESS_TO_LOGGABLE_CSTR(addr), str_state, str_role, channel, str_type, server_name); + addr.ToRedactedStringForLogging().c_str(), str_state, str_role, channel, str_type, + server_name); } static android::bluetooth::SocketConnectionstateEnum toConnectionStateEnum(int state) { diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc index e8bf6b42fe..de2188c5d8 100644 --- a/system/btif/src/btif_sock_rfc.cc +++ b/system/btif/src/btif_sock_rfc.cc @@ -36,7 +36,10 @@ #include "btif/include/btif_sock_sdp.h" #include "btif/include/btif_sock_thread.h" #include "btif/include/btif_sock_util.h" +#include "gd/os/rand.h" #include "include/hardware/bt_sock.h" +#include "lpp/lpp_offload_interface.h" +#include "main/shim/entry.h" #include "osi/include/allocator.h" #include "osi/include/compat.h" #include "osi/include/list.h" @@ -80,6 +83,7 @@ typedef struct { int fd; int app_fd; // Temporary storage for the half of the socketpair that's // sent back to upper layers. + int listen_fd; // listen socket fd from our side int app_uid; // UID of the app for which this socket was created. int mtu; uint8_t* packet; @@ -92,6 +96,12 @@ typedef struct { int64_t tx_bytes; // Cumulative number of bytes received on this socket int64_t rx_bytes; + uint64_t socket_id; // Socket ID in connected state + btsock_data_path_t data_path; // socket data path + char socket_name[128]; // descriptive socket name + uint64_t hub_id; // ID of the hub to which the end point belongs + uint64_t endpoint_id; // ID of the hub end point + bool is_accepting; // is app accepting on server socket? } rfc_slot_t; static rfc_slot_t rfc_slots[MAX_RFC_CHANNEL]; @@ -106,6 +116,7 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id); static uint32_t rfcomm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t rfcomm_slot_id); static bool send_app_scn(rfc_slot_t* rs); static void handle_discovery_comp(tBTA_JV_STATUS status, int scn, uint32_t id); +static uint64_t btif_rfc_sock_generate_socket_id(); static bool is_init_done(void) { return pth != -1; } @@ -227,9 +238,15 @@ static rfc_slot_t* alloc_rfc_slot(const RawAddress* addr, const char* name, cons slot->fd = fds[0]; slot->app_fd = fds[1]; + slot->listen_fd = -1; slot->security = security; slot->scn = channel; slot->app_uid = -1; + slot->socket_id = 0; + slot->data_path = BTSOCK_DATA_PATH_NO_OFFLOAD; + slot->hub_id = 0; + slot->endpoint_id = 0; + slot->is_accepting = false; slot->is_service_uuid_valid = !uuid.IsEmpty(); slot->service_uuid = uuid; @@ -270,6 +287,16 @@ static rfc_slot_t* create_srv_accept_rfc_slot(rfc_slot_t* srv_rs, const RawAddre accept_rs->rfc_port_handle = BTA_JvRfcommGetPortHdl(open_handle); accept_rs->app_uid = srv_rs->app_uid; + if (com::android::bluetooth::flags::socket_settings_api()) { + accept_rs->socket_id = btif_rfc_sock_generate_socket_id(); + accept_rs->data_path = srv_rs->data_path; + strncpy(accept_rs->socket_name, srv_rs->socket_name, sizeof(accept_rs->socket_name) - 1); + accept_rs->socket_name[sizeof(accept_rs->socket_name) - 1] = '\0'; + accept_rs->hub_id = srv_rs->hub_id; + accept_rs->endpoint_id = srv_rs->endpoint_id; + accept_rs->listen_fd = srv_rs->fd; + } + srv_rs->rfc_handle = new_listen_handle; srv_rs->rfc_port_handle = BTA_JvRfcommGetPortHdl(new_listen_handle); @@ -302,8 +329,32 @@ bt_status_t btsock_rfc_control_req(uint8_t dlci, const RawAddress& bd_addr, uint return BT_STATUS_SUCCESS; } +/// Determine the local MTU for the offloaded RFCOMM connection. +/// +/// The local MTU is selected as the minimum of: +/// - The socket hal's offload capabilities (socket_cap.rfcommCapabilities.max_frame_size) +/// - The application's requested maximum RX packet size (app_max_rx_packet_size) +/// +/// However, the MTU must be at least the minimum required by the RFCOMM +/// specification (RFCOMM_MIN_MTU). +static bool btsock_rfc_get_offload_mtu(int app_max_rx_packet_size, int* rx_mtu) { + hal::SocketCapabilities socket_cap = + bluetooth::shim::GetLppOffloadManager()->GetSocketCapabilities(); + if (!socket_cap.rfcomm_capabilities.number_of_supported_sockets) { + return false; + } + // Socket HAL client has already verified that the MTU is in a valid range. + int mtu = static_cast<int>(socket_cap.rfcomm_capabilities.max_frame_size); + mtu = std::min(mtu, app_max_rx_packet_size); + mtu = std::max(mtu, RFCOMM_MIN_MTU); + *rx_mtu = mtu; + return true; +} + bt_status_t btsock_rfc_listen(const char* service_name, const Uuid* service_uuid, int channel, - int* sock_fd, int flags, int app_uid) { + int* sock_fd, int flags, int app_uid, btsock_data_path_t data_path, + const char* socket_name, uint64_t hub_id, uint64_t endpoint_id, + int max_rx_packet_size) { log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL"); log::assert_that((service_uuid != NULL) || (channel >= 1 && channel <= MAX_RFC_CHANNEL) || ((flags & BTSOCK_FLAG_NO_SDP) != 0), @@ -354,13 +405,33 @@ bt_status_t btsock_rfc_listen(const char* service_name, const Uuid* service_uuid // close(rs->app_fd); slot->app_fd = INVALID_FD; // Drop our reference to the fd. slot->app_uid = app_uid; + if (com::android::bluetooth::flags::socket_settings_api()) { + slot->data_path = data_path; + if (socket_name) { + strncpy(slot->socket_name, socket_name, sizeof(slot->socket_name) - 1); + slot->socket_name[sizeof(slot->socket_name) - 1] = '\0'; + } + slot->hub_id = hub_id; + slot->endpoint_id = endpoint_id; + if (data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + if (!btsock_rfc_get_offload_mtu(max_rx_packet_size, &slot->mtu)) { + return BT_STATUS_UNSUPPORTED; + } + } + } btsock_thread_add_fd(pth, slot->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_EXCEPTION, slot->id); + // start monitoring the socketpair to get call back when app is accepting on server socket + if (com::android::bluetooth::flags::socket_settings_api()) { + btsock_thread_add_fd(pth, slot->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_RD, slot->id); + } return BT_STATUS_SUCCESS; } bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const Uuid* service_uuid, int channel, - int* sock_fd, int flags, int app_uid) { + int* sock_fd, int flags, int app_uid, btsock_data_path_t data_path, + const char* socket_name, uint64_t hub_id, uint64_t endpoint_id, + int max_rx_packet_size) { log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL"); log::assert_that((service_uuid != NULL) || (channel >= 1 && channel <= MAX_RFC_CHANNEL), "assert failed: (service_uuid != NULL) || (channel >= 1 && channel <= " @@ -386,8 +457,8 @@ bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const Uuid* service_uu } if (!service_uuid || service_uuid->IsEmpty()) { - tBTA_JV_STATUS ret = - BTA_JvRfcommConnect(slot->security, slot->scn, slot->addr, rfcomm_cback, slot->id); + tBTA_JV_STATUS ret = BTA_JvRfcommConnect(slot->security, slot->scn, slot->addr, rfcomm_cback, + slot->id, RfcommCfgInfo{}); if (ret != tBTA_JV_STATUS::SUCCESS) { log::error("unable to initiate RFCOMM connection. status:{}, scn:{}, bd_addr:{}", bta_jv_status_text(ret), slot->scn, slot->addr); @@ -416,6 +487,20 @@ bt_status_t btsock_rfc_connect(const RawAddress* bd_addr, const Uuid* service_uu *sock_fd = slot->app_fd; // Transfer ownership of fd to caller. slot->app_fd = INVALID_FD; // Drop our reference to the fd. slot->app_uid = app_uid; + if (com::android::bluetooth::flags::socket_settings_api()) { + slot->data_path = data_path; + if (socket_name) { + strncpy(slot->socket_name, socket_name, sizeof(slot->socket_name) - 1); + slot->socket_name[sizeof(slot->socket_name) - 1] = '\0'; + } + slot->hub_id = hub_id; + slot->endpoint_id = endpoint_id; + if (data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + if (!btsock_rfc_get_offload_mtu(max_rx_packet_size, &slot->mtu)) { + return BT_STATUS_UNSUPPORTED; + } + } + } btsock_thread_add_fd(pth, slot->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_RD, slot->id); return BT_STATUS_SUCCESS; @@ -451,8 +536,8 @@ static void cleanup_rfc_slot(rfc_slot_t* slot) { close(slot->fd); log::info( "disconnected from RFCOMM socket connections for device: {}, scn: {}, " - "app_uid: {}, id: {}", - slot->addr, slot->scn, slot->app_uid, slot->id); + "app_uid: {}, id: {}, socket_id: {}", + slot->addr, slot->scn, slot->app_uid, slot->id, slot->socket_id); btif_sock_connection_logger( slot->addr, slot->id, BTSOCK_RFCOMM, SOCKET_CONNECTION_STATE_DISCONNECTED, slot->f.server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, slot->app_uid, slot->scn, @@ -460,6 +545,14 @@ static void cleanup_rfc_slot(rfc_slot_t* slot) { slot->role ? slot->service_name : slot->service_uuid.ToString().c_str()); slot->fd = INVALID_FD; + + if (com::android::bluetooth::flags::socket_settings_api()) { + if (slot->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD && !slot->f.server && + slot->socket_id != 0) { + bluetooth::shim::GetLppOffloadManager()->SocketClosed(slot->socket_id); + slot->socket_id = 0; + } + } } if (slot->app_fd != INVALID_FD) { @@ -500,7 +593,7 @@ static bool send_app_scn(rfc_slot_t* slot) { } static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, int status, - int send_fd) { + int send_fd, uint64_t socket_id) { sock_connect_signal_t cs; cs.size = sizeof(cs); cs.bd_addr = *addr; @@ -510,6 +603,7 @@ static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, cs.max_tx_packet_size = 0; // not used for RFCOMM cs.conn_uuid_lsb = 0; // not used for RFCOMM cs.conn_uuid_msb = 0; // not used for RFCOMM + cs.socket_id = socket_id; if (send_fd == INVALID_FD) { return sock_send_all(fd, (const uint8_t*)&cs, sizeof(cs)) == sizeof(cs); } @@ -556,6 +650,52 @@ static void on_srv_rfc_listen_started(tBTA_JV_RFCOMM_START* p_start, uint32_t id slot->app_uid, slot->scn, 0, 0, slot->service_name); } +static uint32_t on_srv_rfc_connect_offload(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id, + rfc_slot_t* srv_rs) { + rfc_slot_t* accept_rs; + accept_rs = create_srv_accept_rfc_slot(srv_rs, &p_open->rem_bda, p_open->handle, + p_open->new_listen_handle); + if (!accept_rs) { + return 0; + } + + log::info( + "connected to RFCOMM socket connections for device: {}, scn: {}, " + "app_uid: {}, id: {}, socket_id: {}", + accept_rs->addr, accept_rs->scn, accept_rs->app_uid, id, accept_rs->socket_id); + btif_sock_connection_logger(accept_rs->addr, accept_rs->id, BTSOCK_RFCOMM, + SOCKET_CONNECTION_STATE_CONNECTED, + accept_rs->f.server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, + accept_rs->app_uid, accept_rs->scn, 0, 0, accept_rs->service_name); + + bluetooth::hal::SocketContext socket_context = { + .socket_id = accept_rs->socket_id, + .name = accept_rs->socket_name, + .acl_connection_handle = p_open->acl_handle, + .channel_info = bluetooth::hal::RfcommChannelInfo( + p_open->local_cid, p_open->remote_cid, p_open->rx_mtu, p_open->tx_mtu, + p_open->local_credit, p_open->remote_credit, p_open->dlci, p_open->max_frame_size, + p_open->mux_initiator), + .endpoint_info.hub_id = accept_rs->hub_id, + .endpoint_info.endpoint_id = accept_rs->endpoint_id, + }; + if (!srv_rs->is_accepting) { + log::warn("Server socket is not accepting. Disconnect the incoming connection."); + cleanup_rfc_slot(accept_rs); + } else if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) { + log::warn("RFCOMM socket opened failed. Disconnect the incoming connection."); + cleanup_rfc_slot(accept_rs); + } else { + log::info("RFCOMM socket opened successful. Will send connect signal in async callback."); + } + + // Start monitoring the socket. + btsock_thread_add_fd(pth, srv_rs->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_EXCEPTION, srv_rs->id); + // start monitoring the socketpair to get call back when app is accepting on server socket + btsock_thread_add_fd(pth, srv_rs->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_RD, srv_rs->id); + return srv_rs->id; +} + static uint32_t on_srv_rfc_connect(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id) { log::verbose("id:{}", id); std::unique_lock<std::recursive_mutex> lock(slot_lock); @@ -566,6 +706,11 @@ static uint32_t on_srv_rfc_connect(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id) return 0; } + if (com::android::bluetooth::flags::socket_settings_api() && + srv_rs->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + return on_srv_rfc_connect_offload(p_open, id, srv_rs); + } + accept_rs = create_srv_accept_rfc_slot(srv_rs, &p_open->rem_bda, p_open->handle, p_open->new_listen_handle); if (!accept_rs) { @@ -574,21 +719,61 @@ static uint32_t on_srv_rfc_connect(tBTA_JV_RFCOMM_SRV_OPEN* p_open, uint32_t id) log::info( "connected to RFCOMM socket connections for device: {}, scn: {}, " - "app_uid: {}, id: {}", - accept_rs->addr, accept_rs->scn, accept_rs->app_uid, id); + "app_uid: {}, id: {}, socket_id: {}", + accept_rs->addr, accept_rs->scn, accept_rs->app_uid, id, accept_rs->socket_id); btif_sock_connection_logger(accept_rs->addr, accept_rs->id, BTSOCK_RFCOMM, - SOCKET_CONNECTION_STATE_DISCONNECTED, + SOCKET_CONNECTION_STATE_CONNECTED, accept_rs->f.server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, accept_rs->app_uid, accept_rs->scn, 0, 0, accept_rs->service_name); // Start monitoring the socket. btsock_thread_add_fd(pth, srv_rs->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_EXCEPTION, srv_rs->id); btsock_thread_add_fd(pth, accept_rs->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_RD, accept_rs->id); - send_app_connect_signal(srv_rs->fd, &accept_rs->addr, srv_rs->scn, 0, accept_rs->app_fd); + send_app_connect_signal(srv_rs->fd, &accept_rs->addr, srv_rs->scn, 0, accept_rs->app_fd, + accept_rs->socket_id); accept_rs->app_fd = INVALID_FD; // Ownership of the application fd has been transferred. + // start monitoring the socketpair to get call back when app is accepting on server socket + if (com::android::bluetooth::flags::socket_settings_api()) { + btsock_thread_add_fd(pth, srv_rs->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_RD, srv_rs->id); + } return srv_rs->id; } +static void on_cli_rfc_connect_offload(tBTA_JV_RFCOMM_OPEN* p_open, uint32_t id, rfc_slot_t* slot) { + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(p_open->handle); + slot->addr = p_open->rem_bda; + slot->socket_id = btif_rfc_sock_generate_socket_id(); + + log::info( + "connected to RFCOMM socket connections for device: {}, scn: {}, " + "app_uid: {}, id: {}, socket_id: {}", + slot->addr, slot->scn, slot->app_uid, id, slot->socket_id); + btif_sock_connection_logger( + slot->addr, slot->id, BTSOCK_RFCOMM, SOCKET_CONNECTION_STATE_CONNECTED, + slot->f.server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, slot->app_uid, slot->scn, 0, + 0, slot->service_uuid.ToString().c_str()); + + bluetooth::hal::SocketContext socket_context = { + .socket_id = slot->socket_id, + .name = slot->socket_name, + .acl_connection_handle = p_open->acl_handle, + .channel_info = bluetooth::hal::RfcommChannelInfo( + p_open->local_cid, p_open->remote_cid, p_open->rx_mtu, p_open->tx_mtu, + p_open->local_credit, p_open->remote_credit, p_open->dlci, p_open->max_frame_size, + p_open->mux_initiator), + .endpoint_info.hub_id = slot->hub_id, + .endpoint_info.endpoint_id = slot->endpoint_id, + }; + if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) { + log::warn("RFCOMM socket opened failed. Disconnect the incoming connection."); + cleanup_rfc_slot(slot); + } else { + log::info( + "RFCOMM socket opened successful. Will send connect signal in " + "on_btsocket_rfc_opened_complete() asynchronously."); + } +} + static void on_cli_rfc_connect(tBTA_JV_RFCOMM_OPEN* p_open, uint32_t id) { log::verbose("id:{}", id); std::unique_lock<std::recursive_mutex> lock(slot_lock); @@ -605,25 +790,102 @@ static void on_cli_rfc_connect(tBTA_JV_RFCOMM_OPEN* p_open, uint32_t id) { return; } + if (com::android::bluetooth::flags::socket_settings_api() && + slot->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + on_cli_rfc_connect_offload(p_open, id, slot); + return; + } + slot->rfc_port_handle = BTA_JvRfcommGetPortHdl(p_open->handle); slot->addr = p_open->rem_bda; + slot->socket_id = btif_rfc_sock_generate_socket_id(); log::info( "connected to RFCOMM socket connections for device: {}, scn: {}, " - "app_uid: {}, id: {}", - slot->addr, slot->scn, slot->app_uid, id); + "app_uid: {}, id: {}, socket_id: {}", + slot->addr, slot->scn, slot->app_uid, id, slot->socket_id); btif_sock_connection_logger( slot->addr, slot->id, BTSOCK_RFCOMM, SOCKET_CONNECTION_STATE_CONNECTED, slot->f.server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, slot->app_uid, slot->scn, 0, 0, slot->service_uuid.ToString().c_str()); - if (send_app_connect_signal(slot->fd, &slot->addr, slot->scn, 0, -1)) { + if (send_app_connect_signal(slot->fd, &slot->addr, slot->scn, 0, -1, slot->socket_id)) { slot->f.connected = true; } else { log::error("unable to send connect completion signal to caller."); } } +/* only call with slot_lock taken */ +static rfc_slot_t* find_rfc_slot_by_socket_id(uint64_t socket_id) { + for (size_t i = 0; i < ARRAY_SIZE(rfc_slots); ++i) { + if (rfc_slots[i].socket_id == socket_id) { + return &rfc_slots[i]; + } + } + + return nullptr; +} + +bool btsock_rfc_in_use(uint64_t socket_id) { + std::unique_lock<std::recursive_mutex> lock(slot_lock); + return find_rfc_slot_by_socket_id(socket_id) != nullptr; +} + +void on_btsocket_rfc_opened_complete(uint64_t socket_id, bool success) { + rfc_slot_t* slot; + + std::unique_lock<std::recursive_mutex> lock(slot_lock); + slot = find_rfc_slot_by_socket_id(socket_id); + if (!slot) { + log::error("Unable to find rfcomm socket with socket_id: {}", socket_id); + return; + } + if (!success) { + log::error("RFCOMM opened complete failed with socket_id: {}", socket_id); + cleanup_rfc_slot(slot); + return; + } + // If the socket was accepted from listen socket, use listen_fd. + if (slot->listen_fd != -1) { + send_app_connect_signal(slot->listen_fd, &slot->addr, slot->scn, 0, slot->app_fd, + slot->socket_id); + // The fd is closed after sent to app in send_app_connect_signal() + slot->app_fd = -1; + } else { + if (!send_app_connect_signal(slot->fd, &slot->addr, slot->scn, 0, -1, slot->socket_id)) { + log::error("Unable to connect rfcomm socket to application socket_id: {}", slot->id); + return; + } + + log::info("Connected rfcomm socket socket_id: {}", slot->id); + slot->f.connected = true; + } +} + +void on_btsocket_rfc_close(uint64_t socket_id) { + rfc_slot_t* slot; + + std::unique_lock<std::recursive_mutex> lock(slot_lock); + slot = find_rfc_slot_by_socket_id(socket_id); + if (!slot) { + log::error("Unable to find rfcomm socket with socket_id: {}", socket_id); + return; + } + log::info("RFCOMM close request for socket_id: {}", socket_id); + cleanup_rfc_slot(slot); +} + +// TODO(b/380189525): Replace the randomized socket ID with static counter when we don't have +// security concerns about using static counter. +static uint64_t btif_rfc_sock_generate_socket_id() { + uint64_t socket_id; + do { + socket_id = bluetooth::os::GenerateRandomUint64(); + } while (!socket_id); + return socket_id; +} + static void on_rfc_close(tBTA_JV_RFCOMM_CLOSE* /* p_close */, uint32_t id) { log::verbose("id:{}", id); std::unique_lock<std::recursive_mutex> lock(slot_lock); @@ -768,8 +1030,21 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) { log::debug( "Since UUID is not valid; not setting SDP-record and just starting " "the RFCOMM server"); + // Setup optional configurations + RfcommCfgInfo cfg = {}; + // For hardware offload data path, host stack sets the initial credits to 0. The offload + // stack should send initial credits to peer device through RFCOMM signaling command when + // the data path is switched successfully. + if (com::android::bluetooth::flags::socket_settings_api()) { + if (rs->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg.init_credit_present = true; + cfg.init_credit = 0; + cfg.rx_mtu_present = rs->mtu > 0; + cfg.rx_mtu = rs->mtu; + } + } // now start the rfcomm server after sdp & channel # assigned - BTA_JvRfcommStartServer(rs->security, rs->scn, MAX_RFC_SESSION, rfcomm_cback, rs->id); + BTA_JvRfcommStartServer(rs->security, rs->scn, MAX_RFC_SESSION, rfcomm_cback, rs->id, cfg); } break; } @@ -795,8 +1070,22 @@ static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV* p_data, uint32_t id) { break; } + // Setup optional configurations + RfcommCfgInfo cfg = {}; + // For hardware offload data path, host stack sets the initial credits to 0. The offload + // stack should send initial credits to peer device through RFCOMM signaling command when + // the data path is switched successfully. + if (com::android::bluetooth::flags::socket_settings_api()) { + if (slot->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg.init_credit_present = true; + cfg.init_credit = 0; + cfg.rx_mtu_present = slot->mtu > 0; + cfg.rx_mtu = slot->mtu; + } + } // Start the rfcomm server after sdp & channel # assigned. - BTA_JvRfcommStartServer(slot->security, slot->scn, MAX_RFC_SESSION, rfcomm_cback, slot->id); + BTA_JvRfcommStartServer(slot->security, slot->scn, MAX_RFC_SESSION, rfcomm_cback, slot->id, + cfg); break; } @@ -840,7 +1129,21 @@ static void handle_discovery_comp(tBTA_JV_STATUS status, int scn, uint32_t id) { return; } - if (BTA_JvRfcommConnect(slot->security, scn, slot->addr, rfcomm_cback, slot->id) != + // Setup optional configurations + RfcommCfgInfo cfg = {}; + // For hardware offload data path, host stack sets the initial credits to 0. The offload + // stack should send initial credits to peer device through RFCOMM signaling command when + // the data path is switched successfully. + if (com::android::bluetooth::flags::socket_settings_api()) { + if (slot->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg.init_credit_present = true; + cfg.init_credit = 0; + cfg.rx_mtu_present = slot->mtu > 0; + cfg.rx_mtu = slot->mtu; + } + } + + if (BTA_JvRfcommConnect(slot->security, scn, slot->addr, rfcomm_cback, slot->id, cfg) != tBTA_JV_STATUS::SUCCESS) { log::warn( "BTA_JvRfcommConnect() returned BTA_JV_FAILURE for RFCOMM slot with " @@ -926,7 +1229,94 @@ static bool flush_incoming_que_on_wr_signal(rfc_slot_t* slot) { return true; } -void btsock_rfc_signaled(int /* fd */, int flags, uint32_t id) { +static bool btsock_rfc_read_signaled_on_connected_socket(int /* fd */, int flags, uint32_t /* id */, + rfc_slot_t* slot) { + if (!slot->f.connected) { + log::error("socket signaled for read while disconnected, slot: {}, channel: {}", slot->id, + slot->scn); + return false; + } + // Make sure there's data pending in case the peer closed the socket. + int size = 0; + if (!(flags & SOCK_THREAD_FD_EXCEPTION) || (ioctl(slot->fd, FIONREAD, &size) == 0 && size)) { + BTA_JvRfcommWrite(slot->rfc_handle, slot->id); + } + return true; +} + +static bool btsock_rfc_read_signaled_on_listen_socket(int fd, int /* flags */, uint32_t /* id */, + rfc_slot_t* slot) { + int size = 0; + bool ioctl_success = ioctl(slot->fd, FIONREAD, &size) == 0; + if (ioctl_success && size) { + sock_accept_signal_t accept_signal = {}; + ssize_t count; + OSI_NO_INTR(count = recv(fd, reinterpret_cast<uint8_t*>(&accept_signal), sizeof(accept_signal), + MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC)); + if (count != sizeof(accept_signal) || count != accept_signal.size) { + log::error("Unexpected count: {}, sizeof(accept_signal): {}, accept_signal.size: {}", count, + sizeof(accept_signal), accept_signal.size); + return false; + } + slot->is_accepting = accept_signal.is_accepting; + log::info("Server socket: {}, is_accepting: {}", slot->id, slot->is_accepting); + } + return true; +} + +static void btsock_rfc_signaled_flagged(int fd, int flags, uint32_t id) { + bool need_close = false; + std::unique_lock<std::recursive_mutex> lock(slot_lock); + rfc_slot_t* slot = find_rfc_slot_by_id(id); + if (!slot) { + log::warn("RFCOMM slot with id {} not found.", id); + return; + } + + // Data available from app, tell stack we have outgoing data. + if (flags & SOCK_THREAD_FD_RD) { + if (!slot->f.server) { + // app sending data on connection socket + if (!btsock_rfc_read_signaled_on_connected_socket(fd, flags, id, slot)) { + need_close = true; + } + } else { + // app sending signal on listen socket + if (!btsock_rfc_read_signaled_on_listen_socket(fd, flags, id, slot)) { + need_close = true; + } + } + } + + if (flags & SOCK_THREAD_FD_WR) { + // App is ready to receive more data, tell stack to enable data flow. + if (!slot->f.connected || !flush_incoming_que_on_wr_signal(slot)) { + log::error( + "socket signaled for write while disconnected (or write failure), " + "slot: {}, channel: {}", + slot->id, slot->scn); + need_close = true; + } + } + + if (need_close || (flags & SOCK_THREAD_FD_EXCEPTION)) { + // Clean up if there's no data pending. + int size = 0; + if (need_close || ioctl(slot->fd, FIONREAD, &size) != 0 || !size) { + if (com::android::bluetooth::flags::rfcomm_cancel_ongoing_sdp_on_close() && + slot->f.doing_sdp_request) { + BTA_JvCancelDiscovery(slot->id); + } + cleanup_rfc_slot(slot); + } + } +} + +void btsock_rfc_signaled(int fd, int flags, uint32_t id) { + if (com::android::bluetooth::flags::socket_settings_api()) { + btsock_rfc_signaled_flagged(fd, flags, id); + return; + } bool need_close = false; std::unique_lock<std::recursive_mutex> lock(slot_lock); rfc_slot_t* slot = find_rfc_slot_by_id(id); diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc index 9300c23334..e51756483c 100644 --- a/system/btif/src/btif_storage.cc +++ b/system/btif/src/btif_storage.cc @@ -452,7 +452,6 @@ static bt_status_t btif_in_fetch_bonded_devices(btif_bonded_devices_t* p_bonded_ memset(p_bonded_devices, 0, sizeof(btif_bonded_devices_t)); bool bt_linkkey_file_found = false; - int device_type; for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); @@ -473,6 +472,7 @@ static bt_status_t btif_in_fetch_bonded_devices(btif_bonded_devices_t* p_bonded_ btif_config_get_int(name, BTIF_STORAGE_KEY_PIN_LENGTH, &pin_length); BTA_DmAddDevice(bd_addr, dev_class, link_key, (uint8_t)linkkey_type, pin_length); + int device_type = BT_DEVICE_TYPE_UNKNOWN; if (btif_config_get_int(name, BTIF_STORAGE_KEY_DEV_TYPE, &device_type) && (device_type == BT_DEVICE_TYPE_DUMO)) { btif_gatts_add_bonded_dev_from_nv(bd_addr); @@ -1190,7 +1190,7 @@ bt_status_t btif_storage_get_ble_local_key(uint8_t key_type, Octet16* key_value) bt_status_t btif_in_fetch_bonded_ble_device(const std::string& remote_bd_addr, int add, btif_bonded_devices_t* p_bonded_devices) { - int device_type; + int device_type = BT_DEVICE_TYPE_UNKNOWN; tBLE_ADDR_TYPE addr_type; bool device_added = false; bool key_found = false; @@ -1272,7 +1272,7 @@ bool btif_has_ble_keys(const std::string& bdstr) { ******************************************************************************/ bt_status_t btif_storage_get_remote_addr_type(const RawAddress* remote_bd_addr, tBLE_ADDR_TYPE* addr_type) { - int val; + int val = BLE_ADDR_ANONYMOUS; bool ret = btif_config_get_int(remote_bd_addr->ToString(), BTIF_STORAGE_KEY_ADDR_TYPE, &val); *addr_type = static_cast<tBLE_ADDR_TYPE>(val); return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; @@ -1443,7 +1443,7 @@ void btif_debug_linkkey_type_dump(int fd) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto bdstr = bd_addr.ToString(); int linkkey_type; - dprintf(fd, " %s\n", ADDRESS_TO_LOGGABLE_CSTR(bd_addr)); + dprintf(fd, " %s\n", bd_addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " BR: "); if (btif_config_get_int(bdstr, BTIF_STORAGE_KEY_LINK_KEY_TYPE, &linkkey_type)) { diff --git a/system/btif/src/stack_manager.cc b/system/btif/src/stack_manager.cc index ace7f8e7e2..288cbfcdf3 100644 --- a/system/btif/src/stack_manager.cc +++ b/system/btif/src/stack_manager.cc @@ -330,10 +330,10 @@ static void event_start_up_stack(bluetooth::core::CoreInterface* interface, if (!com::android::bluetooth::flags::scan_manager_refactor()) { info("Starting rust module"); module_start_up(get_local_module(RUST_MODULE)); - } - if (com::android::bluetooth::flags::channel_sounding_in_stack()) { - bluetooth::ras::GetRasServer()->Initialize(); - bluetooth::ras::GetRasClient()->Initialize(); + if (com::android::bluetooth::flags::channel_sounding_in_stack()) { + bluetooth::ras::GetRasServer()->Initialize(); + bluetooth::ras::GetRasClient()->Initialize(); + } } stack_is_running = true; @@ -395,6 +395,11 @@ static void event_shut_down_stack(ProfileStopCallback stopProfiles) { static void event_start_up_rust_module(std::promise<void> promise) { info("is bringing up the Rust module"); module_start_up(get_local_module(RUST_MODULE)); + if (com::android::bluetooth::flags::channel_sounding_in_stack()) { + // GATT server requires the rust module to be running + bluetooth::ras::GetRasServer()->Initialize(); + bluetooth::ras::GetRasClient()->Initialize(); + } promise.set_value(); info("finished"); } diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc index 8d78ce68cf..66881a92d8 100644 --- a/system/btif/test/btif_core_test.cc +++ b/system/btif/test/btif_core_test.cc @@ -1022,9 +1022,14 @@ TEST_F(BtifCoreSocketTest, CreateRfcommServerSocket) { static constexpr int kAppUid = 3; const Uuid server_uuid = Uuid::From16Bit(UUID_SERVCLASS_SERIAL_PORT); int socket_number = 0; + btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD; + uint64_t hub_id = 0; + uint64_t endpoint_id = 0; + int max_rx_packet_size = 0; ASSERT_EQ(BT_STATUS_SUCCESS, - btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid, - kChannelOne, &socket_number, kFlags, kAppUid)); + btif_sock_get_interface()->listen( + BTSOCK_RFCOMM, "TestService", &server_uuid, kChannelOne, &socket_number, kFlags, + kAppUid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size)); } TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) { @@ -1033,9 +1038,14 @@ TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) { static constexpr int kAppUid = 3; const Uuid server_uuid = Uuid::From16Bit(UUID_SERVCLASS_SERIAL_PORT); int socket_number = 0; + btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD; + uint64_t hub_id = 0; + uint64_t endpoint_id = 0; + int max_rx_packet_size = 0; ASSERT_EQ(BT_STATUS_SUCCESS, - btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid, - kChannelOne, &socket_number, kFlags, kAppUid)); + btif_sock_get_interface()->listen( + BTSOCK_RFCOMM, "TestService", &server_uuid, kChannelOne, &socket_number, kFlags, + kAppUid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size)); static constexpr int kChannelTwo = 2; static constexpr int kFlagsTwo = 4; static constexpr int kAppUidTwo = 6; @@ -1043,7 +1053,8 @@ TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) { int socket_number_two = 1; ASSERT_EQ(BT_STATUS_SUCCESS, btif_sock_get_interface()->listen( BTSOCK_RFCOMM, "ServiceTwo", &server_uuid_two, kChannelTwo, - &socket_number_two, kFlagsTwo, kAppUidTwo)); + &socket_number_two, kFlagsTwo, kAppUidTwo, data_path, + "TestSocket", hub_id, endpoint_id, max_rx_packet_size)); } TEST_F(BtifCoreSocketTest, CreateManyRfcommServerSockets) { @@ -1059,9 +1070,14 @@ TEST_F(BtifCoreSocketTest, CreateManyRfcommServerSockets) { server_uuid_str[1] = (i / 100) % 10 + '0'; server_uuid_str[0] = (i / 1000) % 10 + '0'; Uuid server_uuid = Uuid::FromString(server_uuid_str); + btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD; + uint64_t hub_id = 0; + uint64_t endpoint_id = 0; + int max_rx_packet_size = 0; ASSERT_EQ(BT_STATUS_SUCCESS, - btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid, channel, - &socket_number, flags, app_uuid)); + btif_sock_get_interface()->listen( + BTSOCK_RFCOMM, "TestService", &server_uuid, channel, &socket_number, flags, + app_uuid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size)); ASSERT_EQ(0, close(socket_number)); } } diff --git a/system/common/Android.bp b/system/common/Android.bp index 4bc0bebf2d..f3f2a619ca 100644 --- a/system/common/Android.bp +++ b/system/common/Android.bp @@ -20,6 +20,7 @@ cc_library_static { ], srcs: [ "address_obfuscator.cc", + "le_conn_params.cc", "message_loop_thread.cc", "metric_id_allocator.cc", "os_utils.cc", diff --git a/system/common/BUILD.gn b/system/common/BUILD.gn index 8352d2bcc4..a1884973dd 100644 --- a/system/common/BUILD.gn +++ b/system/common/BUILD.gn @@ -17,6 +17,7 @@ static_library("common") { sources = [ "address_obfuscator.cc", + "le_conn_params.cc", "message_loop_thread.cc", "metric_id_allocator.cc", "metrics_linux.cc", diff --git a/system/common/le_conn_params.cc b/system/common/le_conn_params.cc new file mode 100644 index 0000000000..0cfc3675a0 --- /dev/null +++ b/system/common/le_conn_params.cc @@ -0,0 +1,142 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "le_conn_params" + +#include "common/le_conn_params.h" + +#include <bluetooth/log.h> + +#include <cstdint> + +#include "os/system_properties.h" +#include "stack/include/btm_ble_api_types.h" + +using namespace bluetooth; + +const std::string LeConnectionParameters::kPropertyAggressiveConnThreshold = + "bluetooth.core.le.aggressive_connection_threshold"; +const std::string LeConnectionParameters::kPropertyMinConnIntervalAggressive = + "bluetooth.core.le.min_connection_interval_aggressive"; +const std::string LeConnectionParameters::kPropertyMaxConnIntervalAggressive = + "bluetooth.core.le.max_connection_interval_aggressive"; +const std::string LeConnectionParameters::kPropertyMinConnIntervalRelaxed = + "bluetooth.core.le.min_connection_interval_relaxed"; +const std::string LeConnectionParameters::kPropertyMaxConnIntervalRelaxed = + "bluetooth.core.le.max_connection_interval_relaxed"; + +bool LeConnectionParameters::initialized = false; +uint32_t LeConnectionParameters::aggressive_conn_threshold = kAggressiveConnThreshold; +uint32_t LeConnectionParameters::min_conn_interval_aggressive = kMinConnIntervalAggressive; +uint32_t LeConnectionParameters::max_conn_interval_aggressive = kMaxConnIntervalAggressive; +uint32_t LeConnectionParameters::min_conn_interval_relaxed = kMinConnIntervalRelaxed; +uint32_t LeConnectionParameters::max_conn_interval_relaxed = kMaxConnIntervalRelaxed; + +void LeConnectionParameters::InitConnParamsWithSystemProperties() { + aggressive_conn_threshold = + os::GetSystemPropertyUint32(kPropertyAggressiveConnThreshold, kAggressiveConnThreshold); + min_conn_interval_aggressive = os::GetSystemPropertyUint32(kPropertyMinConnIntervalAggressive, + kMinConnIntervalAggressive); + max_conn_interval_aggressive = os::GetSystemPropertyUint32(kPropertyMaxConnIntervalAggressive, + kMaxConnIntervalAggressive); + min_conn_interval_relaxed = + os::GetSystemPropertyUint32(kPropertyMinConnIntervalRelaxed, kMinConnIntervalRelaxed); + max_conn_interval_relaxed = + os::GetSystemPropertyUint32(kPropertyMaxConnIntervalRelaxed, kMaxConnIntervalRelaxed); + + log::debug("Before checking validity: threshold={}, aggressive={}/{}, relaxed={}/{}", + aggressive_conn_threshold, min_conn_interval_aggressive, max_conn_interval_aggressive, + min_conn_interval_relaxed, max_conn_interval_relaxed); + + // Check validity of each values + if (aggressive_conn_threshold < 0) { + log::warn("Invalid aggressive connection threshold. Using default value.", + aggressive_conn_threshold); + aggressive_conn_threshold = kAggressiveConnThreshold; + } + + if (min_conn_interval_aggressive < BTM_BLE_CONN_INT_MIN || + min_conn_interval_aggressive > BTM_BLE_CONN_INT_MAX || + max_conn_interval_aggressive < BTM_BLE_CONN_INT_MIN || + max_conn_interval_aggressive > BTM_BLE_CONN_INT_MAX || + max_conn_interval_aggressive < min_conn_interval_aggressive) { + log::warn("Invalid aggressive connection intervals. Using default values."); + min_conn_interval_aggressive = kMinConnIntervalAggressive; + max_conn_interval_aggressive = kMaxConnIntervalAggressive; + } + + if (min_conn_interval_relaxed < BTM_BLE_CONN_INT_MIN || + min_conn_interval_relaxed > BTM_BLE_CONN_INT_MAX || + max_conn_interval_relaxed < BTM_BLE_CONN_INT_MIN || + max_conn_interval_relaxed > BTM_BLE_CONN_INT_MAX || + max_conn_interval_relaxed < min_conn_interval_relaxed) { + log::warn("Invalid relaxed connection intervals. Using default values."); + min_conn_interval_relaxed = kMinConnIntervalRelaxed; + max_conn_interval_relaxed = kMaxConnIntervalRelaxed; + } + + if ((min_conn_interval_aggressive > min_conn_interval_relaxed) && + (max_conn_interval_aggressive > max_conn_interval_relaxed)) { + log::warn( + "Relaxed connection intervals are more aggressive than aggressive ones." + " Setting all intervals to default values."); + min_conn_interval_aggressive = kMinConnIntervalAggressive; + max_conn_interval_aggressive = kMaxConnIntervalAggressive; + min_conn_interval_relaxed = kMinConnIntervalRelaxed; + max_conn_interval_relaxed = kMaxConnIntervalRelaxed; + } + + log::debug("After checking validity: threshold={}, aggressive={}/{}, relaxed={}/{}", + aggressive_conn_threshold, min_conn_interval_aggressive, max_conn_interval_aggressive, + min_conn_interval_relaxed, max_conn_interval_relaxed); + + initialized = true; +} + +uint32_t LeConnectionParameters::GetAggressiveConnThreshold() { + if (!initialized) { + InitConnParamsWithSystemProperties(); + } + return aggressive_conn_threshold; +} + +uint32_t LeConnectionParameters::GetMinConnIntervalAggressive() { + if (!initialized) { + InitConnParamsWithSystemProperties(); + } + return min_conn_interval_aggressive; +} + +uint32_t LeConnectionParameters::GetMaxConnIntervalAggressive() { + if (!initialized) { + InitConnParamsWithSystemProperties(); + } + return max_conn_interval_aggressive; +} + +uint32_t LeConnectionParameters::GetMinConnIntervalRelaxed() { + if (!initialized) { + InitConnParamsWithSystemProperties(); + } + return min_conn_interval_relaxed; +} + +uint32_t LeConnectionParameters::GetMaxConnIntervalRelaxed() { + if (!initialized) { + InitConnParamsWithSystemProperties(); + } + return max_conn_interval_relaxed; +} diff --git a/system/common/le_conn_params.h b/system/common/le_conn_params.h new file mode 100644 index 0000000000..2365742ac2 --- /dev/null +++ b/system/common/le_conn_params.h @@ -0,0 +1,51 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <bluetooth/log.h> + +#include <cstdint> + +class LeConnectionParameters { +public: + static constexpr uint16_t kAggressiveConnThreshold = 2; + static constexpr uint16_t kMinConnIntervalAggressive = 0x0008; // 8, *1.25 becomes 10ms + static constexpr uint16_t kMaxConnIntervalAggressive = 0x0010; // 16, *1.25 becomes 20ms + static constexpr uint16_t kMinConnIntervalRelaxed = 0x0018; // 24, *1.25 becomes 30ms + static constexpr uint16_t kMaxConnIntervalRelaxed = 0x0028; // 40, *1.25 becomes 50ms + + static const std::string kPropertyAggressiveConnThreshold; + static const std::string kPropertyMinConnIntervalAggressive; + static const std::string kPropertyMaxConnIntervalAggressive; + static const std::string kPropertyMinConnIntervalRelaxed; + static const std::string kPropertyMaxConnIntervalRelaxed; + + static void InitConnParamsWithSystemProperties(); + static uint32_t GetAggressiveConnThreshold(); + static uint32_t GetMinConnIntervalAggressive(); + static uint32_t GetMaxConnIntervalAggressive(); + static uint32_t GetMinConnIntervalRelaxed(); + static uint32_t GetMaxConnIntervalRelaxed(); + +private: + static bool initialized; + static uint32_t aggressive_conn_threshold; + static uint32_t min_conn_interval_aggressive; + static uint32_t max_conn_interval_aggressive; + static uint32_t min_conn_interval_relaxed; + static uint32_t max_conn_interval_relaxed; +}; diff --git a/system/common/message_loop_thread.cc b/system/common/message_loop_thread.cc index 3cf5f1af38..1de1ff036e 100644 --- a/system/common/message_loop_thread.cc +++ b/system/common/message_loop_thread.cc @@ -18,7 +18,6 @@ #include <base/functional/callback.h> #include <base/location.h> -#include <base/strings/stringprintf.h> #include <base/time/time.h> #include <bluetooth/log.h> #include <sys/syscall.h> @@ -130,7 +129,7 @@ std::string MessageLoopThread::GetName() const { return thread_name_; } std::string MessageLoopThread::ToString() const { std::lock_guard<std::recursive_mutex> api_lock(api_mutex_); - return base::StringPrintf("%s(%d)", thread_name_.c_str(), thread_id_); + return std::format("{}({})", thread_name_, thread_id_); } bool MessageLoopThread::IsRunning() const { diff --git a/system/device/Android.bp b/system/device/Android.bp index 5411561278..eb71e3f414 100644 --- a/system/device/Android.bp +++ b/system/device/Android.bp @@ -29,9 +29,6 @@ cc_library_static { apex_available: [ "com.android.btservices", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], min_sdk_version: "Tiramisu", header_libs: ["libbluetooth_headers"], static_libs: [ diff --git a/system/device/fuzzer/Android.bp b/system/device/fuzzer/Android.bp index d1b7357de9..c7a6917898 100644 --- a/system/device/fuzzer/Android.bp +++ b/system/device/fuzzer/Android.bp @@ -40,9 +40,6 @@ cc_fuzz { "liblog", "server_configurable_flags", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], static_libs: [ "bluetooth_flags_c_lib", "libbluetooth-types", diff --git a/system/gd/Android.bp b/system/gd/Android.bp index 9ac1e58995..aae5f1deb2 100644 --- a/system/gd/Android.bp +++ b/system/gd/Android.bp @@ -87,6 +87,7 @@ cc_defaults { srcs: [ ":BluetoothHalSources_hci_host", ":BluetoothHalSources_ranging_host", + ":BluetoothHalSources_socket_host", ":BluetoothOsSources_host", ":BluetoothSyspropsSources", ], @@ -95,6 +96,7 @@ cc_defaults { srcs: [ ":BluetoothHalSources_hci_android_hidl", ":BluetoothHalSources_ranging_android", + ":BluetoothHalSources_socket_android", ":BluetoothOsSources_android", ], shared_libs: [ @@ -117,27 +119,22 @@ cc_defaults { whole_static_libs: [ "android.hardware.bluetooth-V1-ndk", "android.hardware.bluetooth.ranging-V2-ndk", + "android.hardware.bluetooth.socket-V1-ndk", + "android.hardware.contexthub-V4-ndk", ], }, }, srcs: [ ":BluetoothCommonSources", - ":BluetoothDumpsysSources", ":BluetoothHalSources", ":BluetoothHciSources", + ":BluetoothLppOffloadSources", ":BluetoothMetricsSources", ":BluetoothNeighborSources", ":BluetoothOsSources", ":BluetoothPacketSources", - ":BluetoothShimSources", ":BluetoothStorageSources", "module.cc", - "module_dumper.cc", - "stack_manager.cc", - ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", ], shared_libs: [ "libcrypto", @@ -156,7 +153,6 @@ cc_defaults { static_libs: [ "libaconfig_storage_read_api_cc", "libbase", - "libbluetooth-dumpsys", "libbluetooth-protos", "libbluetooth-types", "libbluetooth_crypto_toolbox", @@ -238,98 +234,6 @@ cc_library_static { ], } -cc_binary { - name: "bluetooth_stack_with_facade", - defaults: [ - "gd_defaults", - ], - cflags: [ - // The generated gRPC code triggers these warnings. - "-Wno-missing-prototypes", - ], - include_dirs: [ - "packages/modules/Bluetooth/system", - "packages/modules/Bluetooth/system/include", - ], - host_supported: true, - srcs: [ - ":BluetoothFacade_hci_hal", - ":BluetoothFacade_hci_layer", - ":BluetoothFacade_neighbor", - ":TestMockMainShimStack", - "facade/facade_main.cc", - "facade/grpc_root_server.cc", - "facade/read_only_property_server.cc", - "grpc/grpc_module.cc", - ], - generated_headers: [ - "BlueberryFacadeGeneratedStub_h", - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], - generated_sources: [ - "BlueberryFacadeGeneratedStub_cc", - ], - static_libs: [ - "bluetooth_flags_c_lib", - "breakpad_client", - "libPlatformProperties", - "libbluetooth-dumpsys", - "libbluetooth-protos", - "libbluetooth-types", - "libbluetooth_crypto_toolbox", - "libbluetooth_gd", - "libbluetooth_hci_pdl", - "libbluetooth_l2cap_pdl", - "libbluetooth_log", - "libbluetooth_ras_pdl", - "libbluetooth_smp_pdl", - "libbt-common", - "libchrome", - "libcom.android.sysprop.bluetooth.wrapped", - "libflatbuffers-cpp", - "libosi", - ], - shared_libs: [ - "libaconfig_storage_read_api_cc", - "libbase", - "libcrypto", - "libgrpc++", - "libgrpc_wrap", - "liblog", - "libprotobuf-cpp-full", - "libunwindstack", - "server_configurable_flags", - ], - target: { - android: { - shared_libs: [ - "android.hardware.bluetooth@1.0", - "android.hardware.bluetooth@1.1", - "libbinder_ndk", - "libcutils", - "libhidlbase", - "libstatssocket", - "libutils", - ], - static_libs: [ - "android.system.suspend-V1-ndk", - "android.system.suspend.control-V1-ndk", - "libstatslog_bt", - ], - }, - host: { - required: [ - "root-canal", - ], - }, - }, - sanitize: { - address: true, - cfi: true, - }, -} - cc_test { name: "bluetooth_test_with_timerfd", test_suites: ["general-tests"], @@ -363,6 +267,7 @@ cc_test { "libchrome", "libgmock", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], shared_libs: [ "libbase", @@ -430,14 +335,9 @@ cc_test { ":BluetoothOsTestSources", ":BluetoothPacketTestSources", ":BluetoothStorageUnitTestSources", + ":TestCommonMockFunctions", + ":TestMockStackMetrics", "module_unittest.cc", - "stack_manager_unittest.cc", - ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - "BluetoothGeneratedDumpsysInternalTestData_h", - "BluetoothGeneratedDumpsysTestData_h", ], static_libs: [ "bluetooth_flags_c_lib_for_test", @@ -541,9 +441,6 @@ cc_defaults { "libosi", ], host_supported: true, - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], shared_libs: [ "libPlatformProperties", "libaconfig_storage_read_api_cc", @@ -600,8 +497,16 @@ cc_fuzz { name: "bluetooth_gd_acl_manager_fuzz_test", defaults: ["gd_fuzz_defaults"], srcs: [ + ":TestCommonMockFunctions", + ":TestMockStackMetrics", "hci/fuzz/acl_manager_fuzz_test.cc", ], + include_dirs: [ + "packages/modules/Bluetooth/system", + ], + static_libs: [ + "libbt-platform-protos-lite", + ], fuzz_config: { cc: ["android-bluetooth-security@google.com"], componentid: 27441, @@ -629,38 +534,3 @@ cc_benchmark { "server_configurable_flags", ], } - -// Generates binary schema data to be bundled and source file generated -genrule { - name: "BluetoothGeneratedDumpsysBinarySchema_bfbs", - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) $(in) ", - srcs: [ - "dumpsys_data.fbs", - "module_unittest.fbs", - "os/wakelock_manager.fbs", - ], - out: [ - "dumpsys_data.bfbs", - "wakelock_manager.bfbs", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysDataSchema_h", - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -o $(genDir) --cpp $(in) ", - srcs: [ - "dumpsys_data.fbs", - "module_unittest.fbs", - "os/wakelock_manager.fbs", - ], - out: [ - "dumpsys_data_generated.h", - "wakelock_manager_generated.h", - ], -} diff --git a/system/gd/BUILD.gn b/system/gd/BUILD.gn index 14d5044d68..2ee45fde74 100644 --- a/system/gd/BUILD.gn +++ b/system/gd/BUILD.gn @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//bt/system/gd/dumpsys/bundler/bundler.gni") import("//bt/system/gd/packet/parser/packetgen.gni") import("//common-mk/flatbuffer.gni") @@ -47,8 +46,6 @@ config("rust_defaults") { group("gd_default_deps") { deps = [ "//bt/system:libbt-platform-protos-lite", - "//bt/system/gd:BluetoothGeneratedDumpsysDataSchema_h", - "//bt/system/gd/dumpsys:libbluetooth-dumpsys", "//bt/system/pdl:BluetoothGeneratedPackets_h", ] } @@ -56,8 +53,6 @@ group("gd_default_deps") { static_library("libbluetooth_gd") { sources = [ "module.cc", - "module_dumper.cc", - "stack_manager.cc", ] include_dirs = [ "." ] @@ -65,31 +60,14 @@ static_library("libbluetooth_gd") { deps = [ "//bt/system/gd/rust/topshim:libbluetooth_topshim", "//bt/system/gd/common:BluetoothCommonSources", - "//bt/system/gd/dumpsys:BluetoothDumpsysSources", "//bt/system/gd/hal:BluetoothHalSources", "//bt/system/gd/hal:BluetoothHalSources_hci_host", "//bt/system/gd/hal:BluetoothHalSources_ranging_host", + "//bt/system/gd/hal:BluetoothHalSources_socket_host", "//bt/system/gd/metrics:BluetoothMetricsSources", "//bt/system/gd/neighbor:BluetoothNeighborSources", - "//bt/system/gd/shim:BluetoothShimSources", "//bt/system/gd/storage:BluetoothStorageSources", "//bt/system/gd/sysprops:BluetoothSyspropsSources", "//bt/system/pdl:BluetoothGeneratedPackets_h", ] } - -flatbuffer("BluetoothGeneratedDumpsysDataSchema_h") { - sources = [ - "dumpsys_data.fbs", - "os/wakelock_manager.fbs", - ] -} - -bt_flatc_binary_schema("BluetoothGeneratedDumpsysBinarySchema_bfbs") { - sources = [ - "dumpsys_data.fbs", - "os/wakelock_manager.fbs", - ] - - include_dir = "system/gd" -} diff --git a/system/gd/cert/change_waiter.py b/system/gd/cert/change_waiter.py deleted file mode 100644 index 30a72f9671..0000000000 --- a/system/gd/cert/change_waiter.py +++ /dev/null @@ -1,18 +0,0 @@ -import pyinotify -import sys - -wm = pyinotify.WatchManager() -mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | pyinotify.IN_MODIFY - - -class EventHandler(pyinotify.ProcessEvent): - - def process_default(self, event): - quit() - - -handler = EventHandler() -notifier = pyinotify.Notifier(wm, handler) -wdd = wm.add_watch(sys.argv[1], mask, rec=True) - -notifier.loop() diff --git a/system/gd/cert/gen_html_coverage.sh b/system/gd/cert/gen_html_coverage.sh deleted file mode 100755 index bfb1edb3bf..0000000000 --- a/system/gd/cert/gen_html_coverage.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -pushd $ANDROID_BUILD_TOP - -llvm-cov show --format=html --summary-only --show-line-counts-or-regions --show-instantiation-summary --instr-profile=/tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage.profdata --output-dir=/tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage/ out/dist/bluetooth_venv/lib/python3.8/site-packages/bluetooth_stack_with_facade - -popd - -echo "point your browser to file:///tmp/logs/HostOnlyCert/latest/GdDevice_dut_backing_process_coverage/index.html" - diff --git a/system/gd/cert/irun b/system/gd/cert/irun deleted file mode 100755 index 062f07f6cf..0000000000 --- a/system/gd/cert/irun +++ /dev/null @@ -1,10 +0,0 @@ -#! /bin/bash -# interactive version of run - -trap "exit;" SIGINT SIGTERM - -while true -do - ${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/gd/cert/run "$@" - python3 ${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/gd/cert/change_waiter.py ${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system -done diff --git a/system/gd/cert/run b/system/gd/cert/run deleted file mode 100755 index 94708ff69c..0000000000 --- a/system/gd/cert/run +++ /dev/null @@ -1,495 +0,0 @@ -#! /bin/bash - -YELLOW="\033[1;33m" -NOCOLOR="\033[0m" -BLUE="\033[1;34m" -RED="\033[1;91m" - -function happy_hedgehog { - echo -e "\t${BLUE}" - echo -e "\t ___------__" - echo -e "\t |\__-- /\ _-" - echo -e "\t |/_ __ -" - echo -e "\t // \ / \ /__" - echo -e "\t | 0 | 0 |__ --_ Gotta go fast!" - echo -e "\t \\____-- __ \ ___-" - echo -e "\t ( @ __/ / /_" - echo -e "\t -_____--- --_" - echo -e "\t // \ \\ ___-" - echo -e "\t //|\__/ \\ \\" - echo -e "\t \_-\_____/ \-\\" - echo -e "\t // \\--\|" - echo -e "\t ${RED}____${BLUE}// ||${RED}_" - echo -e "\t${RED} /_____\ /___\\" - echo -e "${NOCOLOR}" -} - -function sad_hedgehog { - echo -e "\t${BLUE}" - echo -e "\t ___------__" - echo -e "\t |\__-- /\ _-" - echo -e "\t |/_ __ -" - echo -e "\t // \ / \ /__" - echo -e "\t | 0 | 0 |__ --_ Gotta go sllloowwww!" - echo -e "\t \\____-- __ \ ___-" - echo -e "\t ( @ __ / /_" - echo -e "\t -_____--- --_" - echo -e "\t // \ \\ ___-" - echo -e "\t //|\__/ \\ \\" - echo -e "\t \_-\_____/ \-\\" - echo -e "\t // \\--\|" - echo -e "\t ${RED} ____${BLUE}// ||${RED}_" - echo -e "\t${RED} /_____\ /___\\" - echo -e "{$NOCOLOR}" -} - -PYTHON_BIN="python3.11" - -function check_environment { - if [[ -z "${ANDROID_BUILD_TOP}" ]] || [[ -z "${ANDROID_HOST_OUT}" ]] ; then - echo -e "${RED}ANDROID_BUILD_TOP${NOCOLOR} or ${RED}ANDROID_HOST_OUT${NOCOLOR} is not set for host run" - echo -e "Navigate to android root and run:" - echo -e "${YELLOW}" - echo -e ". build/envsetup.sh" - echo -e "lunch <fish>" - echo -e "${NOCOLOR}" - echo - exit 1 - fi - if ! [ -x "$(command -v ${PYTHON_BIN})" ] ; then - echo -e "${RED}You must have ${PYTHON_BIN} installed${NOCOLOR}" - exit 1 - fi - ${PYTHON_BIN} -m venv -h > /dev/null - if [[ $? -ne 0 ]] ; then - echo -e "${RED}venv not available for ${PYTHON_BIN}${NOCOLOR}" - exit 1 - fi -} - -ASHMEM_OUT="/dev/shm/out" -ASHMEM_DIST="${ASHMEM_OUT}/dist" -ASHMEM_VENV="${ASHMEM_DIST}/bluetooth_venv" -ASHMEM_GOTTA_GO_FAST="/dev/shm/gottagofast" -ASHMEM_HOST_LOGS="${ASHMEM_GOTTA_GO_FAST}/logs" -ASHMEM_OUT_TARGET="${ASHMEM_GOTTA_GO_FAST}/target" -ASHMEM_SOONG="${ASHMEM_GOTTA_GO_FAST}/out/soong" -CERT_HOST_LOGS="/tmp/logs/HostOnlyCert" -CERT_DEVICE_LOGS="TODO: Add this" -CERT_TEST_VENV=${ANDROID_BUILD_TOP}/out/dist/bluetooth_venv -OUT_TARGET="${ANDROID_BUILD_TOP}/out/target" -TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/gd/host_config.yaml" -TEST_FILTER="--presubmit" -TEST_RUNNER="blueberry/tests/gd/gd_test_runner.py" -CPP_BUILD_TARGET="bluetooth_stack_with_facade root-canal gd_hci_packets_python3_gen gd_smp_packets_python3_gen" -RUST_BUILD_TARGET="bluetooth_with_facades root-canal bt_topshim_facade gd_hci_packets_python3_gen gd_smp_packets_python3_gen" -BUILD_TARGET=$CPP_BUILD_TARGET - -CLEAN_VENV=false -GOTTA_GO_FAST=false -NUM_REPETITIONS="1" -SKIP_SOONG_BUILD=false -USE_ASHMEM_VENV=true -VERBOSE_MODE=false -DEVICE_TEST=false - -## Verify devices connected and valid -DUT_SERIAL="DUT Not Set" -DUT_ADB="DUT Not Set" -DUT_NAME="DUT Not Set" - -# Used for position arguments needed for later -POSITIONAL=() -function parse_options { - while [[ $# -gt 0 ]] - do - key="$1" - case $key in - # This will delete the existing venv before running the test - # If you updated external libraries such as ACTS, you need to add this flag - --clean) - CLEAN_VENV=true - shift # past argument - ;; - --help) - echo - echo -e "${YELLOW}Help menu${NOCOLOR}" - echo -e "===================================" - echo -e "${BLUE} --clean${NOCOLOR}" - echo -e " Clean the virtul environment; use if ACTS has been updated." - echo -e "${BLUE} --disable-ashmem-venv${NOCOLOR}" - echo -e " Places the virtual environment on disk rather than in ashmem which is default." - echo -e "${BLUE} --gotta-go-fast${NOCOLOR}" - echo -e " Makes use of ashmem as best as possible for targeted speed increases." - echo -e "${BLUE} --device${NOCOLOR}" - echo -e " Run the test on the 2 real devices." - echo -e "${BLUE} --topshim${NOCOLOR}" - echo -e " Run Topshim combination tests using the default topshim config." - echo -e "${BLUE} --rust${NOCOLOR}" - echo -e " Run the test using the rust implementation on the 2 real devices." - echo -e "${BLUE} --rhost${NOCOLOR}" - echo -e " Run the test using the rust implementation on the host." - echo -e "${BLUE} --repeat=<N>${NOCOLOR}" - echo -e " Repeat the test sequence N (int) number of times." - echo -e "${BLUE} --skip-soong-build${NOCOLOR}" - echo -e " Skips building soong targets. Use when you are just modifying simple python files." - echo -e "${BLUE} --test_config=<configfile>${NOCOLOR}" - echo -e " Override default test configuration." - echo -e "${BLUE} --verbose${NOCOLOR}" - echo -e " Displays device logs and test logs to output." - echo - echo -e "Usage: $0 [--clean|--repeat=<N>|--test_config=<config>] [TestGroupName[.IndividualTestName]]" - echo -e " ${YELLOW}e.g." - echo -e " $0 --clean SecurityTest" - echo -e " $0 --verbose SecurityTest:test_dut_initiated_display_only_display_only ${NOCOLOR}" - echo - shift - exit 0 - ;; - # This will cause the bluetooth_venv to NOT be created in ashmem - # Using ashmem increases --clean build times by 40% (~21 seconds on my machine) - --disable-ashmem-venv) - USE_ASHMEM_VENV=false - shift # past argument - ;; - --gotta-go-fast) - GOTTA_GO_FAST=true - shift # past argument - ;; - --device) - DEVICE_TEST=true - TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/gd/devices_config.yaml" - shift # past argument - ;; - # Repeat running the specified test cases by N times in one single setup - --repeat=*) - NUM_REPETITIONS="${key#*=}" - shift # past argument - ;; - --skip-soong-build) - SKIP_SOONG_BUILD=true - shift - ;; - --test_config=*) - TEST_CONFIG="${key#*=}" - shift # past argument - ;; - --rust) - BUILD_TARGET=$RUST_BUILD_TARGET - export RUST_BACKTRACE=1 - TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/gd/rust_devices_config.yaml" - DEVICE_TEST=true - shift # past argument - ;; - --rhost) - export RUST_BACKTRACE=1 - BUILD_TARGET=$RUST_BUILD_TARGET - TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/gd/rust_host_config.yaml" - shift # past argument - ;; - --topshim) - TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/topshim/topshim_host_config.yaml" - TEST_RUNNER="blueberry/tests/topshim/topshim_test_runner.py" - shift - ;; - # This will log everything to both log file and stdout - --verbose) - VERBOSE_MODE=true - shift # past argument - ;; - --*) # unknown argument - echo "$0: unrecognized argument '$1'" - echo "Try '$0 --help' for more information" - exit 1 - shift - ;; - *) # unknown option - POSITIONAL+=("$1") # save it in an array for later - shift # past argument - ;; - esac - done - set -- "${POSITIONAL[@]}" # restore positional parameters - - # Set the test filter - if [[ -n "$1" ]] ; then - TEST_FILTER="--tests $1" - fi - - INSTALL_ARGS="--reuse-libraries" - if [ "$CLEAN_VENV" == true ] ; then - echo -e "${YELLOW}Cleaning up existing virtualenv${NOCOLOR}" - rm -rf $CERT_TEST_VENV/* - rm -rf $CERT_TEST_VENV - mkdir -p ${CERT_TEST_VENV} - INSTALL_ARGS="" - else - echo -e "${YELLOW}Try to reuse existing virtualenv at ${CERT_TEST_VENV}${NOCOLOR}" - fi - -} - -function select_devices { - if [ "$DEVICE_TEST" == true ] ; then - RR="$(cat ${TEST_CONFIG}|grep \'CERT\\\|DUT\')" - if [ "$RR" != "" ]; then - DUT_SERIAL="$(menu-adb DUT)" - DUT_ADB="adb -s ${DUT_SERIAL}" - DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)" - CERT_SERIAL="$(menu-adb CERT)" - CERT_ADB="adb -s ${CERT_SERIAL}" - CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)" - - if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then - echo - echo -e "${RED}ERROR: CERT and DUT cannot be the same device, or you only have one device connected!${NOCOLOR}" - echo - exit 1 - fi - - ## Set android devices in config - sed -i "s/'DUT'/'${DUT_SERIAL}'/g" ${TEST_CONFIG} - sed -i "s/'CERT'/'${CERT_SERIAL}'/g" ${TEST_CONFIG} - fi - fi -} - -function soong_build { - if [ "$CLEAN_VENV" == true ] ; then - $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc) - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}" - exit 1 - fi - else - $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc) - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}" - exit 1 - fi - fi -} - -function setup_venv { - # Make venv in memory, decreases --clean build times by 40% - # Caveat is you lose the venv if the computer reboots - if [ "${USE_ASHMEM_VENV}" == true ] ; then - echo -e "${BLUE}Using ashmem virtual environment.${NOCOLOR}" - if [[ ! -L ${CERT_TEST_VENV} ]] ; then - echo -e "${BLUE}" - echo -ne "Creating ashmem dist folder..." - mkdir -p "${ASHMEM_VENV}" - # Ensure the directory doesn't exist - rm -rf "${CERT_TEST_VENV}" - echo -e "Done" - echo -ne "Sym linking ${ASHMEM_VENV} to ${CERT_TEST_VENV}..." - ln -s "${ASHMEM_VENV}" "${CERT_TEST_VENV}" - echo -e "Done" - echo -e "${NOCOLOR}" - fi - else - echo -e "${RED}Not using ashmem virtual environment.${NOCOLOR}" - if [[ -L ${CERT_TEST_VENV} ]] ; then - echo -e "${RED}" - echo -en "Removing sym link from ${ASHMEM_VENV} to ${CERT_TEST_VENV}..." - rm -rf ""${ASHMEM_VENV} "${CERT_TEST_VENV}" - echo -e "Done" - echo -en "Cleaning up memory..." - rm -rf "${ASHMEM_VENV}" - echo -e "Done" - echo -e "${NOCOLOR}" - fi - fi - ${PYTHON_BIN} -m venv --clear "${CERT_TEST_VENV}" - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Error setting up virtualenv${NOCOLOR}" - exit 1 - fi - - unzip -o -q "${ANDROID_BUILD_TOP}/out/dist/bluetooth_cert_tests.zip" -d "${CERT_TEST_VENV}/sources" - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Error unzipping bluetooth_cert_tests.zip${NOCOLOR}" - exit 1 - fi - - venv_common -} - -function incremental_venv { -#LINT.IfChange - HOST_BIN="${ANDROID_BUILD_TOP}/out/host/linux-x86/bin" - HOST_LIB="${ANDROID_BUILD_TOP}/out/host/linux-x86/lib64" - DEST_DIR="${ANDROID_BUILD_TOP}/out/dist/bluetooth_venv/sources" - DEST_LIB_DIR="${DEST_DIR}/lib64" - cp {$HOST_BIN,$DEST_DIR}/bluetooth_stack_with_facade - cp {$HOST_BIN,$DEST_DIR}/bluetooth_with_facades - cp {$HOST_BIN,$DEST_DIR}/bt_topshim_facade - cp {$HOST_BIN,$DEST_DIR}/root-canal - - cp {$HOST_LIB,$DEST_LIB_DIR}/libbase.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libbluetooth_gd.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libc++.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libchrome.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libcrypto-host.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libevent-host.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libgrpc++.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libgrpc_wrap.so - cp {$HOST_LIB,$DEST_LIB_DIR}/liblog.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libssl-host.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libz-host.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libprotobuf-cpp-full.so - cp {$HOST_LIB,$DEST_LIB_DIR}/libunwindstack.so - cp {$HOST_LIB,$DEST_LIB_DIR}/liblzma.so - - i="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/setup.py" - cp {${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system,$DEST_DIR}${i#${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system} - for i in `find ${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry -name "*.py" -type f`; do - cp {${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system,$DEST_DIR}${i#${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system} - done -#LINT.ThenChange(../../Android.mk) - - venv_common -} - -function venv_common { - $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/sources/setup.py" --quiet build --force "${INSTALL_ARGS}") - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Error building GD Python libraries${NOCOLOR}" - echo -e "${YELLOW}NOTE:${NOCOLOR} To build external libraries the first time, please add --clean option." - exit 1 - fi - - $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/sources/setup.py" --quiet install --skip-build --force "${INSTALL_ARGS}") - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Error installing GD Python libraries${NOCOLOR}" - exit 1 - fi - -if [ "${VERBOSE_MODE}" == true ] ; then - TEMP_CONFIG=/tmp/temp_mobly_config.yaml - cat "${TEST_CONFIG}" | "${CERT_TEST_VENV}/bin/python" -c " -import sys -import yaml -config = yaml.load(sys.stdin) -config['verbose_mode'] = True -print(yaml.dump(config)) - " > "${TEMP_CONFIG}" - TEST_CONFIG="${TEMP_CONFIG}" - if [[ $? -ne 0 ]] ; then - echo -e "${RED}Setup failed as verbose mode is chosen but cannot be enabled${NOCOLOR}" - exit 1 - fi -fi -} - -function gotta_go_fast { - if [ "${GOTTA_GO_FAST}" == true ] ; then - # Call here to explicitly note the flag is in use - happy_hedgehog - if [[ ! -L "${CERT_HOST_LOGS}" ]] ; then - rm -rf "${CERT_HOST_LOGS}" - mkdir -p "${ASHMEM_HOST_LOGS}" - ln -s "${ASHMEM_HOST_LOGS}" "${CERT_HOST_LOGS}" - fi - - if [[ ! -L "${OUT_TARGET}" ]] ; then - rm -rf "${OUT_TARGET}" - mkdir -p "${ASHMEM_OUT_TARGET}" - ln -s "${ASHMEM_OUT_TARGET}" "${OUT_TARGET}" - fi - else - if [[ -L "${CERT_HOST_LOGS}" ]] ; then - # Call here so we don't spam anyone not using the flag - sad_hedgehog - rm -rf "${CERT_HOST_LOGS}" - rm -rf "${ASHMEM_HOST_LOGS}" - fi - - if [[ -L "${OUT_TARGET}" ]] ; then - rm -rf "${OUT_TARGET}" - rm -rf "${ASHMEM_OUT_TARGET}" - fi - fi -} - -function run_tests { - for n in $(seq "${NUM_REPETITIONS}"); do - $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/sources/${TEST_RUNNER}" \ - "-c ${TEST_CONFIG}" "${TEST_FILTER}") - done - - if [ "${CLEAN_VENV}" != true ] ; then - echo -e "${YELLOW}NOTE:${NOCOLOR} Completed tests using existing external libraries in virtualenv." - echo -e "${YELLOW}NOTE:${NOCOLOR} To update external libraries, please add --clean option." - fi -} - -function menu-adb() { - TMP=$(adb devices -l | grep -v "List of device" | awk '{ print $1 }') - # TODO(optedoblivion): If the device doesn't have a name (offline), it misnames them - NTMP=$(adb devices -l | grep -v "List of device" | awk '{ print $6 }' | cut -d ':' -f 2) - SERIALS=($TMP) - DEVICES=($NTMP) - LEN=${#SERIALS[@]} - result=0 - if [ $LEN -lt 1 ]; then - echo -e "${YELLOW}No devices connected!${NOCOLOR}" - return 1 - fi - - if [ "$LEN" == "" ]; then - LEN=0 - fi - - answer=0 - - DEVICE_NAME="$1 device" - - if [ $LEN -gt 1 ]; then - echo "+-------------------------------------------------+" 1>&2 - echo "| Choose a ${DEVICE_NAME}: " 1>&2 - echo "+-------------------------------------------------+" 1>&2 - echo "| |" 1>&2 - let fixed_len=$LEN-1 - for i in `seq 0 $fixed_len`; - do - serial=${SERIALS[i]} - device=${DEVICES[i]} - echo "| $i) $serial $device" 1>&2 - ## TODO[MSB]: Find character count, fill with space and ending box wall - done - echo "| |" 1>&2 - echo "+-------------------------------------------------+" 1>&2 - echo 1>&2 - echo -n "Index number: " 1>&2 - read answer - fi - - if [ $answer -ge $LEN ]; then - echo - echo "Please choose a correct index!" 1>&2 - echo - return 1 - fi - - SERIAL=${SERIALS[$answer]} - echo $SERIAL -} - -function main { - check_environment - parse_options $@ - select_devices - if [[ "${SKIP_SOONG_BUILD}" != true ]] ; then - soong_build - fi - if [ "$CLEAN_VENV" == true ] ; then - setup_venv - else - incremental_venv - fi - gotta_go_fast - run_tests -} - -main $@ diff --git a/system/gd/cert/set_up_and_run_device_cert.sh b/system/gd/cert/set_up_and_run_device_cert.sh deleted file mode 100755 index 60e9ce00c5..0000000000 --- a/system/gd/cert/set_up_and_run_device_cert.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -function menu-adb() { - TMP=$(adb devices -l | grep -v "List of device" | awk '{ print $1 }') - # TODO(optedoblivion): If the device doesn't have a name (offline), it misnames them - NTMP=$(adb devices -l | grep -v "List of device" | awk '{ print $6 }' | cut -d ':' -f 2) - SERIALS=($TMP) - DEVICES=($NTMP) - LEN=${#SERIALS[@]} - result=0 - if [ $LEN -lt 1 ]; then - echo "No devices connected!" - return 1 - fi - - if [ "$LEN" == "" ]; then - LEN=0 - fi - - answer=0 - - DEVICE_NAME="$1 device" - - if [ $LEN -gt 1 ]; then - echo "+-------------------------------------------------+" 1>&2 - echo "| Choose a ${DEVICE_NAME}: " 1>&2 - echo "+-------------------------------------------------+" 1>&2 - echo "| |" 1>&2 - let fixed_len=$LEN-1 - for i in `seq 0 $fixed_len`; - do - serial=${SERIALS[i]} - device=${DEVICES[i]} - echo "| $i) $serial $device" 1>&2 - ## TODO[MSB]: Find character count, fill with space and ending box wall - done - echo "| |" 1>&2 - echo "+-------------------------------------------------+" 1>&2 - echo 1>&2 - echo -n "Index number: " 1>&2 - read answer - fi - - if [ $answer -ge $LEN ]; then - echo - echo "Please choose a correct index!" 1>&2 - echo - return 1 - fi - - SERIAL=${SERIALS[$answer]} - echo $SERIAL -} - -function UpFind { - while [[ $PWD != / ]] ; do - rc=$(find "$PWD" -maxdepth 1 "$@") - if [ -n "$rc" ]; then - echo $(dirname "$rc") - return - fi - cd .. - done -} - - -function get-android-root() { - android_root=$(UpFind -name dalvik -type d) - if [[ -z $android_root ]] ; then - echo - echo "Needs to be ran in the android tree" - echo - return 1 - fi - echo "${android_root}" -} - -function banner() { - echo - echo "GD On Device Cert Test" - echo -} - -## Main -banner - -DRY_RUN="" -DO_BUILD=0 -echo "$@" -if [ $# -gt 0 ]; then - for var in "$@" - do - if [ "$var" == "-h" ]; then - echo - echo "Usage: $0 [-h|-d]" - echo - echo "Available Options:" - echo "==================" - echo " -h | Help(this) Menu" - echo " -d | Dry run; just prints commands" - echo - return 0 - elif [ "$var" == "-d" ]; then - DRY_RUN="echo" - elif [ "$var" == "-b" ]; then - DO_BUILD=1 - fi - done -fi - -## Verify devices connected and sane -DUT_SERIAL="$(menu-adb DUT)" -DUT_ADB="adb -s ${DUT_SERIAL}" -DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)" - -CERT_SERIAL="$(menu-adb CERT)" -CERT_ADB="adb -s ${CERT_SERIAL}" -CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)" - -if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then - echo - echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!" - echo - return 1 -fi - -## Start builds -if [ $DO_BUILD == 1 ]; then - $DRY_RUN cd $(get-android-root) - $DRY_RUN . build/envsetup.sh - #DUT - $DRY_RUN lunch $DUT_NAME - $DRY_RUN cd $(get-android-root)/packages/modules/Bluetooth/system/gd - $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l` - $DRY_RUN cd $(get-android-root) - # CERT - $DRY_RUN lunch $CERT_NAME - $DRY_RUN cd $(get-android-root)/packages/modules/Bluetooth/system/gd - $DRY_RUN mma -j `cat /proc/cpuinfo | grep core | wc -l` - $DRY_RUN cd $(get-android-root) -fi - -## Set android devices in config -pushd . -cd "${DIR}" -# Reset in case user chooses different item in menu -git checkout devices_config.json -popd -$DRY_RUN sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${DIR}/devices_config.json -$DRY_RUN sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${DIR}/devices_config.json - -## ACTS -#$DRY_RUN source $(get-android-root)/packages/modules/Bluetooth/system/gd/cert/set_up_acts.sh - -## Start test -$DRY_RUN $(get-android-root)/packages/modules/Bluetooth/system/gd/cert/run --device diff --git a/system/gd/common/audit_log.cc b/system/gd/common/audit_log.cc index 5897c70aac..36cee3c801 100644 --- a/system/gd/common/audit_log.cc +++ b/system/gd/common/audit_log.cc @@ -42,10 +42,9 @@ void LogConnectionAdminAuditEvent([[maybe_unused]] const char* action, #if defined(__ANDROID__) && !defined(FUZZ_TARGET) android_log_event_list(SEC_TAG_BLUETOOTH_CONNECTION) - << ADDRESS_TO_LOGGABLE_CSTR(address) - << /* success */ int32_t(status == hci::ErrorCode::SUCCESS) - << common::StringFormat("%s: %s", action, ErrorCodeText(status).c_str()).c_str() - << LOG_ID_SECURITY; + << address.ToRedactedStringForLogging() + << /* success */ int32_t(status == hci::ErrorCode::SUCCESS) << action << ": " + << ErrorCodeText(status) << LOG_ID_SECURITY; #endif /* defined(__ANDROID__) && !defined (FUZZ_TARGET) */ } diff --git a/system/gd/common/interfaces/ILoggable.h b/system/gd/common/interfaces/ILoggable.h deleted file mode 100644 index 0a0899511c..0000000000 --- a/system/gd/common/interfaces/ILoggable.h +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** - * - * Copyright 2022 Google, Inc. - * - * 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 <string> - -namespace bluetooth { -namespace common { - -class ILoggable { -public: - // the interface for - // converting an object to a string for feeding to loggers - // e.g.. logcat - virtual std::string ToStringForLogging() const = 0; - virtual ~ILoggable() = default; -}; - -class IRedactableLoggable : public ILoggable { -public: - // the interface for - // converting an object to a string with sensitive info redacted - // to avoid violating privacy - virtual std::string ToRedactedStringForLogging() const = 0; - virtual ~IRedactableLoggable() = default; -}; - -} // namespace common -} // namespace bluetooth diff --git a/system/gd/dumpsys/Android.bp b/system/gd/dumpsys/Android.bp deleted file mode 100644 index 84074824ac..0000000000 --- a/system/gd/dumpsys/Android.bp +++ /dev/null @@ -1,212 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "system_bt_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["system_bt_license"], -} - -filegroup { - name: "BluetoothDumpsysSources", - srcs: [ - "filter.cc", - "internal/filter_internal.cc", - "reflection_schema.cc", - ], -} - -filegroup { - name: "BluetoothDumpsysTestSources", - srcs: [ - "filter_test.cc", - "internal/filter_internal_test.cc", - "reflection_schema_test.cc", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysTestData_h", - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) --cpp $(in) ", - srcs: [ - "test_data/bar.fbs", - "test_data/baz.fbs", - "test_data/foo.fbs", - "test_data/qux.fbs", - "test_data/root.fbs", - ], - out: [ - "bar_generated.h", - "baz_generated.h", - "foo_generated.h", - "qux_generated.h", - "root_generated.h", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysTestData_bfbs", - visibility: ["//visibility:private"], - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) --cpp $(in) ", - srcs: [ - "test_data/bar.fbs", - "test_data/baz.fbs", - "test_data/foo.fbs", - "test_data/qux.fbs", - "test_data/root.fbs", - ], - out: [ - "bar.bfbs", - "baz.bfbs", - "foo.bfbs", - "qux.bfbs", - "root.bfbs", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysTestSchema_cc", - visibility: ["//visibility:private"], - tools: [ - "bluetooth_flatbuffer_bundler", - ], - cmd: "$(location bluetooth_flatbuffer_bundler) -w -m bluetooth.DumpsysData -f dumpsys_module_schema_data -n testing -g $(genDir) $(locations :BluetoothGeneratedDumpsysBinarySchema_bfbs)", - srcs: [ - ":BluetoothGeneratedDumpsysBinarySchema_bfbs", - ], - out: [ - "dumpsys_module_schema_data.cc", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysBundledSchema_cc", - visibility: ["//visibility:private"], - tools: [ - "bluetooth_flatbuffer_bundler", - ], - cmd: "$(location bluetooth_flatbuffer_bundler) -w -m bluetooth.DumpsysData -f generated_dumpsys_bundled_schema -n bluetooth::dumpsys -g $(genDir) $(locations :BluetoothGeneratedDumpsysBinarySchema_bfbs)", - srcs: [ - ":BluetoothGeneratedDumpsysBinarySchema_bfbs", - ], - out: [ - "generated_dumpsys_bundled_schema.cc", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysBundledTestSchema_cc", - visibility: ["//visibility:private"], - tools: [ - "bluetooth_flatbuffer_bundler", - ], - cmd: "$(location bluetooth_flatbuffer_bundler) -w -m testing.DumpsysTestDataRoot -f generated_dumpsys_bundled_test_schema -n testing -g $(genDir) $(locations :BluetoothGeneratedDumpsysTestData_bfbs)", - srcs: [ - ":BluetoothGeneratedDumpsysTestData_bfbs", - ], - out: [ - "generated_dumpsys_bundled_test_schema.cc", - ], -} - -genrule { - name: "BluetoothFlatbufferTestData_h", - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) --cpp $(in) ", - srcs: [ - "bluetooth_flatbuffer_test.fbs", - ], - out: [ - "bluetooth_flatbuffer_test.bfbs", - "bluetooth_flatbuffer_test_generated.h", - ], -} - -genrule { - name: "BluetoothGeneratedDumpsysInternalTestData_h", - tools: [ - "flatc", - ], - cmd: "$(location flatc) --cpp -o $(genDir)/dumpsys/internal/test_data $(in)", - srcs: [ - "internal/test_data/float.fbs", - "internal/test_data/integer.fbs", - "internal/test_data/string.fbs", - "internal/test_data/struct.fbs", - ], - out: [ - "dumpsys/internal/test_data/float_generated.h", - "dumpsys/internal/test_data/integer_generated.h", - "dumpsys/internal/test_data/string_generated.h", - "dumpsys/internal/test_data/struct_generated.h", - ], -} - -cc_library { - name: "libbluetooth-dumpsys", - host_supported: true, - defaults: [ - "gd_defaults", - ], - generated_sources: [ - "BluetoothGeneratedDumpsysBundledSchema_cc", - ], - apex_available: [ - "com.android.btservices", - ], - min_sdk_version: "30", -} - -cc_library { - name: "libbluetooth-dumpsys-test", - host_supported: true, - defaults: [ - "gd_defaults", - ], - generated_sources: [ - "BluetoothGeneratedDumpsysBundledTestSchema_cc", - ], -} - -cc_library { - name: "libbluetooth-dumpsys-unittest", - host_supported: true, - defaults: [ - "gd_defaults", - ], - generated_headers: [ - "BluetoothGeneratedDumpsysTestSchema_cc", - ], -} - -cc_test { - name: "bluetooth_flatbuffer_tests", - test_suites: ["general-tests"], - defaults: [ - "bluetooth_cflags", - "mts_defaults", - ], - host_supported: true, - test_options: { - unit_test: true, - }, - static_libs: [ - "libflatbuffers-cpp", - "libgmock", - ], - srcs: [ - "bluetooth_flatbuffer_test.cc", - ], - generated_headers: [ - "BluetoothFlatbufferTestData_h", - ], - min_sdk_version: "30", -} diff --git a/system/gd/dumpsys/BUILD.gn b/system/gd/dumpsys/BUILD.gn deleted file mode 100644 index 327089c2ed..0000000000 --- a/system/gd/dumpsys/BUILD.gn +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright 2021 Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import("//bt/system/gd/dumpsys/bundler/bundler.gni") - -source_set("BluetoothDumpsysSources") { - sources = [ - "filter.cc", - "internal/filter_internal.cc", - "reflection_schema.cc", - ] - - cflags_cc = [ "-Wno-enum-compare-switch" ] - - configs += [ - "//bt/system/gd:gd_defaults", - "//bt/system/log:log_defaults", - ] - deps = [ "//bt/system/gd:gd_default_deps" ] -} - -bt_flatc_bundler("libbluetooth-dumpsys") { - root_name = "bluetooth.DumpsysData" - filename = "generated_dumpsys_bundled_schema" - namespace = "bluetooth::dumpsys" - deps = [ "//bt/system/gd:BluetoothGeneratedDumpsysBinarySchema_bfbs" ] - configs = [ - "//bt/system/gd:gd_defaults", - "//bt/system/log:log_defaults", - ] -} diff --git a/system/gd/dumpsys/bluetooth_flatbuffer_test.cc b/system/gd/dumpsys/bluetooth_flatbuffer_test.cc deleted file mode 100644 index 7c77e1075d..0000000000 --- a/system/gd/dumpsys/bluetooth_flatbuffer_test.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "bluetooth_flatbuffer_test_generated.h" -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" -#include "flatbuffers/util.h" - -namespace bluetooth { -namespace dumpsys { - -class BluetoothFlatbufferTest : public ::testing::Test { -protected: - void SetUp() override {} - - void TearDown() override {} -}; - -TEST_F(BluetoothFlatbufferTest, precondition) {} - -TEST_F(BluetoothFlatbufferTest, BuilderTest) { - flatbuffers::FlatBufferBuilder builder(1024); - auto string_private = builder.CreateString("String private"); - auto string_opaque = builder.CreateString("String opaque"); - auto string_anonymized = builder.CreateString("String anonymized"); - auto string_any = builder.CreateString("String any"); - - TestTableBuilder table_builder(builder); - table_builder.add_string_private(string_private); - table_builder.add_string_opaque(string_opaque); - table_builder.add_string_anonymized(string_anonymized); - table_builder.add_string_any(string_any); - - table_builder.add_int_private(123); - table_builder.add_int_opaque(456); - table_builder.add_int_anonymized(789); - table_builder.add_int_any(0xabc); - - builder.Finish(table_builder.Finish()); - - const TestTable* test_table = GetTestTable(builder.GetBufferPointer()); - - ASSERT_EQ("String private", test_table->string_private()->str()); - ASSERT_EQ("String opaque", test_table->string_opaque()->str()); - ASSERT_EQ("String anonymized", test_table->string_anonymized()->str()); - ASSERT_EQ("String any", test_table->string_any()->str()); - - ASSERT_EQ(123, test_table->int_private()); - ASSERT_EQ(456, test_table->int_opaque()); - ASSERT_EQ(789, test_table->int_anonymized()); - ASSERT_EQ(0xabc, test_table->int_any()); -} - -} // namespace dumpsys -} // namespace bluetooth diff --git a/system/gd/dumpsys/bluetooth_flatbuffer_test.fbs b/system/gd/dumpsys/bluetooth_flatbuffer_test.fbs deleted file mode 100644 index f449813620..0000000000 --- a/system/gd/dumpsys/bluetooth_flatbuffer_test.fbs +++ /dev/null @@ -1,17 +0,0 @@ -// Bluetooth module test schema - -attribute "privacy"; - -table TestTable { - string_private:string; // no privacy attribute implies private - string_opaque:string (privacy:"Opaque"); - string_anonymized:string (privacy:"Anonymized"); - string_any:string (privacy:"Any"); - - int_private:int32 (privacy:"Private"); // Explicitly private - int_opaque:int32 (privacy:"Opaque"); - int_anonymized:int32 (privacy:"Anonymized"); - int_any:int32 (privacy:"Any"); -} - -root_type TestTable; diff --git a/system/gd/dumpsys/bundler/Android.bp b/system/gd/dumpsys/bundler/Android.bp deleted file mode 100644 index 301346ec6c..0000000000 --- a/system/gd/dumpsys/bundler/Android.bp +++ /dev/null @@ -1,91 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "system_bt_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["system_bt_license"], -} - -filegroup { - name: "BluetoothFlatbufferBundlerSources", - visibility: ["//visibility:private"], - srcs: [ - "bundler.cc", - "main.cc", - ], -} - -filegroup { - name: "BluetoothFlatbufferBundlerTestSources", - visibility: ["//visibility:private"], - srcs: [ - "bundler.cc", - "test.cc", - ], -} - -// Flatbuffer bundler schema that wraps the bundled binary module schema -genrule { - name: "BluetoothGeneratedBundlerSchema_h_bfbs", - visibility: [ - "//packages/modules/Bluetooth/system/bta", - "//packages/modules/Bluetooth/system/btif", - "//packages/modules/Bluetooth/system/btm", - "//packages/modules/Bluetooth/system/gd", - "//packages/modules/Bluetooth/system/main", - "//packages/modules/Bluetooth/system/rust", - "//packages/modules/Bluetooth/system/stack", - ], - tools: [ - "flatc", - ], - cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) --cpp $(in) ", - srcs: [ - "bundler_schema.fbs", - ], - out: [ - "bundler_schema.bfbs", - "bundler_schema_generated.h", - ], -} - -cc_defaults { - name: "bluetooth_flatbuffer_bundler_defaults", - defaults: ["bluetooth_cflags"], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - ], - sanitize: { - misc_undefined: ["bounds"], - }, - static_libs: [ - "libflatbuffers-cpp", - ], -} - -cc_binary_host { - name: "bluetooth_flatbuffer_bundler", - srcs: [ - ":BluetoothFlatbufferBundlerSources", - ], - defaults: [ - "bluetooth_flatbuffer_bundler_defaults", - ], -} - -cc_test_host { - name: "bluetooth_flatbuffer_bundler_test", - srcs: [ - ":BluetoothFlatbufferBundlerTestSources", - ], - defaults: [ - "bluetooth_flatbuffer_bundler_defaults", - ], - data: [ - "test.bfbs", - ], - test_options: { - unit_test: true, - }, -} diff --git a/system/gd/dumpsys/bundler/BUILD.gn b/system/gd/dumpsys/bundler/BUILD.gn deleted file mode 100644 index 324afbb4f1..0000000000 --- a/system/gd/dumpsys/bundler/BUILD.gn +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright 2021 Google, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import("//bt/system/gd/dumpsys/bundler/bundler.gni") -import("//common-mk/flatbuffer.gni") - -bt_flatc_binary_schema("BluetoothGeneratedBundlerSchema_h_bfbs") { - sources = [ "bundler_schema.fbs" ] - include_dir = "bt/system/gd" - gen_header = true -} - -# -# The remaining rules are for building on the host -# - -config("bundler_defaults") { - cflags = [ "-fPIC" ] - - cflags_cc = [ - "-std=c++17", - "-Wno-unused-variable", - "-Wno-poison-system-directories", - ] -} - -executable("bluetooth_flatbuffer_bundler") { - sources = [ - "bundler.cc", - "main.cc", - ] - - libs = [ "flatbuffers" ] - - deps = [ ":BluetoothGeneratedBundlerSchema_h_bfbs" ] - - configs += [ ":bundler_defaults" ] -} diff --git a/system/gd/dumpsys/bundler/bundler.cc b/system/gd/dumpsys/bundler/bundler.cc deleted file mode 100644 index 72097ac101..0000000000 --- a/system/gd/dumpsys/bundler/bundler.cc +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2020 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 "bundler.h" - -#include <assert.h> -#include <stdlib.h> -#include <unistd.h> - -#include <cassert> -#include <list> -#include <map> -#include <vector> - -#include "bundler_schema_generated.h" -#include "flatbuffers/idl.h" -#include "flatbuffers/util.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - -using namespace bluetooth; -using namespace dumpsys; - -struct Opts opts; - -/** - * Load a binary schema from persistent store using flatbuffer API. - * - * @param filename; Name of file to open and read. - * @param binary_schema: Backing store for flatbuffer binary schema data. - * - * @return: True if operation successful, false otherwise. - */ -bool LoadBinarySchema(const char* filename, std::string* binary_schema) { - assert(filename != nullptr); - assert(binary_schema != nullptr); - if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, binary_schema)) { - fprintf(stderr, "Unable to open binary flatbuffer schema file:%s\n", filename); - return false; - }; - return true; -} - -/** - * Verify a binary schema using flatbuffer API. - * - * @param schema: Raw binary schema to verify - * - * @return: True if operation successful, false otherwise. - */ -bool VerifyBinarySchema(const std::vector<uint8_t>& raw_schema) { - flatbuffers::Verifier verifier(raw_schema.data(), raw_schema.size()); - if (!reflection::VerifySchemaBuffer(verifier)) { - return false; - } - return true; -} - -/** - * Bundle a set of binary flatbuffer schema into the bundler database. - * - * @param builder; Flatbuffer builder - * @param filenames: Set of filenames to include in bundle - * @param vector_map: Filename to filedata mapping - * - * @return: True if operation successful, false otherwise. - */ -bool CreateBinarySchemaBundle(flatbuffers::FlatBufferBuilder* builder, - const std::vector<std::string>& filenames, - std::vector<flatbuffers::Offset<BundledSchemaMap>>* vector_map, - std::list<std::string>* bundled_names) { - assert(builder != nullptr); - assert(vector_map != nullptr); - assert(bundled_names != nullptr); - - for (auto filename : filenames) { - std::string string_schema; - if (!LoadBinarySchema(filename.c_str(), &string_schema)) { - fprintf(stderr, "Unable to load binary schema from filename:%s\n", filename.c_str()); - return false; - } - std::vector<uint8_t> raw_schema(string_schema.begin(), string_schema.end()); - if (!VerifyBinarySchema(raw_schema)) { - fprintf(stderr, "Failed verification on binary schema filename:%s\n", filename.c_str()); - return false; - } - - const reflection::Schema* schema = reflection::GetSchema(raw_schema.data()); - if (schema->root_table() == nullptr) { - fprintf(stderr, "Unable to find root table for binary flatbuffer schema:%s\n", - filename.c_str()); - return false; - } - - bundled_names->push_back(schema->root_table()->name()->str()); - auto name = builder->CreateString(schema->root_table()->name()->str()); - auto data = builder->CreateVector<uint8_t>(raw_schema.data(), raw_schema.size()); - vector_map->push_back(CreateBundledSchemaMap(*builder, name, data)); - - if (opts.verbose) { - fprintf(stdout, "Bundled binary schema file:%s\n", schema->root_table()->name()->c_str()); - } - } - return true; -} - -/** - * Write generated header file containing the bundled binary schema - * data and meta data - * - * @param data: Source file data. - * @param data_len: length of data - */ -void WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len) { - assert(fp != nullptr); - std::string delim(kDefaultNamespaceDelim); - std::string ns_string(opts.ns_name); - std::vector<std::string> namespaces; - - size_t start = 0; - size_t end = ns_string.find(delim); - while (end != std::string::npos) { - namespaces.push_back(ns_string.substr(start, end - start)); - start = end + delim.size(); - end = ns_string.find(delim, start); - } - if (start != 0 && start != std::string::npos) { - namespaces.push_back(ns_string.substr(start)); - } else if (!ns_string.empty()) { - namespaces.push_back(ns_string); - } - - std::string namespace_prefix; - for (const auto& name : namespaces) { - namespace_prefix += (name + '_'); - } - - fprintf(fp, - "// Generated file by bluetooth_flatbuffer bundler\n" - "#include <string>\n"); - for_each(namespaces.begin(), namespaces.end(), - [fp](const std::string& s) { fprintf(fp, "namespace %s {\n", s.c_str()); }); - fprintf(fp, "extern const std::string& GetBundledSchemaData();\n"); - fprintf(fp, "const unsigned char %sdata_[%zu] = {\n", namespace_prefix.c_str(), data_len); - - for (size_t i = 0; i < data_len; i++) { - fprintf(fp, " 0x%02x", data[i]); - if (i != data_len - 1) { - fprintf(fp, ","); - } - if ((i + 1) % 16 == 0) { - fprintf(fp, "\n"); - } - } - fprintf(fp, " };\n"); - fprintf(fp, "const std::string %sstring_data_(%sdata_, %sdata_ + sizeof(%sdata_));\n", - namespace_prefix.c_str(), namespace_prefix.c_str(), namespace_prefix.c_str(), - namespace_prefix.c_str()); - fprintf(fp, "const std::string& GetBundledSchemaData() { return %sstring_data_; }\n", - namespace_prefix.c_str()); - - for_each(namespaces.crbegin(), namespaces.crend(), - [fp](const std::string& s) { fprintf(fp, "} // namespace %s\n", s.c_str()); }); -} - -int ReadBundledSchema() { - const char* filename = opts.filename; - assert(filename != nullptr); - - std::string flatfile_data; - if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, &flatfile_data)) { - fprintf(stderr, "Unable to load schema data file:%s\n", filename); - return -5; - } - - auto bundle_schema = flatbuffers::GetRoot<BundledSchema>(flatfile_data.c_str()); - const flatbuffers::Vector<flatbuffers::Offset<BundledSchemaMap>>* map = bundle_schema->map(); - - fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str()); - fprintf(stdout, "Bundle schema root_name:%s\n", bundle_schema->root_name()->c_str()); - int cnt = 0; - for (auto it = map->cbegin(); it != map->cend(); ++it, cnt++) { - fprintf(stdout, " %d name:%s schema:%s\n", cnt, it->name()->c_str(), "schema"); - } - return EXIT_SUCCESS; -} - -int WriteBundledSchema() { - const char* filename = opts.filename; - assert(filename != nullptr); - - const char* main_root_name = opts.main_root_name; - if (main_root_name == nullptr) { - fprintf(stderr, "Must specify the name of the main root name for this bundle\n"); - return EXIT_FAILURE; - } - - std::vector<std::string> bfbs_filenames; - for (int i = 0; i < opts.arg.c; i++) { - bfbs_filenames.push_back(std::string(opts.arg.v[i])); - } - if (bfbs_filenames.empty()) { - fprintf(stderr, "No bfbs files are specified to bundle\n"); - return EXIT_FAILURE; - } - - flatbuffers::FlatBufferBuilder builder(1024); - - std::list<std::string> bundled_names; - std::vector<flatbuffers::Offset<BundledSchemaMap>> vector_map; - if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map, &bundled_names)) { - fprintf(stderr, "Unable to bundle schema bfbs files\n"); - return EXIT_FAILURE; - } - - if (std::find(bundled_names.begin(), bundled_names.end(), main_root_name) == - bundled_names.end()) { - fprintf(stderr, "The main root name must match one of the bundled schema names\n"); - fprintf(stderr, " main root name:%s\n", main_root_name); - for (auto name : bundled_names) { - fprintf(stderr, " bundled schema name:%s\n", name.c_str()); - } - return EXIT_FAILURE; - } - - const char* title = opts.title; - auto schema_offset = CreateBundledSchemaDirect(builder, title, main_root_name, &vector_map); - builder.Finish(schema_offset); - - std::string final_filename(opts.gen); - final_filename.append("/"); - final_filename.append(filename); - if (!flatbuffers::SaveFile(final_filename.c_str(), (const char*)builder.GetBufferPointer(), - builder.GetSize(), helper::AsBinaryFile)) { - fprintf(stderr, "Unable to save file:%s\n", final_filename.c_str()); - return EXIT_FAILURE; - } - - std::string header(opts.gen); - header += ("/" + std::string(opts.filename) + ".cc"); - FILE* fp = fopen(header.c_str(), "w+"); - if (fp == nullptr) { - fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str()); - return EXIT_FAILURE; - } - WriteHeaderFile(fp, builder.GetBufferPointer(), builder.GetSize()); - fclose(fp); - return EXIT_SUCCESS; -} - -int Usage(int /*argc*/, char** argv) { - fprintf(stderr, - "Usage: %s [-r | -w] [-f <filename>] [-g <gen_out_path>] [-n <namespace> ] [-v] -m " - "<main_root_name> <file.bfbs " - "...>\n", - argv[0]); - fprintf(stderr, " -r|-w : Read or write a dumpsys file\n"); - fprintf(stderr, " -f : Filename bundled schema to read or write (default:%s)\n", - kDefaultBundleDataFile); - fprintf(stderr, " -g : Generated file output path\n"); - fprintf(stderr, " -n : Namespace to embed binary output bundle data source\n"); - fprintf(stderr, " -m : Name of the main root of this bundle\n"); - fprintf(stderr, " -v : Verbose printing mode\n"); - return EXIT_FAILURE; -} - -void ParseArgs(int argc, char** argv) { - int opt; - int parsed_cnt = 1; - while ((opt = getopt(argc, argv, "f:g:m:n:rt:vw")) != -1) { - parsed_cnt++; - switch (opt) { - case 'f': - opts.filename = optarg; - parsed_cnt++; - break; - case 'g': - opts.gen = optarg; - parsed_cnt++; - break; - case 'm': - opts.main_root_name = optarg; - parsed_cnt++; - break; - case 'n': - opts.ns_name = optarg; - parsed_cnt++; - break; - case 'r': - opts.read = true; - break; - case 'w': - opts.write = true; - break; - case 't': - opts.title = optarg; - parsed_cnt++; - break; - case 'v': - opts.verbose = true; - break; - default: - exit(Usage(argc, argv)); - break; - } - } - opts.arg.c = argc - parsed_cnt; - opts.arg.v = &argv[parsed_cnt]; -} diff --git a/system/gd/dumpsys/bundler/bundler.h b/system/gd/dumpsys/bundler/bundler.h deleted file mode 100644 index 9dc4b243d2..0000000000 --- a/system/gd/dumpsys/bundler/bundler.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2020 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 - -namespace { -constexpr char kDefaultBundleDataFile[] = "bundle_bfbs.bin"; -constexpr char kDefaultGeneratedOutputPath[] = "."; -constexpr char kDefaultNamespace[] = ""; -constexpr char kDefaultNamespaceDelim[] = "::"; -constexpr char kDefaultTitle[] = "Bundled schema tables"; -} // namespace - -struct Opts { - bool verbose{false}; - bool read{false}; - bool write{false}; - const char* filename{kDefaultBundleDataFile}; - const char* gen{kDefaultGeneratedOutputPath}; - const char* main_root_name{nullptr}; - const char* ns_name{kDefaultNamespace}; - const char* title{kDefaultTitle}; - struct { - int c{0}; - char** v{nullptr}; - } arg; -}; -extern Opts opts; - -namespace { -namespace helper { // Part of flatbuffers API -constexpr bool AsBinaryFile = true; -constexpr bool AsTextFile = false; -} // namespace helper - -} // namespace - -/** - * Read and parse a previously generated bundle data file - * - **/ -int ReadBundledSchema(); - -/** - * Generate a bundle data file from the binary flatbuffer schema - * files provided as input - * - **/ -int WriteBundledSchema(); - -/** - * Print tool usage options - */ -int Usage(int argc, char** argv); - -/** - * Parse tool usage options - */ -void ParseArgs(int argc, char** argv); diff --git a/system/gd/dumpsys/bundler/bundler_schema.fbs b/system/gd/dumpsys/bundler/bundler_schema.fbs deleted file mode 100644 index a11fb7f150..0000000000 --- a/system/gd/dumpsys/bundler/bundler_schema.fbs +++ /dev/null @@ -1,19 +0,0 @@ -// Bundled Schema -// -// Describes a collection of binary flatbuffer schema. -// - -namespace bluetooth.dumpsys; - -table BundledSchemaMap { - name:string; - data:[ubyte]; -} - -table BundledSchema { - title:string; - root_name:string; - map:[BundledSchemaMap]; -} - -root_type BundledSchema; diff --git a/system/gd/dumpsys/bundler/extract_files_and_call.py b/system/gd/dumpsys/bundler/extract_files_and_call.py deleted file mode 100755 index df0415f611..0000000000 --- a/system/gd/dumpsys/bundler/extract_files_and_call.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Copyright 2021 Google, Inc. -# -# 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. -""" Get contents of given files and pass as args to remaining params. - -Example: - a.files = [ "foo", "bar" ] - b.files = [ "fizz", "buzz" ] - - extract_files_and_call.py a.files b.files -- somebin -a --set foo -c -o - - will result in this call: - - somebin -a --set foo -c -o foo bar fizz buzz - -""" - -from __future__ import print_function - -import subprocess -import sys - - -def file_to_args(filename): - """ Read file and return lines with empties removed. - """ - with open(filename, 'r') as f: - return [x.strip() for x in f.readlines() if x.strip()] - - -def main(): - file_contents = [] - args = [] - for i in range(1, len(sys.argv) - 1): - if sys.argv[i] == '--': - args = sys.argv[i + 1:] + file_contents - break - else: - file_contents.extend(file_to_args(sys.argv[i])) - - subprocess.check_call(args) - - -if __name__ == "__main__": - main() diff --git a/system/gd/dumpsys/bundler/main.cc b/system/gd/dumpsys/bundler/main.cc deleted file mode 100644 index e040ef2b8e..0000000000 --- a/system/gd/dumpsys/bundler/main.cc +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 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 "bundler.h" -#include "bundler_schema_generated.h" - -int main(int argc, char** argv) { - ParseArgs(argc, argv); - if (opts.read) { - exit(ReadBundledSchema()); - } - if (opts.write) { - exit(WriteBundledSchema()); - } -} diff --git a/system/gd/dumpsys/bundler/test.bfbs b/system/gd/dumpsys/bundler/test.bfbs Binary files differdeleted file mode 100644 index 0dda6e54f8..0000000000 --- a/system/gd/dumpsys/bundler/test.bfbs +++ /dev/null diff --git a/system/gd/dumpsys/bundler/test.cc b/system/gd/dumpsys/bundler/test.cc deleted file mode 100644 index eebd2eacbb..0000000000 --- a/system/gd/dumpsys/bundler/test.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <gtest/gtest.h> - -#include <list> -#include <vector> - -#include "bundler.h" -#include "bundler_schema_generated.h" -#include "flatbuffers/flatbuffers.h" - -// Must be run from the same directory as the test data 'test.bfbs'. -// This is how the presubmit unit test pipeline functions. -constexpr char kTestFilename[] = "test.bfbs"; - -bool LoadBinarySchema(const char* filename, std::string* binary_schema); -bool VerifyBinarySchema(const std::vector<uint8_t>& raw_schema); -bool CreateBinarySchemaBundle( - flatbuffers::FlatBufferBuilder* builder, const std::vector<std::string>& filenames, - std::vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>>* vector_map, - std::list<std::string>* bundled_names); -int WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len); - -class BundlerTest : public ::testing::Test { -public: - void SetUp() override {} - - void TearDown() override {} -}; - -TEST_F(BundlerTest, LoadBinarySchema) { - std::string string_schema; - ASSERT_FALSE(LoadBinarySchema(nullptr, &string_schema)); - ASSERT_DEATH(LoadBinarySchema(kTestFilename, nullptr), ""); - ASSERT_TRUE(LoadBinarySchema(kTestFilename, &string_schema)); - ASSERT_FALSE(LoadBinarySchema("does_not_exist.bfbs", &string_schema)); -} - -TEST_F(BundlerTest, VerifyBinarySchema) { - std::string string_schema; - ASSERT_TRUE(LoadBinarySchema(kTestFilename, &string_schema)); - std::vector<uint8_t> raw_schema(string_schema.begin(), string_schema.end()); - ASSERT_TRUE(VerifyBinarySchema(raw_schema)); - - std::vector<uint8_t> bogus_raw_schema(string_schema.begin() + 1, string_schema.end()); - ASSERT_FALSE(VerifyBinarySchema(bogus_raw_schema)); -} - -TEST_F(BundlerTest, CreateBinarySchemaBundle) { - flatbuffers::FlatBufferBuilder builder; - std::vector<std::string> filenames; - std::vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>> vector_map; - std::list<std::string> bundled_names; - ASSERT_TRUE(CreateBinarySchemaBundle(&builder, filenames, &vector_map, &bundled_names)); - ASSERT_EQ((unsigned int)0, vector_map.size()); -} - -TEST_F(BundlerTest, WriteHeaderFile) { - std::vector<uint8_t> data; - data.push_back(0x10); - data.push_back(0x11); - data.push_back(0x12); - data.push_back(0x13); - ASSERT_DEATH(WriteHeaderFile(nullptr, data.data(), data.size()), ""); - FILE* fp = fopen("/tmp/test.h", "w+"); - ASSERT_NE(fp, nullptr); - WriteHeaderFile(fp, data.data(), data.size()); - fseek(fp, 0L, SEEK_SET); - char buf[16]; - fread(buf, 1, 15, fp); - buf[12] = '\0'; - std::string s(buf); - ASSERT_EQ("// Generated", s); - fclose(fp); - unlink("/tmp/test.h"); -} diff --git a/system/gd/dumpsys/dumpsys.h b/system/gd/dumpsys/dumpsys.h deleted file mode 100644 index 6d2cafd465..0000000000 --- a/system/gd/dumpsys/dumpsys.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include <string> - -namespace bluetooth { -namespace dumpsys { -const std::string& GetBundledSchemaData(); -} // namespace dumpsys -} // namespace bluetooth diff --git a/system/gd/dumpsys/filter.cc b/system/gd/dumpsys/filter.cc deleted file mode 100644 index 13cd2e5adf..0000000000 --- a/system/gd/dumpsys/filter.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/filter.h" - -#include <bluetooth/log.h> - -#include <memory> - -using namespace bluetooth; -using namespace dumpsys; - -class Filter { -public: - Filter(const dumpsys::ReflectionSchema& reflection_schema) - : reflection_schema_(reflection_schema) {} - - virtual ~Filter() = default; - - virtual void FilterInPlace(char* dumpsys_data) = 0; - - static std::unique_ptr<Filter> Factory(const dumpsys::ReflectionSchema& reflection_schema); - -protected: - /** - * Given both reflection field data and the populated flatbuffer table data, if any, - * filter the contents of the field based upon the filtering privacy level. - * - * Primitives and composite strings may be successfully processed at this point. - * Other composite types (e.g. structs or tables) must be expanded into the - * respective grouping of subfields. - * - * @param field The reflection field information from the bundled schema - * @param table The populated field data, if any - * - * @return true if field was filtered successfully, false otherwise. - */ - virtual bool FilterField(const reflection::Field* /* field */, flatbuffers::Table* /* table */) { - return false; - } - - /** - * Given both reflection object data and the populated flatbuffer table data, if any, - * filter the object fields based upon the filtering privacy level. - * - * @param object The reflection object information from the bundled schema - * @param table The populated field data, if any - * - */ - virtual void FilterObject(const reflection::Object* /* object */, - flatbuffers::Table* /* table */) {} - - /** - * Given both reflection field data and the populated table data, if any, - * filter the contents of the table based upon the filtering privacy level. - * - * @param schema The reflection schema information from the bundled schema - * @param table The populated field data, if any - * - */ - virtual void FilterTable(const reflection::Schema* /* schema */, - flatbuffers::Table* /* table */) {} - - const dumpsys::ReflectionSchema& reflection_schema_; -}; - -class DeveloperPrivacyFilter : public Filter { -public: - DeveloperPrivacyFilter(const dumpsys::ReflectionSchema& reflection_schema) - : Filter(reflection_schema) {} - void FilterInPlace(char* /* dumpsys_data */) override { /* Nothing to do in this mode */ } -}; - -std::unique_ptr<Filter> Filter::Factory(const dumpsys::ReflectionSchema& reflection_schema) { - return std::make_unique<DeveloperPrivacyFilter>(reflection_schema); -} - -void bluetooth::dumpsys::FilterSchema(const ReflectionSchema& reflection_schema, - std::string* dumpsys_data) { - auto filter = Filter::Factory(reflection_schema); - filter->FilterInPlace(dumpsys_data->data()); -} diff --git a/system/gd/dumpsys/filter.h b/system/gd/dumpsys/filter.h deleted file mode 100644 index b932b4c6bf..0000000000 --- a/system/gd/dumpsys/filter.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 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 <string> - -#include "dumpsys/reflection_schema.h" - -namespace bluetooth { -namespace dumpsys { - -void FilterSchema(const ReflectionSchema& reflection_schema, std::string* dumpsys_data); - -} // namespace dumpsys -} // namespace bluetooth diff --git a/system/gd/dumpsys/filter_test.cc b/system/gd/dumpsys/filter_test.cc deleted file mode 100644 index a1678ec373..0000000000 --- a/system/gd/dumpsys/filter_test.cc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/filter.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <list> -#include <queue> - -#include "dumpsys/dumpsys_test_data.h" -#include "test_data/bar.h" -#include "test_data/baz.h" -#include "test_data/foo.h" -#include "test_data/qux.h" -#include "test_data/root.h" - -using namespace bluetooth; - -namespace testing { - -class DumpsysFilterTest : public Test { -protected: - void SetUp() override { - test_data_classes_.push_back(std::make_unique<BarTestDataClass>()); - test_data_classes_.push_back(std::make_unique<BazTestDataClass>()); - test_data_classes_.push_back(std::make_unique<FooTestDataClass>()); - test_data_classes_.push_back(std::make_unique<QuxTestDataClass>()); - } - - void TearDown() override {} - - std::list<std::unique_ptr<DumpsysTestDataClass>> test_data_classes_; - - std::string PopulateTestSchema(); -}; - -std::string DumpsysFilterTest::PopulateTestSchema() { - flatbuffers::FlatBufferBuilder fb_builder(1024); - - auto string_private = fb_builder.CreateString("String private"); - auto string_opaque = fb_builder.CreateString("String opaque"); - auto string_anonymized = fb_builder.CreateString("String anonymized"); - auto string_any = fb_builder.CreateString("String any"); - - std::queue<TableAddFunction> queue; - for (auto& test_data_class : test_data_classes_) { - queue.push(test_data_class->GetTable(fb_builder)); - } - - testing::DumpsysTestDataRootBuilder builder(fb_builder); - - builder.add_string_private(string_private); - builder.add_string_opaque(string_opaque); - builder.add_string_anonymized(string_anonymized); - builder.add_string_any(string_any); - - builder.add_int_private(123); - builder.add_int_opaque(456); - builder.add_int_anonymized(789); - builder.add_int_any(0xabc); - - while (!queue.empty()) { - queue.front()(&builder); - queue.pop(); - } - fb_builder.Finish(builder.Finish()); - - return std::string(fb_builder.GetBufferPointer(), - fb_builder.GetBufferPointer() + fb_builder.GetSize()); -} - -TEST_F(DumpsysFilterTest, filter_as_developer) { - std::string dumpsys_data = PopulateTestSchema(); - dumpsys::ReflectionSchema reflection_schema(testing::GetBundledSchemaData()); - - dumpsys::FilterSchema(reflection_schema, &dumpsys_data); - - const testing::DumpsysTestDataRoot* data_root = GetDumpsysTestDataRoot(dumpsys_data.data()); - - ASSERT_EQ("String private", data_root->string_private()->str()); - ASSERT_EQ("String opaque", data_root->string_opaque()->str()); - ASSERT_EQ("String anonymized", data_root->string_anonymized()->str()); - ASSERT_EQ("String any", data_root->string_any()->str()); - - ASSERT_EQ(123, data_root->int_private()); - ASSERT_EQ(456, data_root->int_opaque()); - ASSERT_EQ(789, data_root->int_anonymized()); - ASSERT_EQ(0xabc, data_root->int_any()); - - ASSERT_EQ(nullptr, data_root->bar_module_data()); - - const testing::FooTestSchema* foo = data_root->foo_module_data(); - - ASSERT_EQ(123, foo->foo_int_private()); - ASSERT_EQ(123, foo->foo_int_opaque()); - ASSERT_EQ(123, foo->foo_int_anonymized()); - ASSERT_EQ(123, foo->foo_int_any()); - ASSERT_STREQ("123", foo->foo_int_string()->c_str()); - - ASSERT_FLOAT_EQ(123.456, foo->foo_float_private()); - ASSERT_FLOAT_EQ(123.456, foo->foo_float_opaque()); - ASSERT_FLOAT_EQ(123.456, foo->foo_float_anonymized()); - ASSERT_FLOAT_EQ(123.456, foo->foo_float_any()); - ASSERT_STREQ("123.456", foo->foo_float_string()->c_str()); -} - -} // namespace testing diff --git a/system/gd/dumpsys/internal/filter_internal.cc b/system/gd/dumpsys/internal/filter_internal.cc deleted file mode 100644 index 2db2878f95..0000000000 --- a/system/gd/dumpsys/internal/filter_internal.cc +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/internal/filter_internal.h" - -#include <bluetooth/log.h> - -#include <algorithm> -#include <string> - -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" - -#define DBG 0 - -using namespace bluetooth; -using namespace dumpsys; - -constexpr flatbuffers::voffset_t kErasedFromTable = 0; -constexpr bool kFieldIsNotPopulated = true; -constexpr bool kFieldHasBeenFiltered = true; -constexpr bool kFieldContinueFiltering = false; - -void internal::ScrubFromTable(flatbuffers::Table* table, flatbuffers::voffset_t field_offset) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - uint8_t* vtable = const_cast<uint8_t*>(table->GetVTable()); - vtable[field_offset] = kErasedFromTable; -} - -void internal::ReplaceInString(flatbuffers::String* string, int c) { - uint8_t* p = const_cast<uint8_t*>(string->Data()); - memset(p, c, string->size()); -} - -void internal::RandomizeInString(flatbuffers::String* string) { - std::size_t hash = std::hash<std::string>{}(string->str()); - std::string hashed_string = std::to_string(hash); - ReplaceInString(string, ' '); - size_t len = std::min(static_cast<size_t>(string->size()), hashed_string.size()); - uint8_t* p = const_cast<uint8_t*>(string->Data()); - memcpy(p, hashed_string.c_str(), len); -} - -const char* internal::PrivacyLevelName(PrivacyLevel privacy_level) { - switch (privacy_level) { - case kPrivate: - return "Private"; - break; - case kOpaque: - return "Opaque"; - break; - case kAnonymized: - return "Anonymized"; - break; - case kAny: - return "Any"; - break; - } -} -internal::PrivacyLevel internal::GetPrivacyLevelAttribute(const std::string& string) { - if (string == "Any") { - return kAny; - } else if (string == "Anonymized") { - return kAnonymized; - } else if (string == "Opaque") { - return kOpaque; - } else if (string == "Private") { - return kPrivate; - } - return kDefaultPrivacyLevel; -} - -internal::PrivacyLevel internal::FindFieldPrivacyLevel(const reflection::Field& field) { - PrivacyLevel privacy_level = kDefaultPrivacyLevel; - - if (field.attributes() != nullptr) { - auto key = field.attributes()->LookupByKey(kPrivacyAttributeKeyword); - if (key != nullptr) { - privacy_level = internal::GetPrivacyLevelAttribute(key->value()->str()); - } - } - return privacy_level; -} - -const reflection::Object* internal::FindReflectionObject( - const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>>* objects, - const flatbuffers::String* name) { - log::assert_that(objects != nullptr, "assert failed: objects != nullptr"); - log::assert_that(name != nullptr, "assert failed: name != nullptr"); - for (auto it = objects->cbegin(); it != objects->cend(); ++it) { - if (it->name()->str() == name->str()) { - return *it; - } - } - return nullptr; -} - -bool internal::FilterTypeBool(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - - const bool default_val = flatbuffers::GetFieldDefaultI<int8_t>(field); - flatbuffers::voffset_t field_offset = field.offset(); - - // boolean privacy levels are simpler. - switch (privacy_level) { - case kPrivate: - case kOpaque: - case kAnonymized: - flatbuffers::SetField<int8_t>(table, field, default_val); - internal::ScrubFromTable(table, field_offset); - break; - default: - case kAny: - break; - } - return kFieldHasBeenFiltered; -} - -bool internal::FilterTypeInteger(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - log::assert_that(flatbuffers::IsInteger(field.type()->base_type()), - "assert failed: flatbuffers::IsInteger(field.type()->base_type())"); - - int32_t default_val = flatbuffers::GetFieldDefaultI<int32_t>(field); - flatbuffers::voffset_t field_offset = field.offset(); - [[maybe_unused]] int32_t val = table->GetField<int32_t>(field_offset, default_val); - - switch (privacy_level) { - case kPrivate: - flatbuffers::SetField<int32_t>(table, field, default_val); - internal::ScrubFromTable(table, field_offset); - break; - case kOpaque: - flatbuffers::SetField<int32_t>(table, field, default_val); - break; - case kAnonymized: { - auto target_field = flatbuffers::GetFieldI<int32_t>(*table, field); - int32_t new_val = - static_cast<int32_t>(std::hash<std::string>{}(std::to_string(target_field))); - flatbuffers::SetField<int32_t>(table, field, new_val); - } break; - default: - case kAny: - break; - } - - if (DBG) { - log::info("Integer Field_name:{} privacy_level:{} old_value:{} / 0x{:x} ==> new_value:{}", - field.name()->c_str(), PrivacyLevelName(privacy_level), val, val, - table->GetField<int32_t>(field_offset, default_val)); - } - return kFieldHasBeenFiltered; -} - -bool internal::FilterTypeFloat(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - log::assert_that(flatbuffers::IsFloat(field.type()->base_type()), - "assert failed: flatbuffers::IsFloat(field.type()->base_type())"); - - float default_val = flatbuffers::GetFieldDefaultI<float>(field); - flatbuffers::voffset_t field_offset = field.offset(); - [[maybe_unused]] float val = table->GetField<float>(field_offset, default_val); - switch (privacy_level) { - case kPrivate: - flatbuffers::SetField<float>(table, field, default_val); - internal::ScrubFromTable(table, field_offset); - break; - case kOpaque: - flatbuffers::SetField<float>(table, field, default_val); - break; - case kAnonymized: { - auto target_field = flatbuffers::GetFieldF<float>(*table, field); - int32_t new_val = static_cast<float>(std::hash<std::string>{}(std::to_string(target_field))); - flatbuffers::SetField<float>(table, field, new_val); - } break; - default: - case kAny: - break; - } - if (DBG) { - log::info("Float Field_name:{} privacy_level:{} old_value:{:f} ==> new_value:{:f}", - field.name()->c_str(), PrivacyLevelName(privacy_level), val, - table->GetField<float>(field_offset, default_val)); - } - return kFieldHasBeenFiltered; -} - -bool internal::FilterTypeLong(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - - const int64_t default_val = flatbuffers::GetFieldDefaultI<int64_t>(field); - flatbuffers::voffset_t field_offset = field.offset(); - - switch (privacy_level) { - case kPrivate: - flatbuffers::SetField<int64_t>(table, field, default_val); - internal::ScrubFromTable(table, field_offset); - break; - case kOpaque: - flatbuffers::SetField<int64_t>(table, field, default_val); - break; - case kAnonymized: { - auto target_field = flatbuffers::GetFieldI<int64_t>(*table, field); - int64_t new_val = - static_cast<int64_t>(std::hash<std::string>{}(std::to_string(target_field))); - flatbuffers::SetField<int64_t>(table, field, new_val); - } break; - default: - case kAny: - break; - } - return kFieldHasBeenFiltered; -} - -bool internal::FilterTypeString(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - log::assert_that(field.type()->base_type() == reflection::BaseType::String, - "assert failed: field.type()->base_type() == reflection::BaseType::String"); - - flatbuffers::voffset_t field_offset = field.offset(); - - const flatbuffers::String* string = flatbuffers::GetFieldS(*table, field); - if (string == nullptr) { - return kFieldIsNotPopulated; - // Field is not populated - } - log::assert_that(string != nullptr, "assert failed: string != nullptr"); - flatbuffers::String* mutable_string = const_cast<flatbuffers::String*>(string); - - [[maybe_unused]] std::string old_string(string->str()); - switch (privacy_level) { - case kPrivate: - internal::ReplaceInString(mutable_string, '*'); - internal::ScrubFromTable(table, field_offset); - break; - case kOpaque: - internal::ReplaceInString(mutable_string, '*'); - break; - case kAnonymized: - internal::RandomizeInString(mutable_string); - break; - default: - case kAny: - break; - } - if (DBG) { - log::info("Field_name:{} size:{} privacy_level:{} old_string:{} ==> new_string:{}", - field.name()->c_str(), string->size(), PrivacyLevelName(privacy_level), old_string, - string->c_str()); - } - return kFieldHasBeenFiltered; -} - -bool internal::FilterTypeStruct(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level) { - log::assert_that(table != nullptr, "assert failed: table != nullptr"); - log::assert_that(!flatbuffers::IsScalar(field.type()->base_type()), - "assert failed: !flatbuffers::IsScalar(field.type()->base_type())"); - - flatbuffers::voffset_t field_offset = field.offset(); - - if (privacy_level != kAny) { - flatbuffers::SetFieldT(table, field, nullptr); - internal::ScrubFromTable(table, field_offset); - if (DBG) { - log::info("Table Removing field name:{} privacy_level:{}", field.name()->c_str(), - PrivacyLevelName(privacy_level)); - } - } - return kFieldContinueFiltering; -} diff --git a/system/gd/dumpsys/internal/filter_internal.h b/system/gd/dumpsys/internal/filter_internal.h deleted file mode 100644 index a18575c72f..0000000000 --- a/system/gd/dumpsys/internal/filter_internal.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2020 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 <base/strings/stringprintf.h> - -#include <string> - -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" -#include "macros.h" - -namespace bluetooth { -namespace dumpsys { -namespace internal { - -constexpr char kPrivacyAttributeKeyword[] = "privacy"; - -enum PrivacyLevel { - kPrivate = 0, - kOpaque = 1, - kAnonymized = 2, - kAny = 4, - kDefaultPrivacyLevel = kPrivate, -}; - -/** - * Remove the field offset from flatbuffer table eliminating ability to - * access value. - * - * @param table Table under consideration for field removeal - * @param field_offset Virtual offset of field into table. - */ -void ScrubFromTable(flatbuffers::Table* table, flatbuffers::voffset_t field_offset); - -/** - * Overwrite ihe contents of flatbuffer string with the integer value proviced. - * The entire size of the string will be set to the value provided. - * - * @param string Flatbuffer string under consideration for content changing. - * @param value Value to overwrite the string contents. - */ -void ReplaceInString(flatbuffers::String* string, int value); - -/** - * Overwrite the contents of flatbuffer string with a hashed value. - * The portion of the string greater than the hash value will be set to SPACE. - * If the string is not large enough for the entire hash value, the hash - * value will be truncated to the size of the string. - * - * @param string Flatbuffer string under consideration for content changing. - */ -void RandomizeInString(flatbuffers::String* string); - -/** - * Returns the privacy level name corresponding to the axtual numeric level. - * - * @param privacy_level PrivacyLevel - * - * @return Name of privacy level. - */ -const char* PrivacyLevelName(PrivacyLevel privacy_level); - -/** - * Returns the privacy level for the given field. If there is no explicitly - * privacy level for this field, the default privacy level is returned. - * - * @param field The reflection field for the schema - * - * @return Privacy level enumeration value - */ -PrivacyLevel FindFieldPrivacyLevel(const reflection::Field& field); - -/** - * Returns the privacy level for given privacy level keyword name. - * If the privacy level for this field, the default privacy level is returned. - * - * @param name The privacy level name. - * - * @return Privacy level enumeration value. - */ -PrivacyLevel GetPrivacyLevelAttribute(const std::string& name); - -/** - * Find a the reflection object that corresponds to the name provided. - * Returns nullptr is not found. - * - * @param objects Vector container of flatbuffer objects - * @param name Flatbuffer string name to search - * - * @return Reflection object if found, nullptr otherwise. - */ -const reflection::Object* FindReflectionObject( - const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>>* objects, - const flatbuffers::String* name); - -/** - * Process and filter the respective data types. - * - * @param field The reflection field schema. - * @param table The mutable table data corresponding to the schema. - * @param privacy_level The privacy level in which to filter the data. - * - * @return true if successfully filtered, false otherwise. - */ -bool FilterTypeBool(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); -bool FilterTypeFloat(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); -bool FilterTypeInteger(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); -bool FilterTypeLong(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); -bool FilterTypeString(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); -bool FilterTypeStruct(const reflection::Field& field, flatbuffers::Table* table, - PrivacyLevel privacy_level); - -inline std::string FlatbufferTypeText(const flatbuffers::BaseType& type) { - switch (type) { - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_NONE); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_BOOL); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_CHAR); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_UCHAR); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_SHORT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_USHORT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_INT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_UINT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_LONG); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_ULONG); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_FLOAT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_DOUBLE); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_STRING); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_VECTOR); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_STRUCT); - CASE_RETURN_TEXT(flatbuffers::BASE_TYPE_UNION); - default: - return base::StringPrintf("UNKNOWN[%d]", (int)type); - } -} - -} // namespace internal -} // namespace dumpsys -} // namespace bluetooth diff --git a/system/gd/dumpsys/internal/filter_internal_test.cc b/system/gd/dumpsys/internal/filter_internal_test.cc deleted file mode 100644 index 38f3481f0c..0000000000 --- a/system/gd/dumpsys/internal/filter_internal_test.cc +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/internal/filter_internal.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "dumpsys/internal/test_data/float_bfbs.h" -#include "dumpsys/internal/test_data/float_generated.h" -#include "dumpsys/internal/test_data/integer_bfbs.h" -#include "dumpsys/internal/test_data/integer_generated.h" -#include "dumpsys/internal/test_data/string_bfbs.h" -#include "dumpsys/internal/test_data/string_generated.h" -#include "dumpsys/internal/test_data/struct_bfbs.h" -#include "dumpsys/internal/test_data/struct_generated.h" - -namespace testing { - -class DumpsysFilterInternalTest : public Test { -protected: - void SetUp() override {} - void TearDown() override {} - - flatbuffers::Table* GetMutableTable() const { - return flatbuffers::GetMutableRoot<flatbuffers::Table>(fb_builder_.GetBufferPointer()); - } - - void ParseReflectionSchema(unsigned char* bfbs, unsigned int bfbs_len) { - ASSERT_TRUE(reflection_schema_.empty()); - reflection_schema_ = std::vector<uint8_t>(bfbs, bfbs + bfbs_len); - flatbuffers::Verifier verifier(reflection_schema_.data(), reflection_schema_.size()); - ASSERT_TRUE(reflection::VerifySchemaBuffer(verifier)); - schema_ = reflection::GetSchema(reflection_schema_.data()); - ASSERT_TRUE(schema_ != nullptr); - } - - const reflection::Schema* schema_{nullptr}; - flatbuffers::FlatBufferBuilder fb_builder_ = flatbuffers::FlatBufferBuilder(1024); - -private: - std::vector<uint8_t> reflection_schema_; -}; - -class DumpsysFilterInternalIntegerTest : public DumpsysFilterInternalTest { -protected: - void SetUp() override { this->ParseReflectionSchema(integer_bfbs, integer_bfbs_len); } - - const testing::TestTableInteger* CreateInteger(int32_t value) { - TestTableIntegerBuilder builder(fb_builder_); - builder.add_test_int(value); - fb_builder_.Finish(builder.Finish()); - return GetTestTableInteger(fb_builder_.GetBufferPointer()); - } -}; - -class DumpsysFilterInternalFloatTest : public DumpsysFilterInternalTest { -protected: - void SetUp() override { this->ParseReflectionSchema(float_bfbs, float_bfbs_len); } - - const testing::TestTableFloat* CreateFloat(double value) { - TestTableFloatBuilder builder(fb_builder_); - builder.add_test_float(value); - fb_builder_.Finish(builder.Finish()); - return GetTestTableFloat(fb_builder_.GetBufferPointer()); - } -}; - -class DumpsysFilterInternalStringTest : public DumpsysFilterInternalTest { -protected: - void SetUp() override { this->ParseReflectionSchema(string_bfbs, string_bfbs_len); } - - const testing::TestTableString* CreateString(std::string string) { - auto test_string = fb_builder_.CreateString(string); - TestTableStringBuilder builder(fb_builder_); - builder.add_test_string(test_string); - fb_builder_.Finish(builder.Finish()); - return GetTestTableString(fb_builder_.GetBufferPointer()); - } -}; - -class DumpsysFilterInternalStructTest : public DumpsysFilterInternalTest { -protected: - void SetUp() override { this->ParseReflectionSchema(struct_bfbs, struct_bfbs_len); } - - flatbuffers::Offset<TestSubTable> CreateSubTable(int val) { - TestSubTableBuilder builder(fb_builder_); - builder.add_placeholder(val); - return builder.Finish(); - } - - const testing::TestTableStruct* CreateStruct(int val) { - auto sub_table = CreateSubTable(val); - - TestTableStructBuilder builder(fb_builder_); - builder.add_sub_table(sub_table); - fb_builder_.Finish(builder.Finish()); - return GetTestTableStruct(fb_builder_.GetBufferPointer()); - } -}; - -TEST_F(DumpsysFilterInternalIntegerTest, filter_type_integer_any) { - const testing::TestTableInteger* test_table = CreateInteger(123); - ASSERT_EQ(123, test_table->test_int()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeInteger( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAny); - } - ASSERT_EQ(123, test_table->test_int()); -} - -TEST_F(DumpsysFilterInternalIntegerTest, filter_type_integer_anonymized) { - const testing::TestTableInteger* test_table = CreateInteger(123); - ASSERT_EQ(123, test_table->test_int()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeInteger( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_NE(123, test_table->test_int()); -} - -TEST_F(DumpsysFilterInternalIntegerTest, filter_type_integer_opaque) { - const testing::TestTableInteger* test_table = CreateInteger(123); - ASSERT_EQ(123, test_table->test_int()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeInteger( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kOpaque); - } - ASSERT_EQ(0, test_table->test_int()); -} - -TEST_F(DumpsysFilterInternalIntegerTest, filter_type_integer_privacy) { - const testing::TestTableInteger* test_table = CreateInteger(123); - ASSERT_EQ(123, test_table->test_int()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeInteger( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kPrivate); - } - ASSERT_EQ(0, test_table->test_int()); -} - -TEST_F(DumpsysFilterInternalFloatTest, filter_type_float_any) { - const testing::TestTableFloat* test_table = CreateFloat(1.23); - ASSERT_FLOAT_EQ(1.23, test_table->test_float()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeFloat(**it, table, - bluetooth::dumpsys::internal::PrivacyLevel::kAny); - } - ASSERT_FLOAT_EQ(1.23, test_table->test_float()); -} - -TEST_F(DumpsysFilterInternalFloatTest, filter_type_float_anonymized) { - const testing::TestTableFloat* test_table = CreateFloat(1.23); - ASSERT_FLOAT_EQ(1.23, test_table->test_float()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeFloat( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_THAT(test_table->test_float(), Not(FloatEq(1.23))); -} - -TEST_F(DumpsysFilterInternalFloatTest, filter_type_float_opaque) { - const testing::TestTableFloat* test_table = CreateFloat(1.23); - ASSERT_FLOAT_EQ(1.23, test_table->test_float()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeFloat( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kOpaque); - } - ASSERT_FLOAT_EQ(0.0, test_table->test_float()); -} - -TEST_F(DumpsysFilterInternalFloatTest, filter_type_float_private) { - const testing::TestTableFloat* test_table = CreateFloat(1.23); - ASSERT_FLOAT_EQ(1.23, test_table->test_float()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeFloat( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kPrivate); - } - ASSERT_FLOAT_EQ(0.0, test_table->test_float()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_any) { - const testing::TestTableString* test_table = CreateString("This is a string"); - ASSERT_STREQ("This is a string", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAny); - } - ASSERT_STREQ("This is a string", test_table->test_string()->c_str()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_anonymous) { - const testing::TestTableString* test_table = CreateString("This is a string"); - ASSERT_STREQ("This is a string", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_NE("This is a string", test_table->test_string()->c_str()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_anonymous_small) { - const testing::TestTableString* test_table = CreateString("A"); - ASSERT_STREQ("A", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_NE("A", test_table->test_string()->c_str()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_anonymous_large) { - const testing::TestTableString* test_table = - CreateString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); - ASSERT_STREQ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_NE("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - test_table->test_string()->c_str()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_opaque) { - const testing::TestTableString* test_table = CreateString("This is a string"); - ASSERT_STREQ("This is a string", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kOpaque); - } - - std::string opaque_expected(strlen("This is a string"), '*'); - ASSERT_STREQ(opaque_expected.c_str(), test_table->test_string()->c_str()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_private) { - const testing::TestTableString* test_table = CreateString("This is a string"); - ASSERT_STREQ("This is a string", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kPrivate); - } - ASSERT_EQ(nullptr, test_table->test_string()); -} - -TEST_F(DumpsysFilterInternalStringTest, filter_type_string_private_small) { - const testing::TestTableString* test_table = CreateString("A"); - ASSERT_STREQ("A", test_table->test_string()->c_str()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeString( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kPrivate); - } - ASSERT_EQ(nullptr, test_table->test_string()); -} - -TEST_F(DumpsysFilterInternalStructTest, filter_type_struct_any) { - const testing::TestTableStruct* test_table = CreateStruct(456); - ASSERT_EQ(456, test_table->sub_table()->placeholder()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeStruct( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAny); - } - ASSERT_EQ(456, test_table->sub_table()->placeholder()); -} - -TEST_F(DumpsysFilterInternalStructTest, filter_type_struct_anonymous) { - const testing::TestTableStruct* test_table = CreateStruct(456); - ASSERT_EQ(456, test_table->sub_table()->placeholder()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeStruct( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kAnonymized); - } - ASSERT_EQ(nullptr, test_table->sub_table()); -} - -TEST_F(DumpsysFilterInternalStructTest, filter_type_struct_opaque) { - const testing::TestTableStruct* test_table = CreateStruct(456); - ASSERT_EQ(456, test_table->sub_table()->placeholder()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeStruct( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kOpaque); - } - ASSERT_EQ(nullptr, test_table->sub_table()); -} - -TEST_F(DumpsysFilterInternalStructTest, filter_type_struct_private) { - const testing::TestTableStruct* test_table = CreateStruct(456); - ASSERT_EQ(456, test_table->sub_table()->placeholder()); - - flatbuffers::Table* table = GetMutableTable(); - - const reflection::Object* object = schema_->root_table(); - ASSERT_TRUE(object != nullptr); - - for (auto it = object->fields()->cbegin(); it != object->fields()->cend(); ++it) { - bluetooth::dumpsys::internal::FilterTypeStruct( - **it, table, bluetooth::dumpsys::internal::PrivacyLevel::kPrivate); - } - ASSERT_EQ(nullptr, test_table->sub_table()); -} - -} // namespace testing diff --git a/system/gd/dumpsys/internal/test_data/float.bfbs b/system/gd/dumpsys/internal/test_data/float.bfbs Binary files differdeleted file mode 100644 index 547ecd93bf..0000000000 --- a/system/gd/dumpsys/internal/test_data/float.bfbs +++ /dev/null diff --git a/system/gd/dumpsys/internal/test_data/float.fbs b/system/gd/dumpsys/internal/test_data/float.fbs deleted file mode 100644 index 2f832e685c..0000000000 --- a/system/gd/dumpsys/internal/test_data/float.fbs +++ /dev/null @@ -1,7 +0,0 @@ -namespace testing; - -table TestTableFloat { - test_float:float; -} - -root_type TestTableFloat; diff --git a/system/gd/dumpsys/internal/test_data/float_bfbs.h b/system/gd/dumpsys/internal/test_data/float_bfbs.h deleted file mode 100644 index 06488b95a8..0000000000 --- a/system/gd/dumpsys/internal/test_data/float_bfbs.h +++ /dev/null @@ -1,18 +0,0 @@ -unsigned char float_bfbs[] = { - 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x2c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x67, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x6c, 0x6f, 0x61, - 0x74, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x00, 0x00}; -unsigned int float_bfbs_len = 240; diff --git a/system/gd/dumpsys/internal/test_data/integer.bfbs b/system/gd/dumpsys/internal/test_data/integer.bfbs Binary files differdeleted file mode 100644 index 110c65dbee..0000000000 --- a/system/gd/dumpsys/internal/test_data/integer.bfbs +++ /dev/null diff --git a/system/gd/dumpsys/internal/test_data/integer.fbs b/system/gd/dumpsys/internal/test_data/integer.fbs deleted file mode 100644 index b8a5569eb2..0000000000 --- a/system/gd/dumpsys/internal/test_data/integer.fbs +++ /dev/null @@ -1,7 +0,0 @@ -namespace testing; - -table TestTableInteger { - test_int:int; -} - -root_type TestTableInteger; diff --git a/system/gd/dumpsys/internal/test_data/integer_bfbs.h b/system/gd/dumpsys/internal/test_data/integer_bfbs.h deleted file mode 100644 index 7f6ba61d7e..0000000000 --- a/system/gd/dumpsys/internal/test_data/integer_bfbs.h +++ /dev/null @@ -1,19 +0,0 @@ -unsigned char integer_bfbs[] = { - 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x67, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, - 0x67, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x74, - 0x00, 0x00, 0x00, 0x00}; -unsigned int integer_bfbs_len = 244; diff --git a/system/gd/dumpsys/internal/test_data/mkfiles b/system/gd/dumpsys/internal/test_data/mkfiles deleted file mode 100644 index 95577d05d2..0000000000 --- a/system/gd/dumpsys/internal/test_data/mkfiles +++ /dev/null @@ -1,9 +0,0 @@ -../../../../../out/host/linux-x86/bin/flatc -b --schema --cpp string.fbs -xxd -i string.bfbs > string_bfbs.h -../../../../../out/host/linux-x86/bin/flatc -b --schema --cpp integer.fbs -xxd -i integer.bfbs > integer_bfbs.h -../../../../../out/host/linux-x86/bin/flatc -b --schema --cpp float.fbs -xxd -i float.bfbs > float_bfbs.h -../../../../../out/host/linux-x86/bin/flatc -b --schema --cpp struct.fbs -xxd -i struct.bfbs > struct_bfbs.h - diff --git a/system/gd/dumpsys/internal/test_data/root.h b/system/gd/dumpsys/internal/test_data/root.h deleted file mode 100644 index 5830ee8cf0..0000000000 --- a/system/gd/dumpsys/internal/test_data/root.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include "flatbuffers/flatbuffers.h" -#include "root_generated.h" - -using TableAddFunction = std::function<void(testing::DumpsysTestDataRootBuilder* root_builder)>; - -namespace testing { - -struct DumpsysTestDataClass { - virtual TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& builder) = 0; - virtual ~DumpsysTestDataClass() = default; -}; - -} // namespace testing diff --git a/system/gd/dumpsys/internal/test_data/string.bfbs b/system/gd/dumpsys/internal/test_data/string.bfbs Binary files differdeleted file mode 100644 index a414802b43..0000000000 --- a/system/gd/dumpsys/internal/test_data/string.bfbs +++ /dev/null diff --git a/system/gd/dumpsys/internal/test_data/string.fbs b/system/gd/dumpsys/internal/test_data/string.fbs deleted file mode 100644 index ba4f2fdfc9..0000000000 --- a/system/gd/dumpsys/internal/test_data/string.fbs +++ /dev/null @@ -1,7 +0,0 @@ -namespace testing; - -table TestTableString { - test_string:string; -} - -root_type TestTableString; diff --git a/system/gd/dumpsys/internal/test_data/string_bfbs.h b/system/gd/dumpsys/internal/test_data/string_bfbs.h deleted file mode 100644 index 1b241352ce..0000000000 --- a/system/gd/dumpsys/internal/test_data/string_bfbs.h +++ /dev/null @@ -1,16 +0,0 @@ -unsigned char string_bfbs[] = { - 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x34, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x65, 0x73, - 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0c, 0x00, - 0x12, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x00, - 0x00, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00}; -unsigned int string_bfbs_len = 208; diff --git a/system/gd/dumpsys/internal/test_data/struct.bfbs b/system/gd/dumpsys/internal/test_data/struct.bfbs Binary files differdeleted file mode 100644 index c3ecca8841..0000000000 --- a/system/gd/dumpsys/internal/test_data/struct.bfbs +++ /dev/null diff --git a/system/gd/dumpsys/internal/test_data/struct.fbs b/system/gd/dumpsys/internal/test_data/struct.fbs deleted file mode 100644 index 83d4974f9b..0000000000 --- a/system/gd/dumpsys/internal/test_data/struct.fbs +++ /dev/null @@ -1,11 +0,0 @@ -namespace testing; - -table TestSubTable { - placeholder:int; -} - -table TestTableStruct{ - sub_table:TestSubTable; -} - -root_type TestTableStruct; diff --git a/system/gd/dumpsys/internal/test_data/struct_bfbs.h b/system/gd/dumpsys/internal/test_data/struct_bfbs.h deleted file mode 100644 index 5df51a98f4..0000000000 --- a/system/gd/dumpsys/internal/test_data/struct_bfbs.h +++ /dev/null @@ -1,29 +0,0 @@ -unsigned char struct_bfbs[] = { - 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2c, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6c, 0xff, - 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xac, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x08, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, - 0x75, 0x62, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00, - 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x62, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, - 0x72, 0x00}; -unsigned int struct_bfbs_len = 392; diff --git a/system/gd/dumpsys/reflection_schema.cc b/system/gd/dumpsys/reflection_schema.cc deleted file mode 100644 index b2d79313eb..0000000000 --- a/system/gd/dumpsys/reflection_schema.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/reflection_schema.h" - -#include <bluetooth/log.h> - -#include <string> - -#include "bundler_schema_generated.h" -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" - -using namespace bluetooth; - -dumpsys::ReflectionSchema::ReflectionSchema(const std::string& pre_bundled_schema) - : pre_bundled_schema_(pre_bundled_schema) { - bundled_schema_ = - flatbuffers::GetRoot<bluetooth::dumpsys::BundledSchema>(pre_bundled_schema_.data()); - log::assert_that(bundled_schema_ != nullptr, "assert failed: bundled_schema_ != nullptr"); -} - -int dumpsys::ReflectionSchema::GetNumberOfBundledSchemas() const { - return bundled_schema_->map()->size(); -} - -std::string dumpsys::ReflectionSchema::GetTitle() const { return bundled_schema_->title()->str(); } - -std::string dumpsys::ReflectionSchema::GetRootName() const { - return bundled_schema_->root_name()->str(); -} - -const reflection::Schema* dumpsys::ReflectionSchema::GetRootReflectionSchema() const { - return FindInReflectionSchema(GetRootName()); -} - -const reflection::Schema* dumpsys::ReflectionSchema::FindInReflectionSchema( - const std::string& name) const { - const flatbuffers::Vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>>* map = - bundled_schema_->map(); - - for (auto it = map->cbegin(); it != map->cend(); ++it) { - if (it->name()->str() == name) { - flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(it->data()->Data()), - it->data()->size()); - if (!reflection::VerifySchemaBuffer(verifier)) { - log::warn("Unable to verify schema buffer name:{}", name); - return nullptr; - } - return reflection::GetSchema(it->data()->Data()); - } - } - return nullptr; -} - -void dumpsys::ReflectionSchema::PrintReflectionSchema() const { - const flatbuffers::Vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>>* map = - bundled_schema_->map(); - log::info("Bundled schema title:{} root_name:{}", bundled_schema_->title()->c_str(), - bundled_schema_->root_name()->c_str()); - for (auto it = map->cbegin(); it != map->cend(); ++it) { - log::info("schema:{}", it->name()->c_str()); - } -} - -bool dumpsys::ReflectionSchema::VerifyReflectionSchema() const { - const flatbuffers::Vector<flatbuffers::Offset<bluetooth::dumpsys::BundledSchemaMap>>* map = - bundled_schema_->map(); - - for (auto it = map->cbegin(); it != map->cend(); ++it) { - flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(it->data()->Data()), - it->data()->size()); - if (!reflection::VerifySchemaBuffer(verifier)) { - return false; - } - } - return true; -} diff --git a/system/gd/dumpsys/reflection_schema.h b/system/gd/dumpsys/reflection_schema.h deleted file mode 100644 index 7100a6b1b6..0000000000 --- a/system/gd/dumpsys/reflection_schema.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2020 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 <string> - -#include "bundler_schema_generated.h" -#include "flatbuffers/flatbuffers.h" -#include "flatbuffers/idl.h" - -namespace bluetooth { -namespace dumpsys { - -class ReflectionSchema { -public: - ReflectionSchema(const std::string& pre_bundled_schema); - - std::string GetTitle() const; - std::string GetRootName() const; - int GetNumberOfBundledSchemas() const; - - bool VerifyReflectionSchema() const; - const reflection::Schema* GetRootReflectionSchema() const; - const reflection::Schema* FindInReflectionSchema(const std::string& name) const; - void PrintReflectionSchema() const; - -private: - const BundledSchema* bundled_schema_; - const std::string pre_bundled_schema_; -}; - -} // namespace dumpsys -} // namespace bluetooth diff --git a/system/gd/dumpsys/reflection_schema_test.cc b/system/gd/dumpsys/reflection_schema_test.cc deleted file mode 100644 index 83e433eddd..0000000000 --- a/system/gd/dumpsys/reflection_schema_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020 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 "dumpsys/reflection_schema.h" - -#include <gtest/gtest.h> - -#include "dumpsys/dumpsys_test_data.h" - -namespace bluetooth { -namespace dumpsys { -extern const unsigned char* data; -extern const size_t data_size; -const std::string& GetBundledSchemaData(); -} // namespace dumpsys -} // namespace bluetooth - -namespace testing { - -using namespace bluetooth; - -class ReflectionSchemaTest : public Test { -protected: - void SetUp() override {} - - void TearDown() override {} -}; - -TEST_F(ReflectionSchemaTest, verify_test_content) { - dumpsys::ReflectionSchema reflection_schema(testing::GetBundledSchemaData()); - ASSERT_EQ(5, reflection_schema.GetNumberOfBundledSchemas()); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("testing.DumpsysTestDataRoot") != nullptr); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("testing.BarTestSchema") != nullptr); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("testing.BazTestSchema") != nullptr); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("testing.FooTestSchema") != nullptr); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("testing.QuxTestSchema") != nullptr); - ASSERT_TRUE(reflection_schema.FindInReflectionSchema("DoesNotExist") == nullptr); -} - -TEST_F(ReflectionSchemaTest, verify_test_schema) { - dumpsys::ReflectionSchema reflection_schema(testing::GetBundledSchemaData()); - ASSERT_TRUE(reflection_schema.VerifyReflectionSchema()); -} - -TEST_F(ReflectionSchemaTest, verify_production_schema) { - dumpsys::ReflectionSchema reflection_schema(bluetooth::dumpsys::GetBundledSchemaData()); - ASSERT_TRUE(reflection_schema.VerifyReflectionSchema()); -} - -} // namespace testing diff --git a/system/gd/dumpsys/test_data/bar.fbs b/system/gd/dumpsys/test_data/bar.fbs deleted file mode 100644 index dc423fea9e..0000000000 --- a/system/gd/dumpsys/test_data/bar.fbs +++ /dev/null @@ -1,12 +0,0 @@ -namespace testing; - -attribute "privacy"; - -table BarTestSchema { - bar_x:int; - bar_y:int; - another_field:string (privacy:"Any"); -} - -root_type BarTestSchema; - diff --git a/system/gd/dumpsys/test_data/bar.h b/system/gd/dumpsys/test_data/bar.h deleted file mode 100644 index 26af6c09e2..0000000000 --- a/system/gd/dumpsys/test_data/bar.h +++ /dev/null @@ -1,17 +0,0 @@ -/** - * - **/ -#include "bar_generated.h" -#include "root.h" -#include "root_generated.h" - -namespace testing { - -class BarTestDataClass : public DumpsysTestDataClass { -public: - TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& /* fb_builder */) override { - return [](DumpsysTestDataRootBuilder* /* builder */) {}; - } -}; - -} // namespace testing diff --git a/system/gd/dumpsys/test_data/baz.fbs b/system/gd/dumpsys/test_data/baz.fbs deleted file mode 100644 index 38649a9c8c..0000000000 --- a/system/gd/dumpsys/test_data/baz.fbs +++ /dev/null @@ -1,35 +0,0 @@ -namespace testing; - -attribute "privacy"; - -table BazSubTablePrivate { - subtable_int_any:int (privacy:"Any"); - subtable_string_any:string (privacy:"Any"); -} - -table BazSubTableOpaque { - subtable_int_any:int (privacy:"Any"); - subtable_string_any:string (privacy:"Any"); -} - -table BazSubTableAnonymized { - subtable_int_any:int (privacy:"Any"); - subtable_string_any:string (privacy:"Any"); -} - -table BazSubTableAny { - subtable_int_private:int (privacy:"Private"); - subtable_int_opaque:int (privacy:"Opaque"); - subtable_int_anonymized:int (privacy:"Anonymized"); - subtable_int_any:int (privacy:"Any"); - subtable_string_any:string (privacy:"Any"); -} - -table BazTestSchema { - sub_table_private:BazSubTablePrivate; // private by default - sub_table_opaque:BazSubTableOpaque (privacy:"Opaque"); - sub_table_anonymized:BazSubTableAnonymized (privacy:"Anonymized"); - sub_table_any:BazSubTableAny (privacy:"Any"); -} - -root_type BazTestSchema; diff --git a/system/gd/dumpsys/test_data/baz.h b/system/gd/dumpsys/test_data/baz.h deleted file mode 100644 index 326f025bb9..0000000000 --- a/system/gd/dumpsys/test_data/baz.h +++ /dev/null @@ -1,34 +0,0 @@ - -#include "baz_generated.h" -#include "root.h" -#include "root_generated.h" - -namespace testing { - -class BazTestDataClass : public DumpsysTestDataClass { -public: - TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& fb_builder) override { - auto sub_name_private = fb_builder.CreateString("Baz Subtable Private"); - auto sub_name_opaque = fb_builder.CreateString("Baz Subtable Opaque"); - auto sub_name_anonymized = fb_builder.CreateString("Baz Subtable Anonymized"); - auto sub_name_any = fb_builder.CreateString("Baz Subtable Any"); - - auto private_subtable = CreateBazSubTablePrivate(fb_builder, 1, sub_name_private); - auto opaque_subtable = CreateBazSubTableOpaque(fb_builder, 1, sub_name_opaque); - auto anonymized_subtable = CreateBazSubTableAnonymized(fb_builder, 1, sub_name_anonymized); - auto any_subtable = CreateBazSubTableAny(fb_builder, 1, 2, 3, 4, sub_name_any); - - BazTestSchemaBuilder builder(fb_builder); - builder.add_sub_table_private(private_subtable); - builder.add_sub_table_opaque(opaque_subtable); - builder.add_sub_table_anonymized(anonymized_subtable); - builder.add_sub_table_any(any_subtable); - auto baz_table = builder.Finish(); - - return [baz_table](DumpsysTestDataRootBuilder* builder) { - builder->add_baz_module_data(baz_table); - }; - } -}; - -} // namespace testing diff --git a/system/gd/dumpsys/test_data/foo.fbs b/system/gd/dumpsys/test_data/foo.fbs deleted file mode 100644 index b7482c326f..0000000000 --- a/system/gd/dumpsys/test_data/foo.fbs +++ /dev/null @@ -1,25 +0,0 @@ -namespace testing; - -attribute "privacy"; - -table FooTestSchema { - foo_int_private:int; - foo_int_opaque:int (privacy:"Opaque"); - foo_int_anonymized:int (privacy:"Anonymized"); - foo_int_any:int (privacy:"Any"); - foo_int_string:string (privacy:"Any"); - - foo_float_private:float; - foo_float_opaque:float (privacy:"Opaque"); - foo_float_anonymized:float (privacy:"Anonymized"); - foo_float_any:float (privacy:"Any"); - foo_float_string:string (privacy:"Any"); - - foo_bool_private:bool (privacy:"Private"); - foo_bool_opaque:bool (privacy:"Opaque"); - foo_bool_anonymized:bool (privacy:"Anonymized"); - foo_bool_any:bool (privacy:"Any"); - foo_bool_string:string (privacy:"Any"); -} - -root_type FooTestSchema; diff --git a/system/gd/dumpsys/test_data/foo.h b/system/gd/dumpsys/test_data/foo.h deleted file mode 100644 index 7d346657d4..0000000000 --- a/system/gd/dumpsys/test_data/foo.h +++ /dev/null @@ -1,42 +0,0 @@ - -#include "foo_generated.h" -#include "root.h" -#include "root_generated.h" - -namespace testing { - -class FooTestDataClass : public DumpsysTestDataClass { -public: - TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& fb_builder) override { - auto int_string = fb_builder.CreateString("123"); - auto float_string = fb_builder.CreateString("123.456"); - auto bool_string = fb_builder.CreateString("true"); - - FooTestSchemaBuilder builder(fb_builder); - builder.add_foo_int_private(123); - builder.add_foo_int_opaque(123); - builder.add_foo_int_anonymized(123); - builder.add_foo_int_any(123); - builder.add_foo_int_string(int_string); - - builder.add_foo_float_private(123.456); - builder.add_foo_float_opaque(123.456); - builder.add_foo_float_anonymized(123.456); - builder.add_foo_float_any(123.456); - builder.add_foo_float_string(float_string); - - builder.add_foo_bool_private(true); - builder.add_foo_bool_opaque(true); - builder.add_foo_bool_anonymized(true); - builder.add_foo_bool_any(true); - builder.add_foo_bool_string(bool_string); - - auto foo_table = builder.Finish(); - - return [foo_table](DumpsysTestDataRootBuilder* builder) { - builder->add_foo_module_data(foo_table); - }; - } -}; - -} // namespace testing diff --git a/system/gd/dumpsys/test_data/qux.fbs b/system/gd/dumpsys/test_data/qux.fbs deleted file mode 100644 index 15e17dab7d..0000000000 --- a/system/gd/dumpsys/test_data/qux.fbs +++ /dev/null @@ -1,13 +0,0 @@ -namespace testing; - -attribute "privacy"; - -table QuxTestSchema { - qux_int_private:int; - qux_int_opaque:int (privacy:"Opaque"); - qux_int_anonymized:int (privacy:"Anonymized"); - qux_int_any:int (privacy:"Any"); - qux_string_name:string (privacy:"Any"); -} - -root_type QuxTestSchema; diff --git a/system/gd/dumpsys/test_data/qux.h b/system/gd/dumpsys/test_data/qux.h deleted file mode 100644 index 1e70de3879..0000000000 --- a/system/gd/dumpsys/test_data/qux.h +++ /dev/null @@ -1,28 +0,0 @@ - -#include "qux_generated.h" -#include "root.h" -#include "root_generated.h" - -namespace testing { - -class QuxTestDataClass : public DumpsysTestDataClass { -public: - TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& fb_builder) override { - auto name = fb_builder.CreateString("Qux Module String"); - - QuxTestSchemaBuilder builder(fb_builder); - builder.add_qux_int_private(123); - builder.add_qux_int_opaque(456); - builder.add_qux_int_anonymized(789); - builder.add_qux_int_any(0xabc); - builder.add_qux_string_name(name); - - auto qux_table = builder.Finish(); - - return [qux_table](DumpsysTestDataRootBuilder* builder) { - builder->add_qux_module_data(qux_table); - }; - } -}; - -} // namespace testing diff --git a/system/gd/dumpsys/test_data/root.fbs b/system/gd/dumpsys/test_data/root.fbs deleted file mode 100644 index cedd310d11..0000000000 --- a/system/gd/dumpsys/test_data/root.fbs +++ /dev/null @@ -1,27 +0,0 @@ -include "foo.fbs"; -include "bar.fbs"; -include "baz.fbs"; -include "qux.fbs"; - -namespace testing; - -attribute "privacy"; - -table DumpsysTestDataRoot { - string_private:string; - string_opaque:string (privacy:"Opaque"); - string_anonymized:string (privacy:"Anonymized"); - string_any:string (privacy:"Any"); - - int_private:int32 (privacy:"Private"); - int_opaque:int32 (privacy:"Opaque"); - int_anonymized:int32 (privacy:"Anonymized"); - int_any:int32 (privacy:"Any"); - - foo_module_data:FooTestSchema (privacy:"Any"); - bar_module_data:BarTestSchema (privacy:"Any"); - baz_module_data:BazTestSchema (privacy:"Any"); - qux_module_data:QuxTestSchema (privacy:"Any"); -} - -root_type DumpsysTestDataRoot; diff --git a/system/gd/dumpsys/test_data/root.h b/system/gd/dumpsys/test_data/root.h deleted file mode 100644 index 9f58c8ebed..0000000000 --- a/system/gd/dumpsys/test_data/root.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#pragma once - -#include "flatbuffers/flatbuffers.h" -#include "root_generated.h" - -using TableAddFunction = std::function<void(testing::DumpsysTestDataRootBuilder* root_builder)>; - -namespace testing { - -struct DumpsysTestDataClass { - virtual TableAddFunction GetTable(flatbuffers::FlatBufferBuilder& fb_builder) = 0; - virtual ~DumpsysTestDataClass() = default; -}; - -} // namespace testing diff --git a/system/gd/dumpsys_data.fbs b/system/gd/dumpsys_data.fbs deleted file mode 100644 index 2556a4da4f..0000000000 --- a/system/gd/dumpsys_data.fbs +++ /dev/null @@ -1,25 +0,0 @@ -// Top level module dumpsys data schema -// -// Possible field privacy levels from strongest to weakest. -// When unspecified defaults to the strongest privacy level. -// -// privacy:"Private" -// privacy:"Opaque" -// privacy:"Anonymized" -// privacy:"Any" - - -include "module_unittest.fbs"; -include "os/wakelock_manager.fbs"; - -namespace bluetooth; - -attribute "privacy"; - -table DumpsysData { - title:string (privacy:"Any"); - wakelock_manager_data:bluetooth.os.WakelockManagerData (privacy:"Any"); - module_unittest_data:bluetooth.ModuleUnitTestData; // private -} - -root_type DumpsysData; diff --git a/system/gd/facade/facade_main.cc b/system/gd/facade/facade_main.cc deleted file mode 100644 index 4745ce2c06..0000000000 --- a/system/gd/facade/facade_main.cc +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2019 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 <bluetooth/log.h> -#include <sys/types.h> -#include <unistd.h> - -#include <csignal> -#include <cstring> -#include <optional> -#include <string> -#include <thread> - -#include "stack_manager.h" - -// clang-format off -#include <client/linux/handler/exception_handler.h> -#include <unwindstack/AndroidUnwinder.h> -// clang-format on - -#include "facade/grpc_root_server.h" -#include "hal/hci_hal_host.h" -#include "hal/snoop_logger.h" -#include "os/parameter_provider.h" -#include "os/system_properties.h" - -using ::bluetooth::ModuleList; -using ::bluetooth::StackManager; -using ::bluetooth::hal::HciHalHostRootcanalConfig; -using ::bluetooth::os::Thread; -using namespace bluetooth; - -extern "C" const char* __asan_default_options() { return "detect_container_overflow=0"; } - -namespace { -::bluetooth::facade::GrpcRootServer grpc_root_server; - -std::promise<void> interrupt_promise; -std::future<void> interrupt_future; -bool interrupted = false; -struct sigaction old_act = {}; -void interrupt_handler(int signal_number) { - if (!interrupted) { - interrupted = true; - log::info("Stopping gRPC root server due to signal: {}[{}]", strsignal(signal_number), - signal_number); - interrupt_promise.set_value(); - } else { - log::warn("Already interrupted by signal: {}[{}]", strsignal(signal_number), signal_number); - } - if (old_act.sa_handler != nullptr && old_act.sa_handler != SIG_IGN && - old_act.sa_handler != SIG_DFL) { - log::info("Calling saved signal handler"); - old_act.sa_handler(signal_number); - } -} -struct sigaction new_act = {.sa_handler = interrupt_handler}; - -bool crash_callback(const void* crash_context, size_t crash_context_size, void* /* context */) { - std::optional<pid_t> tid; - if (crash_context_size >= sizeof(google_breakpad::ExceptionHandler::CrashContext)) { - auto* ctx = static_cast<const google_breakpad::ExceptionHandler::CrashContext*>(crash_context); - tid = ctx->tid; - int signal_number = ctx->siginfo.si_signo; - log::error("Process crashed, signal: {}[{}], tid: {}", strsignal(signal_number), signal_number, - ctx->tid); - } else { - log::error("Process crashed, signal: unknown, tid: unknown"); - } - unwindstack::AndroidLocalUnwinder unwinder; - unwindstack::AndroidUnwinderData data; - if (!unwinder.Unwind(tid, data)) { - log::error("Unwind failed"); - return false; - } - log::error("Backtrace:"); - for (const auto& frame : data.frames) { - log::error("{}", unwinder.FormatFrame(frame)); - } - return true; -} - -// Need to stop server on a thread that is not part of a signal handler due to an issue with gRPC -// See: https://github.com/grpc/grpc/issues/24884 -void thread_check_shutdown() { - log::info("shutdown thread waiting for interruption"); - interrupt_future.wait(); - log::info("interrupted, stopping server"); - grpc_root_server.StopServer(); -} - -} // namespace - -// The entry point for the binary with libbluetooth + facades -int main(int argc, const char** argv) { - google_breakpad::MinidumpDescriptor descriptor( - google_breakpad::MinidumpDescriptor::kMicrodumpOnConsole); - google_breakpad::ExceptionHandler eh(descriptor, nullptr, nullptr, nullptr, true, -1); - eh.set_crash_handler(crash_callback); - - int root_server_port = 8897; - int grpc_port = 8899; - - const std::string arg_grpc_root_server_port = "--root-server-port="; - const std::string arg_grpc_server_port = "--grpc-port="; - const std::string arg_rootcanal_port = "--rootcanal-port="; - const std::string arg_btsnoop_path = "--btsnoop="; - const std::string arg_btsnooz_path = "--btsnooz="; - const std::string arg_btconfig_path = "--btconfig="; - for (int i = 1; i < argc; i++) { - std::string arg = argv[i]; - if (arg.find(arg_grpc_root_server_port) == 0) { - auto port_number = arg.substr(arg_grpc_root_server_port.size()); - root_server_port = std::stoi(port_number); - } - if (arg.find(arg_grpc_server_port) == 0) { - auto port_number = arg.substr(arg_grpc_server_port.size()); - grpc_port = std::stoi(port_number); - } - if (arg.find(arg_rootcanal_port) == 0) { - auto port_number = arg.substr(arg_rootcanal_port.size()); - HciHalHostRootcanalConfig::Get()->SetPort(std::stoi(port_number)); - } - if (arg.find(arg_btsnoop_path) == 0) { - auto btsnoop_path = arg.substr(arg_btsnoop_path.size()); - ::bluetooth::os::ParameterProvider::OverrideSnoopLogFilePath(btsnoop_path); - log::assert_that(::bluetooth::os::SetSystemProperty( - ::bluetooth::hal::SnoopLogger::kBtSnoopLogModeProperty, - ::bluetooth::hal::SnoopLogger::kBtSnoopLogModeFull), - "assert failed: ::bluetooth::os::SetSystemProperty( " - "::bluetooth::hal::SnoopLogger::kBtSnoopLogModeProperty, " - "::bluetooth::hal::SnoopLogger::kBtSnoopLogModeFull)"); - } - if (arg.find(arg_btsnooz_path) == 0) { - auto btsnooz_path = arg.substr(arg_btsnooz_path.size()); - ::bluetooth::os::ParameterProvider::OverrideSnoozLogFilePath(btsnooz_path); - } - if (arg.find(arg_btconfig_path) == 0) { - auto btconfig_path = arg.substr(arg_btconfig_path.size()); - ::bluetooth::os::ParameterProvider::OverrideConfigFilePath(btconfig_path); - } - } - - int ret = sigaction(SIGINT, &new_act, &old_act); - if (ret < 0) { - log::error("sigaction error: {}", strerror(errno)); - } - - log::info("Starting Server"); - grpc_root_server.StartServer("0.0.0.0", root_server_port, grpc_port); - log::info("Server started"); - auto wait_thread = std::thread([] { grpc_root_server.RunGrpcLoop(); }); - interrupt_future = interrupt_promise.get_future(); - auto shutdown_thread = std::thread{thread_check_shutdown}; - wait_thread.join(); - log::info("Server terminated"); - shutdown_thread.join(); - log::info("Shutdown thread terminated"); - - return 0; -} diff --git a/system/gd/facade/grpc_root_server.cc b/system/gd/facade/grpc_root_server.cc deleted file mode 100644 index 8e3bfb3f3f..0000000000 --- a/system/gd/facade/grpc_root_server.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2019 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 "facade/grpc_root_server.h" - -#include <string> - -#include "blueberry/facade/rootservice.grpc.pb.h" -#include "facade/read_only_property_server.h" -#include "grpc/grpc_module.h" -#include "hal/facade.h" -#include "hci/facade/acl_manager_facade.h" -#include "hci/facade/controller_facade.h" -#include "hci/facade/facade.h" -#include "hci/facade/le_acl_manager_facade.h" -#include "hci/facade/le_advertising_manager_facade.h" -#include "hci/facade/le_initiator_address_facade.h" -#include "hci/facade/le_scanning_manager_facade.h" -#include "neighbor/facade/facade.h" -#include "os/thread.h" -#include "stack_manager.h" - -namespace bluetooth { -namespace facade { - -using ::blueberry::facade::BluetoothModule; -using ::bluetooth::grpc::GrpcModule; -using ::bluetooth::os::Thread; - -class RootFacadeService : public ::blueberry::facade::RootFacade::Service { -public: - explicit RootFacadeService(int grpc_port) : grpc_port_(grpc_port) {} - - ::grpc::Status StartStack(::grpc::ServerContext* /* context */, - const ::blueberry::facade::StartStackRequest* request, - ::blueberry::facade::StartStackResponse* /* response */) override { - if (is_running_) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is running"); - } - - ModuleList modules; - modules.add<::bluetooth::grpc::GrpcModule>(); - - BluetoothModule module_under_test = request->module_under_test(); - switch (module_under_test) { - case BluetoothModule::HAL: - modules.add<::bluetooth::hal::HciHalFacadeModule>(); - break; - case BluetoothModule::HCI: - modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>(); - modules.add<::bluetooth::hci::facade::HciFacadeModule>(); - break; - case BluetoothModule::HCI_INTERFACES: - modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>(); - modules.add<::bluetooth::hci::facade::HciFacadeModule>(); - modules.add<::bluetooth::hci::facade::AclManagerFacadeModule>(); - modules.add<::bluetooth::hci::facade::ControllerFacadeModule>(); - modules.add<::bluetooth::hci::facade::LeAclManagerFacadeModule>(); - modules.add<::bluetooth::hci::facade::LeAdvertisingManagerFacadeModule>(); - modules.add<::bluetooth::hci::facade::LeInitiatorAddressFacadeModule>(); - modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>(); - modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>(); - break; - default: - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test"); - } - - stack_thread_ = std::make_unique<Thread>("stack_thread", Thread::Priority::NORMAL); - stack_manager_.StartUp(&modules, stack_thread_.get()); - - GrpcModule* grpc_module = stack_manager_.GetInstance<GrpcModule>(); - grpc_module->StartServer("0.0.0.0", grpc_port_); - - grpc_loop_thread_ = - std::make_unique<std::thread>([grpc_module] { grpc_module->RunGrpcLoop(); }); - is_running_ = true; - - return ::grpc::Status::OK; - } - - ::grpc::Status StopStack(::grpc::ServerContext* /* context */, - const ::blueberry::facade::StopStackRequest* /* request */, - ::blueberry::facade::StopStackResponse* /* response */) override { - if (!is_running_) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "stack is not running"); - } - - stack_manager_.GetInstance<GrpcModule>()->StopServer(); - grpc_loop_thread_->join(); - grpc_loop_thread_.reset(); - - stack_manager_.ShutDown(); - stack_thread_.reset(); - is_running_ = false; - return ::grpc::Status::OK; - } - -private: - std::unique_ptr<Thread> stack_thread_ = nullptr; - bool is_running_ = false; - std::unique_ptr<std::thread> grpc_loop_thread_ = nullptr; - StackManager stack_manager_; - int grpc_port_ = 8898; -}; - -struct GrpcRootServer::impl { - bool started_ = false; - std::unique_ptr<RootFacadeService> root_facade_service_ = nullptr; - std::unique_ptr<::grpc::Server> server_ = nullptr; -}; - -GrpcRootServer::GrpcRootServer() : pimpl_(new impl()) {} - -GrpcRootServer::~GrpcRootServer() = default; - -void GrpcRootServer::StartServer(const std::string& address, int grpc_root_server_port, - int grpc_port) { - log::assert_that(!pimpl_->started_, "assert failed: !pimpl_->started_"); - pimpl_->started_ = true; - - std::string listening_port = address + ":" + std::to_string(grpc_root_server_port); - ::grpc::ServerBuilder builder; - - pimpl_->root_facade_service_ = std::make_unique<RootFacadeService>(grpc_port); - builder.RegisterService(pimpl_->root_facade_service_.get()); - builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials()); - pimpl_->server_ = builder.BuildAndStart(); - - log::assert_that(pimpl_->server_ != nullptr, "assert failed: pimpl_->server_ != nullptr"); -} - -void GrpcRootServer::StopServer() { - log::assert_that(pimpl_->started_, "assert failed: pimpl_->started_"); - pimpl_->server_->Shutdown(); - pimpl_->started_ = false; -} - -void GrpcRootServer::RunGrpcLoop() { - log::assert_that(pimpl_->started_, "assert failed: pimpl_->started_"); - pimpl_->server_->Wait(); -} - -} // namespace facade -} // namespace bluetooth diff --git a/system/gd/facade/read_only_property_server.cc b/system/gd/facade/read_only_property_server.cc deleted file mode 100644 index d8a646d774..0000000000 --- a/system/gd/facade/read_only_property_server.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2019 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 "facade/read_only_property_server.h" - -#include "hci/controller.h" - -namespace bluetooth { -namespace facade { - -class ReadOnlyPropertyService : public blueberry::facade::ReadOnlyProperty::Service { -public: - ReadOnlyPropertyService(hci::Controller* controller) : controller_(controller) {} - ::grpc::Status ReadLocalAddress(::grpc::ServerContext* /* context */, - const ::google::protobuf::Empty* /* request */, - ::blueberry::facade::BluetoothAddress* response) override { - auto address = controller_->GetMacAddress().ToString(); - response->set_address(address); - return ::grpc::Status::OK; - } - -private: - hci::Controller* controller_; -}; - -void ReadOnlyPropertyServerModule::ListDependencies(ModuleList* list) const { - GrpcFacadeModule::ListDependencies(list); - list->add<hci::Controller>(); -} -void ReadOnlyPropertyServerModule::Start() { - GrpcFacadeModule::Start(); - service_ = std::make_unique<ReadOnlyPropertyService>(GetDependency<hci::Controller>()); -} -void ReadOnlyPropertyServerModule::Stop() { - service_.reset(); - GrpcFacadeModule::Stop(); -} -::grpc::Service* ReadOnlyPropertyServerModule::GetService() const { return service_.get(); } - -const ModuleFactory ReadOnlyPropertyServerModule::Factory = - ::bluetooth::ModuleFactory([]() { return new ReadOnlyPropertyServerModule(); }); - -} // namespace facade -} // namespace bluetooth diff --git a/system/gd/facade/read_only_property_server.h b/system/gd/facade/read_only_property_server.h deleted file mode 100644 index a71d5fbd93..0000000000 --- a/system/gd/facade/read_only_property_server.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include <memory> - -#include "blueberry/facade/rootservice.grpc.pb.h" -#include "grpc/grpc_module.h" - -namespace bluetooth { -namespace facade { - -class ReadOnlyPropertyService; - -class ReadOnlyPropertyServerModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - std::unique_ptr<ReadOnlyPropertyService> service_; -}; - -} // namespace facade -} // namespace bluetooth diff --git a/system/gd/grpc/grpc_event_queue.h b/system/gd/grpc/grpc_event_queue.h deleted file mode 100644 index c7f8c87314..0000000000 --- a/system/gd/grpc/grpc_event_queue.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019 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 <bluetooth/log.h> -#include <grpc++/grpc++.h> - -#include <atomic> -#include <chrono> -#include <utility> - -#include "blueberry/facade/common.pb.h" -#include "common/blocking_queue.h" - -namespace bluetooth { -namespace grpc { - -template <typename T> -class GrpcEventQueue { -public: - /** - * Create a GrpcEventQueue that can be used to shuffle event from one thread to another - * @param log_name - */ - explicit GrpcEventQueue(std::string log_name) : log_name_(std::move(log_name)) {} - - /** - * Run the event loop and blocks until client cancels the stream request - * Event queue will be cleared before entering the loop. Hence, only events occurred after gRPC - * request will be delivered to the user. Hence user is advised to run the loop before generating - * pending events. - * - * @param context client context - * @param writer output writer - * @return gRPC status - */ - ::grpc::Status RunLoop(::grpc::ServerContext* context, ::grpc::ServerWriter<T>* writer) { - using namespace std::chrono_literals; - log::info("{}: Entering Loop", log_name_); - while (!context->IsCancelled()) { - // Wait for 100 ms so that cancellation can be caught in amortized 50 ms latency - if (pending_events_.wait_to_take(100ms)) { - log::info("{}: Got event from queue", log_name_); - writer->Write(pending_events_.take()); - } - } - running_ = false; - log::info("{}: Exited Loop", log_name_); - return ::grpc::Status::OK; - } - - /** - * Called when there is an incoming event - * @param event incoming event - */ - void OnIncomingEvent(T event) { - if (!running_) { - log::info("{}: Discarding an event while not running the loop", log_name_); - return; - } - log::info("{}: Got event, enqueuing", log_name_); - pending_events_.push(std::move(event)); - } - -private: - std::string log_name_; - std::atomic<bool> running_{true}; - common::BlockingQueue<T> pending_events_; -}; - -} // namespace grpc -} // namespace bluetooth diff --git a/system/gd/grpc/grpc_module.cc b/system/gd/grpc/grpc_module.cc deleted file mode 100644 index 53b40f8035..0000000000 --- a/system/gd/grpc/grpc_module.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2019 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 "grpc/grpc_module.h" - -#include <bluetooth/log.h> - -using ::grpc::Server; -using ::grpc::ServerBuilder; - -namespace bluetooth { -namespace grpc { - -void GrpcModule::ListDependencies(ModuleList* /* list */) const {} - -void GrpcModule::Start() { log::assert_that(!started_, "assert failed: !started_"); } - -void GrpcModule::Stop() { log::assert_that(!started_, "assert failed: !started_"); } - -void GrpcModule::StartServer(const std::string& address, int port) { - log::assert_that(!started_, "assert failed: !started_"); - started_ = true; - - std::string listening_port = address + ":" + std::to_string(port); - ServerBuilder builder; - - for (const auto& facade : facades_) { - builder.RegisterService(facade->GetService()); - } - - builder.AddListeningPort(listening_port, ::grpc::InsecureServerCredentials()); - completion_queue_ = builder.AddCompletionQueue(); - server_ = builder.BuildAndStart(); - log::assert_that(server_ != nullptr, "assert failed: server_ != nullptr"); - log::info("gRPC server started on {}", listening_port); - - for (const auto& facade : facades_) { - facade->OnServerStarted(); - } -} - -void GrpcModule::StopServer() { - log::assert_that(started_, "assert failed: started_"); - - server_->Shutdown(); - completion_queue_->Shutdown(); - - for (const auto& facade : facades_) { - facade->OnServerStopped(); - } - - started_ = false; -} - -void GrpcModule::Register(GrpcFacadeModule* facade) { - log::assert_that(!started_, "assert failed: !started_"); - - facades_.push_back(facade); -} - -void GrpcModule::Unregister(GrpcFacadeModule* facade) { - log::assert_that(!started_, "assert failed: !started_"); - - for (auto it = facades_.begin(); it != facades_.end(); it++) { - if (*it == facade) { - facades_.erase(it); - return; - } - } - - log::fatal("module not found"); -} - -void GrpcModule::RunGrpcLoop() { - void* tag; - bool ok; - while (true) { - if (!completion_queue_->Next(&tag, &ok)) { - log::info("gRPC is shutdown"); - break; - } - } -} - -std::string GrpcModule::ToString() const { return "Grpc Module"; } - -const ::bluetooth::ModuleFactory GrpcModule::Factory = - ::bluetooth::ModuleFactory([]() { return new GrpcModule(); }); - -void GrpcFacadeModule::ListDependencies(ModuleList* list) const { list->add<GrpcModule>(); } - -void GrpcFacadeModule::Start() { GetDependency<GrpcModule>()->Register(this); } - -void GrpcFacadeModule::Stop() { GetDependency<GrpcModule>()->Unregister(this); } - -std::string GrpcFacadeModule::ToString() const { return "Grpc Facade Module"; } - -} // namespace grpc -} // namespace bluetooth diff --git a/system/gd/grpc/grpc_module.h b/system/gd/grpc/grpc_module.h deleted file mode 100644 index e1b8158b82..0000000000 --- a/system/gd/grpc/grpc_module.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> -#include <module.h> - -#include <functional> -#include <vector> - -namespace bluetooth { -namespace grpc { - -class GrpcFacadeModule; - -class GrpcModule : public ::bluetooth::Module { -public: - static const ModuleFactory Factory; - - void StartServer(const std::string& address, int port); - - void StopServer(); - - void Register(GrpcFacadeModule* facade); - - void Unregister(GrpcFacadeModule* facade); - - // Blocks for incoming gRPC requests - void RunGrpcLoop(); - -protected: - void ListDependencies(ModuleList* list) const override; - - void Start() override; - - void Stop() override; - - std::string ToString() const override; - -private: - bool started_; - std::unique_ptr<::grpc::Server> server_ = nullptr; - std::unique_ptr<::grpc::ServerCompletionQueue> completion_queue_ = nullptr; - std::vector<GrpcFacadeModule*> facades_; -}; - -class GrpcFacadeModule : public ::bluetooth::Module { - friend GrpcModule; - -protected: - void ListDependencies(ModuleList* list) const override; - - void Start() override; - - void Stop() override; - - virtual ::grpc::Service* GetService() const = 0; - - virtual void OnServerStarted() {} - - virtual void OnServerStopped() {} - - std::string ToString() const override; -}; - -} // namespace grpc -} // namespace bluetooth diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp index 91e1573c0e..28c286ebba 100644 --- a/system/gd/hal/Android.bp +++ b/system/gd/hal/Android.bp @@ -43,6 +43,20 @@ filegroup { } filegroup { + name: "BluetoothHalSources_socket_android", + srcs: [ + "socket_hal_android.cc", + ], +} + +filegroup { + name: "BluetoothHalSources_socket_host", + srcs: [ + "socket_hal_host.cc", + ], +} + +filegroup { name: "BluetoothHalSources_hci_android_hidl", srcs: [ "hci_backend_aidl.cc", @@ -73,13 +87,6 @@ filegroup { } filegroup { - name: "BluetoothFacade_hci_hal", - srcs: [ - "facade.cc", - ], -} - -filegroup { name: "BluetoothHalFuzzSources", srcs: [ "fuzz/fuzz_hci_hal.cc", diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn index 46be6e9d8a..ebf15255af 100644 --- a/system/gd/hal/BUILD.gn +++ b/system/gd/hal/BUILD.gn @@ -53,3 +53,12 @@ source_set("BluetoothHalSources_ranging_host") { configs += [ "//bt/system/gd:gd_defaults" ] deps = [ "//bt/system/gd:gd_default_deps" ] } + +source_set("BluetoothHalSources_socket_host") { + sources = [ + "socket_hal_host.cc", + ] + + configs += [ "//bt/system/gd:gd_defaults" ] + deps = [ "//bt/system/gd:gd_default_deps" ] +} diff --git a/system/gd/hal/facade.cc b/system/gd/hal/facade.cc deleted file mode 100644 index 9cfefd7a77..0000000000 --- a/system/gd/hal/facade.cc +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2019 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 "hal/facade.h" - -#include <memory> -#include <mutex> - -#include "blueberry/facade/hal/hal_facade.grpc.pb.h" -#include "grpc/grpc_event_queue.h" -#include "hal/hci_hal.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -namespace bluetooth { -namespace hal { - -class HciHalFacadeService : public blueberry::facade::hal::HciHalFacade::Service, - public ::bluetooth::hal::HciHalCallbacks { -public: - explicit HciHalFacadeService(HciHal* hal) : hal_(hal) { - hal->registerIncomingPacketCallback(this); - } - - ~HciHalFacadeService() { hal_->unregisterIncomingPacketCallback(); } - - ::grpc::Status SendCommand(::grpc::ServerContext* /* context */, - const ::blueberry::facade::Data* request, - ::google::protobuf::Empty* /* response */) override { - std::unique_lock<std::mutex> lock(mutex_); - can_send_hci_command_ = false; - std::string req_string = request->payload(); - hal_->sendHciCommand(std::vector<uint8_t>(req_string.begin(), req_string.end())); - while (!can_send_hci_command_) { - cv_.wait(lock); - } - return ::grpc::Status::OK; - } - - ::grpc::Status SendAcl(::grpc::ServerContext* /* context */, - const ::blueberry::facade::Data* request, - ::google::protobuf::Empty* /* response */) override { - std::string req_string = request->payload(); - hal_->sendAclData(std::vector<uint8_t>(req_string.begin(), req_string.end())); - return ::grpc::Status::OK; - } - - ::grpc::Status SendSco(::grpc::ServerContext* /* context */, - const ::blueberry::facade::Data* request, - ::google::protobuf::Empty* /* response */) override { - std::string req_string = request->payload(); - hal_->sendScoData(std::vector<uint8_t>(req_string.begin(), req_string.end())); - return ::grpc::Status::OK; - } - - ::grpc::Status StreamEvents(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_hci_events_.RunLoop(context, writer); - } - - ::grpc::Status StreamAcl(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_acl_events_.RunLoop(context, writer); - } - - ::grpc::Status StreamSco(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_sco_events_.RunLoop(context, writer); - } - - ::grpc::Status StreamIso(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_iso_events_.RunLoop(context, writer); - } - - void hciEventReceived(bluetooth::hal::HciPacket event) override { - { - ::blueberry::facade::Data response; - response.set_payload(std::string(event.begin(), event.end())); - pending_hci_events_.OnIncomingEvent(std::move(response)); - } - can_send_hci_command_ = true; - cv_.notify_one(); - } - - void aclDataReceived(bluetooth::hal::HciPacket data) override { - ::blueberry::facade::Data response; - response.set_payload(std::string(data.begin(), data.end())); - pending_acl_events_.OnIncomingEvent(std::move(response)); - } - - void scoDataReceived(bluetooth::hal::HciPacket data) override { - ::blueberry::facade::Data response; - response.set_payload(std::string(data.begin(), data.end())); - pending_sco_events_.OnIncomingEvent(std::move(response)); - } - - void isoDataReceived(bluetooth::hal::HciPacket data) override { - ::blueberry::facade::Data response; - response.set_payload(std::string(data.begin(), data.end())); - pending_iso_events_.OnIncomingEvent(std::move(response)); - } - -private: - HciHal* hal_; - bool can_send_hci_command_ = true; - mutable std::mutex mutex_; - std::condition_variable cv_; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_hci_events_{"StreamEvents"}; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_acl_events_{"StreamAcl"}; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_sco_events_{"StreamSco"}; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_iso_events_{"StreamIso"}; -}; - -void HciHalFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<HciHal>(); -} - -void HciHalFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new HciHalFacadeService(GetDependency<HciHal>()); -} - -void HciHalFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* HciHalFacadeModule::GetService() const { return service_; } - -const ModuleFactory HciHalFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new HciHalFacadeModule(); }); - -} // namespace hal -} // namespace bluetooth diff --git a/system/gd/hal/facade.h b/system/gd/hal/facade.h deleted file mode 100644 index edf785631a..0000000000 --- a/system/gd/hal/facade.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include <list> -#include <mutex> - -#include "grpc/grpc_module.h" -#include "hal/hci_hal.h" - -namespace bluetooth { -namespace hal { - -class HciHalFacadeService; - -class HciHalFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - - void Start() override; - void Stop() override; - - ::grpc::Service* GetService() const override; - -private: - HciHalFacadeService* service_; -}; - -} // namespace hal -} // namespace bluetooth diff --git a/system/gd/hal/ranging_hal.h b/system/gd/hal/ranging_hal.h index 1d034ef69f..5a37900bc5 100644 --- a/system/gd/hal/ranging_hal.h +++ b/system/gd/hal/ranging_hal.h @@ -323,6 +323,7 @@ public: virtual void WriteProcedureData(uint16_t connection_handle, hci::CsRole local_cs_role, const ProcedureDataV2& procedure_data, uint16_t procedure_counter) = 0; + virtual bool IsAbortedProcedureRequired(uint16_t connection_handle) = 0; }; } // namespace hal diff --git a/system/gd/hal/ranging_hal_android.cc b/system/gd/hal/ranging_hal_android.cc index f73a9998df..d093685863 100644 --- a/system/gd/hal/ranging_hal_android.cc +++ b/system/gd/hal/ranging_hal_android.cc @@ -521,6 +521,25 @@ public: } } + bool IsAbortedProcedureRequired(uint16_t connection_handle) override { + auto it = session_trackers_.find(connection_handle); + if (it == session_trackers_.end()) { + log::error("Can't find session for connection_handle:0x{:04x}", connection_handle); + return false; + } + if (it->second->GetSession() == nullptr) { + log::error("Session not opened"); + return false; + } + bool isRequired = false; + auto aidl_ret = it->second->GetSession()->isAbortedProcedureRequired(&isRequired); + if (aidl_ret.isOk()) { + return isRequired; + } + log::error("can not get result for isAbortedProcedureRequired."); + return false; + } + protected: void ListDependencies(ModuleList* /*list*/) const {} diff --git a/system/gd/hal/ranging_hal_host.cc b/system/gd/hal/ranging_hal_host.cc index 0d8385f564..59282dc422 100644 --- a/system/gd/hal/ranging_hal_host.cc +++ b/system/gd/hal/ranging_hal_host.cc @@ -63,6 +63,8 @@ public: void UpdateConnInterval(uint16_t /* connection_handle */, uint16_t /* conn_interval */) override { } + bool IsAbortedProcedureRequired(uint16_t /*connection_handle*/) { return false; } + protected: void ListDependencies(ModuleList* /*list*/) const {} diff --git a/system/gd/hal/snoop_logger.cc b/system/gd/hal/snoop_logger.cc index 84eb0302e5..2e587cbfd6 100644 --- a/system/gd/hal/snoop_logger.cc +++ b/system/gd/hal/snoop_logger.cc @@ -35,7 +35,6 @@ #include "common/strings.h" #include "hal/snoop_logger_common.h" #include "hci/hci_packets.h" -#include "module_dumper_flatbuffer.h" #include "os/files.h" #include "os/parameter_provider.h" #include "os/system_properties.h" diff --git a/system/gd/hal/snoop_logger_test.cc b/system/gd/hal/snoop_logger_test.cc index 380e68a0e5..bfe81f3194 100644 --- a/system/gd/hal/snoop_logger_test.cc +++ b/system/gd/hal/snoop_logger_test.cc @@ -129,7 +129,6 @@ public: class SnoopLoggerModuleTest : public Test { public: - flatbuffers::FlatBufferBuilder* builder_; TestModuleRegistry* test_registry; protected: @@ -149,7 +148,6 @@ protected: temp_dir_ / (std::string(test_info->name()) + "_btsnoop_hci.log.filtered"); temp_snoop_log_filtered_last = temp_dir_ / (std::string(test_info->name()) + "_btsnoop_hci.log.filtered.last"); - builder_ = new flatbuffers::FlatBufferBuilder(); DeleteSnoopLogFiles(); ASSERT_FALSE(std::filesystem::exists(temp_snoop_log_)); @@ -164,7 +162,6 @@ protected: void TearDown() override { DeleteSnoopLogFiles(); - delete builder_; fake_timerfd_reset(); test_registry->StopAll(); delete test_registry; diff --git a/system/gd/hal/socket_hal.h b/system/gd/hal/socket_hal.h new file mode 100644 index 0000000000..3922e29da0 --- /dev/null +++ b/system/gd/hal/socket_hal.h @@ -0,0 +1,212 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include "module.h" + +namespace bluetooth::hal { + +enum SocketStatus { + SUCCESS = 0, + FAILURE, +}; + +struct EndpointInfo { + // The ID of the Hub to which the end point belongs for hardware offload data path. + uint64_t hub_id; + + // The ID of the Hub endpoint for hardware offload data path. + uint64_t endpoint_id; +}; + +struct LeCocCapabilities { + // Maximum number of LE COC sockets supported. If not supported, the value must be zero. + int number_of_supported_sockets; + + // Local Maximum Transmission Unit size in octets. + uint16_t mtu; +}; + +struct RfcommCapabilities { + // Maximum number of RFCOMM sockets supported. If not supported, the value must be zero. + int number_of_supported_sockets; + + // Maximum frame size in octets negotiated during DLCI establishment. + uint16_t max_frame_size; +}; + +struct SocketCapabilities { + LeCocCapabilities le_coc_capabilities; + RfcommCapabilities rfcomm_capabilities; +}; + +struct LeCocChannelInfo { + // L2cap local channel ID. + uint16_t local_cid; + + // L2cap remote channel ID. + uint16_t remote_cid; + + // PSM for L2CAP LE CoC. + uint16_t psm; + + // Local Maximum Transmission Unit for LE COC specifying the maximum SDU size in bytes that the + // local L2CAP layer can receive. + uint16_t local_mtu; + + // Remote Maximum Transmission Unit for LE COC specifying the maximum SDU size in bytes that the + // remote L2CAP layer can receive. + uint16_t remote_mtu; + + // Local Maximum PDU payload Size in bytes that the local L2CAP layer can receive. + uint16_t local_mps; + + // Remote Maximum PDU payload Size in bytes that the remote L2CAP layer can receive. + uint16_t remote_mps; + + // Protocol initial credits at Rx path. + uint16_t initial_rx_credits; + + // Protocol initial credits at Tx path. + uint16_t initial_tx_credits; +}; + +struct RfcommChannelInfo { + // L2cap local channel ID for RFCOMM. + int local_cid; + + // L2cap remote channel ID for RFCOMM. + int remote_cid; + + // Local Maximum Transmission Unit Size in bytes that the local L2CAP layer can receive. + int local_mtu; + + // Remote Maximum Transmission Unit Size in bytes that the remote L2CAP layer can receive. + int remote_mtu; + + // Protocol initial credits at Rx path. + int initial_rx_credits; + + // Protocol initial credits at Tx path. + int initial_tx_credits; + + // Data Link Connection Identifier (DLCI). + int dlci; + + // Maximum frame size negotiated during DLCI establishment. + int max_frame_size; + + // Flag of whether the Android stack initiated the RFCOMM multiplexer control channel. + bool mux_initiator; +}; + +struct SocketContext { + // Identifier assigned to the socket by the host stack when the socket is connected. + uint64_t socket_id; + + // Descriptive socket name provided by the host app when it created this socket. + std::string name; + + // ACL connection handle for the socket. + uint16_t acl_connection_handle; + + // Channel information of different protocol used for the socket. + std::variant<LeCocChannelInfo, RfcommChannelInfo> channel_info; + + // Endpoint information. + EndpointInfo endpoint_info; +}; + +/** + * SocketHalCallback provides an interface for receiving asynchronous events from socket HAL. + * Implementations of this class can be registered with the stack to receive these callbacks. + * + * Callback methods in this interface are invoked from the binder thread. This means that + * implementations must be thread-safe and handle any necessary synchronization to avoid race + * conditions or other concurrency issues. The callee is solely responsible for ensuring thread + * safety within the callback methods. + */ +class SocketHalCallback { +public: + virtual ~SocketHalCallback() = default; + + /** + * Invoked when IBluetoothSocket.opened() has been completed. + * + * @param socket_id Identifier assigned to the socket by the host stack + * @param status Status indicating success or failure + */ + virtual void SocketOpenedComplete(uint64_t socket_id, SocketStatus status) const = 0; + + /** + * Invoked when offload app or stack requests host stack to close the socket. + * + * @param socket_id Identifier assigned to the socket by the host stack + */ + virtual void SocketClose(uint64_t socket_id) const = 0; +}; + +/** + * SocketHal provides an interface to low-power processors, enabling Bluetooth Offload Socket + * functionality. + * + * Bluetooth Offload Socket allows the transfer of channel information from an established + * BluetoothSocket to a low-power processor. This enables the offload stack on the low-power + * processor to handle packet reception, processing, and transmission independently. This offloading + * process prevents the need to wake the main application processor, improving power efficiency. + */ +class SocketHal : public ::bluetooth::Module { +public: + static const ModuleFactory Factory; + + virtual ~SocketHal() = default; + + /** + * Registers a socket hal callback function to receive asynchronous events from socket HAL. + * + * @param callback A pointer to the callback function. Must not be nullptr and must have static + * lifetime. + * @return True if the callback was successfully registered, false otherwise. + */ + virtual bool RegisterCallback(hal::SocketHalCallback const* callback) = 0; + + /** + * Retrieves the supported offloaded socket capabilities. + * + * @return Supported socket capabilities + */ + virtual hal::SocketCapabilities GetSocketCapabilities() const = 0; + + /** + * Notifies the socket HAL that the socket has been opened. + * + * @param context Socket context including socket ID, channel, hub, and endpoint info + * @return Result of calling this method + */ + virtual bool Opened(const hal::SocketContext& context) const = 0; + + /** + * Notifies the socket HAL that the socket has been closed. + * + * @param socket_id Identifier assigned to the socket by the host stack + */ + virtual void Closed(uint64_t socket_id) const = 0; +}; + +} // namespace bluetooth::hal diff --git a/system/gd/hal/socket_hal_android.cc b/system/gd/hal/socket_hal_android.cc new file mode 100644 index 0000000000..4974efb0cf --- /dev/null +++ b/system/gd/hal/socket_hal_android.cc @@ -0,0 +1,275 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <aidl/android/hardware/bluetooth/socket/BnBluetoothSocketCallback.h> +#include <aidl/android/hardware/bluetooth/socket/IBluetoothSocket.h> +#include <aidl/android/hardware/bluetooth/socket/IBluetoothSocketCallback.h> +#include <android/binder_manager.h> +#include <bluetooth/log.h> + +// syslog.h conflicts with libchrome/base/logging.h +#undef LOG_DEBUG +#undef LOG_INFO +#undef LOG_WARNING + +#include "hal/socket_hal.h" + +using ::aidl::android::hardware::bluetooth::socket::BnBluetoothSocketCallback; +using ::aidl::android::hardware::bluetooth::socket::IBluetoothSocket; + +namespace bluetooth::hal { + +constexpr uint16_t kLeCocMtuMin = 23; +constexpr uint16_t kLeCocMtuMax = 65535; +constexpr uint16_t kRfcommFrameSizeMin = 23; +constexpr uint16_t kRfcommFrameSizeMax = 32767; + +class SocketAidlCallback : public BnBluetoothSocketCallback { + class : public hal::SocketHalCallback { + public: + void SocketOpenedComplete(uint64_t /* socket_id */, + hal::SocketStatus /* status */) const override { + log::warn("Dropping SocketOpenedComplete event, since callback is not set"); + } + + void SocketClose(uint64_t /* socket_id */) const override { + log::warn("Dropping SocketClose event, since callback is not set"); + } + } kNullCallbacks; + +public: + SocketAidlCallback() = default; + + void SetCallback(hal::SocketHalCallback const* callback) { + log::assert_that(callback != nullptr, "callback != nullptr"); + socket_hal_cb_ = callback; + } + + ::ndk::ScopedAStatus openedComplete(int64_t socket_id, + ::aidl::android::hardware::bluetooth::socket::Status status, + const std::string& reason) override { + log::info("socket_id: {} status: {} reason: {}", socket_id, static_cast<int>(status), reason); + socket_hal_cb_->SocketOpenedComplete( + socket_id, status == ::aidl::android::hardware::bluetooth::socket::Status::SUCCESS + ? hal::SocketStatus::SUCCESS + : hal::SocketStatus::FAILURE); + return ::ndk::ScopedAStatus::ok(); + } + + ::ndk::ScopedAStatus close(int64_t socket_id, const std::string& reason) override { + log::info("socket_id: {} reason: {}", socket_id, reason); + socket_hal_cb_->SocketClose(socket_id); + return ::ndk::ScopedAStatus::ok(); + } + +private: + hal::SocketHalCallback const* socket_hal_cb_ = &kNullCallbacks; +}; + +class SocketHalAndroid : public SocketHal { +public: + bool IsBound() const { return socket_hal_instance_ != nullptr; } + +protected: + void ListDependencies(ModuleList* /*list*/) const {} + + void Start() override { + std::string instance = std::string() + IBluetoothSocket::descriptor + "/default"; + if (!AServiceManager_isDeclared(instance.c_str())) { + log::error("The service {} is not declared", instance); + return; + } + + ::ndk::SpAIBinder binder(AServiceManager_waitForService(instance.c_str())); + socket_hal_instance_ = IBluetoothSocket::fromBinder(binder); + + if (socket_hal_instance_ == nullptr) { + log::error("Failed to bind to the service {}", instance); + return; + } + + socket_aidl_cb_ = ndk::SharedRefBase::make<SocketAidlCallback>(); + ::ndk::ScopedAStatus status = socket_hal_instance_->registerCallback(socket_aidl_cb_); + if (!status.isOk()) { + log::error("registerCallback failure: {}", status.getDescription()); + socket_hal_instance_ = nullptr; + return; + } + + death_recipient_ = + ::ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new([](void* /* cookie*/) { + log::error("The Socket HAL service died."); + // At shutdown, sometimes the HAL service gets killed before Bluetooth. + std::this_thread::sleep_for(std::chrono::seconds(1)); + log::fatal("Restarting Bluetooth after the socket HAL has died."); + })); + + auto death_link = AIBinder_linkToDeath(socket_hal_instance_->asBinder().get(), + death_recipient_.get(), this); + log::assert_that(death_link == STATUS_OK, + "Unable to set the death recipient for the Socket HAL"); + } + + void Stop() override { + if (IsBound()) { + auto death_unlink = AIBinder_unlinkToDeath(socket_hal_instance_->asBinder().get(), + death_recipient_.get(), this); + if (death_unlink != STATUS_OK) { + log::error("Error unlinking death recipient from the Socket HAL"); + } + socket_hal_instance_ = nullptr; + } + } + + std::string ToString() const override { return std::string("SocketHalAndroid"); } + + hal::SocketCapabilities GetSocketCapabilities() const override { + if (!IsBound()) { + return {}; + } + ::aidl::android::hardware::bluetooth::socket::SocketCapabilities socket_capabilities; + ::ndk::ScopedAStatus status = socket_hal_instance_->getSocketCapabilities(&socket_capabilities); + if (!status.isOk()) { + log::info("Failed to get socket capabilities"); + return {}; + } + if (socket_capabilities.leCocCapabilities.numberOfSupportedSockets < 0) { + log::error("Invalid leCocCapabilities.numberOfSupportedSockets: {}", + socket_capabilities.leCocCapabilities.numberOfSupportedSockets); + return {}; + } + if (socket_capabilities.leCocCapabilities.numberOfSupportedSockets) { + if (socket_capabilities.leCocCapabilities.mtu < kLeCocMtuMin || + socket_capabilities.leCocCapabilities.mtu > kLeCocMtuMax) { + log::error("Invalid leCocCapabilities.mtu: {}", socket_capabilities.leCocCapabilities.mtu); + return {}; + } + } + log::info("le_coc_capabilities number_of_supported_sockets: {}, mtu: {}", + socket_capabilities.leCocCapabilities.numberOfSupportedSockets, + socket_capabilities.leCocCapabilities.mtu); + + if (socket_capabilities.rfcommCapabilities.numberOfSupportedSockets < 0) { + log::error("Invalid rfcommCapabilities.numberOfSupportedSockets: {}", + socket_capabilities.rfcommCapabilities.numberOfSupportedSockets); + return {}; + } + if (socket_capabilities.rfcommCapabilities.numberOfSupportedSockets) { + if (socket_capabilities.rfcommCapabilities.maxFrameSize < kRfcommFrameSizeMin || + socket_capabilities.rfcommCapabilities.maxFrameSize > kRfcommFrameSizeMax) { + log::error("Invalid rfcommCapabilities.maxFrameSize: {}", + socket_capabilities.rfcommCapabilities.maxFrameSize); + return {}; + } + } + log::info("rfcomm_capabilities number_of_supported_sockets: {}, max_frame_size: {}", + socket_capabilities.rfcommCapabilities.numberOfSupportedSockets, + socket_capabilities.rfcommCapabilities.maxFrameSize); + + return hal::SocketCapabilities{ + .le_coc_capabilities.number_of_supported_sockets = + socket_capabilities.leCocCapabilities.numberOfSupportedSockets, + .le_coc_capabilities.mtu = + static_cast<uint16_t>(socket_capabilities.leCocCapabilities.mtu), + .rfcomm_capabilities.number_of_supported_sockets = + socket_capabilities.rfcommCapabilities.numberOfSupportedSockets, + .rfcomm_capabilities.max_frame_size = + static_cast<uint16_t>(socket_capabilities.rfcommCapabilities.maxFrameSize)}; + } + + bool RegisterCallback(hal::SocketHalCallback const* callback) override { + if (!IsBound()) { + return false; + } + socket_aidl_cb_->SetCallback(callback); + return true; + } + + bool Opened(const hal::SocketContext& context) const override { + if (!IsBound()) { + return false; + } + log::info("socket_id: {}, name: {}, acl_connection_handle: {}, hub_id: {}, endpoint_id: {}", + context.socket_id, context.name, context.acl_connection_handle, + context.endpoint_info.hub_id, context.endpoint_info.endpoint_id); + ::aidl::android::hardware::bluetooth::socket::SocketContext hal_context = { + .socketId = static_cast<int64_t>(context.socket_id), + .name = context.name, + .aclConnectionHandle = context.acl_connection_handle, + .endpointId.id = static_cast<int64_t>(context.endpoint_info.endpoint_id), + .endpointId.hubId = static_cast<int64_t>(context.endpoint_info.hub_id), + }; + if (std::holds_alternative<hal::LeCocChannelInfo>(context.channel_info)) { + auto& le_coc_context = std::get<hal::LeCocChannelInfo>(context.channel_info); + hal_context.channelInfo = ::aidl::android::hardware::bluetooth::socket::LeCocChannelInfo( + le_coc_context.local_cid, le_coc_context.remote_cid, le_coc_context.psm, + le_coc_context.local_mtu, le_coc_context.remote_mtu, le_coc_context.local_mps, + le_coc_context.remote_mps, le_coc_context.initial_rx_credits, + le_coc_context.initial_tx_credits); + log::info( + "le_coc local_cid: {}, remote_cid: {}, psm: {}, local_mtu: {}, remote_mtu: {}, " + "local_mps: {}, remote_mps: {}, initial_rx_credits: {}, initial_tx_credits: {}", + le_coc_context.local_cid, le_coc_context.remote_cid, le_coc_context.psm, + le_coc_context.local_mtu, le_coc_context.remote_mtu, le_coc_context.local_mps, + le_coc_context.remote_mps, le_coc_context.initial_rx_credits, + le_coc_context.initial_tx_credits); + } else if (std::holds_alternative<hal::RfcommChannelInfo>(context.channel_info)) { + auto& rfcomm_context = std::get<hal::RfcommChannelInfo>(context.channel_info); + hal_context.channelInfo = ::aidl::android::hardware::bluetooth::socket::RfcommChannelInfo( + rfcomm_context.local_cid, rfcomm_context.remote_cid, rfcomm_context.local_mtu, + rfcomm_context.remote_mtu, rfcomm_context.initial_rx_credits, + rfcomm_context.initial_tx_credits, rfcomm_context.dlci, rfcomm_context.max_frame_size, + rfcomm_context.mux_initiator); + log::info( + "rfcomm local_cid: {}, remote_cid: {}, local_mtu: {}, remote_mtu: {}, " + "initial_rx_credits: {}, initial_tx_credits: {}, dlci: {}, max_frame_size: {}, " + "mux_initiator: {}", + rfcomm_context.local_cid, rfcomm_context.remote_cid, rfcomm_context.local_mtu, + rfcomm_context.remote_mtu, rfcomm_context.initial_rx_credits, + rfcomm_context.initial_tx_credits, rfcomm_context.dlci, rfcomm_context.max_frame_size, + rfcomm_context.mux_initiator); + } else { + log::error("Unsupported protocol"); + return false; + } + ::ndk::ScopedAStatus status = socket_hal_instance_->opened(hal_context); + if (!status.isOk()) { + log::error("Opened failure: {}", status.getDescription()); + return false; + } + return true; + } + + void Closed(uint64_t socket_id) const override { + if (!IsBound()) { + return; + } + log::info("socket_id: {}", socket_id); + ::ndk::ScopedAStatus status = socket_hal_instance_->closed(socket_id); + if (!status.isOk()) { + log::info("Closed failure: {}", status.getDescription()); + } + } + +private: + std::shared_ptr<IBluetoothSocket> socket_hal_instance_; + std::shared_ptr<SocketAidlCallback> socket_aidl_cb_; + ::ndk::ScopedAIBinder_DeathRecipient death_recipient_; +}; + +const ModuleFactory SocketHal::Factory = ModuleFactory([]() { return new SocketHalAndroid(); }); + +} // namespace bluetooth::hal diff --git a/system/gd/hal/socket_hal_host.cc b/system/gd/hal/socket_hal_host.cc new file mode 100644 index 0000000000..292428a821 --- /dev/null +++ b/system/gd/hal/socket_hal_host.cc @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hal/socket_hal.h" + +namespace bluetooth::hal { + +class SocketHalHost : public SocketHal { +protected: + void ListDependencies(ModuleList* /*list*/) const {} + + void Start() override {} + + void Stop() override { socket_hal_cb_ = nullptr; } + + std::string ToString() const override { return std::string("SocketHalHost"); } + + hal::SocketCapabilities GetSocketCapabilities() const override { return {}; } + + bool RegisterCallback(hal::SocketHalCallback const* /*callback*/) override { return false; } + + bool Opened(const hal::SocketContext& /*context*/) const override { return false; } + + void Closed(uint64_t /*socket_id*/) const override {} + +private: + hal::SocketHalCallback* socket_hal_cb_; +}; + +const ModuleFactory SocketHal::Factory = ModuleFactory([]() { return new SocketHalHost(); }); + +} // namespace bluetooth::hal diff --git a/system/gd/hci/Android.bp b/system/gd/hci/Android.bp index d0fc019dd5..b0c7f04510 100644 --- a/system/gd/hci/Android.bp +++ b/system/gd/hci/Android.bp @@ -72,19 +72,6 @@ filegroup { } filegroup { - name: "BluetoothFacade_hci_layer", - srcs: [ - "facade/acl_manager_facade.cc", - "facade/controller_facade.cc", - "facade/facade.cc", - "facade/le_acl_manager_facade.cc", - "facade/le_advertising_manager_facade.cc", - "facade/le_initiator_address_facade.cc", - "facade/le_scanning_manager_facade.cc", - ], -} - -filegroup { name: "BluetoothHciFuzzTestSources", srcs: [ "hci_packets_fuzz_test.cc", diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc index 682800522c..869865e2c2 100644 --- a/system/gd/hci/acl_manager.cc +++ b/system/gd/hci/acl_manager.cc @@ -29,7 +29,6 @@ #include "common/bidi_queue.h" #include "common/byte_array.h" -#include "dumpsys_data_generated.h" #include "hci/acl_manager/acl_scheduler.h" #include "hci/acl_manager/classic_impl.h" #include "hci/acl_manager/le_acceptlist_callbacks.h" @@ -85,7 +84,7 @@ struct AclManager::impl { crash_on_unknown_handle, acl_scheduler_, remote_name_request_module_); le_impl_ = new le_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, - crash_on_unknown_handle); + crash_on_unknown_handle, classic_impl_); } hci_queue_end_ = hci_layer_->GetAclQueueEnd(); diff --git a/system/gd/hci/acl_manager/acl_scheduler.cc b/system/gd/hci/acl_manager/acl_scheduler.cc index 9b12f4b4e7..320373593b 100644 --- a/system/gd/hci/acl_manager/acl_scheduler.cc +++ b/system/gd/hci/acl_manager/acl_scheduler.cc @@ -215,7 +215,7 @@ private: const std::string set_of_incoming_connecting_addresses() const { std::stringstream buffer; for (const auto& c : incoming_connecting_address_set_) { - buffer << " " << c; + buffer << " " << c.ToRedactedStringForLogging(); } return buffer.str(); } diff --git a/system/gd/hci/acl_manager/classic_acl_connection.cc b/system/gd/hci/acl_manager/classic_acl_connection.cc index a2b6b9e30e..301211c345 100644 --- a/system/gd/hci/acl_manager/classic_acl_connection.cc +++ b/system/gd/hci/acl_manager/classic_acl_connection.cc @@ -17,6 +17,7 @@ #include "hci/acl_manager/classic_acl_connection.h" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include "hci/address.h" #include "hci/event_checkers.h" @@ -377,6 +378,14 @@ void ClassicAclConnection::RegisterCallbacks(ConnectionManagementCallbacks* call } bool ClassicAclConnection::Disconnect(DisconnectReason reason) { + if (com::android::bluetooth::flags::dont_send_hci_disconnect_repeatedly()) { + if (is_disconnecting_) { + log::info("Already disconnecting {}", address_); + return true; + } + } + + is_disconnecting_ = true; acl_connection_interface_->EnqueueCommand( DisconnectBuilder::Create(handle_, reason), pimpl_->tracker.client_handler_->BindOnce(check_status<DisconnectStatusView>)); diff --git a/system/gd/hci/acl_manager/classic_acl_connection.h b/system/gd/hci/acl_manager/classic_acl_connection.h index a92761b770..75dacf52d1 100644 --- a/system/gd/hci/acl_manager/classic_acl_connection.h +++ b/system/gd/hci/acl_manager/classic_acl_connection.h @@ -83,6 +83,7 @@ public: private: AclConnectionInterface* acl_connection_interface_; + bool is_disconnecting_ = false; protected: Address address_; diff --git a/system/gd/hci/acl_manager/classic_acl_connection_test.cc b/system/gd/hci/acl_manager/classic_acl_connection_test.cc index 85a206df27..32949927e7 100644 --- a/system/gd/hci/acl_manager/classic_acl_connection_test.cc +++ b/system/gd/hci/acl_manager/classic_acl_connection_test.cc @@ -283,33 +283,31 @@ TEST_F(ClassicAclConnectionTest, simple) { class ClassicAclConnectionWithCallbacksTest : public ClassicAclConnectionTest { protected: - void SetUp() override { - ClassicAclConnectionTest::SetUp(); + void SetUpConnection() { connection_ = std::make_unique<ClassicAclConnection>(queue_, &acl_connection_interface_, kConnectionHandle, address_); connection_->RegisterCallbacks(&callbacks_, handler_); - is_callbacks_registered_ = true; connection_management_callbacks_ = connection_->GetEventCallbacks( [this](uint16_t /* hci_handle */) { is_callbacks_invalidated_ = true; }); is_callbacks_invalidated_ = false; } - void TearDown() override { + void CleanConnection() { connection_.reset(); ASSERT_TRUE(is_callbacks_invalidated_); - ClassicAclConnectionTest::TearDown(); } protected: std::unique_ptr<ClassicAclConnection> connection_; ConnectionManagementCallbacks* connection_management_callbacks_; - bool is_callbacks_registered_{false}; bool is_callbacks_invalidated_{false}; }; TEST_F(ClassicAclConnectionWithCallbacksTest, Disconnect) { for (const auto& reason : disconnect_reason_vector) { + SetUpConnection(); ASSERT_TRUE(connection_->Disconnect(reason)); + CleanConnection(); } for (const auto& reason : disconnect_reason_vector) { @@ -324,7 +322,9 @@ TEST_F(ClassicAclConnectionWithCallbacksTest, Disconnect) { TEST_F(ClassicAclConnectionWithCallbacksTest, OnDisconnection) { for (const auto& error_code : error_code_vector) { + SetUpConnection(); connection_management_callbacks_->OnDisconnection(error_code); + CleanConnection(); } sync_handler(); diff --git a/system/gd/hci/acl_manager/classic_impl.h b/system/gd/hci/acl_manager/classic_impl.h index 08cdcbd231..ecef1f9414 100644 --- a/system/gd/hci/acl_manager/classic_impl.h +++ b/system/gd/hci/acl_manager/classic_impl.h @@ -159,6 +159,10 @@ private: public: bool crash_on_unknown_handle_ = false; + size_t size() const { + std::unique_lock<std::mutex> lock(acl_connections_guard_); + return acl_connections_.size(); + } bool is_empty() const { std::unique_lock<std::mutex> lock(acl_connections_guard_); return acl_connections_.empty(); @@ -278,6 +282,8 @@ public: return connections.is_classic_link_already_connected(address); } + size_t get_connection_count() { return connections.size(); } + void create_connection(Address address) { // TODO: Configure default connection parameters? uint16_t packet_type = 0x4408 /* DM 1,3,5 */ | 0x8810 /*DH 1,3,5 */; diff --git a/system/gd/hci/acl_manager/le_acl_connection.cc b/system/gd/hci/acl_manager/le_acl_connection.cc index 635c05b41b..8e6f93be08 100644 --- a/system/gd/hci/acl_manager/le_acl_connection.cc +++ b/system/gd/hci/acl_manager/le_acl_connection.cc @@ -17,6 +17,7 @@ #include "hci/acl_manager/le_acl_connection.h" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include "hci/acl_manager/le_connection_management_callbacks.h" #include "hci/event_checkers.h" @@ -186,6 +187,14 @@ void LeAclConnection::RegisterCallbacks(LeConnectionManagementCallbacks* callbac } void LeAclConnection::Disconnect(DisconnectReason reason) { + if (com::android::bluetooth::flags::dont_send_hci_disconnect_repeatedly()) { + if (is_disconnecting_) { + log::info("Already disconnecting {}", remote_address_); + return; + } + } + + is_disconnecting_ = true; pimpl_->tracker.le_acl_connection_interface_->EnqueueCommand( DisconnectBuilder::Create(handle_, reason), pimpl_->tracker.client_handler_->BindOnce([](CommandStatusView status) { diff --git a/system/gd/hci/acl_manager/le_acl_connection.h b/system/gd/hci/acl_manager/le_acl_connection.h index 574b104a97..5052be51a7 100644 --- a/system/gd/hci/acl_manager/le_acl_connection.h +++ b/system/gd/hci/acl_manager/le_acl_connection.h @@ -147,6 +147,7 @@ private: virtual bool check_connection_parameters(uint16_t conn_interval_min, uint16_t conn_interval_max, uint16_t expected_conn_latency, uint16_t expected_supervision_timeout); + bool is_disconnecting_ = false; struct impl; struct impl* pimpl_ = nullptr; }; diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h index 9a0be0814f..95e3db16ca 100644 --- a/system/gd/hci/acl_manager/le_impl.h +++ b/system/gd/hci/acl_manager/le_impl.h @@ -16,7 +16,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -27,7 +26,9 @@ #include <vector> #include "common/bind.h" +#include "common/le_conn_params.h" #include "hci/acl_manager/assembler.h" +#include "hci/acl_manager/classic_impl.h" #include "hci/acl_manager/le_acceptlist_callbacks.h" #include "hci/acl_manager/le_acl_connection.h" #include "hci/acl_manager/le_connection_callbacks.h" @@ -41,6 +42,8 @@ #include "os/alarm.h" #include "os/handler.h" #include "os/system_properties.h" +#include "stack/include/btm_ble_api_types.h" +#include "stack/include/stack_metrics_logging.h" namespace bluetooth { namespace hci { @@ -101,6 +104,8 @@ enum class ConnectabilityState { DISARMING = 3, }; +enum class ConnectionMode { RELAXED = 0, AGGRESSIVE = 1 }; + inline std::string connectability_state_machine_text(const ConnectabilityState& state) { switch (state) { CASE_RETURN_TEXT(ConnectabilityState::DISARMED); @@ -126,7 +131,8 @@ struct le_acl_connection { struct le_impl : public bluetooth::hci::LeAddressManagerCallback { le_impl(HciLayer* hci_layer, Controller* controller, os::Handler* handler, - RoundRobinScheduler* round_robin_scheduler, bool crash_on_unknown_handle) + RoundRobinScheduler* round_robin_scheduler, bool crash_on_unknown_handle, + classic_impl* classic_impl) : hci_layer_(hci_layer), controller_(controller), round_robin_scheduler_(round_robin_scheduler) { @@ -134,6 +140,7 @@ struct le_impl : public bluetooth::hci::LeAddressManagerCallback { controller_ = controller; handler_ = handler; connections.crash_on_unknown_handle_ = crash_on_unknown_handle; + classic_impl_ = classic_impl; le_acl_connection_interface_ = hci_layer_->GetLeAclConnectionInterface( handler_->BindOn(this, &le_impl::on_le_event), handler_->BindOn(this, &le_impl::on_le_disconnect), @@ -203,6 +210,10 @@ private: public: bool crash_on_unknown_handle_ = false; + size_t size() const { + std::unique_lock<std::mutex> lock(le_acl_connections_guard_); + return le_acl_connections_.size(); + } bool is_empty() const { std::unique_lock<std::mutex> lock(le_acl_connections_guard_); return le_acl_connections_.empty(); @@ -300,6 +311,17 @@ private: } } connections; + std::string connection_mode_to_string(ConnectionMode connection_mode) { + switch (connection_mode) { + case ConnectionMode::RELAXED: + return "RELAXED"; + case ConnectionMode::AGGRESSIVE: + return "AGGRESSIVE"; + default: + return "UNKNOWN"; + } + } + public: void enqueue_command(std::unique_ptr<CommandBuilder> command_packet) { hci_layer_->EnqueueCommand(std::move(command_packet), @@ -404,6 +426,8 @@ public: return; } + log_le_connection_status(address, true /* is_connect */, status); + const bool in_filter_accept_list = is_device_in_accept_list(remote_address); if (role == hci::Role::CENTRAL) { @@ -487,6 +511,8 @@ public: connection->in_filter_accept_list_ = in_filter_accept_list; connection->locally_initiated_ = (role == hci::Role::CENTRAL); + log::info("addr={}, conn_interval={}", remote_address, conn_interval); + if (packet.GetSubeventCode() == SubeventCode::ENHANCED_CONNECTION_COMPLETE) { LeEnhancedConnectionCompleteView connection_complete = LeEnhancedConnectionCompleteView::Create(packet); @@ -560,6 +586,7 @@ public: arm_on_resume_ = true; add_device_to_accept_list(remote_address); } + log_le_connection_status(remote_address.GetAddress(), false /* is_connect */, reason); } void on_le_connection_update_complete(LeMetaEventView view) { @@ -697,6 +724,7 @@ public: } void add_device_to_accept_list(AddressWithType address_with_type) { + log_le_device_in_accept_list(address_with_type.GetAddress(), true /* is_add */); if (connections.alreadyConnected(address_with_type)) { log::info("Device already connected, return"); return; @@ -719,6 +747,7 @@ public: } void remove_device_from_accept_list(AddressWithType address_with_type) { + log_le_device_in_accept_list(address_with_type.GetAddress(), false /* is_add */); if (accept_list.find(address_with_type) == accept_list.end()) { log::warn("Device not in acceptlist and cannot be removed: {}", address_with_type); return; @@ -839,10 +868,24 @@ public: InitiatorFilterPolicy initiator_filter_policy = InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST; OwnAddressType own_address_type = static_cast<OwnAddressType>( le_address_manager_->GetInitiatorAddress().GetAddressType()); - uint16_t conn_interval_min = - os::GetSystemPropertyUint32(kPropertyMinConnInterval, kConnIntervalMin); - uint16_t conn_interval_max = - os::GetSystemPropertyUint32(kPropertyMaxConnInterval, kConnIntervalMax); + + uint16_t conn_interval_min; + uint16_t conn_interval_max; + + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + size_t num_classic_acl_connections = classic_impl_->get_connection_count(); + size_t num_acl_connections = connections.size(); + + log::debug("ACL connection count: Classic={}, LE={}", num_classic_acl_connections, + num_acl_connections); + + choose_connection_mode(num_classic_acl_connections + num_acl_connections, &conn_interval_min, + &conn_interval_max); + } else { + conn_interval_min = os::GetSystemPropertyUint32(kPropertyMinConnInterval, kConnIntervalMin); + conn_interval_max = os::GetSystemPropertyUint32(kPropertyMaxConnInterval, kConnIntervalMax); + } + uint16_t conn_latency = os::GetSystemPropertyUint32(kPropertyConnLatency, kConnLatency); uint16_t supervision_timeout = os::GetSystemPropertyUint32(kPropertyConnSupervisionTimeout, kSupervisionTimeout); @@ -857,8 +900,7 @@ public: address_with_type = AddressWithType(); } - if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() && - controller_->IsSupported(OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2) && + if (controller_->IsRpaGenerationSupported() && own_address_type != OwnAddressType::PUBLIC_DEVICE_ADDRESS) { log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS"); own_address_type = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS; @@ -925,6 +967,36 @@ public: } } + // Choose which connection mode should be used based on the number of ongoing ACL connections. + // According to the connection mode, connection interval min/max values are set. + void choose_connection_mode(size_t num_acl_connections, uint16_t* conn_interval_min, + uint16_t* conn_interval_max) { + ConnectionMode connection_mode = ConnectionMode::RELAXED; + + uint32_t aggressive_connection_threshold = LeConnectionParameters::GetAggressiveConnThreshold(); + log::debug("num_acl_connections={}, aggressive_connection_threshold={}", num_acl_connections, + aggressive_connection_threshold); + + if (num_acl_connections < aggressive_connection_threshold) { + connection_mode = ConnectionMode::AGGRESSIVE; + } + + switch (connection_mode) { + case ConnectionMode::AGGRESSIVE: + *conn_interval_min = LeConnectionParameters::GetMinConnIntervalAggressive(); + *conn_interval_max = LeConnectionParameters::GetMaxConnIntervalAggressive(); + break; + case ConnectionMode::RELAXED: + default: + *conn_interval_min = LeConnectionParameters::GetMinConnIntervalRelaxed(); + *conn_interval_max = LeConnectionParameters::GetMaxConnIntervalRelaxed(); + break; + } + log::info("Connection mode: {}", connection_mode_to_string(connection_mode)); + log::debug("conn_interval_min={}, conn_interval_max={}", *conn_interval_min, + *conn_interval_max); + } + void disarm_connectability() { switch (connectability_state_) { case ConnectabilityState::ARMED: @@ -1232,6 +1304,7 @@ public: RoundRobinScheduler* round_robin_scheduler_ = nullptr; LeAddressManager* le_address_manager_ = nullptr; LeAclConnectionInterface* le_acl_connection_interface_ = nullptr; + classic_impl* classic_impl_ = nullptr; LeConnectionCallbacks* le_client_callbacks_ = nullptr; os::Handler* le_client_handler_ = nullptr; LeAcceptlistCallbacks* le_acceptlist_callbacks_ = nullptr; diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc index 5a5f15676c..d43d2faca1 100644 --- a/system/gd/hci/acl_manager/le_impl_test.cc +++ b/system/gd/hci/acl_manager/le_impl_test.cc @@ -26,6 +26,7 @@ #include <future> #include "common/bidi_queue.h" +#include "common/le_conn_params.h" #include "hci/acl_manager/le_connection_callbacks.h" #include "hci/acl_manager/le_connection_management_callbacks_mock.h" #include "hci/address_with_type.h" @@ -36,6 +37,7 @@ #include "os/handler.h" #include "packet/bit_inserter.h" #include "packet/raw_builder.h" +#include "stack/l2cap/l2c_api.h" using namespace bluetooth; using namespace std::chrono_literals; @@ -248,8 +250,11 @@ protected: round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd()); hci_queue_.GetDownEnd()->RegisterDequeue( handler_, common::Bind(&LeImplTest::HciDownEndDequeue, common::Unretained(this))); + + classic_impl_ = new classic_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, + false, nullptr, nullptr); le_impl_ = new le_impl(hci_layer_, controller_, handler_, round_robin_scheduler_, - kCrashOnUnknownHandle); + kCrashOnUnknownHandle, classic_impl_); le_impl_->handle_register_le_callbacks(&mock_le_connection_callbacks_, handler_); Address address; @@ -437,6 +442,69 @@ protected: ASSERT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); } + // Need to store the LeAclConnection so it is not immediately dropped => disconnected + std::unique_ptr<LeAclConnection> create_enhanced_connection(std::string remote_address_string, + int handle) { + std::unique_ptr<LeAclConnection> connection; + + hci::Address remote_address; + Address::FromString(remote_address_string, remote_address); + hci::AddressWithType address_with_type(remote_address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); + le_impl_->create_le_connection(address_with_type, true, false); + sync_handler(); + + hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->GetCommand(OpCode::LE_EXTENDED_CREATE_CONNECTION); + hci_layer_->IncomingEvent( + LeExtendedCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); + sync_handler(); + + // Check state is ARMED + EXPECT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); + + // we need to capture the LeAclConnection so it is not immediately dropped => disconnected + EXPECT_CALL(mock_le_connection_callbacks_, OnLeConnectSuccess(address_with_type, _)) + .WillOnce([&](AddressWithType, std::unique_ptr<LeAclConnection> conn) { + connection = std::move(conn); + connection->RegisterCallbacks(&connection_management_callbacks_, handler_); + }); + + hci_layer_->IncomingLeMetaEvent(LeEnhancedConnectionCompleteBuilder::Create( + ErrorCode::SUCCESS, handle, Role::CENTRAL, AddressType::PUBLIC_DEVICE_ADDRESS, + remote_address, Address::kEmpty, Address::kEmpty, 0x0024, 0x0000, 0x0011, + ClockAccuracy::PPM_30)); + sync_handler(); + + hci_layer_->GetCommand(OpCode::LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeRemoveDeviceFromFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->AssertNoQueuedCommand(); + sync_handler(); + EXPECT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); + + return connection; + } + + LeExtendedCreateConnectionView get_view_from_creating_connection( + std::string remote_address_string) { + hci::Address remote_address; + Address::FromString(remote_address_string, remote_address); + hci::AddressWithType address_with_type(remote_address, hci::AddressType::PUBLIC_DEVICE_ADDRESS); + + // Create connection + le_impl_->create_le_connection(address_with_type, true, false); + + hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + sync_handler(); + + return CreateLeConnectionManagementCommandView<LeExtendedCreateConnectionView>( + hci_layer_->GetCommand(OpCode::LE_EXTENDED_CREATE_CONNECTION)); + } + void TearDown() override { com::android::bluetooth::flags::provider_->reset_flags(); @@ -450,6 +518,7 @@ protected: sync_handler(); delete le_impl_; + delete classic_impl_; hci_queue_.GetDownEnd()->UnregisterDequeue(); @@ -511,6 +580,7 @@ protected: Thread* thread_; Handler* handler_; HciLayerFake* hci_layer_{nullptr}; + classic_impl* classic_impl_; TestController* controller_; RoundRobinScheduler* round_robin_scheduler_{nullptr}; @@ -746,6 +816,72 @@ TEST_F(LeImplTest, enhanced_connection_complete_with_central_role) { ASSERT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); } +TEST_F(LeImplTest, aggressive_connection_mode_selected_when_no_ongoing_le_connections_exist) { + if (LeConnectionParameters::GetAggressiveConnThreshold() == 0) { + GTEST_SKIP() << "Skipping test because the threshold is zero"; + } + + com::android::bluetooth::flags::provider_->initial_conn_params_p1(true); + set_random_device_address_policy(); + controller_->AddSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION); + + LeExtendedCreateConnectionView view = get_view_from_creating_connection("FF:EE:DD:CC:BB:AA"); + + ASSERT_TRUE(view.IsValid()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_min_, + LeConnectionParameters::GetMinConnIntervalAggressive()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_max_, + LeConnectionParameters::GetMaxConnIntervalAggressive()); +} + +TEST_F(LeImplTest, aggressive_connection_mode_selected_when_few_le_connections_exist) { + if (LeConnectionParameters::GetAggressiveConnThreshold() == 0) { + GTEST_SKIP() << "Skipping test because the threshold is zero"; + } + + com::android::bluetooth::flags::provider_->initial_conn_params_p1(true); + set_random_device_address_policy(); + controller_->AddSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION); + + std::vector<std::unique_ptr<LeAclConnection>> connections; + for (uint32_t i = 0; i < LeConnectionParameters::GetAggressiveConnThreshold() - 1; i++) { + std::stringstream addr_string_stream; + addr_string_stream << "A0:05:04:03:02:" << std::hex << std::setw(2) << std::setfill('0') << i; + + connections.push_back(create_enhanced_connection(addr_string_stream.str(), i /* handle */)); + } + + LeExtendedCreateConnectionView view = get_view_from_creating_connection("FF:EE:DD:CC:BB:AA"); + + ASSERT_TRUE(view.IsValid()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_min_, + LeConnectionParameters::GetMinConnIntervalAggressive()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_max_, + LeConnectionParameters::GetMaxConnIntervalAggressive()); +} + +TEST_F(LeImplTest, relaxed_connection_mode_selected_when_enough_le_connections_exist) { + com::android::bluetooth::flags::provider_->initial_conn_params_p1(true); + set_random_device_address_policy(); + controller_->AddSupported(OpCode::LE_EXTENDED_CREATE_CONNECTION); + + std::vector<std::unique_ptr<LeAclConnection>> connections; + for (uint32_t i = 0; i < LeConnectionParameters::GetAggressiveConnThreshold(); i++) { + std::stringstream addr_string_stream; + addr_string_stream << "A0:05:04:03:02:" << std::hex << std::setw(2) << std::setfill('0') << i; + + connections.push_back(create_enhanced_connection(addr_string_stream.str(), i /* handle */)); + } + + LeExtendedCreateConnectionView view = get_view_from_creating_connection("FF:EE:DD:CC:BB:AA"); + + ASSERT_TRUE(view.IsValid()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_min_, + LeConnectionParameters::GetMinConnIntervalRelaxed()); + ASSERT_EQ(view.GetPhyScanParameters()[0].conn_interval_max_, + LeConnectionParameters::GetMaxConnIntervalRelaxed()); +} + // b/260917913 TEST_F(LeImplTest, DISABLED_register_with_address_manager__AddressPolicyNotSet) { std::promise<void> promise; diff --git a/system/gd/hci/address.h b/system/gd/hci/address.h index b7e2bdeade..10a817cc1c 100644 --- a/system/gd/hci/address.h +++ b/system/gd/hci/address.h @@ -25,7 +25,6 @@ #include <ostream> #include <string> -#include "common/interfaces/ILoggable.h" #include "os/logging/log_adapter.h" #include "packet/custom_field_fixed_size_interface.h" #include "storage/serializable.h" @@ -34,8 +33,7 @@ namespace bluetooth { namespace hci { class Address final : public packet::CustomFieldFixedSizeInterface<Address>, - public storage::Serializable<Address>, - public bluetooth::common::IRedactableLoggable { + public storage::Serializable<Address> { public: static constexpr size_t kLength = 6; @@ -56,8 +54,8 @@ public: // storage::Serializable methods std::string ToString() const override; std::string ToColonSepHexString() const; - std::string ToStringForLogging() const override; - std::string ToRedactedStringForLogging() const override; + std::string ToStringForLogging() const; + std::string ToRedactedStringForLogging() const; static std::optional<Address> FromString(const std::string& from); std::string ToLegacyConfigString() const override; @@ -89,13 +87,6 @@ private: std::string _ToMaskedColonSepHexString(int bytes_to_mask) const; }; -// TODO: to fine-tune this. -// we need an interface between the logger and ILoggable -inline std::ostream& operator<<(std::ostream& os, const Address& a) { - os << a.ToString(); - return os; -} - } // namespace hci } // namespace bluetooth diff --git a/system/gd/hci/address_with_type.h b/system/gd/hci/address_with_type.h index c196989838..350b88df75 100644 --- a/system/gd/hci/address_with_type.h +++ b/system/gd/hci/address_with_type.h @@ -22,7 +22,6 @@ #include <string> #include <utility> -#include "common/interfaces/ILoggable.h" #include "crypto_toolbox/crypto_toolbox.h" #include "hci/address.h" #include "hci/hci_packets.h" @@ -32,7 +31,7 @@ namespace bluetooth { namespace hci { -class AddressWithType final : public bluetooth::common::IRedactableLoggable { +class AddressWithType final { public: AddressWithType(Address address, AddressType address_type) : address_(std::move(address)), address_type_(address_type) {} @@ -109,16 +108,14 @@ public: } std::string ToString() const { - std::stringstream ss; - ss << address_ << "[" << AddressTypeText(address_type_) << "]"; - return ss.str(); + return address_.ToString() + "[" + AddressTypeText(address_type_) + "]"; } - std::string ToStringForLogging() const override { + std::string ToStringForLogging() const { return address_.ToStringForLogging() + "[" + AddressTypeText(address_type_) + "]"; } - std::string ToRedactedStringForLogging() const override { + std::string ToRedactedStringForLogging() const { return address_.ToRedactedStringForLogging() + "[" + AddressTypeText(address_type_) + "]"; } @@ -127,11 +124,6 @@ private: AddressType address_type_; }; -inline std::ostream& operator<<(std::ostream& os, const AddressWithType& a) { - os << a.ToString(); - return os; -} - } // namespace hci } // namespace bluetooth diff --git a/system/gd/hci/address_with_type_test.cc b/system/gd/hci/address_with_type_test.cc index 3e0f34e4f9..c3b8bb0db9 100644 --- a/system/gd/hci/address_with_type_test.cc +++ b/system/gd/hci/address_with_type_test.cc @@ -294,14 +294,5 @@ TEST(AddressWithTypeTest, ToPeerAddressType) { } } -TEST(AddressWithTypeTest, StringStream) { - AddressWithType address_with_type = AddressWithType(Address{{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}}, - AddressType::PUBLIC_DEVICE_ADDRESS); - - std::stringstream oss; - oss << address_with_type; - ASSERT_STREQ("66:55:44:33:22:11[PUBLIC_DEVICE_ADDRESS(0x00)]", oss.str().c_str()); -} - } // namespace hci } // namespace bluetooth diff --git a/system/gd/hci/class_of_device.h b/system/gd/hci/class_of_device.h index d3a57fcc33..d4d53eb1d7 100644 --- a/system/gd/hci/class_of_device.h +++ b/system/gd/hci/class_of_device.h @@ -43,7 +43,7 @@ public: inline const uint8_t* data() const override { return cod.data(); } // storage::Serializable methods - std::string ToString() const; + std::string ToString() const override; static std::optional<ClassOfDevice> FromString(const std::string& str); std::string ToLegacyConfigString() const override; static std::optional<ClassOfDevice> FromLegacyConfigString(const std::string& str); diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc index 18180a547c..eaba7da848 100644 --- a/system/gd/hci/controller.cc +++ b/system/gd/hci/controller.cc @@ -25,7 +25,6 @@ #include <string> #include <utility> -#include "dumpsys_data_generated.h" #include "hci/controller_interface.h" #include "hci/event_checkers.h" #include "hci/hci_layer.h" @@ -44,8 +43,11 @@ constexpr int kMinEncryptionKeySizeDefault = kMinEncryptionKeySize; constexpr int kMaxEncryptionKeySize = 16; constexpr bool kDefaultVendorCapabilitiesEnabled = true; +constexpr bool kDefaultRpaOffload = false; + static const std::string kPropertyVendorCapabilitiesEnabled = "bluetooth.core.le.vendor_capabilities.enabled"; +static const std::string kPropertyRpaOffload = "bluetooth.core.le.rpa_offload"; using os::Handler; @@ -1544,6 +1546,15 @@ uint64_t Controller::MaskLeEventMask(HciVersion version, uint64_t mask) { } } +bool Controller::IsRpaGenerationSupported(void) const { + static const bool rpa_supported = + com::android::bluetooth::flags::rpa_offload_to_bt_controller() && + os::GetSystemPropertyBool(kPropertyRpaOffload, kDefaultRpaOffload) && + IsSupported(OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2); + + return rpa_supported; +} + const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); }); void Controller::ListDependencies(ModuleList* list) const { diff --git a/system/gd/hci/controller.h b/system/gd/hci/controller.h index dcf0b10e42..70609950da 100644 --- a/system/gd/hci/controller.h +++ b/system/gd/hci/controller.h @@ -217,6 +217,8 @@ public: static uint64_t MaskLeEventMask(HciVersion version, uint64_t mask); + virtual bool IsRpaGenerationSupported(void) const override; + protected: void ListDependencies(ModuleList* list) const override; diff --git a/system/gd/hci/controller_interface.h b/system/gd/hci/controller_interface.h index 3dd3cd03d3..a9ebecd32a 100644 --- a/system/gd/hci/controller_interface.h +++ b/system/gd/hci/controller_interface.h @@ -215,6 +215,8 @@ public: virtual VendorCapabilities GetVendorCapabilities() const = 0; virtual bool IsSupported(OpCode op_code) const = 0; + + virtual bool IsRpaGenerationSupported(void) const = 0; }; } // namespace hci diff --git a/system/gd/hci/controller_interface_mock.h b/system/gd/hci/controller_interface_mock.h index 6c8a2811d9..36bc50a423 100644 --- a/system/gd/hci/controller_interface_mock.h +++ b/system/gd/hci/controller_interface_mock.h @@ -149,6 +149,7 @@ public: MOCK_METHOD(std::vector<uint8_t>, GetLocalSupportedBrEdrCodecIds, (), (const)); MOCK_METHOD(VendorCapabilities, GetVendorCapabilities, (), (const)); MOCK_METHOD(bool, IsSupported, (OpCode op_code), (const)); + MOCK_METHOD(bool, IsRpaGenerationSupported, (), (const)); MOCK_METHOD(uint32_t, GetDabSupportedCodecs, (), (const)); MOCK_METHOD((const std::array<DynamicAudioBufferCodecCapability, 32>&), GetDabCodecCapabilities, diff --git a/system/gd/hci/controller_mock.h b/system/gd/hci/controller_mock.h index 1c05ad983b..fd3a7058d1 100644 --- a/system/gd/hci/controller_mock.h +++ b/system/gd/hci/controller_mock.h @@ -149,6 +149,7 @@ public: MOCK_METHOD(std::vector<uint8_t>, GetLocalSupportedBrEdrCodecIds, (), (const)); MOCK_METHOD(VendorCapabilities, GetVendorCapabilities, (), (const)); MOCK_METHOD(bool, IsSupported, (OpCode op_code), (const)); + MOCK_METHOD(bool, IsRpaGenerationSupported, (), (const)); MOCK_METHOD(uint32_t, GetDabSupportedCodecs, (), (const)); MOCK_METHOD((const std::array<DynamicAudioBufferCodecCapability, 32>&), GetDabCodecCapabilities, diff --git a/system/gd/hci/controller_test.cc b/system/gd/hci/controller_test.cc index 04b1380fa4..05dcaf9d7e 100644 --- a/system/gd/hci/controller_test.cc +++ b/system/gd/hci/controller_test.cc @@ -29,7 +29,6 @@ #include "common/bind.h" #include "hci/address.h" #include "hci/hci_layer_fake.h" -#include "module_dumper.h" #include "os/thread.h" #include "packet/raw_builder.h" diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc index 9bf07a6c47..04c3955204 100644 --- a/system/gd/hci/distance_measurement_manager.cc +++ b/system/gd/hci/distance_measurement_manager.cc @@ -63,17 +63,20 @@ static constexpr uint16_t kMinProcedureInterval = 0x01; static constexpr uint16_t kMaxProcedureInterval = 0xFF; static constexpr uint16_t kMaxProcedureCount = 0x01; static constexpr uint32_t kMinSubeventLen = 0x0004E2; // 1250us -static constexpr uint32_t kMaxSubeventLen = 0x3d0900; // 4s +static constexpr uint32_t kMaxSubeventLen = 0x1E8480; // 2s static constexpr uint8_t kTxPwrDelta = 0x00; static constexpr uint8_t kProcedureDataBufferSize = 0x10; // Buffer size of Procedure data -static constexpr uint16_t kMtuForRasData = 507; // 512 - 5 static constexpr uint16_t kRangingCounterMask = 0x0FFF; static constexpr uint8_t kInvalidConfigId = 0xFF; static constexpr uint8_t kMinConfigId = 0; static constexpr uint8_t kMaxConfigId = 3; static constexpr uint16_t kDefaultIntervalMs = 1000; // 1s static constexpr uint8_t kMaxRetryCounterForCreateConfig = 0x03; +static constexpr uint8_t kMaxRetryCounterForCsEnable = 0x03; static constexpr uint16_t kInvalidConnInterval = 0; // valid value is from 0x0006 to 0x0C80 +static constexpr uint16_t kDefaultRasMtu = 247; // Section 3.1.2 of RAP 1.0 +static constexpr uint8_t kAttHeaderSize = 5; // Section 3.2.2.1 of RAS 1.0 +static constexpr uint8_t kRasSegmentHeaderSize = 1; struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { struct CsProcedureData { @@ -182,6 +185,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { bool ras_connected = false; bool setup_complete = false; uint8_t retry_counter_for_create_config = 0; + uint8_t retry_counter_for_cs_enable = 0; uint16_t n_procedure_count = 0; CsMainModeType main_mode_type = CsMainModeType::MODE_2; CsSubModeType sub_mode_type = CsSubModeType::UNUSED; @@ -204,6 +208,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { RangingHeader ranging_header_; PacketViewForRecombination segment_data_; uint16_t conn_interval_ = kInvalidConnInterval; + uint8_t procedure_sequence_after_enable = -1; }; bool get_free_config_id(uint16_t connection_handle, uint8_t& config_id) { @@ -409,7 +414,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { void start_distance_measurement_with_cs(const Address& cs_remote_address, uint16_t connection_handle) { log::info("connection_handle: {}, address: {}", connection_handle, cs_remote_address); - if (!com::android::bluetooth::flags::channel_sounding_in_stack() && !is_local_cs_ready_) { + if (!com::android::bluetooth::flags::channel_sounding_in_stack() || !is_local_cs_ready_) { log::error("Channel Sounding is not enabled"); distance_measurement_callbacks_->OnDistanceMeasurementStopped( cs_remote_address, REASON_INTERNAL_ERROR, METHOD_CS); @@ -504,7 +509,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { void handle_conn_interval_updated(const Address& address, uint16_t connection_handle, uint16_t conn_interval) { - if (com::android::bluetooth::flags::channel_sounding_25q2_apis()) { + if (!com::android::bluetooth::flags::channel_sounding_25q2_apis()) { log::debug("connection interval is not required."); return; } @@ -532,6 +537,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { } distance_measurement_callbacks_->OnDistanceMeasurementStopped( address, REASON_NO_LE_CONNECTION, METHOD_CS); + gatt_mtus_.erase(it->first); it = cs_requester_trackers_.erase(it); // erase and get the next iterator } else { ++it; @@ -578,6 +584,20 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { it->second.local_hci_role = local_hci_role; } + void handle_mtu_changed(uint16_t connection_handle, uint16_t mtu) { + log::info("gatt mtu is changed as {}", mtu); + gatt_mtus_[connection_handle] = mtu; + } + + uint16_t get_ras_raw_payload_size(uint16_t connection_handle) { + auto it = gatt_mtus_.find(connection_handle); + uint16_t mtu = kDefaultRasMtu; + if (it != gatt_mtus_.end()) { + mtu = gatt_mtus_[connection_handle]; + } + return mtu - kAttHeaderSize - kRasSegmentHeaderSize; + } + void handle_ras_server_disconnected(const Address& identity_address, uint16_t connection_handle) { auto it = cs_responder_trackers_.find(connection_handle); if (it == cs_responder_trackers_.end()) { @@ -590,6 +610,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { return; } cs_responder_trackers_.erase(connection_handle); + gatt_mtus_.erase(connection_handle); } void handle_vendor_specific_reply_complete(const Address address, uint16_t connection_handle, @@ -673,11 +694,25 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { handler_->BindOnceOn(this, &impl::on_cs_setup_command_status_cb, connection_handle)); } - void send_le_cs_security_enable(uint16_t connection_handle) { - if (cs_requester_trackers_.find(connection_handle) == cs_requester_trackers_.end()) { - log::warn("no cs tracker found for {}", connection_handle); + void send_le_cs_security_enable(uint16_t connection_handle, bool local_start) { + if (local_start) { + auto req_it = cs_requester_trackers_.find(connection_handle); + if (req_it != cs_requester_trackers_.end() && + req_it->second.state == CsTrackerState::WAIT_FOR_CONFIG_COMPLETE) { + req_it->second.state = CsTrackerState::WAIT_FOR_SECURITY_ENABLED; + } else { + log::error("no requester tracker. something wrong."); + } + } else { + auto res_it = cs_responder_trackers_.find(connection_handle); + if (res_it != cs_responder_trackers_.end() && + res_it->second.state == CsTrackerState::WAIT_FOR_CONFIG_COMPLETE) { + res_it->second.state = CsTrackerState::WAIT_FOR_SECURITY_ENABLED; + } else { + log::error("no responder tracker. something wrong."); + } } - cs_requester_trackers_[connection_handle].state = CsTrackerState::WAIT_FOR_SECURITY_ENABLED; + hci_layer_->EnqueueCommand( LeCsSecurityEnableBuilder::Create(connection_handle), handler_->BindOnceOn(this, &impl::on_cs_setup_command_status_cb, connection_handle)); @@ -710,7 +745,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { connection_handle, config_id, CsCreateContext::BOTH_LOCAL_AND_REMOTE_CONTROLLER, CsMainModeType::MODE_2, CsSubModeType::UNUSED, kMinMainModeSteps, kMaxMainModeSteps, kMainModeRepetition, kMode0Steps, CsRole::INITIATOR, - CsConfigRttType::RTT_AA_COARSE, CsSyncPhy::LE_1M_PHY, channel_map, + CsConfigRttType::RTT_AA_ONLY, CsSyncPhy::LE_1M_PHY, channel_map, kChannelMapRepetition, CsChannelSelectionType::TYPE_3B, CsCh3cShape::HAT_SHAPE, kCh3cJump), handler_->BindOnceOn(this, &impl::on_cs_setup_command_status_cb, connection_handle)); @@ -809,8 +844,20 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (cs_requester_trackers_.find(connection_handle) != cs_requester_trackers_.end()) { reset_tracker_on_stopped(cs_requester_trackers_[connection_handle]); } - } else { - on_cs_setup_command_status_cb(connection_handle, status_view); + } else if (status_view.GetStatus() != ErrorCode::SUCCESS) { + if (cs_requester_trackers_.count(connection_handle) == 0) { + log::error("Error code {} for connection_handle {}. No request tracker found.", + ErrorCodeText(status), connection_handle); + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + return; + } + log::error("Error code {} for connection_handle {}. Retry counter {}", ErrorCodeText(status), + connection_handle, + cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable); + if (cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable++ >= + kMaxRetryCounterForCsEnable) { + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + } } } @@ -818,7 +865,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { ErrorCode status = status_view.GetStatus(); OpCode op_code = status_view.GetCommandOpCode(); if (status != ErrorCode::SUCCESS) { - log::error("Error code {}, opcode {} for connection-{}", ErrorCodeText(status), + log::error("Error code {}, opcode {} for connection_handle {}", ErrorCodeText(status), OpCodeText(op_code), connection_handle); handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); } @@ -911,7 +958,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { return; } auto req_it = cs_requester_trackers_.find(connection_handle); - if (req_it != cs_requester_trackers_.end() && req_it->second.measurement_ongoing) { + if (req_it != cs_requester_trackers_.end() && + req_it->second.state == CsTrackerState::WAIT_FOR_SECURITY_ENABLED) { send_le_cs_set_procedure_parameters(event_view.GetConnectionHandle(), req_it->second.used_config_id, req_it->second.remote_num_antennas_supported_); @@ -965,6 +1013,10 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { log::warn("Can't find cs tracker for connection_handle {}", connection_handle); return; } + if (!live_tracker->local_start) { + // reset the responder state, as no other event to set the state. + live_tracker->state = CsTrackerState::WAIT_FOR_CONFIG_COMPLETE; + } live_tracker->used_config_id = config_id; log::info("Get {}", event_view.ToString()); @@ -979,10 +1031,9 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { } if (live_tracker->local_hci_role == hci::Role::CENTRAL) { // send the cmd from the BLE central only. - send_le_cs_security_enable(connection_handle); - } - // TODO: else set a timeout alarm to make sure the remote would trigger the cmd. - if (!live_tracker->local_start) { + send_le_cs_security_enable(connection_handle, live_tracker->local_start); + } else { + // TODO: else set a timeout alarm to make sure the remote would trigger the cmd. live_tracker->state = CsTrackerState::WAIT_FOR_SECURITY_ENABLED; } } @@ -1091,8 +1142,20 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { log::debug("on cs procedure enabled complete"); if (event_view.GetStatus() != ErrorCode::SUCCESS) { std::string error_code = ErrorCodeText(event_view.GetStatus()); - log::warn("Received LeCsProcedureEnableCompleteView with error code {}", error_code); - handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + if (cs_requester_trackers_.count(connection_handle) == 0) { + log::warn( + "Received LeCsProcedureEnableCompleteView with error code {}, No request tracker " + "found", + error_code); + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + return; + } + log::warn("Received LeCsProcedureEnableCompleteView with error code {}. Retry counter {}", + error_code, cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable); + if (cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable++ >= + kMaxRetryCounterForCsEnable) { + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + } return; } uint8_t config_id = event_view.GetConfigId(); @@ -1123,6 +1186,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { live_tracker->state = CsTrackerState::STARTED; live_tracker->selected_tx_power = event_view.GetSelectedTxPower(); live_tracker->n_procedure_count = event_view.GetProcedureCount(); + live_tracker->retry_counter_for_cs_enable = 0; if (live_tracker->local_start && live_tracker->waiting_for_start_callback) { live_tracker->waiting_for_start_callback = false; @@ -1130,6 +1194,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { METHOD_CS); } if (live_tracker->local_start && is_hal_v2()) { + // reset the procedure sequence + live_tracker->procedure_sequence_after_enable = -1; ranging_hal_->UpdateProcedureEnableConfig(connection_handle, event_view); } } else if (event_view.GetState() == Enable::DISABLED) { @@ -1171,6 +1237,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { CsTracker* live_tracker = nullptr; CsProcedureData* procedure_data = nullptr; uint8_t valid_requester_states = static_cast<uint8_t>(CsTrackerState::STARTED); + // TODO(b/384928509): Prevent sending CS enable if procedures are not yet complete. + valid_requester_states |= static_cast<uint8_t>(CsTrackerState::WAIT_FOR_PROCEDURE_ENABLED); uint8_t valid_responder_states = static_cast<uint8_t>(CsTrackerState::STARTED); if (event.GetSubeventCode() == SubeventCode::LE_CS_SUBEVENT_RESULT) { auto cs_event_result = LeCsSubeventResultView::Create(event); @@ -1292,9 +1360,9 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { static_cast<bluetooth::ras::SubeventAbortReason>(subevent_abort_reason); } parse_cs_result_data(result_data_structures, *procedure_data, live_tracker->role); - check_cs_procedure_complete(live_tracker, procedure_data, connection_handle); if (live_tracker->local_start) { + check_cs_procedure_complete(live_tracker, procedure_data, connection_handle); // Skip to send remote return; } @@ -1311,7 +1379,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { append_vector(procedure_data->ras_raw_data_, subevent_raw); // erase buffer procedure_data->ras_subevent_data_.clear(); - send_on_demand_data(live_tracker->address, procedure_data); + send_on_demand_data(live_tracker->address, procedure_data, + get_ras_raw_payload_size(connection_handle)); // remove procedure data sent previously if (procedure_done_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE) { delete_consumed_procedure_data(live_tracker, live_tracker->procedure_counter); @@ -1319,20 +1388,21 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { } } - void send_on_demand_data(Address address, CsProcedureData* procedure_data) { + void send_on_demand_data(Address address, CsProcedureData* procedure_data, + uint16_t raw_payload_size) { // Check is last segment or not. uint16_t unsent_data_size = procedure_data->ras_raw_data_.size() - procedure_data->ras_raw_data_index_; if (procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS && - unsent_data_size <= kMtuForRasData) { + unsent_data_size <= raw_payload_size) { procedure_data->segmentation_header_.last_segment_ = 1; - } else if (unsent_data_size < kMtuForRasData) { + } else if (unsent_data_size < raw_payload_size) { log::verbose("waiting for more data, current unsent data size {}", unsent_data_size); return; } // Create raw data for segment_data; - uint16_t copy_size = unsent_data_size < kMtuForRasData ? unsent_data_size : kMtuForRasData; + uint16_t copy_size = unsent_data_size < raw_payload_size ? unsent_data_size : raw_payload_size; auto copy_start = procedure_data->ras_raw_data_.begin() + procedure_data->ras_raw_data_index_; auto copy_end = copy_start + copy_size; std::vector<uint8_t> subevent_data(copy_start, copy_end); @@ -1353,8 +1423,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (procedure_data->segmentation_header_.last_segment_) { // last segment sent, clear buffer procedure_data->ras_raw_data_.clear(); - } else if (unsent_data_size > kMtuForRasData) { - send_on_demand_data(address, procedure_data); + } else if (unsent_data_size > raw_payload_size) { + send_on_demand_data(address, procedure_data, raw_payload_size); } } @@ -1896,40 +1966,59 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { return procedure_data; } + void try_send_data_to_hal(uint16_t connection_handle, const CsTracker* live_tracker, + const CsProcedureData* procedure_data) const { + if (!ranging_hal_->IsBound()) { + return; + } + bool should_send_to_hal = false; + if (ranging_hal_->IsAbortedProcedureRequired(connection_handle)) { + should_send_to_hal = procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS && + procedure_data->remote_status != CsProcedureDoneStatus::PARTIAL_RESULTS; + } else { + should_send_to_hal = + procedure_data->local_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE && + procedure_data->remote_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE && + procedure_data->contains_complete_subevent_; + } + if (should_send_to_hal) { + log::debug("Procedure complete counter:{} data size:{}", (uint16_t)procedure_data->counter, + procedure_data->step_channel.size()); + if (is_hal_v2()) { + ranging_hal_->WriteProcedureData(connection_handle, live_tracker->role, + procedure_data->procedure_data_v2_, + procedure_data->counter); + } else { + // Use algorithm in the HAL + bluetooth::hal::ChannelSoundingRawData raw_data; + raw_data.num_antenna_paths_ = procedure_data->num_antenna_paths; + raw_data.step_channel_ = procedure_data->step_channel; + raw_data.tone_pct_initiator_ = procedure_data->tone_pct_initiator; + raw_data.tone_quality_indicator_initiator_ = + procedure_data->tone_quality_indicator_initiator; + raw_data.tone_pct_reflector_ = procedure_data->tone_pct_reflector; + raw_data.tone_quality_indicator_reflector_ = + procedure_data->tone_quality_indicator_reflector; + raw_data.toa_tod_initiators_ = procedure_data->toa_tod_initiators; + raw_data.tod_toa_reflectors_ = procedure_data->tod_toa_reflectors; + raw_data.packet_quality_initiator = procedure_data->packet_quality_initiator; + raw_data.packet_quality_reflector = procedure_data->packet_quality_reflector; + ranging_hal_->WriteRawData(connection_handle, raw_data); + } + } + } + void check_cs_procedure_complete(CsTracker* live_tracker, CsProcedureData* procedure_data, uint16_t connection_handle) const { - if (live_tracker->local_start && - procedure_data->local_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE && - procedure_data->remote_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE && - procedure_data->contains_complete_subevent_) { - log::debug("Procedure complete counter:{} data size:{}, main_mode_type:{}, sub_mode_type:{}", - (uint16_t)procedure_data->counter, (uint16_t)procedure_data->step_channel.size(), - (uint16_t)live_tracker->main_mode_type, (uint16_t)live_tracker->sub_mode_type); - - if (ranging_hal_->IsBound()) { - if (is_hal_v2()) { - ranging_hal_->WriteProcedureData(connection_handle, live_tracker->role, - procedure_data->procedure_data_v2_, - procedure_data->counter); - } else { - // Use algorithm in the HAL - bluetooth::hal::ChannelSoundingRawData raw_data; - raw_data.num_antenna_paths_ = procedure_data->num_antenna_paths; - raw_data.step_channel_ = procedure_data->step_channel; - raw_data.tone_pct_initiator_ = procedure_data->tone_pct_initiator; - raw_data.tone_quality_indicator_initiator_ = - procedure_data->tone_quality_indicator_initiator; - raw_data.tone_pct_reflector_ = procedure_data->tone_pct_reflector; - raw_data.tone_quality_indicator_reflector_ = - procedure_data->tone_quality_indicator_reflector; - raw_data.toa_tod_initiators_ = procedure_data->toa_tod_initiators; - raw_data.tod_toa_reflectors_ = procedure_data->tod_toa_reflectors; - raw_data.packet_quality_initiator = procedure_data->packet_quality_initiator; - raw_data.packet_quality_reflector = procedure_data->packet_quality_reflector; - ranging_hal_->WriteRawData(connection_handle, raw_data); - } - } + if (is_hal_v2() && procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS && + procedure_data->remote_status != CsProcedureDoneStatus::PARTIAL_RESULTS) { + live_tracker->procedure_sequence_after_enable++; + log::debug("procedure sequence after enabled is {}", + live_tracker->procedure_sequence_after_enable); + procedure_data->procedure_data_v2_.procedure_sequence_ = + live_tracker->procedure_sequence_after_enable; } + try_send_data_to_hal(connection_handle, live_tracker, procedure_data); // If the procedure is completed or aborted, delete all previous data if (procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS && @@ -2413,6 +2502,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { std::unordered_map<Address, RSSITracker> rssi_trackers; std::unordered_map<uint16_t, CsTracker> cs_requester_trackers_; std::unordered_map<uint16_t, CsTracker> cs_responder_trackers_; + std::unordered_map<uint16_t, uint16_t> gatt_mtus_; DistanceMeasurementCallbacks* distance_measurement_callbacks_; CsOptionalSubfeaturesSupported cs_subfeature_supported_; uint8_t num_antennas_supported_ = 0x01; @@ -2506,6 +2596,10 @@ void DistanceMeasurementManager::HandleRasServerConnected(const Address& identit local_hci_role); } +void DistanceMeasurementManager::HandleMtuChanged(uint16_t connection_handle, uint16_t mtu) { + CallOn(pimpl_.get(), &impl::handle_mtu_changed, connection_handle, mtu); +} + void DistanceMeasurementManager::HandleRasServerDisconnected( const bluetooth::hci::Address& identity_address, uint16_t connection_handle) { CallOn(pimpl_.get(), &impl::handle_ras_server_disconnected, identity_address, connection_handle); diff --git a/system/gd/hci/distance_measurement_manager.h b/system/gd/hci/distance_measurement_manager.h index 4f8f19a9ff..da26425c49 100644 --- a/system/gd/hci/distance_measurement_manager.h +++ b/system/gd/hci/distance_measurement_manager.h @@ -94,6 +94,7 @@ public: const std::vector<hal::VendorSpecificCharacteristic>& vendor_specific_reply); void HandleRasServerConnected(const Address& identity_address, uint16_t connection_handle, hci::Role local_hci_role); + void HandleMtuChanged(uint16_t connection_handle, uint16_t mtu); void HandleRasServerDisconnected(const Address& identity_address, uint16_t connection_handle); void HandleVendorSpecificReplyComplete(const Address& address, uint16_t connection_handle, bool success); diff --git a/system/gd/hci/facade/acl_manager_facade.cc b/system/gd/hci/facade/acl_manager_facade.cc deleted file mode 100644 index c885f9d683..0000000000 --- a/system/gd/hci/facade/acl_manager_facade.cc +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright 2019 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/facade/acl_manager_facade.h" - -#include <bluetooth/log.h> - -#include <condition_variable> -#include <memory> -#include <mutex> - -#include "blueberry/facade/hci/acl_manager_facade.grpc.pb.h" -#include "blueberry/facade/hci/acl_manager_facade.pb.h" -#include "common/bind.h" -#include "grpc/grpc_event_queue.h" -#include "hci/acl_manager.h" -#include "hci/address.h" -#include "hci/class_of_device.h" -#include "hci/hci_packets.h" -#include "packet/raw_builder.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -using ::bluetooth::packet::RawBuilder; - -namespace bluetooth { -namespace hci { -namespace facade { - -using acl_manager::ClassicAclConnection; -using acl_manager::ConnectionCallbacks; -using acl_manager::ConnectionManagementCallbacks; - -using namespace blueberry::facade::hci; - -class AclManagerFacadeService : public AclManagerFacade::Service, public ConnectionCallbacks { -public: - AclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler) - : acl_manager_(acl_manager), facade_handler_(facade_handler) { - acl_manager_->RegisterCallbacks(this, facade_handler_); - } - - ~AclManagerFacadeService() { - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - for (auto& connection : acl_connections_) { - connection.second.connection_->GetAclQueueEnd()->UnregisterDequeue(); - } - } - - ::grpc::Status CreateConnection(::grpc::ServerContext* context, const ConnectionMsg* request, - ::grpc::ServerWriter<ConnectionEvent>* writer) override { - log::info("peer={}", request->address()); - Address peer; - log::assert_that(Address::FromString(request->address(), peer), - "assert failed: Address::FromString(request->address(), peer)"); - acl_manager_->CreateConnection(peer); - if (per_connection_events_.size() > current_connection_request_) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, - "Only one outstanding request is supported"); - } - per_connection_events_.emplace_back( - std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>( - std::string("connection attempt ") + - std::to_string(current_connection_request_))); - return per_connection_events_[current_connection_request_]->RunLoop(context, writer); - } - - ::grpc::Status Disconnect(::grpc::ServerContext* /* context */, const HandleMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("handle={}", request->handle()); - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - log::error("Invalid handle"); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } else { - connection->second.connection_->Disconnect( - DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION); - return ::grpc::Status::OK; - } - } - - ::grpc::Status AuthenticationRequested(::grpc::ServerContext* /* context */, - const HandleMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("handle={}", request->handle()); - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - log::error("Invalid handle"); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } else { - connection->second.connection_->AuthenticationRequested(); - return ::grpc::Status::OK; - } - } - -#define GET_CONNECTION(view) \ - std::map<uint16_t, Connection>::iterator connection; \ - do { \ - if (!view.IsValid()) { \ - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \ - } \ - std::unique_lock<std::mutex> lock(acl_connections_mutex_); \ - connection = acl_connections_.find(view.GetConnectionHandle()); \ - if (connection == acl_connections_.end()) { \ - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \ - } \ - } while (0) - - ::grpc::Status ConnectionCommand(::grpc::ServerContext* /* context */, - const ConnectionCommandMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("size={}", request->packet().size()); - auto command_view = ConnectionManagementCommandView::Create(AclCommandView::Create( - CommandView::Create(PacketView<kLittleEndian>(std::make_shared<std::vector<uint8_t>>( - request->packet().begin(), request->packet().end()))))); - if (!command_view.IsValid()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet"); - } - log::info("opcode={}", OpCodeText(command_view.GetOpCode())); - switch (command_view.GetOpCode()) { - case OpCode::AUTHENTICATION_REQUESTED: { - GET_CONNECTION(AuthenticationRequestedView::Create(command_view)); - connection->second.connection_->AuthenticationRequested(); - return ::grpc::Status::OK; - } - case OpCode::DISCONNECT: { - auto view = DisconnectView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->Disconnect(view.GetReason()); - return ::grpc::Status::OK; - } - case OpCode::CHANGE_CONNECTION_PACKET_TYPE: { - auto view = ChangeConnectionPacketTypeView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->ChangeConnectionPacketType(view.GetPacketType()); - return ::grpc::Status::OK; - } - case OpCode::SET_CONNECTION_ENCRYPTION: { - auto view = SetConnectionEncryptionView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->SetConnectionEncryption(view.GetEncryptionEnable()); - return ::grpc::Status::OK; - } - case OpCode::CHANGE_CONNECTION_LINK_KEY: { - GET_CONNECTION(ChangeConnectionLinkKeyView::Create(command_view)); - connection->second.connection_->ChangeConnectionLinkKey(); - return ::grpc::Status::OK; - } - case OpCode::READ_CLOCK_OFFSET: { - GET_CONNECTION(ReadClockOffsetView::Create(command_view)); - connection->second.connection_->ReadClockOffset(); - return ::grpc::Status::OK; - } - case OpCode::HOLD_MODE: { - auto view = HoldModeView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->HoldMode(view.GetHoldModeMaxInterval(), - view.GetHoldModeMinInterval()); - return ::grpc::Status::OK; - } - case OpCode::SNIFF_MODE: { - auto view = SniffModeView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->SniffMode(view.GetSniffMaxInterval(), - view.GetSniffMinInterval(), - view.GetSniffAttempt(), view.GetSniffTimeout()); - return ::grpc::Status::OK; - } - case OpCode::EXIT_SNIFF_MODE: { - GET_CONNECTION(ExitSniffModeView::Create(command_view)); - connection->second.connection_->ExitSniffMode(); - return ::grpc::Status::OK; - } - case OpCode::FLUSH: { - GET_CONNECTION(FlushView::Create(command_view)); - connection->second.connection_->Flush(); - return ::grpc::Status::OK; - } - case OpCode::READ_AUTOMATIC_FLUSH_TIMEOUT: { - GET_CONNECTION(ReadAutomaticFlushTimeoutView::Create(command_view)); - connection->second.connection_->ReadAutomaticFlushTimeout(); - return ::grpc::Status::OK; - } - case OpCode::WRITE_AUTOMATIC_FLUSH_TIMEOUT: { - auto view = WriteAutomaticFlushTimeoutView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->WriteAutomaticFlushTimeout(view.GetFlushTimeout()); - return ::grpc::Status::OK; - } - case OpCode::READ_TRANSMIT_POWER_LEVEL: { - auto view = ReadTransmitPowerLevelView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->ReadTransmitPowerLevel(view.GetTransmitPowerLevelType()); - return ::grpc::Status::OK; - } - case OpCode::READ_LINK_SUPERVISION_TIMEOUT: { - GET_CONNECTION(ReadLinkSupervisionTimeoutView::Create(command_view)); - connection->second.connection_->ReadLinkSupervisionTimeout(); - return ::grpc::Status::OK; - } - case OpCode::WRITE_LINK_SUPERVISION_TIMEOUT: { - auto view = WriteLinkSupervisionTimeoutView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->WriteLinkSupervisionTimeout( - view.GetLinkSupervisionTimeout()); - return ::grpc::Status::OK; - } - case OpCode::READ_FAILED_CONTACT_COUNTER: { - GET_CONNECTION(ReadFailedContactCounterView::Create(command_view)); - connection->second.connection_->ReadFailedContactCounter(); - return ::grpc::Status::OK; - } - case OpCode::RESET_FAILED_CONTACT_COUNTER: { - GET_CONNECTION(ResetFailedContactCounterView::Create(command_view)); - connection->second.connection_->ResetFailedContactCounter(); - return ::grpc::Status::OK; - } - case OpCode::READ_LINK_QUALITY: { - GET_CONNECTION(ReadLinkQualityView::Create(command_view)); - connection->second.connection_->ReadLinkQuality(); - return ::grpc::Status::OK; - } - case OpCode::READ_AFH_CHANNEL_MAP: { - GET_CONNECTION(ReadAfhChannelMapView::Create(command_view)); - connection->second.connection_->ReadAfhChannelMap(); - return ::grpc::Status::OK; - } - case OpCode::READ_RSSI: { - GET_CONNECTION(ReadRssiView::Create(command_view)); - connection->second.connection_->ReadRssi(); - return ::grpc::Status::OK; - } - case OpCode::READ_CLOCK: { - auto view = ReadClockView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->ReadClock(view.GetWhichClock()); - return ::grpc::Status::OK; - } - case OpCode::READ_REMOTE_VERSION_INFORMATION: { - GET_CONNECTION(ReadRemoteVersionInformationView::Create(command_view)); - connection->second.connection_->ReadRemoteVersionInformation(); - return ::grpc::Status::OK; - } - case OpCode::READ_REMOTE_SUPPORTED_FEATURES: { - GET_CONNECTION(ReadRemoteSupportedFeaturesView::Create(command_view)); - connection->second.connection_->ReadRemoteSupportedFeatures(); - return ::grpc::Status::OK; - } - case OpCode::READ_REMOTE_EXTENDED_FEATURES: { - GET_CONNECTION(ReadRemoteExtendedFeaturesView::Create(command_view)); - uint8_t page_number = 0; - connection->second.connection_->ReadRemoteExtendedFeatures(page_number); - return ::grpc::Status::OK; - } - default: - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet"); - } - } -#undef GET_CONNECTION - - ::grpc::Status FetchIncomingConnection(::grpc::ServerContext* context, - const google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<ConnectionEvent>* writer) override { - log::info("wait for one incoming connection"); - if (per_connection_events_.size() > current_connection_request_) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, - "Only one outstanding connection is supported"); - } - per_connection_events_.emplace_back( - std::make_unique<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>( - std::string("incoming connection ") + - std::to_string(current_connection_request_))); - return per_connection_events_[current_connection_request_]->RunLoop(context, writer); - } - - ::grpc::Status SendAclData(::grpc::ServerContext* /* context */, const AclData* request, - ::google::protobuf::Empty* /* response */) override { - log::info("handle={}, size={}", request->handle(), request->payload().size()); - std::promise<void> promise; - auto future = promise.get_future(); - { - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } - // TODO: This is unsafe because connection may have gone - connection->second.connection_->GetAclQueueEnd()->RegisterEnqueue( - facade_handler_, - common::Bind(&AclManagerFacadeService::enqueue_packet, common::Unretained(this), - common::Unretained(request), common::Passed(std::move(promise)))); - auto status = future.wait_for(std::chrono::milliseconds(1000)); - if (status != std::future_status::ready) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Can't send packet"); - } - } - return ::grpc::Status::OK; - } - - std::unique_ptr<BasePacketBuilder> enqueue_packet(const AclData* request, - std::promise<void> promise) { - auto connection = acl_connections_.find(request->handle()); - log::assert_that(connection != acl_connections_.end(), "handle {}", request->handle()); - connection->second.connection_->GetAclQueueEnd()->UnregisterEnqueue(); - std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>( - std::vector<uint8_t>(request->payload().begin(), request->payload().end())); - promise.set_value(); - return packet; - } - - ::grpc::Status FetchAclData(::grpc::ServerContext* context, const HandleMsg* request, - ::grpc::ServerWriter<AclData>* writer) override { - log::info("handle={}", request->handle()); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } - return connection->second.pending_acl_data_.RunLoop(context, writer); - } - - static inline uint16_t to_handle(uint32_t current_request) { - return (current_request + 0x10) % 0xe00; - } - - static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) { - std::vector<uint8_t> bytes; - BitInserter bit_inserter(bytes); - builder->Serialize(bit_inserter); - return std::string(bytes.begin(), bytes.end()); - } - - void on_incoming_acl(std::shared_ptr<ClassicAclConnection> connection, uint16_t handle) { - log::info("handle={}, addr={}", connection->GetHandle(), connection->GetAddress()); - auto packet = connection->GetAclQueueEnd()->TryDequeue(); - auto connection_tracker = acl_connections_.find(handle); - log::assert_that(connection_tracker != acl_connections_.end(), "handle {}", handle); - AclData acl_data; - acl_data.set_handle(handle); - acl_data.set_payload(std::string(packet->begin(), packet->end())); - log::info("length={}", acl_data.payload().size()); - connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data); - } - - void OnConnectSuccess(std::unique_ptr<ClassicAclConnection> connection) override { - log::info("handle={}, addr={}", connection->GetHandle(), connection->GetAddress()); - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - std::shared_ptr<ClassicAclConnection> shared_connection = std::move(connection); - uint16_t handle = to_handle(current_connection_request_); - acl_connections_.erase(handle); - acl_connections_.emplace( - std::piecewise_construct, std::forward_as_tuple(handle), - std::forward_as_tuple(handle, shared_connection, - per_connection_events_[current_connection_request_])); - shared_connection->GetAclQueueEnd()->RegisterDequeue( - facade_handler_, common::Bind(&AclManagerFacadeService::on_incoming_acl, - common::Unretained(this), shared_connection, handle)); - auto callbacks = acl_connections_.find(handle)->second.GetCallbacks(); - shared_connection->RegisterCallbacks(callbacks, facade_handler_); - auto addr = shared_connection->GetAddress(); - std::unique_ptr<BasePacketBuilder> builder = ConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, handle, addr, LinkType::ACL, Enable::DISABLED); - ConnectionEvent success; - success.set_payload(builder_to_string(std::move(builder))); - per_connection_events_[current_connection_request_]->OnIncomingEvent(success); - current_connection_request_++; - } - - void OnConnectRequest(Address /* address */, ClassOfDevice /* cod */) override { - log::error("Remote connect request unimplemented"); - } - - void OnConnectFail(Address address, ErrorCode reason, bool /* locally_initiated */) override { - log::info("addr={}, reason={}", address, ErrorCodeText(reason)); - std::unique_ptr<BasePacketBuilder> builder = - ConnectionCompleteBuilder::Create(reason, 0, address, LinkType::ACL, Enable::DISABLED); - ConnectionEvent fail; - fail.set_payload(builder_to_string(std::move(builder))); - per_connection_events_[current_connection_request_]->OnIncomingEvent(fail); - current_connection_request_++; - } - - class Connection : public ConnectionManagementCallbacks { - public: - Connection(uint16_t handle, std::shared_ptr<ClassicAclConnection> connection, - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>> event_stream) - : handle_(handle), - connection_(std::move(connection)), - event_stream_(std::move(event_stream)) {} - - ConnectionManagementCallbacks* GetCallbacks() { return this; } - - void OnCentralLinkKeyComplete(KeyFlag key_flag) override { - log::info("key_flag:{}", KeyFlagText(key_flag)); - } - - void OnRoleChange(hci::ErrorCode /* hci_status */, Role new_role) override { - log::info("new_role:{}", (uint8_t)new_role); - } - - void OnReadLinkPolicySettingsComplete(uint16_t link_policy_settings) override { - log::info("link_policy_settings:{}", link_policy_settings); - } - - void OnConnectionPacketTypeChanged(uint16_t packet_type) override { - log::info("OnConnectionPacketTypeChanged packet_type:{}", packet_type); - } - - void OnAuthenticationComplete(hci::ErrorCode /* hci_status */) override { - log::info("OnAuthenticationComplete"); - } - - void OnEncryptionChange(EncryptionEnabled enabled) override { - log::info("OnConnectionPacketTypeChanged enabled:{}", (uint8_t)enabled); - } - - void OnChangeConnectionLinkKeyComplete() override { - log::info("OnChangeConnectionLinkKeyComplete"); - } - - void OnReadClockOffsetComplete(uint16_t clock_offset) override { - log::info("OnReadClockOffsetComplete clock_offset:{}", clock_offset); - } - - void OnModeChange(ErrorCode /* status */, Mode current_mode, uint16_t interval) override { - log::info("OnModeChange Mode:{}, interval:{}", (uint8_t)current_mode, interval); - } - - void OnSniffSubrating(hci::ErrorCode /* hci_status */, uint16_t maximum_transmit_latency, - uint16_t maximum_receive_latency, uint16_t minimum_remote_timeout, - uint16_t minimum_local_timeout) override { - log::info( - "OnSniffSubrating maximum_transmit_latency:{}, maximum_receive_latency:{} " - "minimum_remote_timeout:{} minimum_local_timeout:{}", - maximum_transmit_latency, maximum_receive_latency, minimum_remote_timeout, - minimum_local_timeout); - } - - void OnQosSetupComplete(ServiceType service_type, uint32_t token_rate, uint32_t peak_bandwidth, - uint32_t latency, uint32_t delay_variation) override { - log::info( - "OnQosSetupComplete service_type:{}, token_rate:{}, peak_bandwidth:{}, latency:{}, " - "delay_variation:{}", - (uint8_t)service_type, token_rate, peak_bandwidth, latency, delay_variation); - } - - void OnFlowSpecificationComplete(FlowDirection flow_direction, ServiceType service_type, - uint32_t token_rate, uint32_t token_bucket_size, - uint32_t peak_bandwidth, uint32_t access_latency) override { - log::info( - "OnFlowSpecificationComplete flow_direction:{}. service_type:{}, token_rate:{}, " - "token_bucket_size:{}, peak_bandwidth:{}, access_latency:{}", - (uint8_t)flow_direction, (uint8_t)service_type, token_rate, token_bucket_size, - peak_bandwidth, access_latency); - } - - void OnFlushOccurred() override { log::info("OnFlushOccurred"); } - - void OnRoleDiscoveryComplete(Role current_role) override { - log::info("OnRoleDiscoveryComplete current_role:{}", (uint8_t)current_role); - } - - void OnReadAutomaticFlushTimeoutComplete(uint16_t flush_timeout) override { - log::info("OnReadAutomaticFlushTimeoutComplete flush_timeout:{}", flush_timeout); - } - - void OnReadTransmitPowerLevelComplete(uint8_t transmit_power_level) override { - log::info("OnReadTransmitPowerLevelComplete transmit_power_level:{}", transmit_power_level); - } - - void OnReadLinkSupervisionTimeoutComplete(uint16_t link_supervision_timeout) override { - log::info("OnReadLinkSupervisionTimeoutComplete link_supervision_timeout:{}", - link_supervision_timeout); - } - - void OnReadFailedContactCounterComplete(uint16_t failed_contact_counter) override { - log::info("OnReadFailedContactCounterComplete failed_contact_counter:{}", - failed_contact_counter); - } - - void OnReadLinkQualityComplete(uint8_t link_quality) override { - log::info("OnReadLinkQualityComplete link_quality:{}", link_quality); - } - - void OnReadAfhChannelMapComplete(AfhMode afh_mode, - std::array<uint8_t, 10> /* afh_channel_map */) override { - log::info("OnReadAfhChannelMapComplete afh_mode:{}", (uint8_t)afh_mode); - } - - void OnReadRssiComplete(uint8_t rssi) override { - log::info("OnReadRssiComplete rssi:{}", rssi); - } - - void OnReadClockComplete(uint32_t clock, uint16_t accuracy) override { - log::info("OnReadClockComplete clock:{}, accuracy:{}", clock, accuracy); - } - - void OnDisconnection(ErrorCode reason) override { - log::info("reason: {}", ErrorCodeText(reason)); - std::unique_ptr<BasePacketBuilder> builder = - DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, reason); - ConnectionEvent disconnection; - disconnection.set_payload(builder_to_string(std::move(builder))); - event_stream_->OnIncomingEvent(disconnection); - } - void OnReadRemoteVersionInformationComplete(hci::ErrorCode /* error_status */, - uint8_t lmp_version, uint16_t manufacturer_name, - uint16_t sub_version) override { - log::info( - "OnReadRemoteVersionInformationComplete lmp_version:{} manufacturer_name:{} " - "sub_version:{}", - lmp_version, manufacturer_name, sub_version); - } - void OnReadRemoteSupportedFeaturesComplete(uint64_t features) override { - log::info("OnReadRemoteSupportedFeaturesComplete features:0x{:x}", features); - } - void OnReadRemoteExtendedFeaturesComplete(uint8_t page_number, uint8_t max_page_number, - uint64_t features) override { - log::info( - "OnReadRemoteExtendedFeaturesComplete page_number:{} max_page_number:{} " - "features:0x{:x}", - page_number, max_page_number, features); - } - - uint16_t handle_; - std::shared_ptr<ClassicAclConnection> connection_; - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>> event_stream_; - ::bluetooth::grpc::GrpcEventQueue<AclData> pending_acl_data_{std::string("PendingAclData") + - std::to_string(handle_)}; - }; - -private: - AclManager* acl_manager_; - ::bluetooth::os::Handler* facade_handler_; - mutable std::mutex acl_connections_mutex_; - std::map<uint16_t, Connection> acl_connections_; - std::vector<std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<ConnectionEvent>>> - per_connection_events_; - uint32_t current_connection_request_{0}; -}; - -void AclManagerFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<AclManager>(); -} - -void AclManagerFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new AclManagerFacadeService(GetDependency<AclManager>(), GetHandler()); -} - -void AclManagerFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* AclManagerFacadeModule::GetService() const { return service_; } - -const ModuleFactory AclManagerFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new AclManagerFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/acl_manager_facade.h b/system/gd/hci/facade/acl_manager_facade.h deleted file mode 100644 index b55937db53..0000000000 --- a/system/gd/hci/facade/acl_manager_facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "hci/acl_manager.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class AclManagerFacadeService; - -class AclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - AclManagerFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/controller_facade.cc b/system/gd/hci/facade/controller_facade.cc deleted file mode 100644 index ad8a4df217..0000000000 --- a/system/gd/hci/facade/controller_facade.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2020 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/facade/controller_facade.h" - -#include <condition_variable> -#include <memory> -#include <mutex> - -#include "blueberry/facade/hci/controller_facade.grpc.pb.h" -#include "blueberry/facade/hci/controller_facade.pb.h" -#include "common/bind.h" -#include "common/blocking_queue.h" -#include "grpc/grpc_event_queue.h" -#include "hci/address.h" -#include "hci/controller.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -namespace bluetooth { -namespace hci { -namespace facade { - -using namespace blueberry::facade::hci; -using blueberry::facade::BluetoothAddress; - -class ControllerFacadeService : public ControllerFacade::Service { -public: - ControllerFacadeService(Controller* controller, ::bluetooth::os::Handler*) - : controller_(controller) {} - - ::grpc::Status GetMacAddress(::grpc::ServerContext* /* context */, - const ::google::protobuf::Empty* /* request */, - BluetoothAddress* response) override { - Address local_address = controller_->GetMacAddress(); - response->set_address(local_address.ToString()); - return ::grpc::Status::OK; - } - - ::grpc::Status GetLocalName(::grpc::ServerContext* /* context */, - const ::google::protobuf::Empty* /* request */, - NameMsg* response) override { - std::string local_name = controller_->GetLocalName(); - response->set_name(local_name); - return ::grpc::Status::OK; - } - - ::grpc::Status WriteLocalName(::grpc::ServerContext* /* context */, const NameMsg* request, - ::google::protobuf::Empty* /* response */) override { - controller_->WriteLocalName(request->name()); - return ::grpc::Status::OK; - } - - ::grpc::Status IsSupportedCommand(::grpc::ServerContext* /* context */, const OpCodeMsg* request, - SupportedMsg* response) override { - bool ret = controller_->IsSupported(static_cast<OpCode>(request->op_code())); - response->set_supported(ret); - return ::grpc::Status::OK; - } - -#define SUPPORTED_API(name) \ - ::grpc::Status name(::grpc::ServerContext* /* context */, \ - const ::google::protobuf::Empty* /* request */, SupportedMsg* response) \ - override { \ - response->set_supported(controller_->name()); \ - return ::grpc::Status::OK; \ - } - - SUPPORTED_API(SupportsSimplePairing) - SUPPORTED_API(SupportsSecureConnections) - SUPPORTED_API(SupportsSimultaneousLeBrEdr) - SUPPORTED_API(SupportsInterlacedInquiryScan) - SUPPORTED_API(SupportsRssiWithInquiryResults) - SUPPORTED_API(SupportsExtendedInquiryResponse) - SUPPORTED_API(SupportsRoleSwitch) - SUPPORTED_API(Supports3SlotPackets) - SUPPORTED_API(Supports5SlotPackets) - SUPPORTED_API(SupportsClassic2mPhy) - SUPPORTED_API(SupportsClassic3mPhy) - SUPPORTED_API(Supports3SlotEdrPackets) - SUPPORTED_API(Supports5SlotEdrPackets) - SUPPORTED_API(SupportsSco) - SUPPORTED_API(SupportsHv2Packets) - SUPPORTED_API(SupportsHv3Packets) - SUPPORTED_API(SupportsEv3Packets) - SUPPORTED_API(SupportsEv4Packets) - SUPPORTED_API(SupportsEv5Packets) - SUPPORTED_API(SupportsEsco2mPhy) - SUPPORTED_API(SupportsEsco3mPhy) - SUPPORTED_API(Supports3SlotEscoEdrPackets) - SUPPORTED_API(SupportsHoldMode) - SUPPORTED_API(SupportsSniffMode) - SUPPORTED_API(SupportsParkMode) - SUPPORTED_API(SupportsNonFlushablePb) - SUPPORTED_API(SupportsSniffSubrating) - SUPPORTED_API(SupportsEncryptionPause) - SUPPORTED_API(SupportsBle) - SUPPORTED_API(SupportsBleEncryption) - SUPPORTED_API(SupportsBleConnectionParametersRequest) - SUPPORTED_API(SupportsBleExtendedReject) - SUPPORTED_API(SupportsBlePeripheralInitiatedFeaturesExchange) - SUPPORTED_API(SupportsBlePing) - SUPPORTED_API(SupportsBleDataPacketLengthExtension) - SUPPORTED_API(SupportsBlePrivacy) - SUPPORTED_API(SupportsBleExtendedScannerFilterPolicies) - SUPPORTED_API(SupportsBle2mPhy) - SUPPORTED_API(SupportsBleStableModulationIndexTx) - SUPPORTED_API(SupportsBleStableModulationIndexRx) - SUPPORTED_API(SupportsBleCodedPhy) - SUPPORTED_API(SupportsBleExtendedAdvertising) - SUPPORTED_API(SupportsBlePeriodicAdvertising) - SUPPORTED_API(SupportsBleChannelSelectionAlgorithm2) - SUPPORTED_API(SupportsBlePowerClass1) - SUPPORTED_API(SupportsBleMinimumUsedChannels) - SUPPORTED_API(SupportsBleConnectionCteRequest) - SUPPORTED_API(SupportsBleConnectionCteResponse) - SUPPORTED_API(SupportsBleConnectionlessCteTransmitter) - SUPPORTED_API(SupportsBleConnectionlessCteReceiver) - SUPPORTED_API(SupportsBleAntennaSwitchingDuringCteTx) - SUPPORTED_API(SupportsBleAntennaSwitchingDuringCteRx) - SUPPORTED_API(SupportsBleReceivingConstantToneExtensions) - SUPPORTED_API(SupportsBlePeriodicAdvertisingSyncTransferSender) - SUPPORTED_API(SupportsBlePeriodicAdvertisingSyncTransferRecipient) - SUPPORTED_API(SupportsBleSleepClockAccuracyUpdates) - SUPPORTED_API(SupportsBleRemotePublicKeyValidation) - SUPPORTED_API(SupportsBleConnectedIsochronousStreamCentral) - SUPPORTED_API(SupportsBleConnectedIsochronousStreamPeripheral) - SUPPORTED_API(SupportsBleIsochronousBroadcaster) - SUPPORTED_API(SupportsBleSynchronizedReceiver) - SUPPORTED_API(SupportsBleIsochronousChannelsHostSupport) - SUPPORTED_API(SupportsBlePowerControlRequest) - SUPPORTED_API(SupportsBlePowerChangeIndication) - SUPPORTED_API(SupportsBlePathLossMonitoring) - SUPPORTED_API(SupportsBlePeriodicAdvertisingAdi) - - ::grpc::Status GetLeNumberOfSupportedAdvertisingSets( - ::grpc::ServerContext* /* context */, const ::google::protobuf::Empty* /* request */, - SingleValueMsg* response) override { - uint8_t ret = controller_->GetLeNumberOfSupportedAdverisingSets(); - response->set_value(ret); - return ::grpc::Status::OK; - } - -private: - Controller* controller_; -}; - -void ControllerFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<Controller>(); -} - -void ControllerFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new ControllerFacadeService(GetDependency<Controller>(), GetHandler()); -} - -void ControllerFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* ControllerFacadeModule::GetService() const { return service_; } - -const ModuleFactory ControllerFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new ControllerFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/controller_facade.h b/system/gd/hci/facade/controller_facade.h deleted file mode 100644 index 2017b10722..0000000000 --- a/system/gd/hci/facade/controller_facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "hci/controller.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class ControllerFacadeService; - -class ControllerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - ControllerFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/facade.cc b/system/gd/hci/facade/facade.cc deleted file mode 100644 index 7d9d97eaf8..0000000000 --- a/system/gd/hci/facade/facade.cc +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2019 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/facade/facade.h" - -#include <bluetooth/log.h> - -#include <memory> - -#include "blueberry/facade/hci/hci_facade.grpc.pb.h" -#include "common/bind.h" -#include "grpc/grpc_event_queue.h" -#include "hci/controller.h" -#include "hci/hci_layer.h" -#include "hci/hci_packets.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -namespace bluetooth { -namespace hci { -namespace facade { - -using namespace blueberry::facade::hci; - -class HciFacadeService : public HciFacade::Service { -public: - HciFacadeService(HciLayer* hci_layer, Controller* controller, - ::bluetooth::os::Handler* facade_handler) - : hci_layer_(hci_layer), controller_(controller), facade_handler_(facade_handler) {} - - virtual ~HciFacadeService() { - if (unregister_acl_dequeue_) { - hci_layer_->GetAclQueueEnd()->UnregisterDequeue(); - } - if (waiting_acl_packet_ != nullptr) { - hci_layer_->GetAclQueueEnd()->UnregisterEnqueue(); - if (waiting_acl_packet_ != nullptr) { - waiting_acl_packet_.reset(); - } - } - } - - class TestCommandBuilder : public CommandBuilder { - public: - explicit TestCommandBuilder(std::vector<uint8_t> bytes) - : CommandBuilder(OpCode::NONE), bytes_(std::move(bytes)) {} - size_t size() const override { return bytes_.size(); } - void Serialize(BitInserter& bit_inserter) const override { - for (auto&& b : bytes_) { - bit_inserter.insert_byte(b); - } - } - - private: - std::vector<uint8_t> bytes_; - }; - - ::grpc::Status SendCommand(::grpc::ServerContext* /* context */, - const ::blueberry::facade::Data* command, - ::google::protobuf::Empty* /* response */) override { - auto payload = std::vector<uint8_t>(command->payload().begin(), command->payload().end()); - auto packet = std::make_unique<TestCommandBuilder>(payload); - auto opcode = static_cast<const bluetooth::hci::OpCode>(payload.at(1) << 8 | payload.at(0)); - if (Checker::IsCommandStatusOpcode(opcode)) { - hci_layer_->EnqueueCommand(std::move(packet), - facade_handler_->BindOnceOn(this, &HciFacadeService::on_status)); - } else { - hci_layer_->EnqueueCommand(std::move(packet), - facade_handler_->BindOnceOn(this, &HciFacadeService::on_complete)); - } - return ::grpc::Status::OK; - } - - ::grpc::Status RequestEvent(::grpc::ServerContext* /* context */, - const ::blueberry::facade::hci::EventRequest* event, - ::google::protobuf::Empty* /* response */) override { - hci_layer_->RegisterEventHandler(static_cast<EventCode>(event->code()), - facade_handler_->BindOn(this, &HciFacadeService::on_event)); - return ::grpc::Status::OK; - } - - ::grpc::Status RequestLeSubevent(::grpc::ServerContext* /* context */, - const ::blueberry::facade::hci::EventRequest* event, - ::google::protobuf::Empty* /* response */) override { - hci_layer_->RegisterLeEventHandler( - static_cast<SubeventCode>(event->code()), - facade_handler_->BindOn(this, &HciFacadeService::on_le_subevent)); - return ::grpc::Status::OK; - } - - ::grpc::Status StreamEvents(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_events_.RunLoop(context, writer); - } - - ::grpc::Status StreamLeSubevents( - ::grpc::ServerContext* context, const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - return pending_le_events_.RunLoop(context, writer); - } - - class TestAclBuilder : public AclBuilder { - public: - explicit TestAclBuilder(std::vector<uint8_t> payload) - : AclBuilder(0xbad, PacketBoundaryFlag::CONTINUING_FRAGMENT, - BroadcastFlag::ACTIVE_PERIPHERAL_BROADCAST), - bytes_(std::move(payload)) {} - - size_t size() const override { return bytes_.size(); } - void Serialize(BitInserter& bit_inserter) const override { - for (auto&& b : bytes_) { - bit_inserter.insert_byte(b); - } - } - - private: - std::vector<uint8_t> bytes_; - }; - - ::grpc::Status SendAcl(::grpc::ServerContext* /* context */, const ::blueberry::facade::Data* acl, - ::google::protobuf::Empty* /* response */) override { - waiting_acl_packet_ = std::make_unique<TestAclBuilder>( - std::vector<uint8_t>(acl->payload().begin(), acl->payload().end())); - std::promise<void> enqueued; - auto future = enqueued.get_future(); - if (!completed_packets_callback_registered_) { - controller_->RegisterCompletedAclPacketsCallback( - facade_handler_->Bind([](uint16_t, uint16_t) { /* do nothing */ })); - completed_packets_callback_registered_ = true; - } - hci_layer_->GetAclQueueEnd()->RegisterEnqueue( - facade_handler_, common::Bind(&HciFacadeService::handle_enqueue_acl, - common::Unretained(this), common::Unretained(&enqueued))); - auto result = future.wait_for(std::chrono::milliseconds(100)); - log::assert_that(std::future_status::ready == result, - "assert failed: std::future_status::ready == result"); - return ::grpc::Status::OK; - } - - ::grpc::Status StreamAcl(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<::blueberry::facade::Data>* writer) override { - hci_layer_->GetAclQueueEnd()->RegisterDequeue( - facade_handler_, - common::Bind(&HciFacadeService::on_acl_ready, common::Unretained(this))); - unregister_acl_dequeue_ = true; - return pending_acl_events_.RunLoop(context, writer); - } - -private: - std::unique_ptr<AclBuilder> handle_enqueue_acl(std::promise<void>* promise) { - promise->set_value(); - hci_layer_->GetAclQueueEnd()->UnregisterEnqueue(); - return std::move(waiting_acl_packet_); - } - - void on_acl_ready() { - auto acl_ptr = hci_layer_->GetAclQueueEnd()->TryDequeue(); - log::assert_that(acl_ptr != nullptr, "assert failed: acl_ptr != nullptr"); - log::assert_that(acl_ptr->IsValid(), "assert failed: acl_ptr->IsValid()"); - log::info("Got an Acl message for handle 0x{:x}", acl_ptr->GetHandle()); - ::blueberry::facade::Data incoming; - incoming.set_payload(std::string(acl_ptr->begin(), acl_ptr->end())); - pending_acl_events_.OnIncomingEvent(std::move(incoming)); - } - - void on_event(hci::EventView view) { - log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); - log::info("Got an Event {}", EventCodeText(view.GetEventCode())); - ::blueberry::facade::Data response; - response.set_payload(std::string(view.begin(), view.end())); - pending_events_.OnIncomingEvent(std::move(response)); - } - - void on_le_subevent(hci::LeMetaEventView view) { - log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); - log::info("Got an LE Event {}", SubeventCodeText(view.GetSubeventCode())); - ::blueberry::facade::Data response; - response.set_payload(std::string(view.begin(), view.end())); - pending_le_events_.OnIncomingEvent(std::move(response)); - } - - void on_complete(hci::CommandCompleteView view) { - log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); - log::info("Got a Command complete {}", OpCodeText(view.GetCommandOpCode())); - ::blueberry::facade::Data response; - response.set_payload(std::string(view.begin(), view.end())); - pending_events_.OnIncomingEvent(std::move(response)); - } - - void on_status(hci::CommandStatusView view) { - log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); - log::info("Got a Command status {}", OpCodeText(view.GetCommandOpCode())); - ::blueberry::facade::Data response; - response.set_payload(std::string(view.begin(), view.end())); - pending_events_.OnIncomingEvent(std::move(response)); - } - - HciLayer* hci_layer_; - Controller* controller_; - ::bluetooth::os::Handler* facade_handler_; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_events_{"StreamEvents"}; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_le_events_{ - "StreamLeSubevents"}; - ::bluetooth::grpc::GrpcEventQueue<::blueberry::facade::Data> pending_acl_events_{"StreamAcl"}; - bool unregister_acl_dequeue_{false}; - std::unique_ptr<TestAclBuilder> waiting_acl_packet_; - bool completed_packets_callback_registered_{false}; -}; - -void HciFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<HciLayer>(); - list->add<Controller>(); -} - -void HciFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new HciFacadeService(GetDependency<HciLayer>(), GetDependency<Controller>(), - GetHandler()); -} - -void HciFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* HciFacadeModule::GetService() const { return service_; } - -const ModuleFactory HciFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new HciFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/facade.h b/system/gd/hci/facade/facade.h deleted file mode 100644 index 283908ddac..0000000000 --- a/system/gd/hci/facade/facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "hci/hci_layer.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class HciFacadeService; - -class HciFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - HciFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_acl_manager_facade.cc b/system/gd/hci/facade/le_acl_manager_facade.cc deleted file mode 100644 index 3e7ff96892..0000000000 --- a/system/gd/hci/facade/le_acl_manager_facade.cc +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright 2019 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/facade/le_acl_manager_facade.h" - -#include <bluetooth/log.h> - -#include <memory> -#include <mutex> - -#include "blueberry/facade/hci/le_acl_manager_facade.grpc.pb.h" -#include "blueberry/facade/hci/le_acl_manager_facade.pb.h" -#include "common/bind.h" -#include "grpc/grpc_event_queue.h" -#include "hci/acl_manager.h" -#include "hci/hci_packets.h" -#include "hci/octets.h" -#include "packet/raw_builder.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -using ::bluetooth::packet::RawBuilder; - -namespace std { -template <> -struct formatter<blueberry::facade::BluetoothAddressTypeEnum> - : enum_formatter<blueberry::facade::BluetoothAddressTypeEnum> {}; -} // namespace std - -namespace bluetooth { -namespace hci { -namespace facade { - -using acl_manager::LeAclConnection; -using acl_manager::LeConnectionCallbacks; -using acl_manager::LeConnectionManagementCallbacks; - -using namespace blueberry::facade::hci; - -class LeAclManagerFacadeService : public LeAclManagerFacade::Service, public LeConnectionCallbacks { -public: - LeAclManagerFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler) - : acl_manager_(acl_manager), facade_handler_(facade_handler) { - acl_manager_->RegisterLeCallbacks(this, facade_handler_); - } - - ~LeAclManagerFacadeService() { - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - for (auto& conn : acl_connections_) { - if (conn.second.connection_ != nullptr) { - conn.second.connection_->GetAclQueueEnd()->UnregisterDequeue(); - conn.second.connection_.reset(); - } - } - } - - ::grpc::Status CreateConnection(::grpc::ServerContext* context, - const CreateConnectionMsg* request, - ::grpc::ServerWriter<LeConnectionEvent>* writer) override { - log::info("peer={}, type={}, id_direct={}", request->peer_address().address().address(), - request->peer_address().type(), request->is_direct()); - Address peer_address; - log::assert_that( - Address::FromString(request->peer_address().address().address(), peer_address), - "assert failed: Address::FromString(request->peer_address().address().address(), " - "peer_address)"); - AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type())); - bool is_direct = request->is_direct(); - acl_manager_->CreateLeConnection(peer, is_direct); - - if (is_direct) { - if (direct_connection_events_ != nullptr) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, - "Only one outstanding direct request is supported"); - } - direct_connection_events_ = - std::make_shared<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>( - std::string("direct connection attempt ") + peer.ToString()); - direct_connection_address_ = peer; - return direct_connection_events_->RunLoop(context, writer); - } - per_connection_events_.emplace( - peer, std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>( - std::string("connection attempt ") + peer.ToString())); - return per_connection_events_[peer]->RunLoop(context, writer); - } - - ::grpc::Status CancelConnection(::grpc::ServerContext* /* context */, - const ::blueberry::facade::BluetoothAddressWithType* request, - google::protobuf::Empty* /* response */) override { - log::info("peer={}, type={}", request->address().address(), request->type()); - Address peer_address; - log::assert_that( - Address::FromString(request->address().address(), peer_address), - "assert failed: Address::FromString(request->address().address(), peer_address)"); - AddressWithType peer(peer_address, static_cast<AddressType>(request->type())); - if (peer == direct_connection_address_) { - direct_connection_address_ = AddressWithType(); - direct_connection_events_.reset(); - } else { - if (per_connection_events_.count(peer) == 0) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, - "No matching outstanding connection"); - } - } - acl_manager_->CancelLeConnect(peer); - return ::grpc::Status::OK; - } - - ::grpc::Status Disconnect(::grpc::ServerContext* /* context */, const LeHandleMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("handle={}", request->handle()); - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - log::error("Invalid handle"); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } else { - connection->second.connection_->Disconnect( - DisconnectReason::REMOTE_USER_TERMINATED_CONNECTION); - return ::grpc::Status::OK; - } - } - -#define GET_CONNECTION(view) \ - std::map<uint16_t, Connection>::iterator connection; \ - do { \ - if (!view.IsValid()) { \ - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \ - } \ - std::unique_lock<std::mutex> lock(acl_connections_mutex_); \ - connection = acl_connections_.find(view.GetConnectionHandle()); \ - if (connection == acl_connections_.end()) { \ - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); \ - } \ - } while (0) - - ::grpc::Status ConnectionCommand(::grpc::ServerContext* /* context */, - const LeConnectionCommandMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("size={}", request->packet().size()); - auto command_view = ConnectionManagementCommandView::Create(AclCommandView::Create( - CommandView::Create(PacketView<kLittleEndian>(std::make_shared<std::vector<uint8_t>>( - request->packet().begin(), request->packet().end()))))); - if (!command_view.IsValid()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet"); - } - log::info("opcode={}", OpCodeText(command_view.GetOpCode())); - switch (command_view.GetOpCode()) { - case OpCode::DISCONNECT: { - auto view = DisconnectView::Create(command_view); - GET_CONNECTION(view); - connection->second.connection_->Disconnect(view.GetReason()); - return ::grpc::Status::OK; - } - default: - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid command packet"); - } - } -#undef GET_CONNECTION - - ::grpc::Status FetchIncomingConnection(::grpc::ServerContext* context, - const google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<LeConnectionEvent>* writer) override { - log::info("wait for one incoming connection"); - if (incoming_connection_events_ != nullptr) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, - "Only one outstanding incoming connection is supported"); - } - incoming_connection_events_ = - std::make_unique<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>( - std::string("incoming connection ")); - return incoming_connection_events_->RunLoop(context, writer); - } - - ::grpc::Status AddDeviceToResolvingList(::grpc::ServerContext* /* context */, - const IrkMsg* request, - ::google::protobuf::Empty* /* response */) override { - log::info("peer={}, type={}", request->peer().address().address(), request->peer().type()); - Address peer_address; - log::assert_that(Address::FromString(request->peer().address().address(), peer_address), - "assert failed: Address::FromString(request->peer().address().address(), " - "peer_address)"); - AddressWithType peer(peer_address, static_cast<AddressType>(request->peer().type())); - - auto request_peer_irk_length = request->peer_irk().end() - request->peer_irk().begin(); - - if (request_peer_irk_length != kOctet16Length) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Peer IRK"); - } - - auto request_local_irk_length = request->local_irk().end() - request->local_irk().begin(); - if (request_local_irk_length != kOctet16Length) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid Local IRK"); - } - - Octet16 peer_irk = {}; - Octet16 local_irk = {}; - - std::vector<uint8_t> peer_irk_data(request->peer_irk().begin(), request->peer_irk().end()); - std::copy_n(peer_irk_data.begin(), kOctet16Length, peer_irk.begin()); - - std::vector<uint8_t> local_irk_data(request->local_irk().begin(), request->local_irk().end()); - std::copy_n(local_irk_data.begin(), kOctet16Length, local_irk.begin()); - - acl_manager_->AddDeviceToResolvingList(peer, peer_irk, local_irk); - return ::grpc::Status::OK; - } - - ::grpc::Status SendAclData(::grpc::ServerContext* /* context */, const LeAclData* request, - ::google::protobuf::Empty* /* response */) override { - log::info("handle={}, size={}", request->handle(), request->payload().size()); - std::promise<void> promise; - auto future = promise.get_future(); - { - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } - connection->second.connection_->GetAclQueueEnd()->RegisterEnqueue( - facade_handler_, - common::Bind(&LeAclManagerFacadeService::enqueue_packet, common::Unretained(this), - common::Unretained(request), common::Passed(std::move(promise)))); - auto status = future.wait_for(std::chrono::milliseconds(1000)); - if (status != std::future_status::ready) { - return ::grpc::Status(::grpc::StatusCode::RESOURCE_EXHAUSTED, "Can't send packet"); - } - } - return ::grpc::Status::OK; - } - - std::unique_ptr<BasePacketBuilder> enqueue_packet(const LeAclData* request, - std::promise<void> promise) { - auto connection = acl_connections_.find(request->handle()); - log::assert_that(connection != acl_connections_.end(), "handle {}", request->handle()); - connection->second.connection_->GetAclQueueEnd()->UnregisterEnqueue(); - std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>( - std::vector<uint8_t>(request->payload().begin(), request->payload().end())); - promise.set_value(); - return packet; - } - - ::grpc::Status FetchAclData(::grpc::ServerContext* context, const LeHandleMsg* request, - ::grpc::ServerWriter<LeAclData>* writer) override { - log::info("handle={}", request->handle()); - auto connection = acl_connections_.find(request->handle()); - if (connection == acl_connections_.end()) { - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invalid handle"); - } - return connection->second.pending_acl_data_.RunLoop(context, writer); - } - - static inline std::string builder_to_string(std::unique_ptr<BasePacketBuilder> builder) { - std::vector<uint8_t> bytes; - BitInserter bit_inserter(bytes); - builder->Serialize(bit_inserter); - return std::string(bytes.begin(), bytes.end()); - } - - void on_incoming_acl(std::shared_ptr<LeAclConnection> connection, uint16_t handle) { - log::info("handle={}, addr={}", connection->GetHandle(), connection->GetRemoteAddress()); - auto packet = connection->GetAclQueueEnd()->TryDequeue(); - auto connection_tracker = acl_connections_.find(handle); - log::assert_that(connection_tracker != acl_connections_.end(), "handle {}", handle); - LeAclData acl_data; - acl_data.set_handle(handle); - acl_data.set_payload(std::string(packet->begin(), packet->end())); - connection_tracker->second.pending_acl_data_.OnIncomingEvent(acl_data); - } - - void OnLeConnectSuccess(AddressWithType peer, - std::unique_ptr<LeAclConnection> connection) override { - log::info("handle={}, addr={}", connection->GetHandle(), peer); - std::unique_lock<std::mutex> lock(acl_connections_mutex_); - std::shared_ptr<LeAclConnection> shared_connection = std::move(connection); - uint16_t handle = shared_connection->GetHandle(); - auto role = shared_connection->GetRole(); - if (role == Role::PERIPHERAL) { - log::assert_that(incoming_connection_events_ != nullptr, - "assert failed: incoming_connection_events_ != nullptr"); - if (per_connection_events_.find(peer) == per_connection_events_.end()) { - per_connection_events_.emplace(peer, incoming_connection_events_); - } else { - per_connection_events_[peer] = incoming_connection_events_; - } - incoming_connection_events_.reset(); - } else if (direct_connection_address_ == peer) { - direct_connection_address_ = AddressWithType(); - per_connection_events_.emplace(peer, direct_connection_events_); - direct_connection_events_.reset(); - } else { - log::assert_that(per_connection_events_.count(peer) > 0, "No connection request for {}", - ADDRESS_TO_LOGGABLE_CSTR(peer)); - } - acl_connections_.erase(handle); - acl_connections_.emplace( - std::piecewise_construct, std::forward_as_tuple(handle), - std::forward_as_tuple(handle, shared_connection, per_connection_events_[peer])); - shared_connection->GetAclQueueEnd()->RegisterDequeue( - facade_handler_, common::Bind(&LeAclManagerFacadeService::on_incoming_acl, - common::Unretained(this), shared_connection, handle)); - auto callbacks = acl_connections_.find(handle)->second.GetCallbacks(); - shared_connection->RegisterCallbacks(callbacks, facade_handler_); - { - std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create( - ErrorCode::SUCCESS, handle, role, peer.GetAddressType(), peer.GetAddress(), 1, 2, 3, - ClockAccuracy::PPM_20); - LeConnectionEvent success; - success.set_payload(builder_to_string(std::move(builder))); - per_connection_events_[peer]->OnIncomingEvent(success); - } - } - - void OnLeConnectFail(AddressWithType address, ErrorCode reason) override { - log::info("addr={}, reason={}", address, ErrorCodeText(reason)); - std::unique_ptr<BasePacketBuilder> builder = LeConnectionCompleteBuilder::Create( - reason, 0, Role::CENTRAL, address.GetAddressType(), address.GetAddress(), 0, 0, 0, - ClockAccuracy::PPM_20); - LeConnectionEvent fail; - fail.set_payload(builder_to_string(std::move(builder))); - if (address == direct_connection_address_) { - direct_connection_address_ = AddressWithType(); - direct_connection_events_->OnIncomingEvent(fail); - } else { - per_connection_events_[address]->OnIncomingEvent(fail); - } - } - - class Connection : public LeConnectionManagementCallbacks { - public: - Connection(uint16_t handle, std::shared_ptr<LeAclConnection> connection, - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream) - : handle_(handle), - connection_(std::move(connection)), - event_stream_(std::move(event_stream)) {} - void OnConnectionUpdate(hci::ErrorCode /* hci_status */, uint16_t connection_interval, - uint16_t connection_latency, uint16_t supervision_timeout) override { - log::info("interval: 0x{:x}, latency: 0x{:x}, timeout 0x{:x}", connection_interval, - connection_latency, supervision_timeout); - } - - void OnParameterUpdateRequest(uint16_t interval_min, uint16_t interval_max, uint16_t latency, - uint16_t supervision_timeout) override { - log::info("interval_min: 0x{:x}, interval_max: 0x{:x}, latency: 0x{:x}, timeout 0x{:x}", - interval_min, interval_max, latency, supervision_timeout); - } - - void OnDataLengthChange(uint16_t tx_octets, uint16_t tx_time, uint16_t rx_octets, - uint16_t rx_time) override { - log::info("tx_octets: 0x{:x}, tx_time: 0x{:x}, rx_octets 0x{:x}, rx_time 0x{:x}", tx_octets, - tx_time, rx_octets, rx_time); - } - - void OnPhyUpdate(hci::ErrorCode /* hci_status */, uint8_t /* tx_phy */, - uint8_t /* rx_phy */) override {} - void OnDisconnection(ErrorCode reason) override { - log::info("reason: {}", ErrorCodeText(reason)); - std::unique_ptr<BasePacketBuilder> builder = - DisconnectionCompleteBuilder::Create(ErrorCode::SUCCESS, handle_, reason); - LeConnectionEvent disconnection; - disconnection.set_payload(builder_to_string(std::move(builder))); - event_stream_->OnIncomingEvent(disconnection); - } - - void OnReadRemoteVersionInformationComplete(hci::ErrorCode /* hci_status */, - uint8_t /* lmp_version */, - uint16_t /* manufacturer_name */, - uint16_t /* sub_version */) override {} - void OnLeReadRemoteFeaturesComplete(hci::ErrorCode /* hci_status */, - uint64_t /* features */) override {} - - LeConnectionManagementCallbacks* GetCallbacks() { return this; } - void OnLeSubrateChange(hci::ErrorCode hci_status, uint16_t subrate_factor, - uint16_t peripheral_latency, uint16_t continuation_number, - uint16_t supervision_timeout) override { - log::info( - "hci_status: {}, subrate_factor: {:#x}, peripheral_latency: {:#x}, " - "continuation_number: " - "{:#x}, supervision_timeout: {:#x}", - ErrorCodeText(hci_status), subrate_factor, peripheral_latency, continuation_number, - supervision_timeout); - } - - uint16_t handle_; - std::shared_ptr<LeAclConnection> connection_; - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> event_stream_; - ::bluetooth::grpc::GrpcEventQueue<LeAclData> pending_acl_data_{std::string("PendingAclData") + - std::to_string(handle_)}; - }; - - ::grpc::Status IsOnBackgroundList(::grpc::ServerContext* /* context */, - const ::blueberry::facade::hci::BackgroundRequestMsg* request, - ::blueberry::facade::hci::BackgroundResultMsg* msg) { - Address peer_address; - log::assert_that( - Address::FromString(request->peer_address().address().address(), peer_address), - "assert failed: Address::FromString(request->peer_address().address().address(), " - "peer_address)"); - AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type())); - std::promise<bool> promise; - auto future = promise.get_future(); - acl_manager_->IsOnBackgroundList(peer, std::move(promise)); - msg->set_is_on_background_list(future.get()); - return ::grpc::Status::OK; - } - - ::grpc::Status RemoveFromBackgroundList( - ::grpc::ServerContext* /* context */, - const ::blueberry::facade::hci::BackgroundRequestMsg* request, - ::google::protobuf::Empty* /* response */) { - Address peer_address; - log::assert_that( - Address::FromString(request->peer_address().address().address(), peer_address), - "assert failed: Address::FromString(request->peer_address().address().address(), " - "peer_address)"); - AddressWithType peer(peer_address, static_cast<AddressType>(request->peer_address().type())); - acl_manager_->RemoveFromBackgroundList(peer); - return ::grpc::Status::OK; - } - -private: - AclManager* acl_manager_; - ::bluetooth::os::Handler* facade_handler_; - mutable std::mutex acl_connections_mutex_; - std::map<bluetooth::hci::AddressWithType, - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>>> - per_connection_events_; - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> direct_connection_events_; - bluetooth::hci::AddressWithType direct_connection_address_; - std::shared_ptr<::bluetooth::grpc::GrpcEventQueue<LeConnectionEvent>> incoming_connection_events_; - std::map<uint16_t, Connection> acl_connections_; -}; - -void LeAclManagerFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<AclManager>(); -} - -void LeAclManagerFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new LeAclManagerFacadeService(GetDependency<AclManager>(), GetHandler()); -} - -void LeAclManagerFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* LeAclManagerFacadeModule::GetService() const { return service_; } - -const ModuleFactory LeAclManagerFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new LeAclManagerFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_acl_manager_facade.h b/system/gd/hci/facade/le_acl_manager_facade.h deleted file mode 100644 index 931601c273..0000000000 --- a/system/gd/hci/facade/le_acl_manager_facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "hci/acl_manager.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class LeAclManagerFacadeService; - -class LeAclManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - LeAclManagerFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_advertising_manager_facade.cc b/system/gd/hci/facade/le_advertising_manager_facade.cc deleted file mode 100644 index 55ef78ce79..0000000000 --- a/system/gd/hci/facade/le_advertising_manager_facade.cc +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright 2019 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/facade/le_advertising_manager_facade.h" - -#include <bluetooth/log.h> - -#include <cstdint> -#include <unordered_map> -#include <utility> - -#include "blueberry/facade/hci/le_advertising_manager_facade.grpc.pb.h" -#include "blueberry/facade/hci/le_advertising_manager_facade.pb.h" -#include "common/bidi_queue.h" -#include "common/bind.h" -#include "grpc/grpc_event_queue.h" -#include "hardware/ble_advertiser.h" -#include "hci/address.h" -#include "hci/address_with_type.h" -#include "hci/le_advertising_manager.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - -namespace bluetooth { -namespace hci { -namespace facade { - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; -using ::grpc::ServerWriter; -using ::grpc::Status; - -using ::blueberry::facade::BluetoothAddress; -using ::blueberry::facade::BluetoothAddressTypeEnum; -using ::blueberry::facade::BluetoothOwnAddressTypeEnum; -using ::blueberry::facade::hci::AdvertisingConfig; -using ::blueberry::facade::hci::ExtendedAdvertisingConfig; -using ::blueberry::facade::hci::GapDataMsg; -using ::blueberry::facade::hci::PeriodicAdvertisingParameters; - -hci::GapData GapDataFromProto(const GapDataMsg& gap_data_proto) { - hci::GapData gap_data; - auto data_copy = std::make_shared<std::vector<uint8_t>>(gap_data_proto.data().begin(), - gap_data_proto.data().end()); - packet::PacketView<packet::kLittleEndian> packet(data_copy); - auto after = hci::GapData::Parse(&gap_data, packet.begin()); - log::assert_that(after != packet.begin(), "assert failed: after != packet.begin()"); - return gap_data; -} - -bool AdvertisingConfigFromProto(const AdvertisingConfig& config_proto, - hci::AdvertisingConfig* config) { - for (const auto& elem : config_proto.advertisement()) { - config->advertisement.push_back(GapDataFromProto(elem)); - } - - for (const auto& elem : config_proto.scan_response()) { - config->scan_response.push_back(GapDataFromProto(elem)); - } - - if (config_proto.interval_min() > UINT16_MAX || config_proto.interval_min() < 0) { - log::warn("Bad interval_min: {}", config_proto.interval_min()); - return false; - } - config->interval_min = static_cast<uint16_t>(config_proto.interval_min()); - - if (config_proto.interval_max() > UINT16_MAX || config_proto.interval_max() < 0) { - log::warn("Bad interval_max: {}", config_proto.interval_max()); - return false; - } - config->interval_max = static_cast<uint16_t>(config_proto.interval_max()); - - config->advertising_type = static_cast<hci::AdvertisingType>(config_proto.advertising_type()); - - config->requested_advertiser_address_type = - config_proto.own_address_type() == BluetoothOwnAddressTypeEnum::USE_PUBLIC_DEVICE_ADDRESS - ? AdvertiserAddressType::PUBLIC - : AdvertiserAddressType::RESOLVABLE_RANDOM; - - config->peer_address_type = - static_cast<::bluetooth::hci::PeerAddressType>(config_proto.peer_address_type()); - - hci::Address::FromString(config_proto.peer_address().address(), config->peer_address); - - if (config_proto.channel_map() > UINT8_MAX || config_proto.channel_map() < 0) { - log::warn("Bad channel_map: {}", config_proto.channel_map()); - return false; - } - config->channel_map = static_cast<uint8_t>(config_proto.channel_map()); - - if (config_proto.tx_power() > UINT8_MAX || config_proto.tx_power() < 0) { - log::warn("Bad tx_power: {}", config_proto.tx_power()); - return false; - } - - config->filter_policy = static_cast<hci::AdvertisingFilterPolicy>(config_proto.filter_policy()); - - config->tx_power = static_cast<uint8_t>(config_proto.tx_power()); - - config->legacy_pdus = true; - - auto advertising_type = - static_cast<::bluetooth::hci::AdvertisingType>(config_proto.advertising_type()); - - switch (advertising_type) { - case AdvertisingType::ADV_IND: { - config->connectable = true; - config->scannable = true; - } break; - case AdvertisingType::ADV_DIRECT_IND_HIGH: { - config->connectable = true; - config->directed = true; - config->high_duty_cycle = true; - } break; - case AdvertisingType::ADV_SCAN_IND: { - config->scannable = true; - } break; - case AdvertisingType::ADV_NONCONN_IND: { - } break; - case AdvertisingType::ADV_DIRECT_IND_LOW: { - config->directed = true; - config->connectable = true; - } break; - } - - return true; -} - -bool ExtendedAdvertisingConfigFromProto(const ExtendedAdvertisingConfig& config_proto, - hci::AdvertisingConfig* config) { - if (!AdvertisingConfigFromProto(config_proto.advertising_config(), config)) { - log::warn("Error parsing advertising config"); - return false; - } - config->connectable = config_proto.connectable(); - config->scannable = config_proto.scannable(); - config->directed = config_proto.directed(); - config->high_duty_cycle = config_proto.high_duty_directed_connectable(); - config->legacy_pdus = config_proto.legacy_pdus(); - config->anonymous = config_proto.anonymous(); - config->include_tx_power = config_proto.include_tx_power(); - config->use_le_coded_phy = config_proto.use_le_coded_phy(); - config->secondary_max_skip = static_cast<uint8_t>(config_proto.secondary_max_skip()); - config->secondary_advertising_phy = - static_cast<hci::SecondaryPhyType>(config_proto.secondary_advertising_phy()); - config->sid = static_cast<uint8_t>(config_proto.sid()); - config->enable_scan_request_notifications = - static_cast<hci::Enable>(config_proto.enable_scan_request_notifications()); - return true; -} - -bool PeriodicAdvertisingParametersFromProto(const PeriodicAdvertisingParameters& config_proto, - hci::PeriodicAdvertisingParameters* config) { - if (config_proto.min_interval() > UINT16_MAX || config_proto.min_interval() < 0) { - log::warn("Bad interval_min: {}", config_proto.min_interval()); - return false; - } - config->min_interval = static_cast<uint16_t>(config_proto.min_interval()); - if (config_proto.max_interval() > UINT16_MAX || config_proto.max_interval() < 0) { - log::warn("Bad interval_max: {}", config_proto.max_interval()); - return false; - } - config->max_interval = static_cast<uint16_t>(config_proto.max_interval()); - config->properties = static_cast<hci::PeriodicAdvertisingParameters::AdvertisingProperty>( - config_proto.advertising_property()); - return true; -} - -class LeAdvertiser { -public: - LeAdvertiser(hci::AdvertisingConfig config) : config_(std::move(config)) {} - - void ScanCallback(Address /* address */, AddressType /* address_type */) {} - - void TerminatedCallback(ErrorCode /* error_code */, uint8_t, uint8_t) {} - - hci::AdvertiserId GetAdvertiserId() { return id_; } - - void SetAdvertiserId(hci::AdvertiserId id) { id_ = id; } - -private: - hci::AdvertiserId id_ = LeAdvertisingManager::kInvalidId; - hci::AdvertisingConfig config_; -}; - -using ::blueberry::facade::hci::AddressMsg; -using ::blueberry::facade::hci::AdvertisingCallbackMsg; -using ::blueberry::facade::hci::AdvertisingCallbackMsgType; -using ::blueberry::facade::hci::AdvertisingStatus; -using ::blueberry::facade::hci::CreateAdvertiserRequest; -using ::blueberry::facade::hci::CreateAdvertiserResponse; -using ::blueberry::facade::hci::EnableAdvertiserRequest; -using ::blueberry::facade::hci::EnablePeriodicAdvertisingRequest; -using ::blueberry::facade::hci::ExtendedCreateAdvertiserRequest; -using ::blueberry::facade::hci::ExtendedCreateAdvertiserResponse; -using ::blueberry::facade::hci::GetNumberOfAdvertisingInstancesResponse; -using ::blueberry::facade::hci::GetOwnAddressRequest; -using ::blueberry::facade::hci::LeAdvertisingManagerFacade; -using ::blueberry::facade::hci::RemoveAdvertiserRequest; -using ::blueberry::facade::hci::SetDataRequest; -using ::blueberry::facade::hci::SetParametersRequest; -using ::blueberry::facade::hci::SetPeriodicDataRequest; -using ::blueberry::facade::hci::SetPeriodicParametersRequest; - -class LeAdvertisingManagerFacadeService : public LeAdvertisingManagerFacade::Service, - AdvertisingCallback { -public: - LeAdvertisingManagerFacadeService(LeAdvertisingManager* le_advertising_manager, - os::Handler* facade_handler) - : le_advertising_manager_(le_advertising_manager), facade_handler_(facade_handler) { - log::assert_that(le_advertising_manager_ != nullptr, - "assert failed: le_advertising_manager_ != nullptr"); - log::assert_that(facade_handler_ != nullptr, "assert failed: facade_handler_ != nullptr"); - le_advertising_manager_->RegisterAdvertisingCallback(this); - } - - ::grpc::Status CreateAdvertiser(::grpc::ServerContext* /* context */, - const CreateAdvertiserRequest* request, - CreateAdvertiserResponse* response) override { - hci::AdvertisingConfig config = {}; - if (!AdvertisingConfigFromProto(request->config(), &config)) { - log::warn("Error parsing advertising config {}", request->SerializeAsString()); - response->set_advertiser_id(LeAdvertisingManager::kInvalidId); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, - "Error while parsing advertising config"); - } - LeAdvertiser le_advertiser(config); - - pending_advertiser_id_ = std::promise<AdvertiserId>(); - auto future = pending_advertiser_id_->get_future(); - le_advertising_manager_->ExtendedCreateAdvertiser( - kAdvertiserClientIdJni, 0, config, - common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)), - common::Bind(&LeAdvertiser::TerminatedCallback, common::Unretained(&le_advertiser)), 0, - 0, facade_handler_); - - auto advertiser_id = future.get(); - if (advertiser_id != LeAdvertisingManager::kInvalidId) { - le_advertiser.SetAdvertiserId(advertiser_id); - le_advertisers_.push_back(le_advertiser); - } else { - log::warn("Failed to create advertiser"); - } - response->set_advertiser_id(advertiser_id); - return ::grpc::Status::OK; - } - - ::grpc::Status ExtendedCreateAdvertiser(::grpc::ServerContext* /* context */, - const ExtendedCreateAdvertiserRequest* request, - ExtendedCreateAdvertiserResponse* response) override { - hci::AdvertisingConfig config = {}; - if (!ExtendedAdvertisingConfigFromProto(request->config(), &config)) { - log::warn("Error parsing advertising config {}", request->SerializeAsString()); - response->set_advertiser_id(LeAdvertisingManager::kInvalidId); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, - "Error while parsing advertising config"); - } - LeAdvertiser le_advertiser(config); - pending_advertiser_id_ = std::promise<AdvertiserId>(); - auto future = pending_advertiser_id_->get_future(); - le_advertising_manager_->ExtendedCreateAdvertiser( - kAdvertiserClientIdJni, 0, config, - common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)), - common::Bind(&LeAdvertiser::TerminatedCallback, common::Unretained(&le_advertiser)), 0, - 0, facade_handler_); - - auto advertiser_id = future.get(); - if (advertiser_id != LeAdvertisingManager::kInvalidId) { - le_advertiser.SetAdvertiserId(advertiser_id); - le_advertisers_.push_back(le_advertiser); - } else { - log::warn("Failed to create advertiser"); - } - response->set_advertiser_id(advertiser_id); - return ::grpc::Status::OK; - } - - ::grpc::Status EnableAdvertiser(::grpc::ServerContext* /* context */, - const EnableAdvertiserRequest* request, - ::google::protobuf::Empty* /* response */) override { - le_advertising_manager_->EnableAdvertiser(request->advertiser_id(), request->enable(), 0, 0); - return ::grpc::Status::OK; - } - - ::grpc::Status SetData(::grpc::ServerContext* /* context */, const SetDataRequest* request, - ::google::protobuf::Empty* /* response */) override { - std::vector<GapData> advertising_data = {}; - for (const auto& elem : request->data()) { - advertising_data.push_back(GapDataFromProto(elem)); - } - le_advertising_manager_->SetData(request->advertiser_id(), request->set_scan_rsp(), - advertising_data); - return ::grpc::Status::OK; - } - - ::grpc::Status SetParameters(::grpc::ServerContext* /* context */, - const SetParametersRequest* request, - ::google::protobuf::Empty* /* response */) override { - hci::AdvertisingConfig config = {}; - if (!AdvertisingConfigFromProto(request->config(), &config)) { - log::warn("Error parsing advertising config {}", request->SerializeAsString()); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, - "Error while parsing advertising config"); - } - le_advertising_manager_->SetParameters(request->advertiser_id(), config); - return ::grpc::Status::OK; - } - - ::grpc::Status SetPeriodicParameters(::grpc::ServerContext* /* context */, - const SetPeriodicParametersRequest* request, - ::google::protobuf::Empty* /* response */) override { - hci::PeriodicAdvertisingParameters config = {}; - if (!PeriodicAdvertisingParametersFromProto(request->config(), &config)) { - log::warn("Error parsing periodic advertising parameters {}", request->SerializeAsString()); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, - "Error while parsing periodic advertising parameters"); - } - le_advertising_manager_->SetPeriodicParameters(request->advertiser_id(), config); - return ::grpc::Status::OK; - } - - ::grpc::Status SetPeriodicData(::grpc::ServerContext* /* context */, - const SetPeriodicDataRequest* request, - ::google::protobuf::Empty* /* response */) override { - std::vector<GapData> advertising_data = {}; - for (const auto& elem : request->data()) { - advertising_data.push_back(GapDataFromProto(elem)); - } - le_advertising_manager_->SetPeriodicData(request->advertiser_id(), advertising_data); - return ::grpc::Status::OK; - } - - ::grpc::Status EnablePeriodicAdvertising(::grpc::ServerContext* /* context */, - const EnablePeriodicAdvertisingRequest* request, - ::google::protobuf::Empty* /* response */) override { - le_advertising_manager_->EnablePeriodicAdvertising(request->advertiser_id(), request->enable(), - request->include_adi()); - return ::grpc::Status::OK; - } - - ::grpc::Status GetOwnAddress(::grpc::ServerContext* /* context */, - const GetOwnAddressRequest* request, - ::google::protobuf::Empty* /* response */) override { - le_advertising_manager_->GetOwnAddress(request->advertiser_id()); - return ::grpc::Status::OK; - } - - ::grpc::Status GetNumberOfAdvertisingInstances( - ::grpc::ServerContext* /* context */, const ::google::protobuf::Empty* /* request */, - GetNumberOfAdvertisingInstancesResponse* response) override { - response->set_num_advertising_instances( - le_advertising_manager_->GetNumberOfAdvertisingInstances()); - return ::grpc::Status::OK; - } - - ::grpc::Status RemoveAdvertiser(::grpc::ServerContext* /* context */, - const RemoveAdvertiserRequest* request, - ::google::protobuf::Empty* /* response */) override { - if (request->advertiser_id() == LeAdvertisingManager::kInvalidId) { - log::warn("Invalid advertiser ID {}", request->advertiser_id()); - return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "Invlid advertiser ID received"); - } - le_advertising_manager_->RemoveAdvertiser(request->advertiser_id()); - for (auto iter = le_advertisers_.begin(); iter != le_advertisers_.end();) { - if (iter->GetAdvertiserId() == request->advertiser_id()) { - iter = le_advertisers_.erase(iter); - } else { - ++iter; - } - } - return ::grpc::Status::OK; - } - - ::grpc::Status FetchCallbackEvents( - ::grpc::ServerContext* context, const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<AdvertisingCallbackMsg>* writer) override { - return callback_events_.RunLoop(context, writer); - } - - ::grpc::Status FetchAddressEvents(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<AddressMsg>* writer) override { - return address_events_.RunLoop(context, writer); - } - - void OnAdvertisingSetStarted(int reg_id, uint8_t advertiser_id, int8_t /* tx_power */, - AdvertisingStatus status) { - if (pending_advertiser_id_.has_value()) { - pending_advertiser_id_->set_value(advertiser_id); - pending_advertiser_id_.reset(); - } - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::ADVERTISING_SET_STARTED); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - msg.set_data(reg_id); - callback_events_.OnIncomingEvent(msg); - } - - void OnAdvertisingEnabled(uint8_t advertiser_id, bool enable, AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::ADVERTISING_ENABLED); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - msg.set_data(enable ? 1 : 0); - callback_events_.OnIncomingEvent(msg); - } - - void OnAdvertisingDataSet(uint8_t advertiser_id, AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::ADVERTISING_DATA_SET); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnScanResponseDataSet(uint8_t advertiser_id, AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::SCAN_RESPONSE_DATA_SET); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnAdvertisingParametersUpdated(uint8_t advertiser_id, int8_t /* tx_power */, - AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::ADVERTISING_PARAMETERS_UPDATED); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnPeriodicAdvertisingParametersUpdated(uint8_t advertiser_id, AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::PERIODIC_ADVERTISING_PARAMETERS_UPDATED); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnPeriodicAdvertisingDataSet(uint8_t advertiser_id, AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::PERIODIC_ADVERTISING_DATA_SET); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnPeriodicAdvertisingEnabled(uint8_t advertiser_id, bool /* enable */, - AdvertisingStatus status) { - AdvertisingCallbackMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::PERIODIC_ADVERTISING_ENABLED); - msg.set_advertiser_id(advertiser_id); - msg.set_status(static_cast<facade::AdvertisingStatus>(status)); - callback_events_.OnIncomingEvent(msg); - } - - void OnOwnAddressRead(uint8_t advertiser_id, uint8_t address_type, Address address) { - log::info("OnOwnAddressRead Address:{}, address_type:{}", address, address_type); - AddressMsg msg; - msg.set_message_type(AdvertisingCallbackMsgType::OWN_ADDRESS_READ); - msg.set_advertiser_id(advertiser_id); - blueberry::facade::BluetoothAddressWithType facade_address; - facade_address.mutable_address()->set_address(address.ToString()); - facade_address.set_type(static_cast<facade::BluetoothAddressTypeEnum>(address_type)); - *msg.mutable_address() = facade_address; - address_events_.OnIncomingEvent(msg); - } - - std::vector<LeAdvertiser> le_advertisers_; - LeAdvertisingManager* le_advertising_manager_; - std::optional<std::promise<AdvertiserId>> pending_advertiser_id_; - os::Handler* facade_handler_; - ::bluetooth::grpc::GrpcEventQueue<AdvertisingCallbackMsg> callback_events_{"callback events"}; - ::bluetooth::grpc::GrpcEventQueue<AddressMsg> address_events_{"address events"}; -}; - -void LeAdvertisingManagerFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<hci::LeAdvertisingManager>(); -} - -void LeAdvertisingManagerFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new LeAdvertisingManagerFacadeService(GetDependency<hci::LeAdvertisingManager>(), - GetHandler()); -} - -void LeAdvertisingManagerFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* LeAdvertisingManagerFacadeModule::GetService() const { return service_; } - -const ModuleFactory LeAdvertisingManagerFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new LeAdvertisingManagerFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_advertising_manager_facade.h b/system/gd/hci/facade/le_advertising_manager_facade.h deleted file mode 100644 index 99913f9a31..0000000000 --- a/system/gd/hci/facade/le_advertising_manager_facade.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class LeAdvertisingManagerFacadeService; - -class LeAdvertisingManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - - ::grpc::Service* GetService() const override; - -private: - LeAdvertisingManagerFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_initiator_address_facade.cc b/system/gd/hci/facade/le_initiator_address_facade.cc deleted file mode 100644 index dcc2da0e35..0000000000 --- a/system/gd/hci/facade/le_initiator_address_facade.cc +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2020 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/facade/le_initiator_address_facade.h" - -#include "blueberry/facade/hci/le_initiator_address_facade.grpc.pb.h" -#include "blueberry/facade/hci/le_initiator_address_facade.pb.h" -#include "hci/acl_manager.h" -#include "hci/hci_packets.h" -#include "hci/octets.h" -#include "packet/raw_builder.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -using ::bluetooth::packet::RawBuilder; - -namespace bluetooth { -namespace hci { -namespace facade { - -using namespace blueberry::facade::hci; - -class LeInitiatorAddressFacadeService : public LeInitiatorAddressFacade::Service { -public: - LeInitiatorAddressFacadeService(AclManager* acl_manager, ::bluetooth::os::Handler* facade_handler) - : acl_manager_(acl_manager), - address_manager_(acl_manager_->GetLeAddressManager()), - facade_handler_(facade_handler) { - log::assert_that(facade_handler_ != nullptr, "assert failed: facade_handler_ != nullptr"); - } - - ::grpc::Status SetPrivacyPolicyForInitiatorAddress( - ::grpc::ServerContext* /* context */, const PrivacyPolicy* request, - ::google::protobuf::Empty* /* writer */) override { - Address address = Address::kEmpty; - LeAddressManager::AddressPolicy address_policy = - static_cast<LeAddressManager::AddressPolicy>(request->address_policy()); - if (address_policy == LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS) { - log::assert_that( - Address::FromString(request->address_with_type().address().address(), address), - "assert failed: " - "Address::FromString(request->address_with_type().address().address(), " - "address)"); - } - AddressWithType address_with_type( - address, static_cast<AddressType>(request->address_with_type().type())); - auto minimum_rotation_time = std::chrono::milliseconds(request->minimum_rotation_time()); - auto maximum_rotation_time = std::chrono::milliseconds(request->maximum_rotation_time()); - Octet16 irk = {}; - auto request_irk_length = request->rotation_irk().end() - request->rotation_irk().begin(); - if (request_irk_length == kOctet16Length) { - std::vector<uint8_t> irk_data(request->rotation_irk().begin(), request->rotation_irk().end()); - std::copy_n(irk_data.begin(), kOctet16Length, irk.begin()); - acl_manager_->SetPrivacyPolicyForInitiatorAddressForTest( - address_policy, address_with_type, irk, minimum_rotation_time, maximum_rotation_time); - } else { - acl_manager_->SetPrivacyPolicyForInitiatorAddress( - address_policy, address_with_type, minimum_rotation_time, maximum_rotation_time); - log::assert_that(request_irk_length == 0, "assert failed: request_irk_length == 0"); - } - return ::grpc::Status::OK; - } - - ::grpc::Status GetCurrentInitiatorAddress( - ::grpc::ServerContext* /* context */, const ::google::protobuf::Empty* /* request */, - ::blueberry::facade::BluetoothAddressWithType* response) override { - AddressWithType current = address_manager_->GetInitiatorAddress(); - auto bluetooth_address = new ::blueberry::facade::BluetoothAddress(); - bluetooth_address->set_address(current.GetAddress().ToString()); - response->set_type( - static_cast<::blueberry::facade::BluetoothAddressTypeEnum>(current.GetAddressType())); - response->set_allocated_address(bluetooth_address); - return ::grpc::Status::OK; - } - - ::grpc::Status NewResolvableAddress( - ::grpc::ServerContext* /* context */, const ::google::protobuf::Empty* /* request */, - ::blueberry::facade::BluetoothAddressWithType* response) override { - AddressWithType another = address_manager_->NewResolvableAddress(); - auto bluetooth_address = new ::blueberry::facade::BluetoothAddress(); - bluetooth_address->set_address(another.GetAddress().ToString()); - response->set_type( - static_cast<::blueberry::facade::BluetoothAddressTypeEnum>(another.GetAddressType())); - response->set_allocated_address(bluetooth_address); - return ::grpc::Status::OK; - } - -private: - AclManager* acl_manager_; - LeAddressManager* address_manager_; - ::bluetooth::os::Handler* facade_handler_; -}; - -void LeInitiatorAddressFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<AclManager>(); -} - -void LeInitiatorAddressFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new LeInitiatorAddressFacadeService(GetDependency<AclManager>(), GetHandler()); -} - -void LeInitiatorAddressFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* LeInitiatorAddressFacadeModule::GetService() const { return service_; } - -const ModuleFactory LeInitiatorAddressFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new LeInitiatorAddressFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_initiator_address_facade.h b/system/gd/hci/facade/le_initiator_address_facade.h deleted file mode 100644 index 3952b14146..0000000000 --- a/system/gd/hci/facade/le_initiator_address_facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "hci/acl_manager.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class LeInitiatorAddressFacadeService; - -class LeInitiatorAddressFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - LeInitiatorAddressFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_scanning_manager_facade.cc b/system/gd/hci/facade/le_scanning_manager_facade.cc deleted file mode 100644 index 243b46ff4e..0000000000 --- a/system/gd/hci/facade/le_scanning_manager_facade.cc +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2019 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/facade/le_scanning_manager_facade.h" - -#include <bluetooth/log.h> - -#include <cstdint> -#include <unordered_map> -#include <utility> - -#include "blueberry/facade/hci/le_scanning_manager_facade.grpc.pb.h" -#include "blueberry/facade/hci/le_scanning_manager_facade.pb.h" -#include "common/bidi_queue.h" -#include "common/bind.h" -#include "grpc/grpc_event_queue.h" -#include "hci/le_scanning_manager.h" -#include "packet/raw_builder.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; -using ::grpc::ServerWriter; -using ::grpc::Status; - -using namespace blueberry::facade::hci; - -class LeScanningManagerFacadeService : public LeScanningManagerFacade::Service, ScanningCallback { -public: - LeScanningManagerFacadeService(LeScanningManager* le_scanning_manager, - os::Handler* facade_handler) - : le_scanning_manager_(le_scanning_manager), facade_handler_(facade_handler) { - log::assert_that(le_scanning_manager_ != nullptr, - "assert failed: le_scanning_manager_ != nullptr"); - log::assert_that(facade_handler_ != nullptr, "assert failed: facade_handler_ != nullptr"); - le_scanning_manager_->RegisterScanningCallback(this); - } - - ::grpc::Status RegisterScanner(::grpc::ServerContext* /* context */, - const RegisterScannerRequest* request, - ::google::protobuf::Empty* /* response */) override { - uint32_t uuid_raw = request->uuid(); - bluetooth::hci::Uuid uuid = bluetooth::hci::Uuid::From32Bit(uuid_raw); - le_scanning_manager_->RegisterScanner(uuid); - return ::grpc::Status::OK; - } - - ::grpc::Status Unregister(::grpc::ServerContext* /* context */, const UnregisterRequest* request, - ::google::protobuf::Empty* /* response */) override { - le_scanning_manager_->Unregister(request->scanner_id()); - return ::grpc::Status::OK; - } - - ::grpc::Status Scan(::grpc::ServerContext* /* context */, const ScanRequest* request, - ::google::protobuf::Empty* /* response */) override { - le_scanning_manager_->Scan(request->start()); - return ::grpc::Status::OK; - } - - ::grpc::Status SetScanParameters(::grpc::ServerContext* /* context */, - const SetScanParametersRequest* request, - ::google::protobuf::Empty* /* response */) override { - auto scan_type = static_cast<hci::LeScanType>(request->scan_type()); - le_scanning_manager_->SetScanParameters(request->scanner_id(), scan_type, - request->scan_interval(), request->scan_window(), - request->scan_phy()); - return ::grpc::Status::OK; - } - - ::grpc::Status FetchCallbackEvents(::grpc::ServerContext* context, - const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<ScanningCallbackMsg>* writer) override { - return callback_events_.RunLoop(context, writer); - } - - ::grpc::Status FetchAdvertisingReports( - ::grpc::ServerContext* context, const ::google::protobuf::Empty* /* request */, - ::grpc::ServerWriter<AdvertisingReportMsg>* writer) override { - return advertising_reports_.RunLoop(context, writer); - } - - void OnScannerRegistered(const bluetooth::hci::Uuid app_uuid, ScannerId /* scanner_id */, - ScanningStatus status) { - ScanningCallbackMsg msg; - msg.set_message_type(ScanningCallbackMsgType::SCANNER_REGISTERED); - msg.set_status(static_cast<facade::ScanningStatus>(status)); - msg.set_data(app_uuid.As32Bit()); - callback_events_.OnIncomingEvent(msg); - } - - void OnSetScannerParameterComplete(ScannerId scanner_id, ScanningStatus status) { - ScanningCallbackMsg msg; - msg.set_message_type(ScanningCallbackMsgType::SET_SCANNER_PARAMETER_COMPLETE); - msg.set_status(static_cast<facade::ScanningStatus>(status)); - msg.set_data(static_cast<uint32_t>(scanner_id)); - callback_events_.OnIncomingEvent(msg); - } - - void OnScanResult(uint16_t /* event_type */, uint8_t address_type, Address address, - uint8_t /* primary_phy */, uint8_t /* secondary_phy */, - uint8_t /* advertising_sid */, int8_t /* tx_power */, int8_t rssi, - uint16_t /* periodic_advertising_interval */, - std::vector<uint8_t> advertising_data) { - AdvertisingReportMsg advertising_report_msg; - std::vector<LeExtendedAdvertisingResponseRaw> advertisements; - LeExtendedAdvertisingResponseRaw le_extended_advertising_report; - le_extended_advertising_report.address_type_ = (DirectAdvertisingAddressType)address_type; - le_extended_advertising_report.address_ = address; - le_extended_advertising_report.advertising_data_ = advertising_data; - le_extended_advertising_report.rssi_ = rssi; - advertisements.push_back(le_extended_advertising_report); - - auto builder = LeExtendedAdvertisingReportRawBuilder::Create(advertisements); - std::vector<uint8_t> bytes; - BitInserter bit_inserter(bytes); - builder->Serialize(bit_inserter); - advertising_report_msg.set_event(std::string(bytes.begin(), bytes.end())); - advertising_reports_.OnIncomingEvent(std::move(advertising_report_msg)); - } - void OnTrackAdvFoundLost(AdvertisingFilterOnFoundOnLostInfo /* on_found_on_lost_info */){}; - void OnBatchScanReports(int /* client_if */, int /* status */, int /* report_format */, - int /* num_records */, std::vector<uint8_t> /* data */) {} - void OnBatchScanThresholdCrossed(int /* client_if */) {} - void OnTimeout() {} - void OnFilterEnable(Enable /* enable */, uint8_t /* status */) {} - void OnFilterParamSetup(uint8_t /* available_spaces */, ApcfAction /* action */, - uint8_t /* status */) {} - void OnFilterConfigCallback(ApcfFilterType /* filter_type */, uint8_t /* available_spaces */, - ApcfAction /* action */, uint8_t /* status */) {} - - void OnPeriodicSyncStarted(int /* reg_id */, uint8_t /* status */, uint16_t /* sync_handle */, - uint8_t /* advertising_sid */, AddressWithType /* address_with_type */, - uint8_t /* phy */, uint16_t /* interval */) override { - log::info("OnPeriodicSyncStarted in LeScanningManagerFacadeService"); - } - - void OnPeriodicSyncReport(uint16_t /* sync_handle */, int8_t /* tx_power */, int8_t /* rssi */, - uint8_t /* status */, std::vector<uint8_t> /* data */) override { - log::info("OnPeriodicSyncReport in LeScanningManagerFacadeService"); - } - - void OnPeriodicSyncLost(uint16_t /* sync_handle */) override { - log::info("OnPeriodicSyncLost in LeScanningManagerFacadeService"); - } - - void OnPeriodicSyncTransferred(int /* pa_source */, uint8_t /* status */, - Address /* address */) override { - log::info("OnPeriodicSyncTransferred in LeScanningManagerFacadeService"); - } - - void OnBigInfoReport(uint16_t /* sync_handle */, bool /* encrypted */) override { - log::info("OnBigInfoReport in LeScanningManagerFacadeService"); - } - - LeScanningManager* le_scanning_manager_; - os::Handler* facade_handler_; - ::bluetooth::grpc::GrpcEventQueue<AdvertisingReportMsg> advertising_reports_{ - "advertising reports"}; - ::bluetooth::grpc::GrpcEventQueue<ScanningCallbackMsg> callback_events_{"callback events"}; -}; - -void LeScanningManagerFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<hci::LeScanningManager>(); -} - -void LeScanningManagerFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = - new LeScanningManagerFacadeService(GetDependency<hci::LeScanningManager>(), GetHandler()); -} - -void LeScanningManagerFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* LeScanningManagerFacadeModule::GetService() const { return service_; } - -const ModuleFactory LeScanningManagerFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new LeScanningManagerFacadeModule(); }); - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/facade/le_scanning_manager_facade.h b/system/gd/hci/facade/le_scanning_manager_facade.h deleted file mode 100644 index a3e68315d8..0000000000 --- a/system/gd/hci/facade/le_scanning_manager_facade.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" - -namespace bluetooth { -namespace hci { -namespace facade { - -class LeScanningManagerFacadeService; - -class LeScanningManagerFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - - ::grpc::Service* GetService() const override; - -private: - LeScanningManagerFacadeService* service_; -}; - -} // namespace facade -} // namespace hci -} // namespace bluetooth diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc index 256276e95f..bddbcaf4d1 100644 --- a/system/gd/hci/le_address_manager.cc +++ b/system/gd/hci/le_address_manager.cc @@ -157,13 +157,11 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddress( log::info("minimum_rotation_time_={}ms, maximum_rotation_time_={}ms", minimum_rotation_time_.count(), maximum_rotation_time_.count()); } - if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() && - controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) { + if (controller_->IsRpaGenerationSupported()) { auto min_seconds = std::chrono::duration_cast<std::chrono::seconds>(minimum_rotation_time_); auto max_seconds = std::chrono::duration_cast<std::chrono::seconds>(maximum_rotation_time_); - log::info( - "Support RPA offload, set min_seconds={}s, max_seconds={}s", - min_seconds.count(), max_seconds.count()); + log::info("Support RPA offload, set min_seconds={}s, max_seconds={}s", min_seconds.count(), + max_seconds.count()); /* Default to 7 minutes minimum, 15 minutes maximum for random address refreshing; * device can override. */ auto packet = hci::LeSetResolvablePrivateAddressTimeoutV2Builder::Create( @@ -223,13 +221,11 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddressForTest( maximum_rotation_time_ = maximum_rotation_time; log::info("minimum_rotation_time_={}ms, maximum_rotation_time_={}ms", minimum_rotation_time_.count(), maximum_rotation_time_.count()); - if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() && - controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) { + if (controller_->IsRpaGenerationSupported()) { auto min_seconds = std::chrono::duration_cast<std::chrono::seconds>(minimum_rotation_time_); auto max_seconds = std::chrono::duration_cast<std::chrono::seconds>(maximum_rotation_time_); - log::info( - "Support RPA offload, set min_seconds={}s, max_seconds={}s", - min_seconds.count(), max_seconds.count()); + log::info("Support RPA offload, set min_seconds={}s, max_seconds={}s", min_seconds.count(), + max_seconds.count()); /* Default to 7 minutes minimum, 15 minutes maximum for random address refreshing; * device can override. */ auto packet = hci::LeSetResolvablePrivateAddressTimeoutV2Builder::Create( @@ -269,8 +265,7 @@ void LeAddressManager::register_client(LeAddressManagerCallback* callback) { } else if (address_policy_ == AddressPolicy::USE_RESOLVABLE_ADDRESS || address_policy_ == AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) { if (registered_clients_.size() == 1) { - if (!com::android::bluetooth::flags::rpa_offload_to_bt_controller() || - !controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) { + if (!controller_->IsRpaGenerationSupported()) { schedule_rotate_random_address(); log::info("Scheduled address rotation for first client registered"); } diff --git a/system/gd/hci/le_address_manager_test.cc b/system/gd/hci/le_address_manager_test.cc index 7e5c679ff1..33efcb51a2 100644 --- a/system/gd/hci/le_address_manager_test.cc +++ b/system/gd/hci/le_address_manager_test.cc @@ -18,9 +18,9 @@ #include <gtest/gtest.h> +#include "hci/controller.h" #include "hci/hci_layer_fake.h" #include "hci/octets.h" -#include "hci/controller.h" #include "packet/raw_builder.h" using ::bluetooth::hci::Octet16; diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index e40b253f6a..5213569243 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -658,9 +658,7 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb // but we only rotate if the AdvertiserAddressType is non-public // or non-rpa requested by leaudio(since static random addresses don't rotate) if (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC && - !leaudio_requested_nrpa && - (!com::android::bluetooth::flags::rpa_offload_to_bt_controller() || - !controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2))) { + !leaudio_requested_nrpa && (!controller_->IsRpaGenerationSupported())) { // start timer for random address log::info("Scheduling address rotation for advertiser_id={}", id); if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { @@ -906,8 +904,7 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb auto own_address_type = static_cast<OwnAddressType>( advertising_sets_[advertiser_id].current_address.GetAddressType()); - if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() && - controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2) && + if (controller_->IsRpaGenerationSupported() && own_address_type != OwnAddressType::PUBLIC_DEVICE_ADDRESS) { log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS"); own_address_type = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS; @@ -1142,11 +1139,9 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb data_len += data[i].size(); } - int maxDataLength = - (com::android::bluetooth::flags::ble_check_data_length_on_legacy_advertising() && - advertising_sets_[advertiser_id].is_legacy) - ? kLeMaximumLegacyAdvertisingDataLength - : le_maximum_advertising_data_length_; + int maxDataLength = advertising_sets_[advertiser_id].is_legacy + ? kLeMaximumLegacyAdvertisingDataLength + : le_maximum_advertising_data_length_; if (data_len > maxDataLength) { log::warn("advertising data len {} exceeds maxDataLength {}", data_len, maxDataLength); diff --git a/system/gd/hci/le_scanning_manager.cc b/system/gd/hci/le_scanning_manager.cc index 5934cf7047..bba3815048 100644 --- a/system/gd/hci/le_scanning_manager.cc +++ b/system/gd/hci/le_scanning_manager.cc @@ -466,8 +466,7 @@ struct LeScanningManager::impl : public LeAddressManagerCallback { stop_scan(); if (le_address_manager_->GetAddressPolicy() != LeAddressManager::USE_PUBLIC_ADDRESS) { - if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() && - controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) { + if (controller_->IsRpaGenerationSupported()) { log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS"); own_address_type_ = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS; } else { @@ -1578,8 +1577,14 @@ struct LeScanningManager::impl : public LeAddressManagerCallback { return; } paused_ = false; - if (scan_on_resume_ == true) { + if (scan_on_resume_) { scan_on_resume_ = false; + if (com::android::bluetooth::flags::configure_scan_on_resume()) { + // This is a workaround for b/381010390. + // We'll eventually recover scan parameters which could be overridden by + // btm_send_hci_set_scan_params. + configure_scan(); + } start_scan(); } le_address_manager_->AckResume(this); diff --git a/system/gd/hci/le_scanning_manager_test.cc b/system/gd/hci/le_scanning_manager_test.cc index 4176e857b8..44269651ce 100644 --- a/system/gd/hci/le_scanning_manager_test.cc +++ b/system/gd/hci/le_scanning_manager_test.cc @@ -828,6 +828,11 @@ TEST_F(LeScanningManagerExtendedTest, on_pause_on_resume_test) { // Ensure scan is resumed (enabled) test_le_address_manager->client_->OnResume(); + if (com::android::bluetooth::flags::configure_scan_on_resume()) { + ASSERT_EQ(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS, test_hci_layer_->GetCommand().GetOpCode()); + test_hci_layer_->IncomingEvent( + LeSetExtendedScanParametersCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS)); + } ASSERT_EQ(OpCode::LE_SET_EXTENDED_SCAN_ENABLE, test_hci_layer_->GetCommand().GetOpCode()); test_hci_layer_->IncomingEvent( LeSetExtendedScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS)); diff --git a/system/gd/shim/Android.bp b/system/gd/lpp/Android.bp index c2b1d8b68e..2be7b85d9e 100644 --- a/system/gd/shim/Android.bp +++ b/system/gd/lpp/Android.bp @@ -8,15 +8,8 @@ package { } filegroup { - name: "BluetoothShimSources", + name: "BluetoothLppOffloadSources", srcs: [ - "dumpsys.cc", - ], -} - -filegroup { - name: "BluetoothShimTestSources", - srcs: [ - "dumpsys_test.cc", + "lpp_offload_manager.cc", ], } diff --git a/system/gd/shim/BUILD.gn b/system/gd/lpp/BUILD.gn index d30e609c2f..b773001ce8 100644 --- a/system/gd/shim/BUILD.gn +++ b/system/gd/lpp/BUILD.gn @@ -1,5 +1,5 @@ # -# Copyright 2021 Google, Inc. +# Copyright 2024 Google, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,20 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -source_set("BluetoothShimSources") { +source_set("BluetoothLppOffloadSources") { sources = [ - "dumpsys.cc", + "lpp_offload_manager.cc", ] + include_dirs = [ "//bt/system/gd" ] + deps = [ - "//bt/flags:bluetooth_flags_c_lib", - "//bt/system/gd/dumpsys:libbluetooth-dumpsys", - "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs", - "//bt/system/pdl:BluetoothGeneratedPackets_h", + "//bt/system/gd:gd_default_deps", ] configs += [ - "//bt/system/gd:gd_defaults", - "//bt/system:external_flatbuffers", + "//bt/system:target_defaults", + "//bt/system/log:log_defaults", ] } diff --git a/system/gd/lpp/lpp_offload_interface.h b/system/gd/lpp/lpp_offload_interface.h new file mode 100644 index 0000000000..388466f2e5 --- /dev/null +++ b/system/gd/lpp/lpp_offload_interface.h @@ -0,0 +1,75 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "hal/socket_hal.h" + +namespace bluetooth::lpp { + +/** + * Interface to low-power processors (LPPs) for supporting LPP offload features. + * + * This interface allows inheritance from multiple offload HAL interfaces, enabling a unified + * offload function management approach through a single interface accessible from the upper layer. + */ +class LppOffloadInterface { +public: + LppOffloadInterface() = default; + + virtual ~LppOffloadInterface() = default; + + LppOffloadInterface(const LppOffloadInterface&) = delete; + + LppOffloadInterface& operator=(const LppOffloadInterface&) = delete; + + /** + * Registers a socket hal callback function to receive asynchronous events from socket HAL. + * + * The provided callback function must be executed on the main thread. + * + * @param callback A pointer to the callback function. Must not be nullptr and must have static + * lifetime. + * @return True if the callback was successfully registered, false otherwise. + */ + virtual bool RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) = 0; + + /** + * Retrieves the supported offload socket capabilities. + * + * @return Supported socket capabilities + */ + virtual hal::SocketCapabilities GetSocketCapabilities() const = 0; + + /** + * Notifies the socket HAL that the socket has been opened. + * + * If this method returns true, SocketHalCallback.SocketOpenedComplete() shall be called to + * indicate the result of this operation. + * + * @param context Socket context including socket ID, channel, hub, and endpoint info + * @return True if calling this method was successful, false otherwise + */ + virtual bool SocketOpened(const hal::SocketContext& context) = 0; + + /** + * Notifies the socket HAL that the socket has been closed. + * + * @param socket_id Identifier assigned to the socket by the host stack + */ + virtual void SocketClosed(uint64_t socket_id) = 0; +}; + +} // namespace bluetooth::lpp diff --git a/system/gd/facade/grpc_root_server.h b/system/gd/lpp/lpp_offload_interface_mock.h index 062ddcf150..03cd5f4f52 100644 --- a/system/gd/facade/grpc_root_server.h +++ b/system/gd/lpp/lpp_offload_interface_mock.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,32 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #pragma once -#include <grpc++/grpc++.h> +#include <gmock/gmock.h> -#include <memory> -#include <string> +#include "lpp/lpp_offload_interface.h" -namespace bluetooth { -namespace facade { +// Unit test interfaces +namespace bluetooth::lpp::testing { -class GrpcRootServer { +class MockLppOffloadInterface : public LppOffloadInterface { public: - GrpcRootServer(); - ~GrpcRootServer(); - - void StartServer(const std::string& address, int grpc_root_server_port, int grpc_port); - - void StopServer(); - - void RunGrpcLoop(); - -private: - struct impl; - std::unique_ptr<impl> pimpl_; + MOCK_METHOD(bool, RegisterSocketHalCallback, (hal::SocketHalCallback*), (override)); + MOCK_METHOD(hal::SocketCapabilities, GetSocketCapabilities, (), (const override)); + MOCK_METHOD(bool, SocketOpened, (const hal::SocketContext&), (override)); + MOCK_METHOD(void, SocketClosed, (uint64_t), (override)); }; -} // namespace facade -} // namespace bluetooth +} // namespace bluetooth::lpp::testing diff --git a/system/gd/lpp/lpp_offload_manager.cc b/system/gd/lpp/lpp_offload_manager.cc new file mode 100644 index 0000000000..c5c1fb247e --- /dev/null +++ b/system/gd/lpp/lpp_offload_manager.cc @@ -0,0 +1,100 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "lpp_offload_manager.h" + +#include <bluetooth/log.h> + +#include <string> + +#include "hal/socket_hal.h" +#include "module.h" +#include "os/handler.h" +#include "os/system_properties.h" + +namespace bluetooth::lpp { + +const ModuleFactory LppOffloadManager::Factory = + ModuleFactory([]() { return new LppOffloadManager(); }); + +struct LppOffloadManager::impl { + ~impl() {} + + void start(os::Handler* handler, hal::SocketHal* socket_hal) { + log::info(""); + handler_ = handler; + socket_hal_ = socket_hal; + socket_capabilities_ = socket_hal_->GetSocketCapabilities(); + } + + void stop() { + log::info(""); + socket_capabilities_ = {}; + } + + bool register_socket_hal_callbacks(hal::SocketHalCallback* callbacks) { + log::info(""); + return socket_hal_->RegisterCallback(callbacks); + } + + hal::SocketCapabilities get_socket_capabilities() const { + log::info(""); + return socket_capabilities_; + } + + bool socket_opened(const hal::SocketContext& context) { + log::info("socket_id: {}", context.socket_id); + return socket_hal_->Opened(context); + } + + void socket_closed(uint64_t socket_id) { + log::info("socket_id: {}", socket_id); + return socket_hal_->Closed(socket_id); + } + + os::Handler* handler_; + hal::SocketHal* socket_hal_; + hal::SocketCapabilities socket_capabilities_; +}; + +LppOffloadManager::LppOffloadManager() { pimpl_ = std::make_unique<impl>(); } + +LppOffloadManager::~LppOffloadManager() = default; + +void LppOffloadManager::ListDependencies(ModuleList* list) const { list->add<hal::SocketHal>(); } + +void LppOffloadManager::Start() { pimpl_->start(GetHandler(), GetDependency<hal::SocketHal>()); } + +void LppOffloadManager::Stop() { pimpl_->stop(); } + +std::string LppOffloadManager::ToString() const { return "Low Power Processor Offload Manager"; } + +bool LppOffloadManager::RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) { + return pimpl_->register_socket_hal_callbacks(callbacks); +} + +hal::SocketCapabilities LppOffloadManager::GetSocketCapabilities() const { + return pimpl_->get_socket_capabilities(); +} + +bool LppOffloadManager::SocketOpened(const hal::SocketContext& context) { + return pimpl_->socket_opened(context); +} + +void LppOffloadManager::SocketClosed(uint64_t socket_id) { + CallOn(pimpl_.get(), &impl::socket_closed, socket_id); +} + +} // namespace bluetooth::lpp diff --git a/system/gd/lpp/lpp_offload_manager.h b/system/gd/lpp/lpp_offload_manager.h new file mode 100644 index 0000000000..90097f77bd --- /dev/null +++ b/system/gd/lpp/lpp_offload_manager.h @@ -0,0 +1,62 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <bluetooth/log.h> + +#include <memory> +#include <string> + +#include "lpp_offload_interface.h" +#include "module.h" + +namespace bluetooth::lpp { + +class LppOffloadManager : public bluetooth::Module, public LppOffloadInterface { +public: + LppOffloadManager(); + + LppOffloadManager(const LppOffloadManager&) = delete; + + LppOffloadManager& operator=(const LppOffloadManager&) = delete; + + ~LppOffloadManager(); + + bool RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) override; + + hal::SocketCapabilities GetSocketCapabilities() const override; + + bool SocketOpened(const hal::SocketContext& context) override; + + void SocketClosed(uint64_t socket_id) override; + + static const ModuleFactory Factory; + +protected: + void ListDependencies(ModuleList* list) const override; + + void Start() override; + + void Stop() override; + + std::string ToString() const override; + +private: + struct impl; + std::unique_ptr<impl> pimpl_; +}; + +} // namespace bluetooth::lpp diff --git a/system/gd/metrics/bluetooth_event.h b/system/gd/metrics/bluetooth_event.h index 89fc1384a5..acdb7a84f6 100644 --- a/system/gd/metrics/bluetooth_event.h +++ b/system/gd/metrics/bluetooth_event.h @@ -19,6 +19,7 @@ #include "bta/include/bta_sec_api.h" #include "hci/address.h" #include "hci/hci_packets.h" +#include "os/metrics.h" #include "stack/include/btm_status.h" #include "stack/include/hci_error_code.h" #include "types/raw_address.h" @@ -47,5 +48,7 @@ void LogSDPComplete(const RawAddress& raw_address, tBTA_STATUS status); void LogLePairingFail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing); +android::bluetooth::State MapErrorCodeToState(hci::ErrorCode reason); + } // namespace metrics } // namespace bluetooth diff --git a/system/gd/metrics/chromeos/metrics_event.cc b/system/gd/metrics/chromeos/metrics_event.cc index 806db26bfe..3715dcec71 100644 --- a/system/gd/metrics/chromeos/metrics_event.cc +++ b/system/gd/metrics/chromeos/metrics_event.cc @@ -21,7 +21,6 @@ #include <base/strings/pattern.h> #include <base/strings/string_number_conversions.h> #include <base/strings/string_util.h> -#include <base/strings/stringprintf.h> #include <map> #include <utility> @@ -42,8 +41,8 @@ namespace { // these consts path below are for getting the chipset info constexpr char kChipsetInfoWlanDirPath[] = "/sys/class/net/wlan0/device"; constexpr char kChipsetInfoMlanDirPath[] = "/sys/class/net/mlan0/device"; -constexpr char kChipsetInfoModaliasPath[] = "/sys/class/bluetooth/hci%d/device/modalias"; -constexpr char kChipInfoModuleDirPath[] = "/sys/class/bluetooth/hci%d/device/driver/module"; +constexpr char kChipsetInfoModaliasPath[] = "/sys/class/bluetooth/hci{}/device/modalias"; +constexpr char kChipInfoModuleDirPath[] = "/sys/class/bluetooth/hci{}/device/driver/module"; } // namespace // topshim::btif::BtBondState is a copy of hardware/bluetooth.h:bt_bond_state_t @@ -676,7 +675,7 @@ static int64_t GetChipsetInfoId(const char* path, const char* file) { static std::string GetChipsetInfoModuleName() { std::string module; int adapter_index = GetAdapterIndex(); - std::string path = base::StringPrintf(kChipsetInfoModaliasPath, adapter_index); + std::string path = std::format(kChipsetInfoModaliasPath, adapter_index); if (base::ReadFileToString(base::FilePath(path), &module)) { return base::CollapseWhitespaceASCII(module, false); @@ -689,7 +688,7 @@ static MetricTransportType GetChipsetInfoTransport(void) { base::FilePath module_realpath; std::string module_name; int adapter_index = GetAdapterIndex(); - std::string path = base::StringPrintf(kChipInfoModuleDirPath, adapter_index); + std::string path = std::format(kChipInfoModuleDirPath, adapter_index); // examples of module_realpath: /sys/module/btusb and /sys/module/hci_uart module_realpath = base::MakeAbsoluteFilePath(base::FilePath(path)); diff --git a/system/gd/module.cc b/system/gd/module.cc index 5a2cdf142c..97f6132f31 100644 --- a/system/gd/module.cc +++ b/system/gd/module.cc @@ -45,13 +45,6 @@ Module* Module::GetDependency(const ModuleFactory* module) const { log::fatal("Module was not listed as a dependency in ListDependencies"); } -bluetooth::DumpsysDataFinisher EmptyDumpsysDataFinisher = - [](bluetooth::DumpsysDataBuilder* /* dumpsys_data_builder */) {}; - -DumpsysDataFinisher Module::GetDumpsysData(flatbuffers::FlatBufferBuilder* /* builder */) const { - return EmptyDumpsysDataFinisher; -} - Module* ModuleRegistry::Get(const ModuleFactory* module) const { auto instance = started_modules_.find(module); log::assert_that(instance != started_modules_.end(), diff --git a/system/gd/module.h b/system/gd/module.h index 44aa0bcb29..e708035a77 100644 --- a/system/gd/module.h +++ b/system/gd/module.h @@ -17,7 +17,6 @@ #pragma once #include <bluetooth/log.h> -#include <flatbuffers/flatbuffers.h> #include <chrono> #include <functional> @@ -33,9 +32,11 @@ #include "os/thread.h" namespace bluetooth { +namespace shim { +class Stack; +} // namespace shim class Module; -class ModuleDumper; class ModuleRegistry; class TestModuleRegistry; class FuzzTestModuleRegistry; @@ -68,11 +69,6 @@ private: std::vector<const ModuleFactory*> list_; }; -struct DumpsysDataBuilder; -using DumpsysDataFinisher = std::function<void(DumpsysDataBuilder*)>; - -extern DumpsysDataFinisher EmptyDumpsysDataFinisher; - // Each leaf node module must have a factory like so: // // static const ModuleFactory Factory; @@ -81,7 +77,6 @@ extern DumpsysDataFinisher EmptyDumpsysDataFinisher; // The module registry will also use the factory as the identifier // for that module. class Module { - friend ModuleDumper; friend ModuleRegistry; friend TestModuleRegistry; @@ -120,8 +115,6 @@ protected: GetHandler()->CallOn(obj, std::forward<Functor>(functor), std::forward<Args>(args)...); } - virtual DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const; - private: Module* GetDependency(const ModuleFactory* module) const; @@ -132,8 +125,7 @@ private: class ModuleRegistry { friend Module; - friend ModuleDumper; - friend class StackManager; + friend shim::Stack; public: template <class T> diff --git a/system/gd/module_dumper.cc b/system/gd/module_dumper.cc deleted file mode 100644 index 23bb6aa8f2..0000000000 --- a/system/gd/module_dumper.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ -#define LOG_TAG "BtGdModule" - -#include "module_dumper.h" - -#include <sstream> - -#include "dumpsys_data_generated.h" -#include "module.h" -#include "os/wakelock_manager.h" - -using ::bluetooth::os::WakelockManager; - -namespace bluetooth { - -void ModuleDumper::DumpState(std::string* output, std::ostringstream& /*oss*/) const { - log::assert_that(output != nullptr, "assert failed: output != nullptr"); - - flatbuffers::FlatBufferBuilder builder(1024); - auto title = builder.CreateString(title_); - - auto wakelock_offset = WakelockManager::Get().GetDumpsysData(&builder); - - std::queue<DumpsysDataFinisher> queue; - for (auto it = module_registry_.start_order_.rbegin(); it != module_registry_.start_order_.rend(); - it++) { - auto instance = module_registry_.started_modules_.find(*it); - log::assert_that(instance != module_registry_.started_modules_.end(), - "assert failed: instance != module_registry_.started_modules_.end()"); - log::verbose("Starting dumpsys module:{}", instance->second->ToString()); - queue.push(instance->second->GetDumpsysData(&builder)); - log::verbose("Finished dumpsys module:{}", instance->second->ToString()); - } - - DumpsysDataBuilder data_builder(builder); - data_builder.add_title(title); - data_builder.add_wakelock_manager_data(wakelock_offset); - - while (!queue.empty()) { - queue.front()(&data_builder); - queue.pop(); - } - - builder.Finish(data_builder.Finish()); - *output = std::string(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); -} - -} // namespace bluetooth diff --git a/system/gd/module_dumper.h b/system/gd/module_dumper.h deleted file mode 100644 index d1953a224d..0000000000 --- a/system/gd/module_dumper.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019 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 <sstream> -#include <string> - -#include "module.h" - -namespace bluetooth { - -class ModuleRegistry; - -class ModuleDumper { -public: - ModuleDumper(int /*fd*/, const ModuleRegistry& module_registry, const char* title) - : module_registry_(module_registry), title_(title) {} - void DumpState(std::string* output, std::ostringstream& oss) const; - -private: - const ModuleRegistry& module_registry_; - const std::string title_; -}; - -} // namespace bluetooth diff --git a/system/gd/module_dumper_flatbuffer.h b/system/gd/module_dumper_flatbuffer.h deleted file mode 100644 index 21b19a7291..0000000000 --- a/system/gd/module_dumper_flatbuffer.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <functional> - -namespace bluetooth { - -struct DumpsysDataBuilder; -using DumpsysDataFinisher = std::function<void(DumpsysDataBuilder* dumpsys_data_builder)>; - -extern DumpsysDataFinisher EmptyDumpsysDataFinisher; - -} // namespace bluetooth diff --git a/system/gd/module_unittest.cc b/system/gd/module_unittest.cc index 123cd3e469..24ccc8b3d2 100644 --- a/system/gd/module_unittest.cc +++ b/system/gd/module_unittest.cc @@ -22,10 +22,7 @@ #include <sstream> #include <string> -#include "dumpsys_data_generated.h" #include "gtest/gtest.h" -#include "module_dumper.h" -#include "module_unittest_generated.h" #include "os/handler.h" #include "os/thread.h" @@ -161,48 +158,6 @@ protected: const ModuleFactory TestModuleTwoDependencies::Factory = ModuleFactory([]() { return new TestModuleTwoDependencies(); }); -// To generate module unittest flatbuffer headers: -// $ flatc --cpp module_unittest.fbs -class TestModuleDumpState : public Module { -public: - static const ModuleFactory Factory; - - std::string test_string_{"Initial Test String"}; - -protected: - void ListDependencies(ModuleList* list) const { list->add<TestModuleNoDependency>(); } - - void Start() override { - EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>()); - - // A module is not considered started until Start() finishes - EXPECT_FALSE(GetModuleRegistry()->IsStarted<TestModuleDumpState>()); - test_module_one_dependency_handler = GetHandler(); - } - - void Stop() override { - EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleNoDependency>()); - - // A module is not considered stopped until after Stop() finishes - EXPECT_TRUE(GetModuleRegistry()->IsStarted<TestModuleDumpState>()); - } - - std::string ToString() const override { return std::string("TestModuleDumpState"); } - - DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) const override { - auto string = fb_builder->CreateString(test_string_.c_str()); - - auto builder = ModuleUnitTestDataBuilder(*fb_builder); - builder.add_title(string); - auto table = builder.Finish(); - - return [table](DumpsysDataBuilder* builder) { builder->add_module_unittest_data(table); }; - } -}; - -const ModuleFactory TestModuleDumpState::Factory = - ModuleFactory([]() { return new TestModuleDumpState(); }); - TEST_F(ModuleTest, no_dependency) { ModuleList list; list.add<TestModuleNoDependency>(); @@ -270,38 +225,5 @@ TEST_F(ModuleTest, shutdown_with_unhandled_callback) { registry_->StopAll(); } -TEST_F(ModuleTest, dump_state) { - static const char* title = "Test Dump Title"; - ModuleList list; - list.add<TestModuleDumpState>(); - registry_->Start(&list, thread_); - - ModuleDumper dumper(STDOUT_FILENO, *registry_, title); - - std::string output; - std::ostringstream oss; - dumper.DumpState(&output, oss); - - auto data = flatbuffers::GetRoot<DumpsysData>(output.data()); - EXPECT_STREQ(title, data->title()->c_str()); - - auto test_data = data->module_unittest_data(); - EXPECT_STREQ("Initial Test String", test_data->title()->c_str()); - - TestModuleDumpState* test_module = static_cast<TestModuleDumpState*>( - registry_->Start(&TestModuleDumpState::Factory, nullptr)); - test_module->test_string_ = "A Second Test String"; - - oss.clear(); - output.clear(); - dumper.DumpState(&output, oss); - - data = flatbuffers::GetRoot<DumpsysData>(output.data()); - test_data = data->module_unittest_data(); - EXPECT_STREQ("A Second Test String", test_data->title()->c_str()); - - registry_->StopAll(); -} - } // namespace } // namespace bluetooth diff --git a/system/gd/module_unittest.fbs b/system/gd/module_unittest.fbs deleted file mode 100644 index 3c0a1b6bd0..0000000000 --- a/system/gd/module_unittest.fbs +++ /dev/null @@ -1,10 +0,0 @@ -// module_unittest -namespace bluetooth; - -attribute "privacy"; - -table ModuleUnitTestData { - title:string (privacy:"Any"); -} - -root_type ModuleUnitTestData; diff --git a/system/gd/module_unittest_generated.h b/system/gd/module_unittest_generated.h deleted file mode 100644 index 1176e5cbc9..0000000000 --- a/system/gd/module_unittest_generated.h +++ /dev/null @@ -1,86 +0,0 @@ -// automatically generated by the FlatBuffers compiler, do not modify - -#ifndef FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_ -#define FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_ - -#include "flatbuffers/flatbuffers.h" - -namespace bluetooth { - -struct ModuleUnitTestData; -struct ModuleUnitTestDataBuilder; - -struct ModuleUnitTestData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef ModuleUnitTestDataBuilder Builder; - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_TITLE = 4 }; - const flatbuffers::String* title() const { - return GetPointer<const flatbuffers::String*>(VT_TITLE); - } - bool Verify(flatbuffers::Verifier& verifier) const { - return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_TITLE) && - verifier.VerifyString(title()) && verifier.EndTable(); - } -}; - -struct ModuleUnitTestDataBuilder { - typedef ModuleUnitTestData Table; - flatbuffers::FlatBufferBuilder& fbb_; - flatbuffers::uoffset_t start_; - void add_title(flatbuffers::Offset<flatbuffers::String> title) { - fbb_.AddOffset(ModuleUnitTestData::VT_TITLE, title); - } - explicit ModuleUnitTestDataBuilder(flatbuffers::FlatBufferBuilder& _fbb) : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - ModuleUnitTestDataBuilder& operator=(const ModuleUnitTestDataBuilder&); - flatbuffers::Offset<ModuleUnitTestData> Finish() { - const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset<ModuleUnitTestData>(end); - return o; - } -}; - -inline flatbuffers::Offset<ModuleUnitTestData> CreateModuleUnitTestData( - flatbuffers::FlatBufferBuilder& _fbb, flatbuffers::Offset<flatbuffers::String> title = 0) { - ModuleUnitTestDataBuilder builder_(_fbb); - builder_.add_title(title); - return builder_.Finish(); -} - -inline flatbuffers::Offset<ModuleUnitTestData> CreateModuleUnitTestDataDirect( - flatbuffers::FlatBufferBuilder& _fbb, const char* title = nullptr) { - auto title__ = title ? _fbb.CreateString(title) : 0; - return bluetooth::CreateModuleUnitTestData(_fbb, title__); -} - -inline const bluetooth::ModuleUnitTestData* GetModuleUnitTestData(const void* buf) { - return flatbuffers::GetRoot<bluetooth::ModuleUnitTestData>(buf); -} - -inline const bluetooth::ModuleUnitTestData* GetSizePrefixedModuleUnitTestData(const void* buf) { - return flatbuffers::GetSizePrefixedRoot<bluetooth::ModuleUnitTestData>(buf); -} - -inline bool VerifyModuleUnitTestDataBuffer(flatbuffers::Verifier& verifier) { - return verifier.VerifyBuffer<bluetooth::ModuleUnitTestData>(nullptr); -} - -inline bool VerifySizePrefixedModuleUnitTestDataBuffer(flatbuffers::Verifier& verifier) { - return verifier.VerifySizePrefixedBuffer<bluetooth::ModuleUnitTestData>(nullptr); -} - -inline void FinishModuleUnitTestDataBuffer( - flatbuffers::FlatBufferBuilder& fbb, - flatbuffers::Offset<bluetooth::ModuleUnitTestData> root) { - fbb.Finish(root); -} - -inline void FinishSizePrefixedModuleUnitTestDataBuffer( - flatbuffers::FlatBufferBuilder& fbb, - flatbuffers::Offset<bluetooth::ModuleUnitTestData> root) { - fbb.FinishSizePrefixed(root); -} - -} // namespace bluetooth - -#endif // FLATBUFFERS_GENERATED_MODULEUNITTEST_BLUETOOTH_H_ diff --git a/system/gd/neighbor/Android.bp b/system/gd/neighbor/Android.bp index 9f4f37ad1b..a8f1b64eb9 100644 --- a/system/gd/neighbor/Android.bp +++ b/system/gd/neighbor/Android.bp @@ -14,10 +14,3 @@ filegroup { "scan.cc", ], } - -filegroup { - name: "BluetoothFacade_neighbor", - srcs: [ - "facade/facade.cc", - ], -} diff --git a/system/gd/neighbor/facade/facade.cc b/system/gd/neighbor/facade/facade.cc deleted file mode 100644 index b3df4a876b..0000000000 --- a/system/gd/neighbor/facade/facade.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2019 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 "neighbor/facade/facade.h" - -#include <bluetooth/log.h> - -#include <memory> - -#include "blueberry/facade/neighbor/facade.grpc.pb.h" - -using ::grpc::ServerAsyncResponseWriter; -using ::grpc::ServerAsyncWriter; -using ::grpc::ServerContext; - -namespace bluetooth { -namespace neighbor { -namespace facade { - -using namespace blueberry::facade::neighbor; - -class NeighborFacadeService : public NeighborFacade::Service { -public: - NeighborFacadeService(ScanModule* scan_module) : scan_module_(scan_module) {} - - ::grpc::Status EnablePageScan(::grpc::ServerContext* /* context */, const EnableMsg* request, - ::google::protobuf::Empty* /* response */) override { - if (request->enabled()) { - scan_module_->SetPageScan(); - } else { - scan_module_->ClearPageScan(); - } - return ::grpc::Status::OK; - } - -private: - ScanModule* scan_module_; -}; - -void NeighborFacadeModule::ListDependencies(ModuleList* list) const { - ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list); - list->add<ScanModule>(); -} - -void NeighborFacadeModule::Start() { - ::bluetooth::grpc::GrpcFacadeModule::Start(); - service_ = new NeighborFacadeService(GetDependency<ScanModule>()); -} - -void NeighborFacadeModule::Stop() { - delete service_; - ::bluetooth::grpc::GrpcFacadeModule::Stop(); -} - -::grpc::Service* NeighborFacadeModule::GetService() const { return service_; } - -const ModuleFactory NeighborFacadeModule::Factory = - ::bluetooth::ModuleFactory([]() { return new NeighborFacadeModule(); }); - -} // namespace facade -} // namespace neighbor -} // namespace bluetooth diff --git a/system/gd/neighbor/facade/facade.h b/system/gd/neighbor/facade/facade.h deleted file mode 100644 index 92432ed530..0000000000 --- a/system/gd/neighbor/facade/facade.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019 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 <grpc++/grpc++.h> - -#include "grpc/grpc_module.h" -#include "neighbor/scan.h" - -namespace bluetooth { -namespace neighbor { -namespace facade { - -class NeighborFacadeService; - -class NeighborFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule { -public: - static const ModuleFactory Factory; - - void ListDependencies(ModuleList* list) const override; - void Start() override; - void Stop() override; - ::grpc::Service* GetService() const override; - -private: - NeighborFacadeService* service_; -}; - -} // namespace facade -} // namespace neighbor -} // namespace bluetooth diff --git a/system/gd/os/android/metrics.cc b/system/gd/os/android/metrics.cc index 112dd68f14..1a899062cd 100644 --- a/system/gd/os/android/metrics.cc +++ b/system/gd/os/android/metrics.cc @@ -86,7 +86,7 @@ void LogMetricLinkLayerConnectionEvent(const Address* address, uint32_t connecti common::ToHexString(cmd_status), common::ToHexString(reason_code), common::ToHexString(hci_cmd), common::ToHexString(hci_event), common::ToHexString(hci_ble_event), - address ? ADDRESS_TO_LOGGABLE_CSTR(*address) : "(NULL)", connection_handle, + address ? address->ToRedactedStringForLogging() : "(NULL)", connection_handle, common::ToHexString(link_type), ret); } } diff --git a/system/gd/os/linux_generic/reactor.cc b/system/gd/os/linux_generic/reactor.cc index 4fc2ac92b3..13f14dceb1 100644 --- a/system/gd/os/linux_generic/reactor.cc +++ b/system/gd/os/linux_generic/reactor.cc @@ -106,13 +106,13 @@ Reactor::Reactor() : epoll_fd_(0), control_fd_(0), is_running_(false) { epoll_event control_epoll_event = {EPOLLIN, {.ptr = nullptr}}; int result; RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, control_fd_, &control_epoll_event)); - log::assert_that(result != -1, "assert failed: result != -1"); + log::assert_that(result != -1, "epoll_ctl fail: result={} errno={}", result, strerror(errno)); } Reactor::~Reactor() { int result; RUN_NO_INTR(result = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, control_fd_, nullptr)); - log::assert_that(result != -1, "assert failed: result != -1"); + log::assert_that(result != -1, "epoll_ctl fail: result={} errno={}", result, strerror(errno)); RUN_NO_INTR(result = close(control_fd_)); log::assert_that(result != -1, "assert failed: result != -1"); @@ -225,7 +225,8 @@ Reactor::Reactable* Reactor::Register(int fd, Closure on_read_ready, Closure on_ }; int register_fd; RUN_NO_INTR(register_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event)); - log::assert_that(register_fd != -1, "assert failed: register_fd != -1"); + log::assert_that(register_fd != -1, "epoll_ctl fail: register_fd={} errno={}", register_fd, + strerror(errno)); return reactable; } @@ -306,7 +307,8 @@ void Reactor::ModifyRegistration(Reactor::Reactable* reactable, ReactOn react_on }; int modify_fd; RUN_NO_INTR(modify_fd = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, reactable->fd_, &event)); - log::assert_that(modify_fd != -1, "assert failed: modify_fd != -1"); + log::assert_that(modify_fd != -1, "epoll_ctl fail: modify_fd={} errno={}", modify_fd, + strerror(errno)); } } // namespace os diff --git a/system/gd/os/linux_generic/wakelock_manager.cc b/system/gd/os/linux_generic/wakelock_manager.cc index b8cc9000ba..7cca56201a 100644 --- a/system/gd/os/linux_generic/wakelock_manager.cc +++ b/system/gd/os/linux_generic/wakelock_manager.cc @@ -134,9 +134,10 @@ struct WakelockManager::Stats { total_acquired_interval_ms += delta_ms; } - flatbuffers::Offset<WakelockManagerData> GetDumpsysData( - flatbuffers::FlatBufferBuilder* fb_builder, bool is_native) const { + template <typename OutputT> + void Dump(OutputT&& out, bool is_native) { const uint64_t just_now_ms = now_ms(); + // Compute the last acquired interval if the wakelock is still acquired uint64_t delta_ms = 0; uint64_t last_interval_ms = last_acquired_interval_ms; @@ -154,31 +155,36 @@ struct WakelockManager::Stats { } last_interval_ms = delta_ms; } + uint64_t total_interval_ms = total_acquired_interval_ms + delta_ms; if (acquired_count > 0) { avg_interval_ms = total_interval_ms / acquired_count; } - WakelockManagerDataBuilder builder(*fb_builder); - builder.add_title(fb_builder->CreateString("Bluetooth Wakelock Statistics")); - builder.add_is_acquired(is_acquired); - builder.add_is_native(is_native); - builder.add_acquired_count(acquired_count); - builder.add_released_count(released_count); - builder.add_acquired_error_count(acquired_errors); - builder.add_released_error_count(released_errors); - builder.add_last_acquire_error_code(last_acquired_error); - builder.add_last_release_error_code(last_released_error); - builder.add_last_acquired_timestamp_millis(last_interval_ms); - builder.add_last_released_timestamp_millis(last_released_timestamp_ms); - builder.add_last_interval_millis(last_acquired_interval_ms); - builder.add_max_interval_millis(max_interval_ms); - builder.add_min_interval_millis(min_interval_ms); - builder.add_avg_interval_millis(avg_interval_ms); - builder.add_total_interval_millis(total_interval_ms); - builder.add_total_time_since_reset_millis(just_now_ms - last_reset_timestamp_ms); - return builder.Finish(); + std::format_to(out, "\nWakelock Dumpsys:\n"); + std::format_to(out, + " is_acquired: {}\n" + " is_native: {}\n" + " acquired_count: {}\n" + " released_count: {}\n" + " acquired_error_count: {}\n" + " released_error_count: {}\n" + " last_acquired_error_code: {}\n" + " last_released_error_code: {}\n" + " last_acquired_timestamp_ms: {}\n" + " last_released_timestamp_ms: {}\n" + " last_interval_ms: {}\n" + " max_interval_ms: {}\n" + " min_interval_ms: {}\n" + " avg_interval_ms: {}\n" + " total_interval_ms: {}\n" + " total_time_since_reeset_ms: {}\n", + is_acquired, is_native, acquired_count, released_count, acquired_errors, + released_errors, last_acquired_error, last_released_error, last_interval_ms, + last_released_timestamp_ms, last_acquired_interval_ms, max_interval_ms, + min_interval_ms, avg_interval_ms, total_interval_ms, + just_now_ms - last_reset_timestamp_ms); } }; @@ -266,10 +272,11 @@ void WakelockManager::CleanUp() { initialized_ = false; } -flatbuffers::Offset<WakelockManagerData> WakelockManager::GetDumpsysData( - flatbuffers::FlatBufferBuilder* fb_builder) { +void WakelockManager::Dump(int fd) const { std::lock_guard<std::recursive_mutex> lock_guard(mutex_); - return pstats_->GetDumpsysData(fb_builder, is_native_); + std::string out; + pstats_->Dump(std::back_inserter(out), is_native_); + dprintf(fd, "%s", out.c_str()); } WakelockManager::WakelockManager() : pstats_(std::make_unique<Stats>()) {} diff --git a/system/gd/os/linux_generic/wakelock_manager_unittest.cc b/system/gd/os/linux_generic/wakelock_manager_unittest.cc index e070d24fb5..d8b4dbf537 100644 --- a/system/gd/os/linux_generic/wakelock_manager_unittest.cc +++ b/system/gd/os/linux_generic/wakelock_manager_unittest.cc @@ -24,20 +24,14 @@ #include <unordered_map> #include "common/bind.h" -#include "flatbuffers/flatbuffers.h" #include "os/handler.h" #include "os/thread.h" -#include "wakelock_manager_generated.h" namespace testing { -using bluetooth::os::FinishWakelockManagerDataBuffer; -using bluetooth::os::GetWakelockManagerData; using bluetooth::os::Handler; using bluetooth::os::Thread; using bluetooth::os::WakelockManager; -using bluetooth::os::WakelockManagerData; -using bluetooth::os::WakelockManagerDataBuilder; class TestOsCallouts : public WakelockManager::OsCallouts { public: @@ -165,28 +159,8 @@ TEST_F(WakelockManagerTest, test_with_os_callouts_in_a_loop_and_dump) { ASSERT_THAT(os_callouts.GetNetAcquiredCount(WakelockManager::kBtWakelockId), Optional(Eq(0))); } - { - flatbuffers::FlatBufferBuilder builder(1024); - auto offset = WakelockManager::Get().GetDumpsysData(&builder); - FinishWakelockManagerDataBuffer(builder, offset); - auto data = GetWakelockManagerData(builder.GetBufferPointer()); - - ASSERT_EQ(data->acquired_count(), 1000); - ASSERT_EQ(data->released_count(), 1000); - } - WakelockManager::Get().CleanUp(); SyncHandler(); - - { - flatbuffers::FlatBufferBuilder builder(1024); - auto offset = WakelockManager::Get().GetDumpsysData(&builder); - FinishWakelockManagerDataBuffer(builder, offset); - auto data = GetWakelockManagerData(builder.GetBufferPointer()); - - ASSERT_EQ(data->acquired_count(), 0); - ASSERT_EQ(data->released_count(), 0); - } } } // namespace testing diff --git a/system/gd/os/logging/log_adapter.h b/system/gd/os/logging/log_adapter.h index d010fd4c24..0da4866fec 100644 --- a/system/gd/os/logging/log_adapter.h +++ b/system/gd/os/logging/log_adapter.h @@ -20,9 +20,6 @@ #include <string> -#define ADDRESS_TO_LOGGABLE_STR(addr) (addr).ToRedactedStringForLogging() -#define ADDRESS_TO_LOGGABLE_CSTR(addr) ADDRESS_TO_LOGGABLE_STR(addr).c_str() - #define PRIVATE_CELL(number) \ (number.replace(0, (number.size() > 2) ? number.size() - 2 : 0, \ (number.size() > 2) ? number.size() - 2 : 0, '*') \ diff --git a/system/gd/os/rand.h b/system/gd/os/rand.h index 04e29d30f4..03f29bc815 100644 --- a/system/gd/os/rand.h +++ b/system/gd/os/rand.h @@ -40,5 +40,12 @@ inline uint32_t GenerateRandom() { return ret; } +inline uint64_t GenerateRandomUint64() { + uint64_t ret{}; + log::assert_that(RAND_bytes((uint8_t*)(&ret), sizeof(uint64_t)) == 1, + "assert failed: RAND_bytes((uint8_t*)(&ret), sizeof(uint64_t)) == 1"); + return ret; +} + } // namespace os } // namespace bluetooth diff --git a/system/gd/os/wakelock_manager.fbs b/system/gd/os/wakelock_manager.fbs deleted file mode 100644 index 7b85e28044..0000000000 --- a/system/gd/os/wakelock_manager.fbs +++ /dev/null @@ -1,26 +0,0 @@ - -namespace bluetooth.os; - -attribute "privacy"; - -table WakelockManagerData { - title:string; - is_acquired:bool; - is_native:bool; - acquired_count:int; - released_count:int; - acquired_error_count:int; - released_error_count:int; - last_acquire_error_code:int; - last_release_error_code:int; - last_acquired_timestamp_millis:int64; - last_released_timestamp_millis:int64; - last_interval_millis:int64; - max_interval_millis:int64; - min_interval_millis:int64; - avg_interval_millis:int64; - total_interval_millis:int64; - total_time_since_reset_millis:int64; -} - -root_type WakelockManagerData; diff --git a/system/gd/os/wakelock_manager.h b/system/gd/os/wakelock_manager.h index 7f59895502..d920a5b4b4 100644 --- a/system/gd/os/wakelock_manager.h +++ b/system/gd/os/wakelock_manager.h @@ -18,14 +18,11 @@ #pragma once -#include <flatbuffers/flatbuffers.h> - #include <memory> #include <mutex> #include <string> #include "handler.h" -#include "wakelock_manager_generated.h" namespace bluetooth { namespace os { @@ -73,16 +70,16 @@ public: // This will NOT clean up the callouts void CleanUp(); - // Dump wakelock-related debug info to a flat buffer defined in wakelock_manager.fbs - flatbuffers::Offset<WakelockManagerData> GetDumpsysData( - flatbuffers::FlatBufferBuilder* fb_builder); + /// Write debug information relevant for the wakelock manager + /// to the dumpsys output file descriptor. + void Dump(int fd) const; ~WakelockManager(); private: WakelockManager(); - std::recursive_mutex mutex_; + mutable std::recursive_mutex mutex_; bool initialized_ = false; OsCallouts* os_callouts_ = nullptr; Handler* os_callouts_handler_ = nullptr; diff --git a/system/gd/rust/linux/client/src/callbacks.rs b/system/gd/rust/linux/client/src/callbacks.rs index 0d1d998b7d..e9cd5fb7f4 100644 --- a/system/gd/rust/linux/client/src/callbacks.rs +++ b/system/gd/rust/linux/client/src/callbacks.rs @@ -1402,6 +1402,10 @@ impl IBluetoothQACallback for QACallback { fn on_send_hid_data_completed(&mut self, status: BtStatus) { print_info!("Send HID data: {:?}", status); } + + fn on_send_hid_virtual_unplug_completed(&mut self, status: BtStatus) { + print_info!("Send HID virtual unplug: {:?}", status); + } } impl RPCProxy for QACallback { diff --git a/system/gd/rust/linux/client/src/command_handler.rs b/system/gd/rust/linux/client/src/command_handler.rs index f7e6ab63ee..fa98c510be 100644 --- a/system/gd/rust/linux/client/src/command_handler.rs +++ b/system/gd/rust/linux/client/src/command_handler.rs @@ -314,6 +314,7 @@ fn build_commands() -> HashMap<String, CommandOption> { String::from("hid get-report <address> <Input|Output|Feature> <report_id>"), String::from("hid set-report <address> <Input|Output|Feature> <report_value>"), String::from("hid send-data <address> <data>"), + String::from("hid virtual-unplug <address>"), ], description: String::from("Socket manager utilities."), function_pointer: CommandHandler::cmd_hid, @@ -2044,6 +2045,16 @@ impl CommandHandler { self.context.lock().unwrap().qa_dbus.as_mut().unwrap().send_hid_data(addr, data); } + "virtual-unplug" => { + let addr = RawAddress::from_string(get_arg(args, 1)?).ok_or("Invalid Address")?; + self.context + .lock() + .unwrap() + .qa_dbus + .as_mut() + .unwrap() + .send_hid_virtual_unplug(addr); + } _ => return Err(CommandError::InvalidArgs), }; diff --git a/system/gd/rust/linux/client/src/dbus_iface.rs b/system/gd/rust/linux/client/src/dbus_iface.rs index 6209ee58c4..f9d3ae625a 100644 --- a/system/gd/rust/linux/client/src/dbus_iface.rs +++ b/system/gd/rust/linux/client/src/dbus_iface.rs @@ -2583,6 +2583,10 @@ impl IBluetoothQA for BluetoothQADBus { fn send_hid_data(&self, addr: RawAddress, data: String) { dbus_generated!() } + #[dbus_method("SendHIDVirtualUnplug")] + fn send_hid_virtual_unplug(&self, addr: RawAddress) { + dbus_generated!() + } } #[allow(dead_code)] @@ -2624,6 +2628,10 @@ impl IBluetoothQACallback for IBluetoothQACallbackDBus { fn on_send_hid_data_completed(&mut self, status: BtStatus) { dbus_generated!() } + #[dbus_method("OnSendHIDVirtualUnplugComplete", DBusLog::Disable)] + fn on_send_hid_virtual_unplug_completed(&mut self, status: BtStatus) { + dbus_generated!() + } } #[derive(Clone)] diff --git a/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs b/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs index 846c7713b4..d2509567fb 100644 --- a/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs +++ b/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs @@ -21,9 +21,4 @@ impl IBluetoothExperimental for BluetoothExperimentalDBus { fn set_ll_privacy(&mut self, enabled: bool) -> bool { dbus_generated!() } - - #[dbus_method("SetDevCoredump")] - fn set_devcoredump(&mut self, enabled: bool) -> bool { - dbus_generated!() - } } diff --git a/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs b/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs index ca98b2ba9b..9b22d7952a 100644 --- a/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs +++ b/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs @@ -302,9 +302,4 @@ impl IBluetoothExperimental for BluetoothManager { true } - - fn set_devcoredump(&mut self, enabled: bool) -> bool { - warn!("Set Floss DevCoreDump={}", enabled); - config_util::write_coredump_state_to_file(enabled) - } } diff --git a/system/gd/rust/linux/mgmt/src/config_util.rs b/system/gd/rust/linux/mgmt/src/config_util.rs index c7f452bfe3..13ffb243d9 100644 --- a/system/gd/rust/linux/mgmt/src/config_util.rs +++ b/system/gd/rust/linux/mgmt/src/config_util.rs @@ -17,12 +17,6 @@ const BTMANAGERD_CONF: &str = "/var/lib/bluetooth/btmanagerd.json"; /// Folder to keep files which override floss configuration const FLOSS_SYSPROPS_OVERRIDE_DIR: &str = "/var/lib/bluetooth/sysprops.conf.d"; -/// File to store bluetooth devcoredump configuration. This file is used by the -/// udev rule to copy the devcoredump state to .../device/coredump_enabled sysfs -/// entry. It is also used by the user-space crash reporter to enable/disable -/// parsing of the devcoredump. -const FLOSS_COREDUMP_CONF_PATH: &str = "/var/run/bluetooth/coredump_disabled"; - /// Key used for default adapter entry. const DEFAULT_ADAPTER_KEY: &str = "default_adapter"; @@ -252,34 +246,6 @@ pub fn write_floss_address_privacy_enabled(enabled: bool) -> std::io::Result<()> ) } -pub fn set_adapter_coredump_state(enabled: bool) -> std::io::Result<()> { - let data = format!("{}\n", !enabled as i32); - - for hci in list_hci_devices_string() { - let path = format!("{}/{}/device/coredump_disabled", HCI_DEVICES_DIR, hci); - - std::fs::write(path, &data)?; - } - - Ok(()) -} - -pub fn write_coredump_state_to_file(enabled: bool) -> bool { - let data = format!("{}\n", !enabled as i32); - - if let Err(e) = std::fs::write(FLOSS_COREDUMP_CONF_PATH, data) { - log::error!("Failed to write devcoredump state (error: {})", e); - return false; - } - - if let Err(e) = set_adapter_coredump_state(enabled) { - log::error!("Failed to set devcoredump state (error: {})", e); - return false; - } - - true -} - #[cfg(test)] mod tests { use super::*; diff --git a/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs b/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs index 4a38e9ee1b..27b20a76e4 100644 --- a/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs +++ b/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs @@ -2,7 +2,4 @@ pub trait IBluetoothExperimental { /// Set LL privacy status fn set_ll_privacy(&mut self, enabled: bool) -> bool; - - /// Set devcoredump status - fn set_devcoredump(&mut self, enabled: bool) -> bool; } diff --git a/system/gd/rust/linux/service/src/iface_bluetooth_qa.rs b/system/gd/rust/linux/service/src/iface_bluetooth_qa.rs index 192b346e66..48873f4637 100644 --- a/system/gd/rust/linux/service/src/iface_bluetooth_qa.rs +++ b/system/gd/rust/linux/service/src/iface_bluetooth_qa.rs @@ -65,6 +65,10 @@ impl IBluetoothQA for IBluetoothQADBus { fn send_hid_data(&self, addr: RawAddress, data: String) { dbus_generated!() } + #[dbus_method("SendHIDVirtualUnplug")] + fn send_hid_virtual_unplug(&self, addr: RawAddress) { + dbus_generated!() + } } #[dbus_proxy_obj(QACallback, "org.chromium.bluetooth.QACallback")] @@ -97,4 +101,8 @@ impl IBluetoothQACallback for IBluetoothQACallbackDBus { fn on_send_hid_data_completed(&mut self, status: BtStatus) { dbus_generated!() } + #[dbus_method("OnSendHIDVirtualUnplugComplete")] + fn on_send_hid_virtual_unplug_completed(&mut self, status: BtStatus) { + dbus_generated!() + } } diff --git a/system/gd/rust/linux/stack/src/battery_provider_manager.rs b/system/gd/rust/linux/stack/src/battery_provider_manager.rs index 3946a4f6fc..a9f8a3c984 100644 --- a/system/gd/rust/linux/stack/src/battery_provider_manager.rs +++ b/system/gd/rust/linux/stack/src/battery_provider_manager.rs @@ -57,8 +57,20 @@ impl BatteryProviderManager { } /// Get the best battery info available for a given device. - pub fn get_battery_info(&self, remote_address: RawAddress) -> Option<BatterySet> { - self.battery_info.get(&remote_address)?.pick_best() + pub fn get_battery_info(&mut self, remote_address: RawAddress) -> Option<BatterySet> { + if let Some(info) = self.battery_info.get(&remote_address) { + return info.pick_best(); + } + + // If no battery info found, refresh and retry + self.refresh_battery_info(); + match self.battery_info.get(&remote_address) { + None => { + debug!("No battery info found for [{}]", DisplayAddress(&remote_address)); + return None; + } + Some(info) => return info.pick_best(), + } } /// Removes a battery provider callback. diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs index 20442315c0..2472368243 100644 --- a/system/gd/rust/linux/stack/src/bluetooth.rs +++ b/system/gd/rust/linux/stack/src/bluetooth.rs @@ -995,6 +995,11 @@ impl Bluetooth { ) } + // TODO(b/328675014): Add BtAddrType and BtTransport parameters + pub(crate) fn send_hid_virtual_unplug_internal(&mut self, mut addr: RawAddress) -> BtStatus { + self.hh.as_mut().unwrap().virtual_unplug(&mut addr, BtAddrType::Public, BtTransport::Auto) + } + /// Returns all bonded and connected devices. pub(crate) fn get_bonded_and_connected_devices(&mut self) -> Vec<BluetoothDevice> { self.remote_devices diff --git a/system/gd/rust/linux/stack/src/bluetooth_qa.rs b/system/gd/rust/linux/stack/src/bluetooth_qa.rs index d061187586..dd3ddc27a5 100644 --- a/system/gd/rust/linux/stack/src/bluetooth_qa.rs +++ b/system/gd/rust/linux/stack/src/bluetooth_qa.rs @@ -37,9 +37,12 @@ pub trait IBluetoothQA { /// Sets HID report to the peer. /// Result will be returned in the callback |OnSetHIDReportComplete| fn set_hid_report(&self, addr: RawAddress, report_type: BthhReportType, report: String); - /// Snd HID data report to the peer. + /// Sends HID data report to the peer. /// Result will be returned in the callback |OnSendHIDDataComplete| fn send_hid_data(&self, addr: RawAddress, data: String); + /// Sends HID virtual unplug to the peer. + /// Result will be returned in the callback |OnSendHIDVirtualUnplugComplete| + fn send_hid_virtual_unplug(&self, addr: RawAddress); } pub trait IBluetoothQACallback: RPCProxy { @@ -50,6 +53,7 @@ pub trait IBluetoothQACallback: RPCProxy { fn on_get_hid_report_completed(&mut self, status: BtStatus); fn on_set_hid_report_completed(&mut self, status: BtStatus); fn on_send_hid_data_completed(&mut self, status: BtStatus); + fn on_send_hid_virtual_unplug_completed(&mut self, status: BtStatus); } pub struct BluetoothQA { @@ -99,6 +103,11 @@ impl BluetoothQA { cb.on_send_hid_data_completed(status); }); } + pub fn on_send_hid_virtual_unplug_completed(&mut self, status: BtStatus) { + self.callbacks.for_all_callbacks(|cb: &mut Box<dyn IBluetoothQACallback + Send>| { + cb.on_send_hid_virtual_unplug_completed(status); + }); + } } impl IBluetoothQA for BluetoothQA { @@ -166,4 +175,10 @@ impl IBluetoothQA for BluetoothQA { let _ = txl.send(Message::QaSendHidData(addr, data)).await; }); } + fn send_hid_virtual_unplug(&self, addr: RawAddress) { + let txl = self.tx.clone(); + tokio::spawn(async move { + let _ = txl.send(Message::QaSendHidVirtualUnplug(addr)).await; + }); + } } diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs index e540e68d3e..8027c1c61f 100644 --- a/system/gd/rust/linux/stack/src/lib.rs +++ b/system/gd/rust/linux/stack/src/lib.rs @@ -171,6 +171,7 @@ pub enum Message { QaGetHidReport(RawAddress, BthhReportType, u8), QaSetHidReport(RawAddress, BthhReportType, String), QaSendHidData(RawAddress, String), + QaSendHidVirtualUnplug(RawAddress), // UHid callbacks UHidHfpOutputCallback(RawAddress, u8, u8), @@ -589,6 +590,10 @@ impl Stack { let status = bluetooth.lock().unwrap().send_hid_data_internal(addr, data); bluetooth_qa.lock().unwrap().on_send_hid_data_completed(status); } + Message::QaSendHidVirtualUnplug(addr) => { + let status = bluetooth.lock().unwrap().send_hid_virtual_unplug_internal(addr); + bluetooth_qa.lock().unwrap().on_send_hid_virtual_unplug_completed(status); + } // UHid callbacks Message::UHidHfpOutputCallback(addr, id, data) => { diff --git a/system/gd/rust/topshim/Android.bp b/system/gd/rust/topshim/Android.bp index b7d3af79ea..5d0e9c2102 100644 --- a/system/gd/rust/topshim/Android.bp +++ b/system/gd/rust/topshim/Android.bp @@ -52,7 +52,6 @@ cc_library_static { "vc/vc_shim.cc", ], generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", "cxx-bridge-header", "libbt_topshim_bridge_header", ], diff --git a/system/gd/rust/topshim/facade/.gitignore b/system/gd/rust/topshim/facade/.gitignore deleted file mode 100644 index 7cceb192b2..0000000000 --- a/system/gd/rust/topshim/facade/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_pb2*.py diff --git a/system/gd/rust/topshim/facade/Android.bp b/system/gd/rust/topshim/facade/Android.bp deleted file mode 100644 index 505d5e8624..0000000000 --- a/system/gd/rust/topshim/facade/Android.bp +++ /dev/null @@ -1,104 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "system_bt_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["system_bt_license"], -} - -rust_defaults { - name: "bt_topshim_facade.defaults", - defaults: [ - "gd_rust_defaults", - ], - crate_name: "bt_topshim_facade", - srcs: ["src/main.rs"], - ld_flags: [ - "-fsanitize-minimal-runtime", - "-fsanitize=undefined", - ], - rustlibs: [ - "libbluetooth_core_rs_for_facade", - "libbt_common", - "libbt_topshim", - "libbt_topshim_facade_protobuf", - "libclap", - "libfutures", - "libgrpcio", - "liblog_rust", - "libnix", - "libnum_traits", - "libtokio", - ], - static_libs: [ - "aics", - "avrcp-target-service", - "lib-bt-packets", - "lib-bt-packets-avrcp", - "lib-bt-packets-base", - "libFraunhoferAAC", - "libbluetooth-dumpsys", - "libbluetooth-types", - "libbluetooth_crypto_toolbox", - "libbluetooth_gd", // Gabeldorsche - "libbluetooth_log", - "libbt-audio-asrc", - "libbt-audio-hal-interface", - "libbt-bta", - "libbt-bta-core", - "libbt-btu-main-thread", - "libbt-common", - "libbt-hci", - "libbt-jni-thread", - "libbt-sbc-decoder", - "libbt-sbc-encoder", - "libbt-stack", - "libbt-stack-core", - "libbt_topshim_cxx", - "libbtcore", - "libbtdevice", - "libbte", - "libbthalutils", - "libbtif", - "libbtif-core", - "libchrome", - "libevent", - "libflatbuffers-cpp", - "libg722codec", - "liblc3", - "libopus", - "libosi", - "libudrv-uipc", - ], - shared_libs: [ - "libaconfig_storage_read_api_cc", - "libbinder", - "libcrypto", - "libcutils", - "libgrpc++", - "libgrpc_wrap", - "liblog", - "server_configurable_flags", - ], - proc_macros: [ - "libpaste", - ], -} - -rust_binary_host { - name: "bt_topshim_facade", - defaults: ["bt_topshim_facade.defaults"], - static_libs: [ - "bluetooth_flags_c_lib", - ], -} - -rust_test_host { - name: "bt_topshim_facade.test", - defaults: ["bt_topshim_facade.defaults"], - static_libs: [ - "bluetooth_flags_c_lib_for_test", - ], - test_suites: ["general-tests"], -} diff --git a/system/gd/rust/topshim/facade/src/adapter_service.rs b/system/gd/rust/topshim/facade/src/adapter_service.rs deleted file mode 100644 index bd56ea5bad..0000000000 --- a/system/gd/rust/topshim/facade/src/adapter_service.rs +++ /dev/null @@ -1,385 +0,0 @@ -//! Adapter service facade - -use bt_topshim::btif; -use bt_topshim::btif::{BaseCallbacks, BaseCallbacksDispatcher, BluetoothInterface, BtIoCap}; - -use crate::utils::converters::{bluetooth_property_to_event_data, event_data_from_string}; -use bt_topshim_facade_protobuf::empty::Empty; -use bt_topshim_facade_protobuf::facade::{ - EventType, FetchEventsRequest, FetchEventsResponse, SetDefaultEventMaskExceptRequest, - SetDiscoveryModeRequest, SetLocalIoCapsRequest, SetLocalIoCapsResponse, ToggleDiscoveryRequest, - ToggleDiscoveryResponse, ToggleStackRequest, ToggleStackResponse, -}; -use bt_topshim_facade_protobuf::facade_grpc::{create_adapter_service, AdapterService}; -use futures::sink::SinkExt; -use grpcio::*; -use num_traits::cast::FromPrimitive; - -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; -use tokio::sync::mpsc; -use tokio::sync::Mutex as TokioMutex; - -fn get_bt_dispatcher( - btif: Arc<Mutex<BluetoothInterface>>, - tx: mpsc::Sender<BaseCallbacks>, -) -> BaseCallbacksDispatcher { - BaseCallbacksDispatcher { - dispatch: Box::new(move |cb: BaseCallbacks| { - if tx.clone().try_send(cb.clone()).is_err() { - println!("Cannot send event {:?}", cb); - } - match cb { - BaseCallbacks::AdapterState(state) => { - println!("State changed to {:?}", state); - } - BaseCallbacks::SspRequest(addr, variant, passkey) => { - println!( - "SSP Request made for address {:?} with variant {:?} and passkey {:?}", - addr.to_string(), - variant, - passkey - ); - btif.lock().unwrap().ssp_reply(&addr, variant, 1, passkey); - } - BaseCallbacks::AdapterProperties(status, _, properties) => { - println!( - "Adapter attributes changed, status = {:?}, properties = {:?}", - status, properties - ); - } - BaseCallbacks::DiscoveryState(state) => { - println!("Discovery state changed, state = {:?}, ", state); - } - BaseCallbacks::DeviceFound(_, properties) => { - println!("Device found with properties : {:?}", properties) - } - BaseCallbacks::BondState(_, address, state, _) => { - println!( - "Device in state {:?} with device address {}", - state, - address.to_string() - ); - } - _ => (), - } - }), - } -} - -/// Main object for Adapter facade service -#[derive(Clone)] -pub struct AdapterServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - btif_intf: Arc<Mutex<BluetoothInterface>>, - event_rx: Arc<TokioMutex<mpsc::Receiver<BaseCallbacks>>>, - #[allow(dead_code)] - event_tx: mpsc::Sender<BaseCallbacks>, -} - -fn encode_hex(bytes: &[u8]) -> String { - let mut s = String::with_capacity(2 * bytes.len()); - for &b in bytes { - let bstr: String = format!("{:02X}", b); - s.push_str(&bstr); - } - s -} - -impl AdapterServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - let (event_tx, rx) = mpsc::channel(10); - btif_intf - .lock() - .unwrap() - .initialize(get_bt_dispatcher(btif_intf.clone(), event_tx.clone()), 0); - create_adapter_service(Self { - rt, - btif_intf, - event_rx: Arc::new(TokioMutex::new(rx)), - event_tx, - }) - } -} - -impl AdapterService for AdapterServiceImpl { - fn fetch_events( - &mut self, - ctx: RpcContext<'_>, - _req: FetchEventsRequest, - mut sink: ServerStreamingSink<FetchEventsResponse>, - ) { - let rx = self.event_rx.clone(); - ctx.spawn(async move { - while let Some(event) = rx.lock().await.recv().await { - match event { - BaseCallbacks::AdapterState(_state) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::ADAPTER_STATE.into(); - rsp.params.insert( - String::from("state"), - event_data_from_string(String::from("ON")), - ); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::SspRequest(_, _, _) => {} - BaseCallbacks::LeRandCallback(random) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::LE_RAND.into(); - rsp.params.insert( - String::from("data"), - event_data_from_string(random.to_string()), - ); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::GenerateLocalOobData(transport, data) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::GENERATE_LOCAL_OOB_DATA.into(); - rsp.params.insert( - String::from("is_valid"), - event_data_from_string(String::from(if data.is_valid { - "1" - } else { - "0" - })), - ); - rsp.params.insert( - String::from("transport"), - event_data_from_string(format!("{}", transport)), - ); - rsp.params.insert( - String::from("address"), - event_data_from_string(encode_hex(&data.address)), - ); - rsp.params.insert( - String::from("confirmation"), - event_data_from_string(encode_hex(&data.c)), - ); - rsp.params.insert( - String::from("randomizer"), - event_data_from_string(encode_hex(&data.r)), - ); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::AdapterProperties(status, _, properties) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::ADAPTER_PROPERTY.into(); - rsp.params.insert( - String::from("status"), - event_data_from_string(format!("{:?}", status)), - ); - for property in properties.clone() { - let (key, event_data) = bluetooth_property_to_event_data(property); - if key == "skip" { - continue; - } - rsp.params.insert(key, event_data); - } - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::DiscoveryState(state) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::DISCOVERY_STATE.into(); - rsp.params.insert( - String::from("discovery_state"), - event_data_from_string(format!("{:?}", state)), - ); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::DeviceFound(_, properties) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::DEVICE_FOUND.into(); - for property in properties.clone() { - let (key, event_data) = bluetooth_property_to_event_data(property); - if key == "skip" { - continue; - } - rsp.params.insert(key, event_data); - } - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - BaseCallbacks::BondState(_, address, state, _) => { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::BOND_STATE.into(); - rsp.params.insert( - String::from("bond_state"), - event_data_from_string(format!("{:?}", state)), - ); - rsp.params.insert( - String::from("address"), - event_data_from_string(address.to_string()), - ); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - _ => (), - } - } - }) - } - - fn toggle_stack( - &mut self, - ctx: RpcContext<'_>, - req: ToggleStackRequest, - sink: UnarySink<ToggleStackResponse>, - ) { - match req.start_stack { - true => self.btif_intf.lock().unwrap().enable(), - false => self.btif_intf.lock().unwrap().disable(), - }; - ctx.spawn(async move { - sink.success(ToggleStackResponse::default()).await.unwrap(); - }) - } - - fn set_discovery_mode( - &mut self, - ctx: RpcContext<'_>, - req: SetDiscoveryModeRequest, - sink: UnarySink<Empty>, - ) { - let scan_mode = if req.enable_inquiry_scan { - btif::BtScanMode::ConnectableDiscoverable - } else if req.enable_page_scan { - btif::BtScanMode::Connectable - } else { - btif::BtScanMode::None_ - }; - self.btif_intf.lock().unwrap().set_scan_mode(scan_mode); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn clear_event_filter(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - self.btif_intf.lock().unwrap().clear_event_filter(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn clear_event_mask(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - self.btif_intf.lock().unwrap().clear_event_mask(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn clear_filter_accept_list( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().clear_filter_accept_list(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn disconnect_all_acls(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - self.btif_intf.lock().unwrap().disconnect_all_acls(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn le_rand(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - self.btif_intf.lock().unwrap().le_rand(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn allow_wake_by_hid(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - self.btif_intf.lock().unwrap().allow_wake_by_hid(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn restore_filter_accept_list( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().restore_filter_accept_list(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_default_event_mask_except( - &mut self, - ctx: RpcContext<'_>, - req: SetDefaultEventMaskExceptRequest, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().set_default_event_mask_except(req.mask, req.le_mask); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_event_filter_inquiry_result_all_devices( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().set_event_filter_inquiry_result_all_devices(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_event_filter_connection_setup_all_devices( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().set_event_filter_connection_setup_all_devices(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_local_io_caps( - &mut self, - ctx: RpcContext<'_>, - req: SetLocalIoCapsRequest, - sink: UnarySink<SetLocalIoCapsResponse>, - ) { - let status = self.btif_intf.lock().unwrap().set_adapter_property( - btif::BluetoothProperty::LocalIoCaps( - BtIoCap::from_i32(req.io_capability).unwrap_or(BtIoCap::Unknown), - ), - ); - let mut resp = SetLocalIoCapsResponse::new(); - resp.status = status; - ctx.spawn(async move { - sink.success(resp).await.unwrap(); - }) - } - - fn toggle_discovery( - &mut self, - ctx: RpcContext<'_>, - req: ToggleDiscoveryRequest, - sink: UnarySink<ToggleDiscoveryResponse>, - ) { - let status = match req.is_start { - true => self.btif_intf.lock().unwrap().start_discovery(), - false => self.btif_intf.lock().unwrap().cancel_discovery(), - }; - let mut resp = ToggleDiscoveryResponse::new(); - resp.status = status; - ctx.spawn(async move { - sink.success(resp).await.unwrap(); - }) - } -} diff --git a/system/gd/rust/topshim/facade/src/gatt_service.rs b/system/gd/rust/topshim/facade/src/gatt_service.rs deleted file mode 100644 index 2c27378e2c..0000000000 --- a/system/gd/rust/topshim/facade/src/gatt_service.rs +++ /dev/null @@ -1,745 +0,0 @@ -//! GATT service facade - -use bt_topshim::btif::{BluetoothInterface, Uuid}; -use bt_topshim::profiles::gatt::{ - AdvertiseParameters, Gatt, GattFilterParam, PeriodicAdvertisingParameters, -}; -use bt_topshim::profiles::gatt::{ - GattAdvCallbacksDispatcher, GattAdvInbandCallbacksDispatcher, GattClientCallbacksDispatcher, - GattScannerCallbacksDispatcher, GattScannerInbandCallbacksDispatcher, - GattServerCallbacksDispatcher, -}; -use bt_topshim_facade_protobuf::empty::Empty; -//use bt_topshim_facade_protobuf::facade::{ -// EventType, FetchEventsRequest, FetchEventsResponse, SetDiscoveryModeRequest, -// SetDiscoveryModeResponse, ToggleStackRequest, ToggleStackResponse, -//}; -use bt_topshim_facade_protobuf::facade_grpc::{create_gatt_service, GattService}; -//use futures::sink::SinkExt; -use crate::btif::RawAddress; -use grpcio::*; - -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; -use tokio::sync::mpsc; -use tokio::sync::Mutex as TokioMutex; - -struct GattCallbacks {} - -/// Main object for GATT facade service -#[derive(Clone)] -pub struct GattServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - #[allow(dead_code)] - btif_intf: Arc<Mutex<BluetoothInterface>>, - #[allow(dead_code)] - gatt: Arc<Mutex<Gatt>>, - #[allow(dead_code)] - event_rx: Arc<TokioMutex<mpsc::Receiver<GattCallbacks>>>, - #[allow(dead_code)] - event_tx: mpsc::Sender<GattCallbacks>, -} - -impl GattServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - let (event_tx, rx) = mpsc::channel(10); - let btif_clone = btif_intf.clone(); - let me = Self { - rt, - btif_intf, - gatt: Arc::new(Mutex::new(Gatt::new(&btif_clone.lock().unwrap()))), - event_rx: Arc::new(TokioMutex::new(rx)), - event_tx, - }; - me.gatt.lock().unwrap().initialize( - GattClientCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("Received Gatt Client Callback: {:?}", cb); - }), - }, - GattServerCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("Received Gatt Server Callback: {:?}", cb); - }), - }, - GattScannerCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("received Gatt scanner callback: {:?}", cb); - }), - }, - GattScannerInbandCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("received Gatt scanner inband callback: {:?}", cb); - }), - }, - GattAdvInbandCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("received Gatt advertiser inband callback: {:?}", cb); - }), - }, - GattAdvCallbacksDispatcher { - dispatch: Box::new(move |cb| { - println!("received Gatt advertising callback: {:?}", cb); - }), - }, - ); - - create_gatt_service(me) - } - - fn create_raw_address(&self) -> RawAddress { - RawAddress { address: [0; 6] } - } - - fn create_advertise_parameters(&self) -> AdvertiseParameters { - AdvertiseParameters { - advertising_event_properties: 0, - min_interval: 0, - max_interval: 0, - channel_map: 0, - tx_power: 0, - primary_advertising_phy: 0, - secondary_advertising_phy: 0, - scan_request_notification_enable: 0, - own_address_type: 0, - peer_address: self.create_raw_address(), - peer_address_type: 0, - discoverable: false, - } - } - - fn create_periodic_advertising_parameters(&self) -> PeriodicAdvertisingParameters { - PeriodicAdvertisingParameters { - enable: false, - include_adi: false, - min_interval: 0, - max_interval: 0, - periodic_advertising_properties: 0, - } - } - - fn create_gatt_filter_param(&self) -> GattFilterParam { - GattFilterParam { - feat_seln: 0, - list_logic_type: 0, - filt_logic_type: 0, - rssi_high_thres: 0, - rssi_low_thres: 0, - dely_mode: 0, - found_timeout: 0, - lost_timeout: 0, - found_timeout_cnt: 0, - num_of_tracking_entries: 0, - } - } - - fn create_uuid(&self) -> Uuid { - Uuid::from([0; 16]) - } -} - -impl GattService for GattServiceImpl { - // fn fetch_events( - // &mut self, - // ctx: RpcContext<'_>, - // _req: FetchEventsRequest, - // mut sink: ServerStreamingSink<FetchEventsResponse>, - // ) { - // let rx = self.event_rx.clone(); - // ctx.spawn(async move { - // while let Some(event) = rx.lock().await.recv().await { - // match event { - // GattCallbacks::AdapterState(_state) => { - // let mut rsp = FetchEventsResponse::new(); - // rsp.event_type = EventType::ADAPTER_STATE; - // rsp.data = "ON".to_string(); - // sink.send((rsp, WriteFlags::default())).await.unwrap(); - // } - // GattCallbacks::SspRequest(_, _, _, _, _) => {} - // GattCallbacks::LeRandCallback(random) => { - // let mut rsp = FetchEventsResponse::new(); - // rsp.event_type = EventType::LE_RAND; - // rsp.data = random.to_string(); - // sink.send((rsp, WriteFlags::default())).await.unwrap(); - // } - // _ => (), - // } - // } - // }) - // } - - // TODO(optedoblivion): Implement all send messages and returns - // Advertising - fn register_advertiser(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.register_advertiser(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn unregister_advertiser(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.unregister(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn get_own_address(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.get_own_address(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_parameters(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.set_parameters(0, self.create_advertise_parameters()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_data(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.set_data(0, true, vec![0]); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn advertising_enable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.enable(0, true, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn advertising_disable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.enable(0, false, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn start_advertising(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.start_advertising(0, self.create_advertise_parameters(), vec![0], vec![0], 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn start_advertising_set(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.start_advertising_set( - 0, - self.create_advertise_parameters(), - vec![0], - vec![0], - self.create_periodic_advertising_parameters(), - vec![0], - 0, - 0, - ); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_periodic_advertising_parameters( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser - .set_periodic_advertising_parameters(0, self.create_periodic_advertising_parameters()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_periodic_advertising_data( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.set_periodic_advertising_data(0, vec![0]); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_periodic_advertising_enable( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let advertiser = &mut self.gatt.lock().unwrap().advertiser; - advertiser.set_periodic_advertising_enable(0, true, false); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - // Scanner - fn register_scanner(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.register_scanner(self.create_uuid()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn unregister_scanner(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.unregister(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn start_scan(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.start_scan(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn stop_scan(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.stop_scan(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn scan_filter_setup(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.scan_filter_setup(0, 0, 0, self.create_gatt_filter_param()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn scan_filter_add(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - println!("Unimplemented!"); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn scan_filter_clear(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.scan_filter_clear(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn scan_filter_enable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.scan_filter_enable(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn scan_filter_disable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.scan_filter_disable(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn set_scan_parameters(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.set_scan_parameters(0, 0, 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn batch_scan_config_storage( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.batchscan_config_storage(0, 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn batch_scan_enable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.batchscan_enable(0, 0, 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn batch_scan_disable(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.batchscan_disable(); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn batch_scan_read_reports( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.batchscan_read_reports(0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn start_sync(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.start_sync(0, self.create_raw_address(), 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn stop_sync(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.stop_sync(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn cancel_create_sync(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.cancel_create_sync(0, self.create_raw_address()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn transfer_sync(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.transfer_sync(self.create_raw_address(), 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn transfer_set_info(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.transfer_set_info(self.create_raw_address(), 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn sync_tx_parameters(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let scanner = &mut self.gatt.lock().unwrap().scanner; - scanner.sync_tx_parameters(self.create_raw_address(), 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - // GATT Client - fn register_client(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.register_client(&self.create_uuid(), true); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn unregister_client(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.unregister_client(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn client_connect(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.connect(0, &self.create_raw_address(), 0, true, 0, true, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn client_disconnect(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.disconnect(0, &self.create_raw_address(), 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn refresh(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.refresh(0, &self.create_raw_address()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn search_service(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.search_service(0, None); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn btif_gattc_discover_service_by_uuid( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let client = &mut self.gatt.lock().unwrap().client; - client.btif_gattc_discover_service_by_uuid(0, &self.create_uuid()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn read_characteristic(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.read_characteristic(0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn read_using_characteristic_uuid( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let client = &mut self.gatt.lock().unwrap().client; - client.read_using_characteristic_uuid(0, &self.create_uuid(), 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn write_characteristic(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.write_characteristic(0, 0, 0, 0, &[0]); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn read_descriptor(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.read_descriptor(0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn write_descriptor(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.write_descriptor(0, 0, 0, &[0]); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn execute_write(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.execute_write(0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn register_for_notification( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let client = &mut self.gatt.lock().unwrap().client; - client.register_for_notification(0, &self.create_raw_address(), 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn deregister_for_notification( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let client = &mut self.gatt.lock().unwrap().client; - client.deregister_for_notification(0, &self.create_raw_address(), 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn read_remote_rssi(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.read_remote_rssi(0, &self.create_raw_address()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn get_device_type(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.get_device_type(&self.create_raw_address()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn configure_mtu(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.configure_mtu(0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn conn_parameter_update(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.conn_parameter_update(&self.create_raw_address(), 0, 0, 0, 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn client_set_preferred_phy( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - let client = &mut self.gatt.lock().unwrap().client; - client.set_preferred_phy(&self.create_raw_address(), 0, 0, 0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn client_read_phy(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.read_phy(0, &self.create_raw_address()); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn test_command(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - println!("Not implemented!"); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn get_gatt_db(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - let client = &mut self.gatt.lock().unwrap().client; - client.get_gatt_db(0); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn register_server(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn unregister_server(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn server_connect(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn server_disconnect(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn add_service(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn stop_service(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn delete_service(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn send_indication(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn send_response(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn server_set_preferred_phy( - &mut self, - ctx: RpcContext<'_>, - _req: Empty, - sink: UnarySink<Empty>, - ) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn server_read_phy(&mut self, ctx: RpcContext<'_>, _req: Empty, sink: UnarySink<Empty>) { - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } -} diff --git a/system/gd/rust/topshim/facade/src/hf_client_service.rs b/system/gd/rust/topshim/facade/src/hf_client_service.rs deleted file mode 100644 index 1a5faf8ccc..0000000000 --- a/system/gd/rust/topshim/facade/src/hf_client_service.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! HF Client service facade - -use bt_topshim::btif::{BluetoothInterface, RawAddress, ToggleableProfile}; -use bt_topshim::profiles::hf_client::{BthfClientCallbacksDispatcher, HfClient}; -use bt_topshim_facade_protobuf::facade::{ - ConnectAudioRequest, ConnectAudioResponse, DisconnectAudioRequest, DisconnectAudioResponse, - StartSlcRequest, StartSlcResponse, StopSlcRequest, StopSlcResponse, -}; -use bt_topshim_facade_protobuf::facade_grpc::{create_hf_client_service, HfClientService}; -use grpcio::*; -use num_traits::cast::ToPrimitive; - -use std::str::from_utf8; -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; - -fn get_hf_client_dispatcher() -> BthfClientCallbacksDispatcher { - BthfClientCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } -} - -/// Main object for Hf(Hands free) client facade service -#[derive(Clone)] -pub struct HfClientServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - pub hf_client: Arc<Mutex<HfClient>>, -} - -impl HfClientServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - let hf_client = Arc::new(Mutex::new(HfClient::new(&btif_intf.lock().unwrap()))); - hf_client.lock().unwrap().initialize(get_hf_client_dispatcher()); - hf_client.lock().unwrap().enable(); - create_hf_client_service(Self { rt, hf_client }) - } -} - -impl HfClientService for HfClientServiceImpl { - fn start_slc( - &mut self, - ctx: RpcContext<'_>, - req: StartSlcRequest, - sink: UnarySink<StartSlcResponse>, - ) { - let hf_client = self.hf_client.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - let status = hf_client.lock().unwrap().connect(addr); - let mut resp = StartSlcResponse::new(); - resp.status = status.to_i32().unwrap(); - sink.success(resp).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn stop_slc( - &mut self, - ctx: RpcContext<'_>, - req: StopSlcRequest, - sink: UnarySink<StopSlcResponse>, - ) { - let hf_client = self.hf_client.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - let status = hf_client.lock().unwrap().disconnect(addr); - let mut resp = StopSlcResponse::new(); - resp.status = status.to_i32().unwrap(); - sink.success(resp).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn connect_audio( - &mut self, - ctx: RpcContext<'_>, - req: ConnectAudioRequest, - sink: UnarySink<ConnectAudioResponse>, - ) { - let hf_client = self.hf_client.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - let status = hf_client.lock().unwrap().connect_audio(addr); - let mut resp = ConnectAudioResponse::new(); - resp.status = status.to_i32().unwrap(); - sink.success(resp).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn disconnect_audio( - &mut self, - ctx: RpcContext<'_>, - req: DisconnectAudioRequest, - sink: UnarySink<DisconnectAudioResponse>, - ) { - let hf_client = self.hf_client.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - let status = hf_client.lock().unwrap().disconnect_audio(addr); - let mut resp = DisconnectAudioResponse::new(); - resp.status = status.to_i32().unwrap(); - sink.success(resp).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } -} diff --git a/system/gd/rust/topshim/facade/src/hfp_service.rs b/system/gd/rust/topshim/facade/src/hfp_service.rs deleted file mode 100644 index 4f08a44c50..0000000000 --- a/system/gd/rust/topshim/facade/src/hfp_service.rs +++ /dev/null @@ -1,212 +0,0 @@ -//! HFP service facade - -use bt_topshim::btif::{BluetoothInterface, RawAddress, ToggleableProfile}; -use bt_topshim::profiles::hfp::{Hfp, HfpCallbacks, HfpCallbacksDispatcher}; -use bt_topshim_facade_protobuf::empty::Empty; -use bt_topshim_facade_protobuf::facade::{ - ConnectAudioRequest, DisconnectAudioRequest, EventType, FetchEventsRequest, - FetchEventsResponse, SetVolumeRequest, StartSlcRequest, StopSlcRequest, -}; -use bt_topshim_facade_protobuf::facade_grpc::{create_hfp_service, HfpService}; -use futures::sink::SinkExt; -use grpcio::*; - -use std::str::from_utf8; -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; -use tokio::sync::mpsc; - -fn get_hfp_dispatcher( - _hfp: Arc<Mutex<Hfp>>, - tx: Arc<Mutex<Option<mpsc::Sender<HfpCallbacks>>>>, -) -> HfpCallbacksDispatcher { - HfpCallbacksDispatcher { - dispatch: Box::new(move |cb: HfpCallbacks| { - println!("Hfp Callback found {:?}", cb); - if let HfpCallbacks::ConnectionState(state, address) = &cb { - println!("Hfp Connection state changed to {:?} for address {:?}", state, address); - } - let guard_tx = tx.lock().unwrap(); - if let Some(event_tx) = guard_tx.as_ref() { - let txclone = event_tx.clone(); - if txclone.try_send(cb.clone()).is_err() { - println!("Cannot send event {:?}", cb); - } - /*tokio::spawn(async move { - let _ = txclone.send(cb).await; - });*/ - } - }), - } -} - -/// Main object for Hfp facade service -#[derive(Clone)] -pub struct HfpServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - pub btif_hfp: Arc<Mutex<Hfp>>, - #[allow(dead_code)] - event_tx: Arc<Mutex<Option<mpsc::Sender<HfpCallbacks>>>>, -} - -impl HfpServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - let btif_hfp = Arc::new(Mutex::new(Hfp::new(&btif_intf.lock().unwrap()))); - let event_tx = Arc::new(Mutex::new(None)); - btif_hfp.lock().unwrap().initialize(get_hfp_dispatcher(btif_hfp.clone(), event_tx.clone())); - btif_hfp.lock().unwrap().enable(); - create_hfp_service(Self { rt, btif_hfp, event_tx }) - } -} - -impl HfpService for HfpServiceImpl { - fn start_slc(&mut self, ctx: RpcContext<'_>, req: StartSlcRequest, sink: UnarySink<Empty>) { - let hfp = self.btif_hfp.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - hfp.lock().unwrap().connect(addr); - sink.success(Empty::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn stop_slc(&mut self, ctx: RpcContext<'_>, req: StopSlcRequest, sink: UnarySink<Empty>) { - let hfp = self.btif_hfp.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - hfp.lock().unwrap().disconnect(addr); - sink.success(Empty::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn connect_audio( - &mut self, - ctx: RpcContext<'_>, - req: ConnectAudioRequest, - sink: UnarySink<Empty>, - ) { - let hfp = self.btif_hfp.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - hfp.lock().unwrap().connect_audio( - addr, - req.is_sco_offload_enabled, - req.disabled_codecs, - ); - hfp.lock().unwrap().set_active_device(addr); - sink.success(Empty::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn disconnect_audio( - &mut self, - ctx: RpcContext<'_>, - req: DisconnectAudioRequest, - sink: UnarySink<Empty>, - ) { - let hfp = self.btif_hfp.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - hfp.lock().unwrap().disconnect_audio(addr); - sink.success(Empty::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn set_volume(&mut self, ctx: RpcContext<'_>, req: SetVolumeRequest, sink: UnarySink<Empty>) { - let hfp = self.btif_hfp.clone(); - ctx.spawn(async move { - let addr_bytes = &req.connection.unwrap().cookie; - let bt_addr = from_utf8(addr_bytes).unwrap(); - if let Some(addr) = RawAddress::from_string(bt_addr) { - // TODO(aritrasen): Consider using TryFrom and cap the maximum volume here - // since `as` silently deals with data overflow, which might not be preferred. - hfp.lock().unwrap().set_volume(req.volume as i8, addr); - sink.success(Empty::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }) - } - - fn fetch_events( - &mut self, - ctx: RpcContext<'_>, - _req: FetchEventsRequest, - mut sink: ServerStreamingSink<FetchEventsResponse>, - ) { - let (tx, mut rx) = mpsc::channel(10); - { - let mut guard = self.event_tx.lock().unwrap(); - if guard.is_some() { - ctx.spawn(async move { - sink.fail(RpcStatus::with_message( - RpcStatusCode::UNAVAILABLE, - String::from("Profile is currently already connected and streaming"), - )) - .await - .unwrap(); - }); - return; - } else { - *guard = Some(tx); - } - } - - ctx.spawn(async move { - while let Some(event) = rx.recv().await { - if let HfpCallbacks::ConnectionState(state, address) = event { - let mut rsp = FetchEventsResponse::new(); - rsp.event_type = EventType::HFP_CONNECTION_STATE.into(); - rsp.data = format!("{:?}, {}", state, address.to_string()); - sink.send((rsp, WriteFlags::default())).await.unwrap(); - } - } - }) - } -} diff --git a/system/gd/rust/topshim/facade/src/main.rs b/system/gd/rust/topshim/facade/src/main.rs deleted file mode 100644 index a8a9745752..0000000000 --- a/system/gd/rust/topshim/facade/src/main.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Starts the facade services that allow us to test the Bluetooth stack - -use bt_topshim::btif; - -use clap::{value_parser, Arg, Command}; -use futures::channel::mpsc; -use futures::executor::block_on; -use futures::stream::StreamExt; -use grpcio::*; -use log::debug; -use nix::sys::signal; -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; - -mod adapter_service; -mod gatt_service; -mod hf_client_service; -mod hfp_service; -mod media_service; -mod security_service; -mod utils; - -// This is needed for linking, libbt_shim_bridge needs symbols defined by -// bt_shim, however bt_shim depends on rust crates (future, tokio) that -// we use too, if we build and link them separately we ends with duplicate -// symbols. To solve that we build bt_shim with bt_topshim_facade so the rust -// compiler share the transitive dependencies. -// -// The `::*` is here to circuvent the single_component_path_imports from -// clippy that is denied on the rust command line so we can't just allow it. -// This is fine for now since bt_shim doesn't export anything -#[allow(unused)] -use bluetooth_core_rs_for_facade::*; - -fn main() { - // 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)); -} - -fn clap_command() -> Command { - Command::new("bluetooth_topshim_facade") - .about("The bluetooth topshim stack, with testing facades enabled and exposed via gRPC.") - .arg( - Arg::new("grpc-port") - .long("grpc-port") - .value_parser(value_parser!(u16)) - .default_value("8899"), - ) - .arg( - Arg::new("root-server-port") - .long("root-server-port") - .value_parser(value_parser!(u16)) - .default_value("8897"), - ) - .arg( - Arg::new("signal-port") - .long("signal-port") - .value_parser(value_parser!(u16)) - .default_value("8895"), - ) - .arg(Arg::new("rootcanal-port").long("rootcanal-port").value_parser(value_parser!(u16))) - .arg(Arg::new("btsnoop").long("btsnoop")) - .arg(Arg::new("btsnooz").long("btsnooz")) - .arg(Arg::new("btconfig").long("btconfig")) - .arg( - Arg::new("start-stack-now") - .long("start-stack-now") - .value_parser(value_parser!(bool)) - .default_value("true"), - ) -} - -async fn async_main(rt: Arc<Runtime>, mut sigint: mpsc::UnboundedReceiver<()>) { - let matches = clap_command().get_matches(); - - let grpc_port = *matches.get_one::<u16>("grpc-port").unwrap(); - let _rootcanal_port = matches.get_one::<u16>("rootcanal-port").cloned(); - let env = Arc::new(Environment::new(2)); - - let btif_intf = Arc::new(Mutex::new(btif::get_btinterface())); - - // AdapterServiceImpl::create initializes the stack; not the best practice because the side effect is hidden - let adapter_service_impl = - adapter_service::AdapterServiceImpl::create(rt.clone(), btif_intf.clone()); - - let security_service_impl = - security_service::SecurityServiceImpl::create(rt.clone(), btif_intf.clone()); - - let gatt_service_impl = gatt_service::GattServiceImpl::create(rt.clone(), btif_intf.clone()); - - let hf_client_service_impl = - hf_client_service::HfClientServiceImpl::create(rt.clone(), btif_intf.clone()); - - let hfp_service_impl = hfp_service::HfpServiceImpl::create(rt.clone(), btif_intf.clone()); - - let media_service_impl = media_service::MediaServiceImpl::create(rt.clone(), btif_intf.clone()); - - let start_stack_now = *matches.get_one::<bool>("start-stack-now").unwrap(); - - if start_stack_now { - btif_intf.clone().lock().unwrap().enable(); - } - - let mut server = ServerBuilder::new(env) - .register_service(adapter_service_impl) - .register_service(security_service_impl) - .register_service(gatt_service_impl) - .register_service(hf_client_service_impl) - .register_service(hfp_service_impl) - .register_service(media_service_impl) - .build() - .unwrap(); - let addr = format!("0.0.0.0:{}", grpc_port); - let creds = ServerCredentials::insecure(); - server.add_listening_port(addr, creds).unwrap(); - server.start(); - - sigint.next().await; - block_on(server.shutdown()).unwrap(); -} - -// TODO: remove as this is a temporary nix-based hack to catch SIGINT -/// # 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); - - let sig_action = signal::SigAction::new( - signal::SigHandler::Handler(handle_sigint), - 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(); - } - - rx -} - -static SIGINT_TX: Mutex<Option<mpsc::UnboundedSender<()>>> = Mutex::new(None); - -extern "C" fn handle_sigint(_: i32) { - let mut sigint_tx = SIGINT_TX.lock().unwrap(); - if let Some(tx) = &*sigint_tx { - debug!("Stopping gRPC root server due to SIGINT"); - tx.unbounded_send(()).unwrap(); - } - *sigint_tx = None; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn verify_comand() { - clap_command().debug_assert(); - } -} diff --git a/system/gd/rust/topshim/facade/src/media_service.rs b/system/gd/rust/topshim/facade/src/media_service.rs deleted file mode 100644 index aba1a21f94..0000000000 --- a/system/gd/rust/topshim/facade/src/media_service.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Media service facade - -use bt_topshim::btif::{BluetoothInterface, RawAddress}; -use bt_topshim::profiles::a2dp::{ - A2dp, A2dpCallbacksDispatcher, A2dpSink, A2dpSinkCallbacksDispatcher, -}; -use bt_topshim::profiles::avrcp::{Avrcp, AvrcpCallbacksDispatcher}; -use bt_topshim_facade_protobuf::facade::{ - A2dpSourceConnectRequest, A2dpSourceConnectResponse, StartA2dpRequest, StartA2dpResponse, -}; -use bt_topshim_facade_protobuf::facade_grpc::{create_media_service, MediaService}; - -use grpcio::*; - -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; - -fn get_a2dp_dispatcher() -> A2dpCallbacksDispatcher { - A2dpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } -} - -fn get_a2dp_sink_dispatcher() -> A2dpSinkCallbacksDispatcher { - A2dpSinkCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } -} - -fn get_avrcp_dispatcher() -> AvrcpCallbacksDispatcher { - AvrcpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } -} - -/// Main object for Media facade service -#[derive(Clone)] -pub struct MediaServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - pub btif_a2dp: Arc<Mutex<A2dp>>, - btif_a2dp_sink: Arc<Mutex<A2dpSink>>, - #[allow(dead_code)] - pub btif_avrcp: Arc<Mutex<Avrcp>>, -} - -impl MediaServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - let mut btif_a2dp = A2dp::new(&btif_intf.lock().unwrap()); - let btif_a2dp_sink = A2dpSink::new(&btif_intf.lock().unwrap()); - let mut btif_avrcp = Avrcp::new(&btif_intf.lock().unwrap()); - btif_a2dp.initialize(get_a2dp_dispatcher()); - btif_avrcp.initialize(get_avrcp_dispatcher()); - - create_media_service(Self { - rt, - btif_a2dp: Arc::new(Mutex::new(btif_a2dp)), - btif_a2dp_sink: Arc::new(Mutex::new(btif_a2dp_sink)), - btif_avrcp: Arc::new(Mutex::new(btif_avrcp)), - }) - } -} - -impl MediaService for MediaServiceImpl { - fn start_a2dp( - &mut self, - ctx: RpcContext<'_>, - req: StartA2dpRequest, - sink: UnarySink<StartA2dpResponse>, - ) { - if req.start_a2dp_source { - ctx.spawn(async move { - sink.success(StartA2dpResponse::default()).await.unwrap(); - }) - } else if req.start_a2dp_sink { - self.btif_a2dp_sink.lock().unwrap().initialize(get_a2dp_sink_dispatcher()); - ctx.spawn(async move { - sink.success(StartA2dpResponse::default()).await.unwrap(); - }) - } - } - - fn a2dp_source_connect( - &mut self, - ctx: RpcContext<'_>, - req: A2dpSourceConnectRequest, - sink: UnarySink<A2dpSourceConnectResponse>, - ) { - let a2dp = self.btif_a2dp.clone(); - ctx.spawn(async move { - if let Some(addr) = RawAddress::from_string(req.address.clone()) { - a2dp.lock().unwrap().connect(addr); - a2dp.lock().unwrap().set_active_device(addr); - sink.success(A2dpSourceConnectResponse::default()).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", req.address), - )) - .await - .unwrap(); - } - }) - } -} diff --git a/system/gd/rust/topshim/facade/src/security_service.rs b/system/gd/rust/topshim/facade/src/security_service.rs deleted file mode 100644 index e378a777ff..0000000000 --- a/system/gd/rust/topshim/facade/src/security_service.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Security service facade - -use bt_topshim::btif::{BluetoothInterface, BtTransport, RawAddress}; - -use bt_topshim_facade_protobuf::empty::Empty; -use bt_topshim_facade_protobuf::facade::{ - CreateBondRequest, CreateBondResponse, GenerateOobDataRequest, RemoveBondRequest, -}; -use bt_topshim_facade_protobuf::facade_grpc::{create_security_service, SecurityService}; -use grpcio::*; - -use std::sync::{Arc, Mutex}; -use tokio::runtime::Runtime; - -/// Main object for Adapter facade service -#[derive(Clone)] -pub struct SecurityServiceImpl { - #[allow(dead_code)] - rt: Arc<Runtime>, - #[allow(dead_code)] - btif_intf: Arc<Mutex<BluetoothInterface>>, -} - -#[allow(dead_code)] -impl SecurityServiceImpl { - /// Create a new instance of the root facade service - pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { - create_security_service(Self { rt, btif_intf }) - } -} - -impl SecurityService for SecurityServiceImpl { - fn remove_bond(&mut self, ctx: RpcContext<'_>, req: RemoveBondRequest, sink: UnarySink<Empty>) { - let raw_address = RawAddress::from_string(req.address).unwrap(); - self.btif_intf.lock().unwrap().remove_bond(&raw_address); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn generate_local_oob_data( - &mut self, - ctx: RpcContext<'_>, - req: GenerateOobDataRequest, - sink: UnarySink<Empty>, - ) { - self.btif_intf.lock().unwrap().generate_local_oob_data(req.transport); - ctx.spawn(async move { - sink.success(Empty::default()).await.unwrap(); - }) - } - - fn create_bond( - &mut self, - ctx: RpcContext<'_>, - req: CreateBondRequest, - sink: UnarySink<CreateBondResponse>, - ) { - let btif = self.btif_intf.clone(); - ctx.spawn(async move { - let bt_addr = &req.address; - if let Some(addr) = RawAddress::from_string(bt_addr) { - let status = - btif.lock().unwrap().create_bond(&addr, BtTransport::from(req.transport)); - let mut resp = CreateBondResponse::new(); - resp.status = status; - sink.success(resp).await.unwrap(); - } else { - sink.fail(RpcStatus::with_message( - RpcStatusCode::INVALID_ARGUMENT, - format!("Invalid Request Address: {}", bt_addr), - )) - .await - .unwrap(); - } - }); - } -} diff --git a/system/gd/rust/topshim/facade/src/utils.rs b/system/gd/rust/topshim/facade/src/utils.rs deleted file mode 100644 index 31d5d773b7..0000000000 --- a/system/gd/rust/topshim/facade/src/utils.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Utility modules and functions for facade services. - -pub mod converters { - use bt_topshim::btif::BluetoothProperty; - use bt_topshim_facade_protobuf::facade::EventData; - - pub fn bluetooth_property_to_event_data(property: BluetoothProperty) -> (String, EventData) { - match property { - BluetoothProperty::BdName(name) => { - (String::from("BdName"), event_data_from_string(name)) - } - BluetoothProperty::BdAddr(address) => { - (String::from("BdAddr"), event_data_from_string(address.to_string())) - } - BluetoothProperty::Uuids(uuids) => { - let mut event = EventData::new(); - for uuid in uuids { - event.data.push(format!("{:?}", uuid)); - } - (String::from("Uuids"), event) - } - BluetoothProperty::LocalIoCaps(caps) => { - (String::from("LocalIoCaps"), event_data_from_string(format!("{:?}", caps))) - } - _ => (String::from("skip"), EventData::new()), - } - } - - pub fn event_data_from_string(data: String) -> EventData { - let mut event = EventData::new(); - event.data.push(data); - event - } -} diff --git a/system/gd/rust/topshim/macros/src/lib.rs b/system/gd/rust/topshim/macros/src/lib.rs index 65aa24ddb1..78fc677491 100644 --- a/system/gd/rust/topshim/macros/src/lib.rs +++ b/system/gd/rust/topshim/macros/src/lib.rs @@ -103,17 +103,13 @@ pub fn cb_variant(input: TokenStream) -> TokenStream { let ident = format_ident!("_{}", i); params.extend(quote! { #ident: #start, }); - match end { - Some(v) => { - // Argument needs an into translation if it doesn't match the start - if start != v { - args.extend(quote! { #end::from(#ident), }); - } else { - args.extend(quote! {#ident,}); - } + if let Some(v) = end { + // Argument needs an into translation if it doesn't match the start + if start != v { + args.extend(quote! { #end::from(#ident), }); + } else { + args.extend(quote! {#ident,}); } - // If there's no end type, just consume it instead. - None => (), } } diff --git a/system/gd/rust/topshim/src/profiles/gatt.rs b/system/gd/rust/topshim/src/profiles/gatt.rs index a6884310b6..7bed7bdf78 100644 --- a/system/gd/rust/topshim/src/profiles/gatt.rs +++ b/system/gd/rust/topshim/src/profiles/gatt.rs @@ -2,8 +2,8 @@ use crate::bindings::root as bindings; use crate::btif::{ptr_to_vec, BluetoothInterface, BtStatus, RawAddress, SupportedProfiles, Uuid}; use crate::profiles::gatt::bindings::{ btgatt_callbacks_t, btgatt_client_callbacks_t, btgatt_client_interface_t, btgatt_interface_t, - btgatt_scanner_callbacks_t, btgatt_server_callbacks_t, btgatt_server_interface_t, - BleAdvertiserInterface, BleScannerInterface, + btgatt_server_callbacks_t, btgatt_server_interface_t, BleAdvertiserInterface, + BleScannerInterface, }; use crate::topstack::get_dispatchers; use crate::utils::LTCheckedPtr; @@ -1723,7 +1723,6 @@ pub struct Gatt { callbacks: Option<Box<bindings::btgatt_callbacks_t>>, gatt_client_callbacks: Option<Box<bindings::btgatt_client_callbacks_t>>, gatt_server_callbacks: Option<Box<bindings::btgatt_server_callbacks_t>>, - gatt_scanner_callbacks: Option<Box<bindings::btgatt_scanner_callbacks_t>>, } impl Gatt { @@ -1759,7 +1758,6 @@ impl Gatt { callbacks: None, gatt_client_callbacks: None, gatt_server_callbacks: None, - gatt_scanner_callbacks: None, } } @@ -1869,18 +1867,10 @@ impl Gatt { subrate_chg_cb: Some(gs_subrate_chg_cb), }); - let gatt_scanner_callbacks = Box::new(btgatt_scanner_callbacks_t { - scan_result_cb: None, - batchscan_reports_cb: None, - batchscan_threshold_cb: None, - track_adv_event_cb: None, - }); - let callbacks = Box::new(btgatt_callbacks_t { size: std::mem::size_of::<btgatt_callbacks_t>(), client: &*gatt_client_callbacks, server: &*gatt_server_callbacks, - scanner: &*gatt_scanner_callbacks, }); let cb_ptr = LTCheckedPtr::from(&callbacks); @@ -1890,7 +1880,6 @@ impl Gatt { self.callbacks = Some(callbacks); self.gatt_client_callbacks = Some(gatt_client_callbacks); self.gatt_server_callbacks = Some(gatt_server_callbacks); - self.gatt_scanner_callbacks = Some(gatt_scanner_callbacks); // Register callbacks for gatt scanner and advertiser mutcxxcall!(self.scanner, RegisterCallbacks); diff --git a/system/gd/rust/topshim/src/profiles/socket.rs b/system/gd/rust/topshim/src/profiles/socket.rs index 49a0f15cde..d8cfd4590a 100644 --- a/system/gd/rust/topshim/src/profiles/socket.rs +++ b/system/gd/rust/topshim/src/profiles/socket.rs @@ -164,6 +164,12 @@ impl BtSocket { let name = CString::new(service_name).expect("Service name has null in it."); let name_ptr = LTCheckedPtr::from(&name); + let data_path: u32 = 0; + let sock_name = CString::new("test").expect("Socket name has null in it"); + let hub_id: u64 = 0; + let endpoint_id: u64 = 0; + let max_rx_packet_size: i32 = 0; + let status: BtStatus = ccall!( self, listen, @@ -173,7 +179,12 @@ impl BtSocket { channel, sockfd_ptr.into(), flags, - calling_uid + calling_uid, + data_path, + sock_name.as_ptr(), + hub_id, + endpoint_id, + max_rx_packet_size ) .into(); @@ -194,6 +205,12 @@ impl BtSocket { let uuid_ptr = LTCheckedPtr::from(&service_uuid); let addr_ptr = LTCheckedPtr::from_ref(&addr); + let data_path: u32 = 0; + let sock_name = CString::new("test").expect("Socket name has null in it"); + let hub_id: u64 = 0; + let endpoint_id: u64 = 0; + let max_rx_packet_size: i32 = 0; + let status: BtStatus = ccall!( self, connect, @@ -203,7 +220,12 @@ impl BtSocket { channel, sockfd_ptr.into(), flags, - calling_uid + calling_uid, + data_path, + sock_name.as_ptr(), + hub_id, + endpoint_id, + max_rx_packet_size ) .into(); @@ -274,6 +296,7 @@ mod tests { max_rx_packet_size: 17_u16, conn_uuid_lsb: 0x0000113500001135_u64, conn_uuid_msb: 0x1135000011350000_u64, + socket_id: 0x1135113511351135_u64, }; // SAFETY: The sock_connect_signal_t type has size CONNECT_COMPLETE_SIZE, // and has no padding, so it's safe to convert it to a byte array. diff --git a/system/gd/shim/dumpsys.cc b/system/gd/shim/dumpsys.cc deleted file mode 100644 index ae5d2c9fad..0000000000 --- a/system/gd/shim/dumpsys.cc +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2019 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. - */ -#define LOG_TAG "bt_gd_shim" - -#include "dumpsys/dumpsys.h" - -#include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> -#include <unistd.h> - -#include <future> -#include <sstream> -#include <string> - -#include "dumpsys/filter.h" -#include "hal/snoop_logger.h" -#include "hci/acl_manager.h" -#include "hci/controller_interface.h" -#include "main/shim/entry.h" -#include "main/shim/stack.h" -#include "module.h" -#include "module_dumper.h" -#include "os/system_properties.h" -#include "shim/dumpsys.h" - -namespace bluetooth { -namespace shim { - -static const std::string kReadOnlyDebuggableProperty = "ro.debuggable"; - -namespace { -constexpr char kModuleName[] = "shim::Dumpsys"; -constexpr char kDumpsysTitle[] = "----- Gd Dumpsys ------"; -} // namespace - -struct Dumpsys::impl { -public: - void DumpWithArgsSync(int fd, const char** args, std::promise<void> promise); - int GetNumberOfBundledSchemas() const; - - impl(const Dumpsys& dumpsys_module, const dumpsys::ReflectionSchema& reflection_schema); - ~impl() = default; - -protected: - void FilterSchema(std::string* dumpsys_data) const; - std::string PrintAsJson(std::string* dumpsys_data) const; - - bool IsDebuggable() const; - -private: - void DumpWithArgsAsync(int fd, const char** args) const; - - const Dumpsys& dumpsys_module_; - const dumpsys::ReflectionSchema reflection_schema_; -}; - -const ModuleFactory Dumpsys::Factory = - ModuleFactory([]() { return new Dumpsys(bluetooth::dumpsys::GetBundledSchemaData()); }); - -Dumpsys::impl::impl(const Dumpsys& dumpsys_module, - const dumpsys::ReflectionSchema& reflection_schema) - : dumpsys_module_(dumpsys_module), reflection_schema_(std::move(reflection_schema)) {} - -int Dumpsys::impl::GetNumberOfBundledSchemas() const { - return reflection_schema_.GetNumberOfBundledSchemas(); -} - -bool Dumpsys::impl::IsDebuggable() const { - return os::GetSystemProperty(kReadOnlyDebuggableProperty) == "1"; -} - -void Dumpsys::impl::FilterSchema(std::string* dumpsys_data) const { - log::assert_that(dumpsys_data != nullptr, "assert failed: dumpsys_data != nullptr"); - dumpsys::FilterSchema(reflection_schema_, dumpsys_data); -} - -std::string Dumpsys::impl::PrintAsJson(std::string* dumpsys_data) const { - log::assert_that(dumpsys_data != nullptr, "assert failed: dumpsys_data != nullptr"); - - const std::string root_name = reflection_schema_.GetRootName(); - if (root_name.empty()) { - char buf[255]; - snprintf(buf, sizeof(buf), "ERROR: Unable to find root name in prebundled reflection schema\n"); - log::warn("{}", buf); - return std::string(buf); - } - - const reflection::Schema* schema = reflection_schema_.FindInReflectionSchema(root_name); - if (schema == nullptr) { - char buf[255]; - snprintf(buf, sizeof(buf), "ERROR: Unable to find schema root name:%s\n", root_name.c_str()); - log::warn("{}", buf); - return std::string(buf); - } - - flatbuffers::IDLOptions options{}; - options.output_default_scalars_in_json = true; - flatbuffers::Parser parser{options}; - if (!parser.Deserialize(schema)) { - char buf[255]; - snprintf(buf, sizeof(buf), "ERROR: Unable to deserialize bundle root name:%s\n", - root_name.c_str()); - log::warn("{}", buf); - return std::string(buf); - } - - std::string jsongen; - // GenerateText was renamed to GenText in 23.5.26 because the return behavior was changed. - // https://github.com/google/flatbuffers/commit/950a71ab893e96147c30dd91735af6db73f72ae0 -#if FLATBUFFERS_VERSION_MAJOR < 23 || \ - (FLATBUFFERS_VERSION_MAJOR == 23 && \ - (FLATBUFFERS_VERSION_MINOR < 5 || \ - (FLATBUFFERS_VERSION_MINOR == 5 && FLATBUFFERS_VERSION_REVISION < 26))) - flatbuffers::GenerateText(parser, dumpsys_data->data(), &jsongen); -#else - const char* error = flatbuffers::GenText(parser, dumpsys_data->data(), &jsongen); - if (error != nullptr) { - log::warn("{}", error); - } -#endif - return jsongen; -} - -void Dumpsys::impl::DumpWithArgsAsync(int fd, const char** /*args*/) const { - const auto registry = dumpsys_module_.GetModuleRegistry(); - bluetooth::shim::GetController()->Dump(fd); - bluetooth::shim::GetAclManager()->Dump(fd); - bluetooth::shim::GetSnoopLogger()->DumpSnoozLogToFile(); - - ModuleDumper dumper(fd, *registry, kDumpsysTitle); - std::string dumpsys_data; - std::ostringstream oss; - dumper.DumpState(&dumpsys_data, oss); - - dprintf(fd, " ----- Filtering as Developer -----\n"); - FilterSchema(&dumpsys_data); - - dprintf(fd, "%s", PrintAsJson(&dumpsys_data).c_str()); -} - -void Dumpsys::impl::DumpWithArgsSync(int fd, const char** args, std::promise<void> promise) { - if (bluetooth::shim::Stack::GetInstance()->LockForDumpsys([=, *this]() { - log::info("Started dumpsys procedure"); - this->DumpWithArgsAsync(fd, args); - })) { - log::info("Successful dumpsys procedure"); - } else { - log::info("Failed dumpsys procedure as stack was not longer active"); - } - promise.set_value(); -} - -Dumpsys::Dumpsys(const std::string& pre_bundled_schema) - : reflection_schema_(dumpsys::ReflectionSchema(pre_bundled_schema)) {} - -void Dumpsys::Dump(int fd, const char** args, std::promise<void> promise) { - if (fd <= 0) { - promise.set_value(); - return; - } - CallOn(pimpl_.get(), &Dumpsys::impl::DumpWithArgsSync, fd, args, std::move(promise)); -} - -/** - * Module methods - */ -void Dumpsys::ListDependencies(ModuleList* /* list */) const {} - -void Dumpsys::Start() { pimpl_ = std::make_unique<impl>(*this, reflection_schema_); } - -void Dumpsys::Stop() { pimpl_.reset(); } - -std::string Dumpsys::ToString() const { return kModuleName; } - -} // namespace shim -} // namespace bluetooth diff --git a/system/gd/shim/dumpsys.h b/system/gd/shim/dumpsys.h deleted file mode 100644 index 95756cdfe9..0000000000 --- a/system/gd/shim/dumpsys.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019 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 <future> -#include <memory> -#include <string> - -#include "dumpsys/reflection_schema.h" -#include "module.h" - -namespace bluetooth { -namespace shim { - -constexpr char kArgumentDeveloper[] = "--dev"; - -class Dumpsys : public bluetooth::Module { -public: - void Dump(int fd, const char** args, std::promise<void> promise); - - Dumpsys(const std::string& pre_bundled_schema); - Dumpsys(const Dumpsys&) = delete; - Dumpsys& operator=(const Dumpsys&) = delete; - - ~Dumpsys() = default; - - static const ModuleFactory Factory; - -protected: - void ListDependencies(ModuleList* list) const override; // Module - void Start() override; // Module - void Stop() override; // Module - std::string ToString() const override; // Module - -private: - struct impl; - std::unique_ptr<impl> pimpl_; - const dumpsys::ReflectionSchema reflection_schema_; -}; - -} // namespace shim -} // namespace bluetooth diff --git a/system/gd/shim/dumpsys_test.cc b/system/gd/shim/dumpsys_test.cc deleted file mode 100644 index 8107f20a6c..0000000000 --- a/system/gd/shim/dumpsys_test.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2020 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 "shim/dumpsys.h" - -#include <gtest/gtest.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include <future> - -#include "module.h" -#include "os/thread.h" -#include "test_data/dumpsys_test_data_bin.h" - -namespace testing { - -using bluetooth::TestModuleRegistry; -using namespace bluetooth; - -namespace { - -bool SimpleJsonValidator(int fd, int* dumpsys_byte_cnt) { - char buf{0}; - bool within_double_quotes{false}; - int left_bracket{0}, right_bracket{0}; - while (read(fd, &buf, 1) != -1) { - switch (buf) { - (*dumpsys_byte_cnt)++; - case '"': - within_double_quotes = !within_double_quotes; - break; - case '{': - if (!within_double_quotes) { - left_bracket++; - } - break; - case '}': - if (!within_double_quotes) { - right_bracket++; - } - break; - default: - break; - } - } - return left_bracket == right_bracket; -} - -} // namespace - -// To create dumpsys_test_header_bin.h: -// make bluetooth_flatbuffer_bundler -// ${ANDROID_BUILD_TOP}/out/host/linux-x86/bin/bluetooth_flatbuffer_bundler -w -m -// bluetooth.DumpsysData -f test_gen/dumpsys_test_data_bin -n bluetooth::test test_gen/* - -class DumpsysTest : public Test { -protected: - void SetUp() override { - dumpsys_module_ = new bluetooth::shim::Dumpsys(bluetooth::test::GetBundledSchemaData()); - fake_registry_.InjectTestModule(&shim::Dumpsys::Factory, dumpsys_module_); - } - - void TearDown() override { fake_registry_.StopAll(); } - - int GetSocketBufferSize(int sockfd) { - int socket_buffer_size; - socklen_t optlen = sizeof(socket_buffer_size); - getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&socket_buffer_size, &optlen); - return socket_buffer_size; - } - - void SetSocketBufferSize(int sockfd, int socket_buffer_size) { - socklen_t optlen = sizeof(socket_buffer_size); - ASSERT_EQ(0, - setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const void*)&socket_buffer_size, optlen)); - } - - TestModuleRegistry fake_registry_; - os::Thread& thread_ = fake_registry_.GetTestThread(); - bluetooth::shim::Dumpsys* dumpsys_module_ = nullptr; - os::Handler* client_handler_ = nullptr; -}; - -TEST_F(DumpsysTest, dump_as_developer) { - const char* args[]{bluetooth::shim::kArgumentDeveloper, nullptr}; - - int sv[2]; - ASSERT_EQ(0, socketpair(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, sv)); - int socket_buffer_size = GetSocketBufferSize(sv[0]); - - std::promise<void> promise; - std::future future = promise.get_future(); - dumpsys_module_->Dump(sv[0], args, std::move(promise)); - future.wait(); - - int dumpsys_byte_cnt = 0; - ASSERT_TRUE(SimpleJsonValidator(sv[1], &dumpsys_byte_cnt)); - ASSERT_TRUE(dumpsys_byte_cnt < socket_buffer_size); -} - -TEST_F(DumpsysTest, dump_as_user) { - const char* args[]{"not-a-developer-option", nullptr}; - - int sv[2]; - ASSERT_EQ(0, socketpair(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0, sv)); - int socket_buffer_size = GetSocketBufferSize(sv[0]); - - std::promise<void> promise; - std::future future = promise.get_future(); - dumpsys_module_->Dump(sv[0], args, std::move(promise)); - future.wait(); - - int dumpsys_byte_cnt = 0; - ASSERT_TRUE(SimpleJsonValidator(sv[1], &dumpsys_byte_cnt)); - ASSERT_TRUE(dumpsys_byte_cnt < socket_buffer_size); -} - -} // namespace testing diff --git a/system/gd/shim/test_data/dumpsys_data.bfbs b/system/gd/shim/test_data/dumpsys_data.bfbs Binary files differdeleted file mode 100644 index afe2ea5407..0000000000 --- a/system/gd/shim/test_data/dumpsys_data.bfbs +++ /dev/null diff --git a/system/gd/shim/test_data/dumpsys_test_data_bin b/system/gd/shim/test_data/dumpsys_test_data_bin Binary files differdeleted file mode 100644 index b9c410d5c7..0000000000 --- a/system/gd/shim/test_data/dumpsys_test_data_bin +++ /dev/null diff --git a/system/gd/shim/test_data/dumpsys_test_data_bin.h b/system/gd/shim/test_data/dumpsys_test_data_bin.h deleted file mode 100644 index d1f118d0fa..0000000000 --- a/system/gd/shim/test_data/dumpsys_test_data_bin.h +++ /dev/null @@ -1,76 +0,0 @@ -// Generated file by bluetooth_flatbuffer bundler -#pragma once -#include <sys/types.h> - -#include <string> -namespace bluetooth { -namespace test { -extern const unsigned char* data; -extern const size_t data_size; -const std::string& GetBundledSchemaData(); -} // namespace test -} // namespace bluetooth -const unsigned char data_[] = { - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x62, - 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, - 0x73, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x42, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x64, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x73, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, - 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x42, 0x46, 0x42, 0x53, 0x10, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x38, 0x01, - 0x00, 0x00, 0x3c, 0xfe, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0xf4, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, - 0x74, 0x68, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, 0x44, 0x61, 0x74, 0x61, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x82, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x14, 0x00, 0x08, - 0x00, 0x0c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x4c, 0x00, 0x00, - 0x00, 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x74, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x41, 0x6e, 0x79, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x63, 0x79, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0xb6, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, - 0x00, 0x00, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0x14, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, - 0x74, 0x68, 0x2e, 0x73, 0x68, 0x69, 0x6d, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x68, - 0xff, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x50, 0xff, 0xff, - 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x6e, - 0x79, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x00, 0x46, - 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x62, - 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x55, 0x6e, 0x69, 0x74, 0x54, 0x65, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x00, 0x14, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x41, 0x6e, 0x79, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x63, 0x79, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x00, 0x00, - 0x00, 0x15, 0x00, 0x00, 0x00, 0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x2e, - 0x44, 0x75, 0x6d, 0x70, 0x73, 0x79, 0x73, 0x44, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00}; -const std::string string_data_(data_, data_ + sizeof(data_)); -const unsigned char* bluetooth::test::data = data_; -const size_t bluetooth::test::data_size = 884; -const std::string& bluetooth::test::GetBundledSchemaData() { return string_data_; } diff --git a/system/gd/stack_manager.cc b/system/gd/stack_manager.cc deleted file mode 100644 index 856d10a3c4..0000000000 --- a/system/gd/stack_manager.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2019 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_manager.h" - -#include <bluetooth/log.h> - -#include <chrono> -#include <future> -#include <queue> - -#include "common/bind.h" -#include "module.h" -#include "os/handler.h" -#include "os/system_properties.h" -#include "os/thread.h" -#include "os/wakelock_manager.h" - -using ::bluetooth::os::Handler; -using ::bluetooth::os::Thread; -using ::bluetooth::os::WakelockManager; - -namespace bluetooth { - -void StackManager::StartUp(ModuleList* modules, Thread* stack_thread) { - management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL); - handler_ = new Handler(management_thread_); - - WakelockManager::Get().Acquire(); - - std::promise<void> promise; - auto future = promise.get_future(); - handler_->Post(common::BindOnce(&StackManager::handle_start_up, common::Unretained(this), modules, - stack_thread, std::move(promise))); - - auto init_status = future.wait_for( - std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ true))); - - WakelockManager::Get().Release(); - - log::info("init_status == {}", int(init_status)); - - log::assert_that(init_status == std::future_status::ready, "Can't start stack, last instance: {}", - registry_.last_instance_); - - log::info("init complete"); -} - -void StackManager::handle_start_up(ModuleList* modules, Thread* stack_thread, - std::promise<void> promise) { - registry_.Start(modules, stack_thread); - promise.set_value(); -} - -void StackManager::ShutDown() { - WakelockManager::Get().Acquire(); - - std::promise<void> promise; - auto future = promise.get_future(); - handler_->Post(common::BindOnce(&StackManager::handle_shut_down, common::Unretained(this), - std::move(promise))); - - auto stop_status = future.wait_for( - std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ false))); - - WakelockManager::Get().Release(); - WakelockManager::Get().CleanUp(); - - log::assert_that(stop_status == std::future_status::ready, "Can't stop stack, last instance: {}", - registry_.last_instance_); - - handler_->Clear(); - handler_->WaitUntilStopped(std::chrono::milliseconds(2000)); - delete handler_; - delete management_thread_; -} - -void StackManager::handle_shut_down(std::promise<void> promise) { - registry_.StopAll(); - promise.set_value(); -} - -std::chrono::milliseconds StackManager::get_gd_stack_timeout_ms(bool is_start) { - auto gd_timeout = os::GetSystemPropertyUint32( - is_start ? "bluetooth.gd.start_timeout" : "bluetooth.gd.stop_timeout", - /* default_value = */ is_start ? 3000 : 5000); - return std::chrono::milliseconds(gd_timeout * - os::GetSystemPropertyUint32("ro.hw_timeout_multiplier", - /* default_value = */ 1)); -} - -} // namespace bluetooth diff --git a/system/gd/stack_manager.h b/system/gd/stack_manager.h deleted file mode 100644 index 892efc3c3b..0000000000 --- a/system/gd/stack_manager.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2019 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 "module.h" -#include "os/handler.h" -#include "os/thread.h" - -namespace bluetooth { - -class StackManager { -public: - void StartUp(ModuleList* modules, os::Thread* stack_thread); - void ShutDown(); - - template <class T> - T* GetInstance() const { - return static_cast<T*>(registry_.Get(&T::Factory)); - } - - template <class T> - bool IsStarted() const { - return registry_.IsStarted(&T::Factory); - } - -private: - os::Thread* management_thread_ = nullptr; - os::Handler* handler_ = nullptr; - ModuleRegistry registry_; - - void handle_start_up(ModuleList* modules, os::Thread* stack_thread, std::promise<void> promise); - void handle_shut_down(std::promise<void> promise); - static std::chrono::milliseconds get_gd_stack_timeout_ms(bool is_start); -}; - -} // namespace bluetooth diff --git a/system/gd/stack_manager_unittest.cc b/system/gd/stack_manager_unittest.cc deleted file mode 100644 index f3fb3b5c82..0000000000 --- a/system/gd/stack_manager_unittest.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2019 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_manager.h" - -#include "gtest/gtest.h" -#include "os/thread.h" - -namespace bluetooth { -namespace { - -TEST(StackManagerTest, DISABLED_start_and_shutdown_no_module) { - StackManager stack_manager; - ModuleList module_list; - os::Thread thread{"test_thread", os::Thread::Priority::NORMAL}; - stack_manager.StartUp(&module_list, &thread); - stack_manager.ShutDown(); -} - -class TestModuleNoDependency : public Module { -public: - static const ModuleFactory Factory; - -protected: - void ListDependencies(ModuleList* /* list */) const {} - void Start() override {} - void Stop() override {} - std::string ToString() const override { return std::string("TestModuleDep"); } -}; - -const ModuleFactory TestModuleNoDependency::Factory = - ModuleFactory([]() { return new TestModuleNoDependency(); }); - -TEST(StackManagerTest, DISABLED_get_module_instance) { - StackManager stack_manager; - ModuleList module_list; - module_list.add<TestModuleNoDependency>(); - os::Thread thread{"test_thread", os::Thread::Priority::NORMAL}; - stack_manager.StartUp(&module_list, &thread); - EXPECT_NE(stack_manager.GetInstance<TestModuleNoDependency>(), nullptr); - stack_manager.ShutDown(); -} - -} // namespace -} // namespace bluetooth diff --git a/system/gd/storage/storage_module.cc b/system/gd/storage/storage_module.cc index 7cdfd507da..ce5a89c6f7 100644 --- a/system/gd/storage/storage_module.cc +++ b/system/gd/storage/storage_module.cc @@ -178,10 +178,10 @@ void StorageModule::Start() { // Cleanup temporary pairings if we have left guest mode if (!com::android::bluetooth::flags::guest_mode_bond() && !is_restricted_mode_) { - config->RemoveSectionWithProperty("Restricted"); + pimpl_->cache_.RemoveSectionWithProperty("Restricted"); } - config->FixDeviceTypeInconsistencies(); + pimpl_->cache_.FixDeviceTypeInconsistencies(); if (bluetooth::os::ParameterProvider::GetBtKeystoreInterface() != nullptr) { bluetooth::os::ParameterProvider::GetBtKeystoreInterface() ->ConvertEncryptOrDecryptKeyIfNeeded(); diff --git a/system/gd/storage/storage_module_test.cc b/system/gd/storage/storage_module_test.cc index 07163922fe..2f600944ae 100644 --- a/system/gd/storage/storage_module_test.cc +++ b/system/gd/storage/storage_module_test.cc @@ -168,6 +168,7 @@ static const std::string kReadTestConfig = "[01:02:03:ab:cd:ea]\n" "Name = hello world\n" "LinkKey = fedcba0987654321fedcba0987654328\n" + "DevType = 1\n" "\n"; TEST_F(StorageModuleTest, read_existing_config_test) { diff --git a/system/include/Android.bp b/system/include/Android.bp index 5540d0dd45..2482effbf3 100644 --- a/system/include/Android.bp +++ b/system/include/Android.bp @@ -32,7 +32,6 @@ cc_library_headers { visibility: [ "//packages/apps/Test/connectivity/sl4n", "//packages/modules/Bluetooth:__subpackages__", - "//vendor:__subpackages__", ], header_libs: [ "aics_headers", @@ -47,7 +46,6 @@ cc_library_headers { "libbtcore_headers", ], export_include_dirs: ["./"], - vendor_available: true, host_supported: true, apex_available: [ "//apex_available:platform", diff --git a/system/include/hardware/ble_scanner.h b/system/include/hardware/ble_scanner.h index 67e0011987..6adb09d26c 100644 --- a/system/include/hardware/ble_scanner.h +++ b/system/include/hardware/ble_scanner.h @@ -28,30 +28,6 @@ #include "types/bluetooth/uuid.h" #include "types/raw_address.h" -/** Callback invoked when batchscan reports are obtained */ -typedef void (*batchscan_reports_callback)(int client_if, int status, int report_format, - int num_records, std::vector<uint8_t> data); - -/** Callback invoked when batchscan storage threshold limit is crossed */ -typedef void (*batchscan_threshold_callback)(int client_if); - -/** Track ADV VSE callback invoked when tracked device is found or lost */ -typedef void (*track_adv_event_callback)(btgatt_track_adv_info_t* p_track_adv_info); - -/** Callback for scan results */ -typedef void (*scan_result_callback)(uint16_t event_type, uint8_t addr_type, RawAddress* bda, - uint8_t primary_phy, uint8_t secondary_phy, - uint8_t advertising_sid, int8_t tx_power, int8_t rssi, - uint16_t periodic_adv_int, std::vector<uint8_t> adv_data, - RawAddress* original_bda); - -typedef struct { - scan_result_callback scan_result_cb; - batchscan_reports_callback batchscan_reports_cb; - batchscan_threshold_callback batchscan_threshold_cb; - track_adv_event_callback track_adv_event_cb; -} btgatt_scanner_callbacks_t; - class AdvertisingTrackInfo { public: // For MSFT-based advertisement monitor. diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h index 02a8f51778..a8a169a58d 100644 --- a/system/include/hardware/bluetooth.h +++ b/system/include/hardware/bluetooth.h @@ -238,6 +238,11 @@ typedef struct { bool le_channel_sounding_supported; } bt_local_le_features_t; +typedef struct { + uint8_t number_of_supported_offloaded_le_coc_sockets; + uint8_t number_of_supported_offloaded_rfcomm_sockets; +} bt_lpp_offload_features_t; + /** Bluetooth Vendor and Product ID info */ typedef struct { uint8_t vendor_id_src; @@ -415,6 +420,13 @@ typedef enum { */ BT_PROPERTY_REMOTE_MAX_SESSION_KEY_SIZE, + /** + * Description - Low power processor offload features + * Access mode - GET. + * Data Type - bt_lpp_offload_features_t. + */ + BT_PROPERTY_LPP_OFFLOAD_FEATURES, + BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF, } bt_property_type_t; @@ -1010,6 +1022,8 @@ template <> struct formatter<bt_bond_state_t> : enum_formatter<bt_bond_state_t> {}; template <> struct formatter<bt_property_type_t> : enum_formatter<bt_property_type_t> {}; +template <> +struct formatter<bt_ssp_variant_t> : enum_formatter<bt_ssp_variant_t> {}; } // namespace std #endif // __has_include(<bluetooth/log.h>) diff --git a/system/include/hardware/bt_gatt.h b/system/include/hardware/bt_gatt.h index 50096274e3..aa55e03701 100644 --- a/system/include/hardware/bt_gatt.h +++ b/system/include/hardware/bt_gatt.h @@ -37,9 +37,6 @@ typedef struct { /** GATT Server callbacks */ const btgatt_server_callbacks_t* server; - - /** LE scanner callbacks */ - const btgatt_scanner_callbacks_t* scanner; } btgatt_callbacks_t; /** Represents the standard Bluetooth GATT interface. */ diff --git a/system/include/hardware/bt_hh.h b/system/include/hardware/bt_hh.h index 38f41ff44a..3de962b666 100644 --- a/system/include/hardware/bt_hh.h +++ b/system/include/hardware/bt_hh.h @@ -17,7 +17,6 @@ #ifndef ANDROID_INCLUDE_BT_HH_H #define ANDROID_INCLUDE_BT_HH_H -#include <base/strings/stringprintf.h> #include <stdint.h> #include <string> @@ -51,7 +50,7 @@ inline std::string bthh_connection_state_text(const bthh_connection_state_t& sta CASE_RETURN_TEXT(BTHH_CONN_STATE_ACCEPTING); CASE_RETURN_TEXT(BTHH_CONN_STATE_UNKNOWN); default: - return base::StringPrintf("UNKNOWN[%d]", state); + return std::format("UNKNOWN[{}]", static_cast<int>(state)); } } diff --git a/system/include/hardware/bt_sock.h b/system/include/hardware/bt_sock.h index c7fff44daf..0436fad9cb 100644 --- a/system/include/hardware/bt_sock.h +++ b/system/include/hardware/bt_sock.h @@ -38,6 +38,18 @@ typedef enum { BTSOCK_L2CAP_LE = 4 } btsock_type_t; +/** + * Data path used for Bluetooth socket communication. + * + * NOTE: The values must be same as: + * - BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD = 0 + * - BluetoothSocketSettings.DATA_PATH_HARDWARE_OFFLOAD = 1 + */ +typedef enum { + BTSOCK_DATA_PATH_NO_OFFLOAD = 0, + BTSOCK_DATA_PATH_HARDWARE_OFFLOAD = 1, +} btsock_data_path_t; + /** Represents the standard BT SOCKET interface. */ typedef struct { int16_t size; @@ -56,9 +68,17 @@ typedef struct { // The connection uuid. (L2CAP only) uint64_t conn_uuid_lsb; uint64_t conn_uuid_msb; + + // Socket ID in connected state + uint64_t socket_id; } __attribute__((packed)) sock_connect_signal_t; typedef struct { + uint16_t size; + uint16_t is_accepting; +} __attribute__((packed)) sock_accept_signal_t; + +typedef struct { /** set to size of this struct*/ size_t size; @@ -73,7 +93,8 @@ typedef struct { */ bt_status_t (*listen)(btsock_type_t type, const char* service_name, const bluetooth::Uuid* service_uuid, int channel, int* sock_fd, int flags, - int callingUid); + int callingUid, btsock_data_path_t data_path, const char* socket_name, + uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size); /** * Connect to a RFCOMM UUID channel of remote device, It returns the socket fd @@ -83,7 +104,9 @@ typedef struct { * purposes. */ bt_status_t (*connect)(const RawAddress* bd_addr, btsock_type_t type, const bluetooth::Uuid* uuid, - int channel, int* sock_fd, int flags, int callingUid); + int channel, int* sock_fd, int flags, int callingUid, + btsock_data_path_t data_path, const char* socket_name, uint64_t hub_id, + uint64_t endpoint_id, int max_rx_packet_size); /** * Set the LE Data Length value to this connected peer to the @@ -128,6 +151,9 @@ __END_DECLS namespace std { template <> struct formatter<btsock_type_t> : enum_formatter<btsock_type_t> {}; + +template <> +struct formatter<btsock_data_path_t> : enum_formatter<btsock_data_path_t> {}; } // namespace std #endif // __has_include(<bluetooth/log.h>) diff --git a/system/main/Android.bp b/system/main/Android.bp index 5b270923f4..5272905831 100644 --- a/system/main/Android.bp +++ b/system/main/Android.bp @@ -41,10 +41,6 @@ cc_library_static { "packages/modules/Bluetooth/system/udrv/include", "system/security/keystore/include", ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], apex_available: [ "com.android.btservices", ], @@ -129,10 +125,6 @@ cc_library_static { "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/stack/include", ], - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], whole_static_libs: [ "libbluetooth_gd", // Gabeldorsche ], @@ -191,13 +183,11 @@ cc_test { "shim/stack.cc", "shim/utils.cc", "test/common_stack_test.cc", - "test/main_shim_dumpsys_test.cc", "test/main_shim_test.cc", ], static_libs: [ "bluetooth_flags_c_lib_for_test", "libbase", - "libbluetooth-dumpsys", "libbluetooth-types", "libbluetooth_core_rs_bridge", "libbluetooth_crypto_toolbox", @@ -236,10 +226,6 @@ cc_test { undefined: true, }, }, - generated_headers: [ - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", - ], min_sdk_version: "Tiramisu", header_libs: ["libbluetooth_headers"], } diff --git a/system/main/shim/BUILD.gn b/system/main/shim/BUILD.gn index 88bbba0c6b..ff7bdae78a 100644 --- a/system/main/shim/BUILD.gn +++ b/system/main/shim/BUILD.gn @@ -29,10 +29,9 @@ source_set("BluetoothStackManagerSources") { ] deps = [ - "//bt/system/gd:BluetoothGeneratedDumpsysDataSchema_h", "//bt/system/gd/common:BluetoothCommonSources", - "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs", "//bt/system/gd/hci:BluetoothHciSources", + "//bt/system/gd/lpp:BluetoothLppOffloadSources", "//bt/system/gd/os:BluetoothOsSources_linux_generic", "//bt/system/gd/packet:BluetoothPacketSources", "//bt/system/gd/rust/topshim:libbluetooth_topshim", @@ -75,10 +74,9 @@ source_set("LibBluetoothShimSources") { deps = [ "//bt/sysprop:libcom.android.sysprop.bluetooth", - "//bt/system/gd:BluetoothGeneratedDumpsysDataSchema_h", "//bt/system/gd/common:BluetoothCommonSources", - "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs", "//bt/system/gd/hci:BluetoothHciSources", + "//bt/system/gd/lpp:BluetoothLppOffloadSources", "//bt/system/gd/os:BluetoothOsSources_linux_generic", "//bt/system/gd/packet:BluetoothPacketSources", "//bt/system/gd/rust/topshim:libbluetooth_topshim", diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc index 7ab98517cb..d90e06a878 100644 --- a/system/main/shim/acl.cc +++ b/system/main/shim/acl.cc @@ -17,7 +17,6 @@ #include "main/shim/acl.h" #include <base/location.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> #include <time.h> @@ -37,7 +36,6 @@ #include <vector> #include "common/bind.h" -#include "common/interfaces/ILoggable.h" #include "common/strings.h" #include "common/sync_map_count.h" #include "hci/acl_manager.h" @@ -77,7 +75,7 @@ extern tBTM_CB btm_cb; using namespace bluetooth; using ::bluetooth::os::WakelockManager; -class ConnectAddressWithType : public bluetooth::common::IRedactableLoggable { +class ConnectAddressWithType { public: explicit ConnectAddressWithType(hci::AddressWithType address_with_type) : address_(address_with_type.GetAddress()), @@ -90,8 +88,8 @@ public: return ss.str(); } - std::string ToStringForLogging() const override { return ToString(); } - std::string ToRedactedStringForLogging() const override { + std::string ToStringForLogging() const { return ToString(); } + std::string ToRedactedStringForLogging() const { std::stringstream ss; ss << address_.ToRedactedStringForLogging() << "[" << FilterAcceptListAddressTypeText(type_) << "]"; @@ -204,56 +202,9 @@ std::string EpochMillisToString(long long time_ms) { struct tm tm; localtime_r(&time_sec, &tm); std::string s = common::StringFormatTime(kConnectionDescriptorTimeFormat, tm); - return base::StringPrintf("%s.%03u", s.c_str(), - static_cast<unsigned int>(time_ms % MillisPerSecond)); + return std::format("{}.{:03}", s, time_ms % MillisPerSecond); } -inline bool IsRpa(const hci::AddressWithType address_with_type) { - return address_with_type.GetAddressType() == hci::AddressType::RANDOM_DEVICE_ADDRESS && - ((address_with_type.GetAddress().address.data()[5] & 0xc0) == 0x40); -} - -class ShadowAcceptlist { -public: - explicit ShadowAcceptlist(uint8_t max_acceptlist_size) - : max_acceptlist_size_(max_acceptlist_size) {} - - bool Add(const hci::AddressWithType& address_with_type) { - if (acceptlist_set_.size() == max_acceptlist_size_) { - log::error("Acceptlist is full size:{}", acceptlist_set_.size()); - return false; - } - if (!acceptlist_set_.insert(ConnectAddressWithType(address_with_type)).second) { - log::warn("Attempted to add duplicate le address to acceptlist:{}", address_with_type); - } - return true; - } - - bool Remove(const hci::AddressWithType& address_with_type) { - auto iter = acceptlist_set_.find(ConnectAddressWithType(address_with_type)); - if (iter == acceptlist_set_.end()) { - log::warn("Unknown device being removed from acceptlist:{}", address_with_type); - return false; - } - acceptlist_set_.erase(ConnectAddressWithType(*iter)); - return true; - } - - std::unordered_set<ConnectAddressWithType> GetCopy() const { return acceptlist_set_; } - - bool IsFull() const { - return acceptlist_set_.size() == static_cast<size_t>(max_acceptlist_size_); - } - - void Clear() { acceptlist_set_.clear(); } - - uint8_t GetMaxSize() const { return max_acceptlist_size_; } - -private: - uint8_t max_acceptlist_size_{0}; - std::unordered_set<ConnectAddressWithType> acceptlist_set_; -}; - class ShadowAddressResolutionList { public: explicit ShadowAddressResolutionList(uint8_t max_address_resolution_size) @@ -314,17 +265,15 @@ struct ConnectionDescriptor { virtual std::string GetPrivateRemoteAddress() const = 0; virtual ~ConnectionDescriptor() {} std::string ToString() const { - return base::StringPrintf( - "peer:%s handle:0x%04x is_locally_initiated:%s" - " creation_time:%s teardown_time:%s disconnect_reason:%s", - GetPrivateRemoteAddress().c_str(), handle_, is_locally_initiated_ ? "true" : "false", + return std::format( + "peer:{} handle:0x{:04x} is_locally_initiated:{} creation_time:{} teardown_time:{} " + "disconnect_reason:{}", + GetPrivateRemoteAddress(), handle_, is_locally_initiated_, common::StringFormatTimeWithMilliseconds(kConnectionDescriptorTimeFormat, - creation_time_) - .c_str(), + creation_time_), common::StringFormatTimeWithMilliseconds(kConnectionDescriptorTimeFormat, - teardown_time_) - .c_str(), - hci::ErrorCodeText(disconnect_reason_).c_str()); + teardown_time_), + hci::ErrorCodeText(disconnect_reason_)); } }; @@ -337,7 +286,7 @@ struct ClassicConnectionDescriptor : public ConnectionDescriptor { disconnect_reason), remote_address_(remote_address) {} virtual std::string GetPrivateRemoteAddress() const { - return ADDRESS_TO_LOGGABLE_CSTR(remote_address_); + return remote_address_.ToRedactedStringForLogging(); } }; @@ -350,7 +299,7 @@ struct LeConnectionDescriptor : public ConnectionDescriptor { disconnect_reason), remote_address_with_type_(remote_address_with_type) {} std::string GetPrivateRemoteAddress() const { - return ADDRESS_TO_LOGGABLE_CSTR(remote_address_with_type_); + return remote_address_with_type_.ToRedactedStringForLogging(); } }; @@ -658,10 +607,9 @@ public: void OnRoleChange(hci::ErrorCode hci_status, hci::Role new_role) override { TRY_POSTING_ON_MAIN(interface_.on_role_change, ToLegacyHciErrorCode(hci_status), ToRawAddress(connection_->GetAddress()), ToLegacyRole(new_role)); - BTM_LogHistory( - kBtmLogTag, ToRawAddress(connection_->GetAddress()), "Role change", - base::StringPrintf("classic New_role:%s status:%s", hci::RoleText(new_role).c_str(), - hci::ErrorCodeText(hci_status).c_str())); + BTM_LogHistory(kBtmLogTag, ToRawAddress(connection_->GetAddress()), "Role change", + std::format("classic New_role:{} status:{}", hci::RoleText(new_role), + hci::ErrorCodeText(hci_status))); } void OnDisconnection(hci::ErrorCode reason) override { @@ -868,9 +816,8 @@ private: }; struct shim::Acl::impl { - impl(uint8_t max_acceptlist_size, uint8_t max_address_resolution_size) - : shadow_acceptlist_(ShadowAcceptlist(max_acceptlist_size)), - shadow_address_resolution_list_(ShadowAddressResolutionList(max_address_resolution_size)) {} + impl(uint8_t max_address_resolution_size) + : shadow_address_resolution_list_(ShadowAddressResolutionList(max_address_resolution_size)) {} std::map<HciHandle, std::unique_ptr<ClassicShimAclConnection>> handle_to_classic_connection_map_; std::map<HciHandle, std::unique_ptr<LeShimAclConnection>> handle_to_le_connection_map_; @@ -881,7 +828,6 @@ struct shim::Acl::impl { FixedQueue<std::unique_ptr<ConnectionDescriptor>> connection_history_ = FixedQueue<std::unique_ptr<ConnectionDescriptor>>(kConnectionHistorySize); - ShadowAcceptlist shadow_acceptlist_; ShadowAddressResolutionList shadow_address_resolution_list_; struct timed_wakelock wakeup_wakelock_; @@ -1049,9 +995,9 @@ struct shim::Acl::impl { auto remote_address = connection->second->GetRemoteAddress(); connection->second->InitiateDisconnect(ToDisconnectReasonFromLegacy(reason)); log::debug("Disconnection initiated classic remote:{} handle:{}", remote_address, handle); - BTM_LogHistory(kBtmLogTag, ToRawAddress(remote_address), "Disconnection initiated", - base::StringPrintf("classic reason:%s comment:%s", - hci_status_code_text(reason).c_str(), comment.c_str())); + BTM_LogHistory( + kBtmLogTag, ToRawAddress(remote_address), "Disconnection initiated", + std::format("classic reason:{} comment:{}", hci_status_code_text(reason), comment)); classic_acl_disconnect_reason_.Put(comment); } else { log::warn("Unable to disconnect unknown classic connection handle:0x{:04x}", handle); @@ -1068,8 +1014,7 @@ struct shim::Acl::impl { handle); BTM_LogHistory(kBtmLogTag, ToLegacyAddressWithType(remote_address_with_type), "Disconnection initiated", - base::StringPrintf("Le reason:%s comment:%s", - hci_status_code_text(reason).c_str(), comment.c_str())); + std::format("Le reason:{} comment:{}", hci_status_code_text(reason), comment)); le_acl_disconnect_reason_.Put(comment); } else { log::warn("Unable to disconnect unknown le connection handle:0x{:04x}", handle); @@ -1090,12 +1035,6 @@ struct shim::Acl::impl { void accept_le_connection_from(const hci::AddressWithType& address_with_type, bool is_direct, std::promise<bool> promise) { - if (shadow_acceptlist_.IsFull()) { - log::error("Acceptlist is full preventing new Le connection"); - promise.set_value(false); - return; - } - shadow_acceptlist_.Add(address_with_type); promise.set_value(true); GetAclManager()->CreateLeConnection(address_with_type, is_direct); log::debug("Allow Le connection from remote:{}", address_with_type); @@ -1161,20 +1100,13 @@ struct shim::Acl::impl { } void ignore_le_connection_from(const hci::AddressWithType& address_with_type) { - shadow_acceptlist_.Remove(address_with_type); GetAclManager()->CancelLeConnect(address_with_type); log::debug("Ignore Le connection from remote:{}", address_with_type); BTM_LogHistory(kBtmLogTag, ToLegacyAddressWithType(address_with_type), "Ignore connection from", "Le"); } - void clear_acceptlist() { - auto shadow_acceptlist = shadow_acceptlist_.GetCopy(); - size_t count = shadow_acceptlist.size(); - GetAclManager()->ClearFilterAcceptList(); - shadow_acceptlist_.Clear(); - log::debug("Cleared entire Le address acceptlist count:{}", count); - } + void clear_acceptlist() { GetAclManager()->ClearFilterAcceptList(); } void AddToAddressResolution(const hci::AddressWithType& address_with_type, const std::array<uint8_t, 16>& peer_irk, @@ -1210,12 +1142,6 @@ struct shim::Acl::impl { for (auto& entry : history) { log::debug("{}", entry); } - const auto acceptlist = shadow_acceptlist_.GetCopy(); - log::debug("Shadow le accept list size:{:<3} controller_max_size:{}", acceptlist.size(), - shadow_acceptlist_.GetMaxSize()); - for (auto& entry : acceptlist) { - log::debug("acceptlist:{}", entry); - } } #define DUMPSYS_TAG "shim::acl" @@ -1237,23 +1163,14 @@ struct shim::Acl::impl { } } - auto acceptlist = shadow_acceptlist_.GetCopy(); - LOG_DUMPSYS(fd, - "Shadow le accept list size:%-3zu " - "controller_max_size:%hhu", - acceptlist.size(), shadow_acceptlist_.GetMaxSize()); - unsigned cnt = 0; - for (auto& entry : acceptlist) { - LOG_DUMPSYS(fd, " %03u %s", ++cnt, ADDRESS_TO_LOGGABLE_CSTR(entry)); - } auto address_resolution_list = shadow_address_resolution_list_.GetCopy(); LOG_DUMPSYS(fd, "Shadow le address resolution list size:%-3zu " "controller_max_size:%hhu", address_resolution_list.size(), shadow_address_resolution_list_.GetMaxSize()); - cnt = 0; + unsigned cnt = 0; for (auto& entry : address_resolution_list) { - LOG_DUMPSYS(fd, " %03u %s", ++cnt, ADDRESS_TO_LOGGABLE_CSTR(entry)); + LOG_DUMPSYS(fd, " %03u %s", ++cnt, entry.ToRedactedStringForLogging().c_str()); } } #undef DUMPSYS_TAG @@ -1276,7 +1193,7 @@ void DumpsysAcl(int fd) { } LOG_DUMPSYS(fd, "remote_addr:%s handle:0x%04x transport:%s", - ADDRESS_TO_LOGGABLE_CSTR(link.remote_addr), link.hci_handle, + link.remote_addr.ToRedactedStringForLogging().c_str(), link.hci_handle, bt_transport_text(link.transport).c_str()); LOG_DUMPSYS(fd, " link_up_issued:%5s", (link.link_up_issued) ? "true" : "false"); LOG_DUMPSYS(fd, " flush_timeout:0x%04x", link.flush_timeout_in_ticks); @@ -1307,7 +1224,7 @@ void DumpsysAcl(int fd) { bd_features_text(link.peer_le_features).c_str()); LOG_DUMPSYS(fd, " [le] active_remote_addr:%s[%s]", - ADDRESS_TO_LOGGABLE_CSTR(link.active_remote_addr), + link.active_remote_addr.ToRedactedStringForLogging().c_str(), AddressTypeText(link.active_remote_addr_type).c_str()); } } @@ -1358,11 +1275,11 @@ void shim::Acl::Dump(int fd) const { } shim::Acl::Acl(os::Handler* handler, const acl_interface_t& acl_interface, - uint8_t max_acceptlist_size, uint8_t max_address_resolution_size) + uint8_t max_address_resolution_size) : handler_(handler), acl_interface_(acl_interface) { log::assert_that(handler_ != nullptr, "assert failed: handler_ != nullptr"); ValidateAclInterface(acl_interface_); - pimpl_ = std::make_unique<Acl::impl>(max_acceptlist_size, max_address_resolution_size); + pimpl_ = std::make_unique<Acl::impl>(max_address_resolution_size); GetAclManager()->RegisterCallbacks(this, handler_); GetAclManager()->RegisterLeCallbacks(this, handler_); GetController()->RegisterCompletedMonitorAclPacketsCallback( @@ -1473,7 +1390,7 @@ void shim::Acl::OnClassicLinkDisconnected(HciHandle handle, hci::ErrorCode reaso log::debug("Disconnected classic link remote:{} handle:{} reason:{}", remote_address, handle, ErrorCodeText(reason)); BTM_LogHistory(kBtmLogTag, ToRawAddress(remote_address), "Disconnected", - base::StringPrintf("classic reason:%s", ErrorCodeText(reason).c_str())); + std::format("classic reason:{}", ErrorCodeText(reason))); pimpl_->connection_history_.Push(std::make_unique<ClassicConnectionDescriptor>( remote_address, creation_time, teardown_time, handle, is_locally_initiated, reason)); } @@ -1514,7 +1431,7 @@ void shim::Acl::OnLeLinkDisconnected(HciHandle handle, hci::ErrorCode reason) { log::debug("Disconnected le link remote:{} handle:{} reason:{}", remote_address_with_type, handle, ErrorCodeText(reason)); BTM_LogHistory(kBtmLogTag, ToLegacyAddressWithType(remote_address_with_type), "Disconnected", - base::StringPrintf("Le reason:%s", ErrorCodeText(reason).c_str())); + std::format("Le reason:{}", ErrorCodeText(reason))); pimpl_->connection_history_.Push(std::make_unique<LeConnectionDescriptor>( remote_address_with_type, creation_time, teardown_time, handle, is_locally_initiated, reason)); @@ -1561,9 +1478,9 @@ void shim::Acl::OnConnectRequest(hci::Address address, hci::ClassOfDevice cod) { TRY_POSTING_ON_MAIN(acl_interface_.connection.classic.on_connect_request, bd_addr, cod); log::debug("Received connect request remote:{} gd_cod:{} legacy_dev_class:{}", address, cod.ToString(), dev_class_text(dev_class)); - BTM_LogHistory(kBtmLogTag, ToRawAddress(address), "Connection request", - base::StringPrintf("gd_cod:%s legacy_dev_class:%s", cod.ToString().c_str(), - dev_class_text(dev_class).c_str())); + BTM_LogHistory( + kBtmLogTag, ToRawAddress(address), "Connection request", + std::format("gd_cod:{} legacy_dev_class:{}", cod.ToString(), dev_class_text(dev_class))); } void shim::Acl::OnConnectFail(hci::Address address, hci::ErrorCode reason, bool locally_initiated) { @@ -1573,7 +1490,7 @@ void shim::Acl::OnConnectFail(hci::Address address, hci::ErrorCode reason, bool log::warn("Connection failed classic remote:{} reason:{}", address, hci::ErrorCodeText(reason)); metrics::LogAclCompletionEvent(address, reason, locally_initiated); BTM_LogHistory(kBtmLogTag, ToRawAddress(address), "Connection failed", - base::StringPrintf("classic reason:%s", hci::ErrorCodeText(reason).c_str())); + std::format("classic reason:{}", hci::ErrorCodeText(reason))); } void shim::Acl::OnLeConnectSuccess(hci::AddressWithType address_with_type, @@ -1625,18 +1542,6 @@ void shim::Acl::OnLeConnectSuccess(hci::AddressWithType address_with_type, std::chrono::system_clock::now())); pimpl_->handle_to_le_connection_map_[handle]->RegisterCallbacks(); - // Once an le connection has successfully been established - // the device address is removed from the controller accept list. - - if (IsRpa(address_with_type)) { - log::debug("Connection address is rpa:{} identity_addr:{}", address_with_type, - peer_address_with_type); - pimpl_->shadow_acceptlist_.Remove(peer_address_with_type); - } else { - log::debug("Connection address is not rpa addr:{}", address_with_type); - pimpl_->shadow_acceptlist_.Remove(address_with_type); - } - if (!pimpl_->handle_to_le_connection_map_[handle]->IsInFilterAcceptList() && connection_role == hci::Role::CENTRAL) { pimpl_->handle_to_le_connection_map_[handle]->InitiateDisconnect( @@ -1675,10 +1580,9 @@ void shim::Acl::OnLeConnectFail(hci::AddressWithType address_with_type, hci::Err enhanced, status); bluetooth::metrics::LogLeAclCompletionEvent(address_with_type.GetAddress(), reason, true); - pimpl_->shadow_acceptlist_.Remove(address_with_type); log::warn("Connection failed le remote:{}", address_with_type); BTM_LogHistory(kBtmLogTag, ToLegacyAddressWithType(address_with_type), "Connection failed", - base::StringPrintf("le reason:%s", hci::ErrorCodeText(reason).c_str())); + std::format("le reason:{}", hci::ErrorCodeText(reason))); } void shim::Acl::DisconnectClassic(uint16_t handle, tHCI_STATUS reason, std::string comment) { diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h index 013660d7f4..ee73e006ea 100644 --- a/system/main/shim/acl.h +++ b/system/main/shim/acl.h @@ -37,7 +37,7 @@ class Acl : public hci::acl_manager::ConnectionCallbacks, public hci::acl_manager::LeConnectionCallbacks, public LinkConnectionInterface { public: - Acl(os::Handler* handler, const acl_interface_t& acl_interface, uint8_t max_acceptlist_size, + Acl(os::Handler* handler, const acl_interface_t& acl_interface, uint8_t max_address_resolution_size); Acl(const Acl&) = delete; diff --git a/system/main/shim/acl_api.cc b/system/main/shim/acl_api.cc index 90d9c7b9f5..08249592e7 100644 --- a/system/main/shim/acl_api.cc +++ b/system/main/shim/acl_api.cc @@ -111,7 +111,6 @@ void bluetooth::shim::ACL_ConfigureLePrivacy(bool is_le_privacy_enabled) { android::sysprop::bluetooth::Ble::random_address_rotation_interval_max().value_or(15)); Stack::GetInstance() - ->GetStackManager() ->GetInstance<bluetooth::hci::AclManager>() ->SetPrivacyPolicyForInitiatorAddress(address_policy, empty_address_with_type, minimum_rotation_time, maximum_rotation_time); diff --git a/system/main/shim/distance_measurement_manager.cc b/system/main/shim/distance_measurement_manager.cc index 194eea195b..61150f3b07 100644 --- a/system/main/shim/distance_measurement_manager.cc +++ b/system/main/shim/distance_measurement_manager.cc @@ -202,6 +202,21 @@ public: } // Must be called from main_thread + // Callbacks of bluetooth::ras::RasServerCallbacks + void OnMtuChangedFromServer(const RawAddress& address, uint16_t mtu) override { + handle_mtu_changed(address, mtu); + } + + void OnMtuChangedFromClient(const RawAddress& address, uint16_t mtu) override { + handle_mtu_changed(address, mtu); + } + + void handle_mtu_changed(const RawAddress& address, uint16_t mtu) { + uint16_t connection_handle = GetConnectionHandleAndRole(address); + bluetooth::shim::GetDistanceMeasurementManager()->HandleMtuChanged(connection_handle, mtu); + } + + // Must be called from main_thread // Callbacks of bluetooth::ras::RasSeverCallbacks void OnRasServerDisconnected(const RawAddress& identity_address) override { bluetooth::shim::GetDistanceMeasurementManager()->HandleRasServerDisconnected( diff --git a/system/main/shim/dumpsys.cc b/system/main/shim/dumpsys.cc index 52e1a80850..77e45c17a1 100644 --- a/system/main/shim/dumpsys.cc +++ b/system/main/shim/dumpsys.cc @@ -25,7 +25,6 @@ #include "main/shim/entry.h" #include "main/shim/shim.h" #include "main/shim/stack.h" -#include "shim/dumpsys.h" namespace { @@ -46,7 +45,7 @@ void bluetooth::shim::UnregisterDumpsysFunction(const void* token) { dumpsys_functions_.erase(token); } -void bluetooth::shim::Dump(int fd, const char** args) { +void bluetooth::shim::Dump(int fd) { if (dumpsys_functions_.empty()) { dprintf(fd, "%s No registered dumpsys shim legacy targets\n", kModuleName); } else { @@ -57,13 +56,7 @@ void bluetooth::shim::Dump(int fd, const char** args) { } std::promise<void> promise; std::future future = promise.get_future(); - if (bluetooth::shim::Stack::GetInstance()->CallOnModule<shim::Dumpsys>( - [&promise, fd, args](shim::Dumpsys* mod) { - mod->Dump(fd, args, std::move(promise)); - })) { - log::assert_that(future.wait_for(std::chrono::seconds(1)) == std::future_status::ready, - "Timed out waiting for dumpsys to complete"); - } else { - dprintf(fd, "%s NOTE: gd dumpsys module not loaded or started\n", kModuleName); - } + bluetooth::shim::Stack::GetInstance()->Dump(fd, std::move(promise)); + log::assert_that(future.wait_for(std::chrono::seconds(1)) == std::future_status::ready, + "Timed out waiting for dumpsys to complete"); } diff --git a/system/main/shim/dumpsys.h b/system/main/shim/dumpsys.h index 854609c735..746f9bd3df 100644 --- a/system/main/shim/dumpsys.h +++ b/system/main/shim/dumpsys.h @@ -48,7 +48,7 @@ using DumpsysFunction = std::function<void(int fd)>; * Entrypoint from legacy stack to provide dumpsys functionality * for both the legacy shim and the Gabeldorsche stack. */ -void Dump(int fd, const char** args); +void Dump(int fd); /** * Dumpsys access for legacy shim modules. diff --git a/system/main/shim/entry.cc b/system/main/shim/entry.cc index a1c61b7ab0..ff0014b481 100644 --- a/system/main/shim/entry.cc +++ b/system/main/shim/entry.cc @@ -26,10 +26,10 @@ #include "hci/le_scanning_manager.h" #include "hci/msft.h" #include "hci/remote_name_request.h" +#include "lpp/lpp_offload_manager.h" #include "main/shim/stack.h" #include "metrics/counter_metrics.h" #include "os/handler.h" -#include "shim/dumpsys.h" #include "storage/storage_module.h" namespace bluetooth { @@ -38,49 +38,45 @@ namespace shim { os::Handler* GetGdShimHandler() { return Stack::GetInstance()->GetHandler(); } hci::LeAdvertisingManager* GetAdvertising() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::LeAdvertisingManager>(); + return Stack::GetInstance()->GetInstance<hci::LeAdvertisingManager>(); } hci::ControllerInterface* GetController() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::Controller>(); + return Stack::GetInstance()->GetInstance<hci::Controller>(); } -Dumpsys* GetDumpsys() { return Stack::GetInstance()->GetStackManager()->GetInstance<Dumpsys>(); } - -hci::HciInterface* GetHciLayer() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::HciLayer>(); -} +hci::HciInterface* GetHciLayer() { return Stack::GetInstance()->GetInstance<hci::HciLayer>(); } hci::RemoteNameRequestModule* GetRemoteNameRequest() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::RemoteNameRequestModule>(); + return Stack::GetInstance()->GetInstance<hci::RemoteNameRequestModule>(); } hci::LeScanningManager* GetScanning() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::LeScanningManager>(); + return Stack::GetInstance()->GetInstance<hci::LeScanningManager>(); } hci::DistanceMeasurementManager* GetDistanceMeasurementManager() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::DistanceMeasurementManager>(); + return Stack::GetInstance()->GetInstance<hci::DistanceMeasurementManager>(); } -hal::SnoopLogger* GetSnoopLogger() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hal::SnoopLogger>(); +hal::SnoopLogger* GetSnoopLogger() { return Stack::GetInstance()->GetInstance<hal::SnoopLogger>(); } + +lpp::LppOffloadInterface* GetLppOffloadManager() { + return Stack::GetInstance()->GetInstance<lpp::LppOffloadManager>(); } storage::StorageModule* GetStorage() { - return Stack::GetInstance()->GetStackManager()->GetInstance<storage::StorageModule>(); + return Stack::GetInstance()->GetInstance<storage::StorageModule>(); } -hci::AclManager* GetAclManager() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::AclManager>(); -} +hci::AclManager* GetAclManager() { return Stack::GetInstance()->GetInstance<hci::AclManager>(); } metrics::CounterMetrics* GetCounterMetrics() { - return Stack::GetInstance()->GetStackManager()->GetInstance<metrics::CounterMetrics>(); + return Stack::GetInstance()->GetInstance<metrics::CounterMetrics>(); } hci::MsftExtensionManager* GetMsftExtensionManager() { - return Stack::GetInstance()->GetStackManager()->GetInstance<hci::MsftExtensionManager>(); + return Stack::GetInstance()->GetInstance<hci::MsftExtensionManager>(); } } // namespace shim diff --git a/system/main/shim/entry.h b/system/main/shim/entry.h index e50cbc4d91..5202ecacb3 100644 --- a/system/main/shim/entry.h +++ b/system/main/shim/entry.h @@ -48,6 +48,10 @@ class LeScanningManager; class MsftExtensionManager; } // namespace hci +namespace lpp { +class LppOffloadInterface; +} + namespace metrics { class CounterMetrics; } @@ -57,18 +61,17 @@ class StorageModule; } namespace shim { -class Dumpsys; /* This returns a handler that might be used in shim to receive callbacks from * within the stack. */ os::Handler* GetGdShimHandler(); hci::LeAdvertisingManager* GetAdvertising(); bluetooth::hci::ControllerInterface* GetController(); -Dumpsys* GetDumpsys(); hci::HciInterface* GetHciLayer(); hci::RemoteNameRequestModule* GetRemoteNameRequest(); hci::DistanceMeasurementManager* GetDistanceMeasurementManager(); hci::LeScanningManager* GetScanning(); +lpp::LppOffloadInterface* GetLppOffloadManager(); hal::SnoopLogger* GetSnoopLogger(); storage::StorageModule* GetStorage(); hci::AclManager* GetAclManager(); diff --git a/system/main/shim/le_advertising_manager.cc b/system/main/shim/le_advertising_manager.cc index e91f67d71f..6fcae663c0 100644 --- a/system/main/shim/le_advertising_manager.cc +++ b/system/main/shim/le_advertising_manager.cc @@ -86,7 +86,7 @@ public: native_reg_id_map[client_id].erase(reg_id); } BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Le advert stopped", - base::StringPrintf("advert_id:%d", advertiser_id)); + std::format("advert_id:{}", advertiser_id)); } // ::BleAdvertiserInterface @@ -173,7 +173,7 @@ public: log::info("create advertising set, client_id:{}, reg_id:{}", client_id, reg_id); BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Le advert started", - base::StringPrintf("reg_id:%d", reg_id)); + std::format("reg_id:{}", reg_id)); return; } diff --git a/system/main/shim/le_scanning_manager.cc b/system/main/shim/le_scanning_manager.cc index 6133da1797..8214dea25c 100644 --- a/system/main/shim/le_scanning_manager.cc +++ b/system/main/shim/le_scanning_manager.cc @@ -164,10 +164,10 @@ void BleScannerInterfaceImpl::Scan(bool start) { // stopped const uint64_t duration_timestamp = timestamper_in_milliseconds.GetTimestamp() - btm_cb.neighbor.le_scan.start_time_ms; - BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Le scan stopped", - base::StringPrintf("duration_s:%6.3f results:%-3lu", - (double)duration_timestamp / 1000.0, - (unsigned long)btm_cb.neighbor.le_scan.results)); + BTM_LogHistory( + kBtmLogTag, RawAddress::kEmpty, "Le scan stopped", + std::format("duration_s:{:6.3f} results:{:<3}", (double)duration_timestamp / 1000.0, + btm_cb.neighbor.le_scan.results)); btm_cb.ble_ctr_cb.reset_ble_observe(); btm_cb.neighbor.le_scan = {}; } else { diff --git a/system/main/shim/metrics_api.cc b/system/main/shim/metrics_api.cc index 4ee87ce62d..65491b2468 100644 --- a/system/main/shim/metrics_api.cc +++ b/system/main/shim/metrics_api.cc @@ -16,7 +16,6 @@ #include "main/shim/metrics_api.h" -#include "hci/address.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" #include "metrics/bluetooth_event.h" @@ -156,6 +155,33 @@ void LogMetricLePairingFail(const RawAddress& raw_address, uint8_t failure_reaso bluetooth::metrics::LogLePairingFail(raw_address, failure_reason, is_outgoing); } +void LogMetricLeConnectionStatus(hci::Address address, bool is_connect, hci::ErrorCode reason) { + bluetooth::os::LogMetricBluetoothEvent( + address, + is_connect ? android::bluetooth::EventType::GATT_CONNECT_NATIVE + : android::bluetooth::EventType::GATT_DISCONNECT_NATIVE, + bluetooth::metrics::MapErrorCodeToState(reason)); +} + +void LogMetricLeDeviceInAcceptList(hci::Address address, bool is_add) { + bluetooth::os::LogMetricBluetoothEvent( + address, android::bluetooth::EventType::LE_DEVICE_IN_ACCEPT_LIST, + is_add ? android::bluetooth::State::START : android::bluetooth::State::END); +} + +void LogMetricLeConnectionLifecycle(hci::Address address, bool is_connect, bool is_direct) { + if (is_connect) { + bluetooth::os::LogMetricBluetoothEvent(address, + android::bluetooth::EventType::GATT_CONNECT_NATIVE, + is_direct ? android::bluetooth::State::DIRECT_CONNECT + : android::bluetooth::State::INDIRECT_CONNECT); + } else { + bluetooth::os::LogMetricBluetoothEvent(address, + android::bluetooth::EventType::GATT_DISCONNECT_NATIVE, + android::bluetooth::State::START); + } +} + bool CountCounterMetrics(int32_t key, int64_t count) { auto counter_metrics = GetCounterMetrics(); if (counter_metrics == nullptr) { diff --git a/system/main/shim/metrics_api.h b/system/main/shim/metrics_api.h index c030d7cc57..c0165b04b6 100644 --- a/system/main/shim/metrics_api.h +++ b/system/main/shim/metrics_api.h @@ -20,6 +20,8 @@ #include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> #include <frameworks/proto_logging/stats/enums/bluetooth/le/enums.pb.h> +#include "hci/address.h" +#include "hci/hci_packets.h" #include "os/metrics.h" #include "types/raw_address.h" @@ -256,6 +258,29 @@ void LogMetricManufacturerInfo(const RawAddress& address, void LogMetricLePairingFail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing); +/** + * Logs GATT connect/disconnect status + * @param address Address of the device + * @param is_connect indicates connection or disconnection + * @param reason the reason/status for the connection event + */ +void LogMetricLeConnectionStatus(hci::Address address, bool is_connect, hci::ErrorCode reason); + +/** + * Logs LE filter accept list events + * @param address Address of the device + * @param is_add indicates addition or removal of the device in the accept list + */ +void LogMetricLeDeviceInAcceptList(hci::Address address, bool is_connect); + +/** + * Logs GATT lifecycle events + * @param address Address of the device + * @param is_connect indicates connection or disconnection + * @param is_direct indicates direct or background connection, ignored for disconnection + */ +void LogMetricLeConnectionLifecycle(hci::Address address, bool is_connect, bool is_direct); + bool CountCounterMetrics(int32_t key, int64_t count); } // namespace shim diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc index 73526bc4dd..6da9117ae4 100644 --- a/system/main/shim/stack.cc +++ b/system/main/shim/stack.cc @@ -19,13 +19,18 @@ #include "main/shim/stack.h" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <fcntl.h> #include <unistd.h> +#include <chrono> +#include <future> +#include <queue> #include <string> #include "common/strings.h" #include "hal/hci_hal.h" +#include "hal/snoop_logger.h" #include "hci/acl_manager.h" #include "hci/acl_manager/acl_scheduler.h" #include "hci/controller.h" @@ -36,6 +41,7 @@ #include "hci/le_scanning_manager.h" #include "hci/msft.h" #include "hci/remote_name_request.h" +#include "lpp/lpp_offload_manager.h" #include "main/shim/acl.h" #include "main/shim/acl_interface.h" #include "main/shim/distance_measurement_manager.h" @@ -44,17 +50,21 @@ #include "main/shim/le_advertising_manager.h" #include "main/shim/le_scanning_manager.h" #include "metrics/counter_metrics.h" -#include "shim/dumpsys.h" +#include "os/system_properties.h" +#include "os/wakelock_manager.h" #include "storage/storage_module.h" + #if TARGET_FLOSS #include "sysprops/sysprops_module.h" #endif +using ::bluetooth::os::Handler; +using ::bluetooth::os::Thread; +using ::bluetooth::os::WakelockManager; + namespace bluetooth { namespace shim { -using ::bluetooth::common::StringFormat; - struct Stack::impl { Acl* acl_ = nullptr; }; @@ -74,12 +84,15 @@ void Stack::StartEverything() { #if TARGET_FLOSS modules.add<sysprops::SyspropsModule>(); +#else + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3286716 + modules.add<lpp::LppOffloadManager>(); + } #endif modules.add<metrics::CounterMetrics>(); modules.add<hal::HciHal>(); modules.add<hci::HciLayer>(); modules.add<storage::StorageModule>(); - modules.add<shim::Dumpsys>(); modules.add<hci::Controller>(); modules.add<hci::acl_manager::AclScheduler>(); @@ -89,18 +102,21 @@ void Stack::StartEverything() { modules.add<hci::MsftExtensionManager>(); modules.add<hci::LeScanningManager>(); modules.add<hci::DistanceMeasurementManager>(); - Start(&modules); + + stack_thread_ = new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME); + StartUp(&modules, stack_thread_); + + stack_handler_ = new os::Handler(stack_thread_); + + log::info("Successfully toggled Gd stack"); + is_running_ = true; // Make sure the leaf modules are started - log::assert_that(stack_manager_.GetInstance<storage::StorageModule>() != nullptr, - "assert failed: stack_manager_.GetInstance<storage::StorageModule>() != " - "nullptr"); - log::assert_that(stack_manager_.GetInstance<shim::Dumpsys>() != nullptr, - "assert failed: stack_manager_.GetInstance<shim::Dumpsys>() != nullptr"); - if (stack_manager_.IsStarted<hci::Controller>()) { + log::assert_that(GetInstance<storage::StorageModule>() != nullptr, + "assert failed: GetInstance<storage::StorageModule>() != nullptr"); + if (IsStarted<hci::Controller>()) { pimpl_->acl_ = - new Acl(stack_handler_, GetAclInterface(), GetController()->GetLeFilterAcceptListSize(), - GetController()->GetLeResolvingListSize()); + new Acl(stack_handler_, GetAclInterface(), GetController()->GetLeResolvingListSize()); } else { log::error("Unable to create shim ACL layer as Controller has not started"); } @@ -117,25 +133,13 @@ void Stack::StartModuleStack(const ModuleList* modules, const os::Thread* thread stack_thread_ = const_cast<os::Thread*>(thread); log::info("Starting Gd stack"); - stack_manager_.StartUp(const_cast<ModuleList*>(modules), stack_thread_); + StartUp(const_cast<ModuleList*>(modules), stack_thread_); stack_handler_ = new os::Handler(stack_thread_); num_modules_ = modules->NumModules(); is_running_ = true; } -void Stack::Start(ModuleList* modules) { - log::assert_that(!is_running_, "Gd stack already running"); - log::info("Starting Gd stack"); - - stack_thread_ = new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME); - stack_manager_.StartUp(modules, stack_thread_); - - stack_handler_ = new os::Handler(stack_thread_); - - log::info("Successfully toggled Gd stack"); -} - void Stack::Stop() { std::lock_guard<std::recursive_mutex> lock(mutex_); bluetooth::shim::hci_on_shutting_down(); @@ -152,7 +156,26 @@ void Stack::Stop() { stack_handler_->Clear(); - stack_manager_.ShutDown(); + WakelockManager::Get().Acquire(); + + std::promise<void> promise; + auto future = promise.get_future(); + management_handler_->Post( + common::BindOnce(&Stack::handle_shut_down, common::Unretained(this), std::move(promise))); + + auto stop_status = future.wait_for( + std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ false))); + + WakelockManager::Get().Release(); + WakelockManager::Get().CleanUp(); + + log::assert_that(stop_status == std::future_status::ready, "Can't stop stack, last instance: {}", + registry_.last_instance_); + + management_handler_->Clear(); + management_handler_->WaitUntilStopped(std::chrono::milliseconds(2000)); + delete management_handler_; + delete management_thread_; delete stack_handler_; stack_handler_ = nullptr; @@ -169,18 +192,6 @@ bool Stack::IsRunning() { return is_running_; } -StackManager* Stack::GetStackManager() { - std::lock_guard<std::recursive_mutex> lock(mutex_); - log::assert_that(is_running_, "assert failed: is_running_"); - return &stack_manager_; -} - -const StackManager* Stack::GetStackManager() const { - std::lock_guard<std::recursive_mutex> lock(mutex_); - log::assert_that(is_running_, "assert failed: is_running_"); - return &stack_manager_; -} - Acl* Stack::GetAcl() { std::lock_guard<std::recursive_mutex> lock(mutex_); log::assert_that(is_running_, "assert failed: is_running_"); @@ -194,17 +205,64 @@ os::Handler* Stack::GetHandler() { return stack_handler_; } -bool Stack::IsDumpsysModuleStarted() const { +void Stack::Dump(int fd, std::promise<void> promise) const { std::lock_guard<std::recursive_mutex> lock(mutex_); - return GetStackManager()->IsStarted<Dumpsys>(); + if (is_running_ && fd >= 0) { + stack_handler_->Call( + [](int fd, std::promise<void> promise) { + bluetooth::shim::GetController()->Dump(fd); + bluetooth::shim::GetAclManager()->Dump(fd); + bluetooth::os::WakelockManager::Get().Dump(fd); + bluetooth::shim::GetSnoopLogger()->DumpSnoozLogToFile(); + promise.set_value(); + }, + fd, std::move(promise)); + } else { + promise.set_value(); + } } -bool Stack::LockForDumpsys(std::function<void()> dumpsys_callback) { - std::lock_guard<std::recursive_mutex> lock(mutex_); - if (is_running_) { - dumpsys_callback(); - } - return is_running_; +void Stack::StartUp(ModuleList* modules, Thread* stack_thread) { + management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL); + management_handler_ = new Handler(management_thread_); + + WakelockManager::Get().Acquire(); + + std::promise<void> promise; + auto future = promise.get_future(); + management_handler_->Post(common::BindOnce(&Stack::handle_start_up, common::Unretained(this), + modules, stack_thread, std::move(promise))); + + auto init_status = future.wait_for( + std::chrono::milliseconds(get_gd_stack_timeout_ms(/* is_start = */ true))); + + WakelockManager::Get().Release(); + + log::info("init_status == {}", int(init_status)); + + log::assert_that(init_status == std::future_status::ready, "Can't start stack, last instance: {}", + registry_.last_instance_); + + log::info("init complete"); +} + +void Stack::handle_start_up(ModuleList* modules, Thread* stack_thread, std::promise<void> promise) { + registry_.Start(modules, stack_thread); + promise.set_value(); +} + +void Stack::handle_shut_down(std::promise<void> promise) { + registry_.StopAll(); + promise.set_value(); +} + +std::chrono::milliseconds Stack::get_gd_stack_timeout_ms(bool is_start) { + auto gd_timeout = os::GetSystemPropertyUint32( + is_start ? "bluetooth.gd.start_timeout" : "bluetooth.gd.stop_timeout", + /* default_value = */ is_start ? 3000 : 5000); + return std::chrono::milliseconds(gd_timeout * + os::GetSystemPropertyUint32("ro.hw_timeout_multiplier", + /* default_value = */ 1)); } } // namespace shim diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h index 0fd99aa6c2..0dfd3fe3ab 100644 --- a/system/main/shim/stack.h +++ b/system/main/shim/stack.h @@ -22,7 +22,6 @@ #include "module.h" #include "os/handler.h" #include "os/thread.h" -#include "stack_manager.h" // The shim layer implementation on the Gd stack side. namespace bluetooth { @@ -47,30 +46,26 @@ public: void Stop(); bool IsRunning(); - bool IsDumpsysModuleStarted() const; - StackManager* GetStackManager(); - const StackManager* GetStackManager() const; + template <class T> + T* GetInstance() const { + return static_cast<T*>(registry_.Get(&T::Factory)); + } + + template <class T> + bool IsStarted() const { + return registry_.IsStarted(&T::Factory); + } Acl* GetAcl(); os::Handler* GetHandler(); - bool LockForDumpsys(std::function<void()> dumpsys_callback); + void Dump(int fd, std::promise<void> promise) const; // Start the list of modules with the given stack manager thread void StartModuleStack(const ModuleList* modules, const os::Thread* thread); - // Run the callable object on the module instance - template <typename T> - bool CallOnModule(std::function<void(T* mod)> run) { - std::lock_guard<std::recursive_mutex> lock(mutex_); - if (is_running_) { - run(stack_manager_.GetInstance<T>()); - } - return is_running_; - } - size_t NumModules() const { return num_modules_; } private: @@ -78,12 +73,20 @@ private: std::shared_ptr<impl> pimpl_; mutable std::recursive_mutex mutex_; - StackManager stack_manager_; bool is_running_ = false; os::Thread* stack_thread_ = nullptr; os::Handler* stack_handler_ = nullptr; size_t num_modules_{0}; - void Start(ModuleList* modules); + + void StartUp(ModuleList* modules, os::Thread* stack_thread); + + os::Thread* management_thread_ = nullptr; + os::Handler* management_handler_ = nullptr; + ModuleRegistry registry_; + + void handle_start_up(ModuleList* modules, os::Thread* stack_thread, std::promise<void> promise); + void handle_shut_down(std::promise<void> promise); + static std::chrono::milliseconds get_gd_stack_timeout_ms(bool is_start); }; } // namespace shim diff --git a/system/main/test/main_shim_dumpsys_test.cc b/system/main/test/main_shim_dumpsys_test.cc deleted file mode 100644 index a61a55cda5..0000000000 --- a/system/main/test/main_shim_dumpsys_test.cc +++ /dev/null @@ -1,74 +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 <gmock/gmock.h> -#include <gtest/gtest.h> -#include <unistd.h> - -#include <future> - -#include "module.h" -#include "os/handler.h" -#include "os/system_properties.h" -#include "os/thread.h" -#include "shim/dumpsys.h" -#include "stack_manager.h" - -using namespace bluetooth; -using namespace testing; - -namespace { - -constexpr char kTrue[] = "1"; -constexpr char kFalse[] = "0"; -constexpr char kReadOnlyDebuggableProperty[] = "ro.debuggable"; - -} // namespace - -class MainShimDumpsysTest : public testing::Test { -public: -protected: - void SetUp() override { - ModuleList modules; - modules.add<shim::Dumpsys>(); - - os::Thread* thread = new os::Thread("thread", os::Thread::Priority::NORMAL); - stack_manager_.StartUp(&modules, thread); - } - void TearDown() override { stack_manager_.ShutDown(); } - StackManager stack_manager_; - - os::Thread* thread_{nullptr}; - os::Handler* handler_{nullptr}; -}; - -TEST_F(MainShimDumpsysTest, dumpsys_developer) { - ASSERT_TRUE(os::SetSystemProperty(kReadOnlyDebuggableProperty, kTrue)); - - std::promise<void> promise; - auto future = promise.get_future(); - stack_manager_.GetInstance<shim::Dumpsys>()->Dump(STDOUT_FILENO, nullptr, std::move(promise)); - future.get(); -} - -TEST_F(MainShimDumpsysTest, dumpsys_user) { - ASSERT_TRUE(os::SetSystemProperty(kReadOnlyDebuggableProperty, kFalse)); - - std::promise<void> promise; - auto future = promise.get_future(); - stack_manager_.GetInstance<shim::Dumpsys>()->Dump(STDOUT_FILENO, nullptr, std::move(promise)); - future.get(); -} diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc index f5ca4026a2..9de16ba92b 100644 --- a/system/main/test/main_shim_test.cc +++ b/system/main/test/main_shim_test.cc @@ -80,8 +80,7 @@ using HciHandle = uint16_t; namespace test = bluetooth::hci::testing; -const uint8_t kMaxLeAcceptlistSize = 16; -const uint8_t kMaxAddressResolutionSize = kMaxLeAcceptlistSize; +const uint8_t kMaxAddressResolutionSize = 16; tL2C_CB l2cb; tBTM_CB btm_cb; @@ -379,8 +378,7 @@ protected: EXPECT_CALL(*test::mock_acl_manager_, RegisterLeCallbacks(_, _)).Times(1); EXPECT_CALL(*test::mock_controller_, RegisterCompletedMonitorAclPacketsCallback(_)).Times(1); EXPECT_CALL(*test::mock_controller_, UnregisterCompletedMonitorAclPacketsCallback).Times(1); - return std::make_unique<shim::Acl>(handler_, GetMockAclInterface(), kMaxLeAcceptlistSize, - kMaxAddressResolutionSize); + return std::make_unique<shim::Acl>(handler_, GetMockAclInterface(), kMaxAddressResolutionSize); } }; diff --git a/system/osi/src/stack_power_telemetry.cc b/system/osi/src/stack_power_telemetry.cc index 262bb2afb2..18199bcd18 100644 --- a/system/osi/src/stack_power_telemetry.cc +++ b/system/osi/src/stack_power_telemetry.cc @@ -750,7 +750,7 @@ void power_telemetry::PowerTelemetry::Dumpsys(int32_t fd) { const RawAddress& bd_addr = itr.first; std::list<ChannelDetails> channel_details_list = itr.second; for (auto& channel_details : channel_details_list) { - dprintf(fd, "%-19s ", ADDRESS_TO_LOGGABLE_CSTR(bd_addr)); + dprintf(fd, "%-19s ", bd_addr.ToRedactedStringForLogging().c_str()); dprintf(fd, "%-7s %-7d %-7d %-8d %-22s %-22s %-14s", (channel_details.channel_type == ChannelType::kRfcomm) ? "RFCOMM" : "L2CAP", channel_details.src.cid, channel_details.dst.cid, channel_details.psm, @@ -799,7 +799,7 @@ void power_telemetry::PowerTelemetry::Dumpsys(int32_t fd) { uint16_t handle = itr.first; SniffData sniff_data = itr.second; dprintf(fd, "%-8d %-19s %-19d %-24ld %-19d %-24ld\n", handle, - ADDRESS_TO_LOGGABLE_CSTR(sniff_data.bd_addr), sniff_data.active_count, + sniff_data.bd_addr.ToRedactedStringForLogging().c_str(), sniff_data.active_count, (long)sniff_data.active_duration_ts, sniff_data.sniff_count, (long)sniff_data.sniff_duration_ts); } @@ -812,13 +812,15 @@ void power_telemetry::PowerTelemetry::Dumpsys(int32_t fd) { for (auto it : ldc.acl.link_details_map) { uint16_t handle = it.first; LinkDetails lds = it.second; - dprintf(fd, "%-6d %-19s %-22s %-22s %-8d\n", handle, ADDRESS_TO_LOGGABLE_CSTR(lds.bd_addr), + dprintf(fd, "%-6d %-19s %-22s %-22s %-8d\n", handle, + lds.bd_addr.ToRedactedStringForLogging().c_str(), GetTimeString(lds.duration.begin).c_str(), GetTimeString(lds.duration.end).c_str(), lds.tx_power_level); } for (auto& it : ldc.acl.link_details_list) { - dprintf(fd, "%-6d %-19s %-22s %-22s %-8d\n", it.handle, ADDRESS_TO_LOGGABLE_CSTR(it.bd_addr), + dprintf(fd, "%-6d %-19s %-22s %-22s %-8d\n", it.handle, + it.bd_addr.ToRedactedStringForLogging().c_str(), GetTimeString(it.duration.begin).c_str(), GetTimeString(it.duration.end).c_str(), it.tx_power_level); } @@ -828,7 +830,8 @@ void power_telemetry::PowerTelemetry::Dumpsys(int32_t fd) { "DisconnectedTimeStamp"); for (auto&& ldc : pimpl_->log_data_containers_) { for (auto& it : ldc.sco.link_details_list) { - dprintf(fd, "%-6d %-19s %-22s %-22s\n", it.handle, ADDRESS_TO_LOGGABLE_CSTR(it.bd_addr), + dprintf(fd, "%-6d %-19s %-22s %-22s\n", it.handle, + it.bd_addr.ToRedactedStringForLogging().c_str(), GetTimeString(it.duration.begin).c_str(), GetTimeString(it.duration.end).c_str()); } } diff --git a/system/pdl/hci/hci_packets.pdl b/system/pdl/hci/hci_packets.pdl index ab1e390c42..3810ffefd0 100644 --- a/system/pdl/hci/hci_packets.pdl +++ b/system/pdl/hci/hci_packets.pdl @@ -4917,7 +4917,6 @@ packet LeSubrateRequestStatus : CommandStatus (command_op_code = LE_SUBRATE_REQU } packet LeCsReadLocalSupportedCapabilities : DistanceMeasurementCommand (op_code = LE_CS_READ_LOCAL_SUPPORTED_CAPABILITIES) { - } struct CsRoleSupported { @@ -4954,7 +4953,7 @@ struct CsOptionalCsSyncPhysSupported { } struct CsOptionalSubfeaturesSupported { - companion_signal : 1, + _reserved_ : 1, frequency_actuation_error : 1, channel_selection_algorithm : 1, phase_based_ranging : 1, @@ -5140,7 +5139,7 @@ enum CsRole : 8 { } enum CsConfigRttType : 8 { - RTT_AA_COARSE = 0x00, + RTT_AA_ONLY = 0x00, RTT_WITH_32_BIT_SOUNDING_SEQUENCE = 0x01, RTT_WITH_96_BIT_SOUNDING_SEQUENCE = 0x02, RTT_WITH_32_BIT_RANDOM_SEQUENCE = 0x03, @@ -5293,7 +5292,7 @@ packet LeCsTest : DistanceMeasurementCommand (op_code = LE_CS_TEST) { snr_control_reflector: CsSnrControl, drbg_nonce : 16, channel_map_repetition : 8, - override_config : 8, + override_config : 16, override_parameters_length : 8, override_parameters_data : 8[], } @@ -6890,6 +6889,7 @@ packet LeCsProcedureEnableComplete : LeMetaEvent (subevent_code = LE_CS_PROCEDUR event_interval : 16, procedure_interval : 16, procedure_count : 16, + max_procedure_len : 16, } struct LeCsMode0InitatorData { @@ -6975,8 +6975,8 @@ struct LeCsMode3InitatorData { packet_quality : 8, packet_nadm : CsPacketNadm, packet_rssi : 8, - packet_antenna : 8, toa_tod_initiator : 16, + packet_antenna : 8, antenna_permutation_index : 8, tone_data : LeCsToneDataWithQuality[], } @@ -6986,10 +6986,10 @@ struct LeCsMode3InitatorDataWithPacketPct { packet_quality : 8, packet_nadm : CsPacketNadm, packet_rssi : 8, + toa_tod_initiator : 16, packet_antenna : 8, packet_pct1 : LeCsToneData, packet_pct2 : LeCsToneData, - toa_tod_initiator : 16, antenna_permutation_index : 8, tone_data : LeCsToneDataWithQuality[], } @@ -6999,8 +6999,8 @@ struct LeCsMode3ReflectorData { packet_quality : 8, packet_nadm : CsPacketNadm, packet_rssi : 8, - packet_antenna : 8, tod_toa_reflector : 16, + packet_antenna : 8, antenna_permutation_index : 8, tone_data : LeCsToneDataWithQuality[], } @@ -7010,10 +7010,10 @@ struct LeCsMode3ReflectorDataWithPacketPct { packet_quality : 8, packet_nadm : CsPacketNadm, packet_rssi : 8, + tod_toa_reflector : 16, packet_antenna : 8, packet_pct1 : LeCsToneData, packet_pct2 : LeCsToneData, - tod_toa_reflector : 16, antenna_permutation_index : 8, tone_data : LeCsToneDataWithQuality[], } diff --git a/system/pdl/hci/include/hci/address.h b/system/pdl/hci/include/hci/address.h index bc9c8d9127..7694689bf2 100644 --- a/system/pdl/hci/include/hci/address.h +++ b/system/pdl/hci/include/hci/address.h @@ -25,7 +25,6 @@ #include <ostream> #include <string> -#include "common/interfaces/ILoggable.h" #include "os/logging/log_adapter.h" #include "packet/custom_field_fixed_size_interface.h" #include "storage/serializable.h" @@ -34,8 +33,7 @@ namespace bluetooth { namespace hci { class Address final : public packet::CustomFieldFixedSizeInterface<Address>, - public storage::Serializable<Address>, - public bluetooth::common::IRedactableLoggable { + public storage::Serializable<Address> { public: static constexpr size_t kLength = 6; @@ -56,11 +54,10 @@ public: // storage::Serializable methods std::string ToString() const override; std::string ToColonSepHexString() const; - std::string ToStringForLogging() const override; - std::string ToRedactedStringForLogging() const override; - - static std::optional<Address> FromString(const std::string& from); + std::string ToStringForLogging() const; + std::string ToRedactedStringForLogging() const; std::string ToLegacyConfigString() const override; + static std::optional<Address> FromString(const std::string& from); static std::optional<Address> FromLegacyConfigString(const std::string& str); bool operator<(const Address& rhs) const { return address < rhs.address; } @@ -89,13 +86,6 @@ private: std::string _ToMaskedColonSepHexString(int bytes_to_mask) const; }; -// TODO: to fine-tune this. -// we need an interface between the logger and ILoggable -inline std::ostream& operator<<(std::ostream& os, const Address& a) { - os << a.ToString(); - return os; -} - } // namespace hci } // namespace bluetooth diff --git a/system/profile/avrcp/connection_handler.cc b/system/profile/avrcp/connection_handler.cc index 146c079c77..0851b3320b 100644 --- a/system/profile/avrcp/connection_handler.cc +++ b/system/profile/avrcp/connection_handler.cc @@ -249,7 +249,7 @@ void ConnectionHandler::InitiatorControlCb(uint8_t handle, uint8_t event, uint16 DCHECK(!connection_cb_.is_null()); log::info("handle=0x{:x} result=0x{:x} addr={}", handle, result, - peer_addr ? ADDRESS_TO_LOGGABLE_STR(*peer_addr) : "none"); + peer_addr ? peer_addr->ToRedactedStringForLogging() : "none"); switch (event) { case AVRC_OPEN_IND_EVT: { @@ -339,7 +339,7 @@ void ConnectionHandler::AcceptorControlCb(uint8_t handle, uint8_t event, uint16_ DCHECK(!connection_cb_.is_null()); log::info("handle=0x{:x} result=0x{:x} addr={}", handle, result, - peer_addr ? ADDRESS_TO_LOGGABLE_STR(*peer_addr) : "none"); + peer_addr ? peer_addr->ToRedactedStringForLogging() : "none"); switch (event) { case AVRC_OPEN_IND_EVT: { @@ -406,10 +406,8 @@ void ConnectionHandler::AcceptorControlCb(uint8_t handle, uint8_t event, uint16_ // as this one which will be closed when the device is disconnected. AvrcpConnect(false, RawAddress::kAny); - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay()) { - // Check peer audio role: src or sink and connect A2DP after 3 seconds - SdpLookupAudioRole(handle); - } + // Check peer audio role: src or sink and connect A2DP after 3 seconds + SdpLookupAudioRole(handle); } break; case AVRC_CLOSE_IND_EVT: { @@ -645,7 +643,7 @@ bool ConnectionHandler::SdpLookupAudioRole(uint16_t handle) { log::info( "Performing SDP for AUDIO_SINK on connected device: address={}, " "handle={}", - ADDRESS_TO_LOGGABLE_STR(device->GetAddress()), handle); + device->GetAddress(), handle); return device->find_sink_service(base::Bind(&ConnectionHandler::SdpLookupAudioRoleCb, weak_ptr_factory_.GetWeakPtr(), handle)); @@ -660,8 +658,8 @@ void ConnectionHandler::SdpLookupAudioRoleCb(uint16_t handle, bool found, } auto device = device_map_[handle]; - log::debug("SDP callback for address={}, handle={}, AUDIO_SINK {}", - ADDRESS_TO_LOGGABLE_STR(device->GetAddress()), handle, found ? "found" : "not found"); + log::debug("SDP callback for address={}, handle={}, AUDIO_SINK {}", device->GetAddress(), handle, + found ? "found" : "not found"); if (found) { device->connect_a2dp_sink_delayed(handle); diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc index a5012e8389..ef4fcda929 100644 --- a/system/profile/avrcp/device.cc +++ b/system/profile/avrcp/device.cc @@ -154,6 +154,7 @@ void Device::VendorPacketHandler(uint8_t label, std::shared_ptr<VendorPacket> pk // TODO (apanicke): Add a retry mechanism if the response has a // different volume than the one we set. For now, we don't care // about the response to this message. + active_labels_.erase(label); break; default: log::warn("{}: Unhandled Response: pdu={}", address_, pkt->GetCommandPdu()); @@ -1541,15 +1542,11 @@ void Device::SetBrowsedPlayerResponse(uint8_t label, std::shared_ptr<SetBrowsedP return; } - // SetBrowsedPlayer can be called to retrieve the current path - // and to verify that the player is still present, so we need to - // keep current_path_ as is if the player is already the one browsed. - // Otherwise, the current_path in the callback will contain the root id. - if (pkt->GetPlayerId() != curr_browsed_player_id_) { - curr_browsed_player_id_ = pkt->GetPlayerId(); - current_path_ = std::stack<std::string>(); - current_path_.push(current_path); - } + curr_browsed_player_id_ = pkt->GetPlayerId(); + + // Clear the path and push the new root or current path. + current_path_ = std::stack<std::string>(); + current_path_.push(current_path); auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR, 0x0000, num_items, 0, current_path); diff --git a/system/profile/avrcp/tests/avrcp_connection_handler_test.cc b/system/profile/avrcp/tests/avrcp_connection_handler_test.cc index 080bda09f3..54f78edcd4 100644 --- a/system/profile/avrcp/tests/avrcp_connection_handler_test.cc +++ b/system/profile/avrcp/tests/avrcp_connection_handler_test.cc @@ -208,10 +208,8 @@ TEST_F(AvrcpConnectionHandlerTest, remoteDeviceConnectionTest) { // device connects. EXPECT_CALL(mock_avrcp_, OpenBrowse(1, AVCT_ROLE_ACCEPTOR)).Times(1); - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay()) { - // Set an expectation that SDP for audio will be performed - EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); - } + // Set an expectation that SDP for audio will be performed + EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); @@ -248,10 +246,8 @@ TEST_F(AvrcpConnectionHandlerTest, noAbsoluteVolumeTest) { tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, false); - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay()) { - // Set an expectation that SDP for audio will be performed - EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); - } + // Set an expectation that SDP for audio will be performed + EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); EXPECT_CALL(mock_volume_, DeviceConnected(RawAddress::kAny)).Times(1); @@ -292,10 +288,8 @@ TEST_F(AvrcpConnectionHandlerTest, absoluteVolumeTest) { tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, true); - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay()) { - // Set an expectation that SDP for audio will be performed - EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); - } + // Set an expectation that SDP for audio will be performed + EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); @@ -324,10 +318,8 @@ TEST_F(AvrcpConnectionHandlerTest, disconnectTest) { &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); - if (com::android::bluetooth::flags::avrcp_connect_a2dp_with_delay()) { - // Set an expectation that SDP for audio will be performed - EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); - } + // Set an expectation that SDP for audio will be performed + EXPECT_CALL(mock_a2dp_, find_audio_sink_service(_, _)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); diff --git a/system/rust/Android.bp b/system/rust/Android.bp index 04be44dbec..592a686216 100644 --- a/system/rust/Android.bp +++ b/system/rust/Android.bp @@ -61,17 +61,6 @@ rust_defaults { apex_available: ["com.android.btservices"], } -rust_library { - name: "libbluetooth_core_rs_for_facade", - crate_name: "bluetooth_core_rs_for_facade", - defaults: ["libbluetooth_core_rs_defaults"], - rustlibs: [ - "libbluetooth_aconfig_flags_rust", - "libtokio", - ], - host_supported: true, -} - rust_ffi_static { name: "libbluetooth_core_rs", crate_name: "bluetooth_core", @@ -127,9 +116,6 @@ cc_library_static { generated_headers: [ "cxx-bridge-header", "libbluetooth_core_rs_bridge_codegen_header", - - "BluetoothGeneratedBundlerSchema_h_bfbs", - "BluetoothGeneratedDumpsysDataSchema_h", ], export_generated_headers: [ "cxx-bridge-header", diff --git a/system/setup.py b/system/setup.py deleted file mode 100644 index b593205e6e..0000000000 --- a/system/setup.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - 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. - -from distutils import log -import os -from setuptools import find_packages -from setuptools import setup -from setuptools.command.install import install -import stat -import subprocess -import sys - -reuse_libraries = False -force_install = False - -install_requires = [ - 'grpcio', - 'psutil', - 'protobuf>=3.14.0, <4.0', - 'mobly', -] - -host_executables = [ - 'root-canal', - 'bluetooth_stack_with_facade', # c++ - 'bluetooth_with_facades', # rust - 'bt_topshim_facade', # topshim -] - - -def set_permissions_for_host_executables(outputs): - for file in outputs: - if os.path.basename(file) in host_executables: - current_mode = os.stat(file).st_mode - new_mode = current_mode | stat.S_IEXEC - os.chmod(file, new_mode) - log.log(log.INFO, "Changed file mode of %s from %s to %s" % (file, oct(current_mode), oct(new_mode))) - - -class InstallLocalPackagesForInstallation(install): - - def run(self): - global reuse_libraries, force_install - install_args = [sys.executable, '-m', 'pip', 'install'] - subprocess.check_call(install_args + ['--upgrade', 'pip']) - - for package in install_requires: - self.announce('Installing %s...' % package, log.INFO) - cmd = install_args + ['-v', '--no-cache-dir', package] - if force_install and not reuse_libraries: - cmd.append("--force-reinstall") - subprocess.check_call(cmd) - self.announce('Dependencies installed.') - - install.run(self) - set_permissions_for_host_executables(self.get_outputs()) - - -def main(): - global reuse_libraries, force_install - if sys.argv[-1] == "--reuse-libraries": - reuse_libraries = True - sys.argv = sys.argv[:-1] - if "--force" in sys.argv: - force_install = True - # Relative path from calling directory to this file - our_dir = os.path.dirname(__file__) - # Must cd into this dir for package resolution to work - # This won't affect the calling shell - os.chdir(our_dir) - setup( - name='bluetooth_cert_tests', - version='1.0', - author='Android Open Source Project', - license='Apache2.0', - description="""Bluetooth Cert Tests Package""", - packages=[''] + find_packages(exclude=['llvm_binutils', 'llvm_binutils.*']), - install_requires=install_requires, - package_data={ - '': host_executables + ['*.so', 'lib64/*.so', 'target/*', 'llvm_binutils/bin/*', 'llvm_binutils/lib64/*'], - }, - cmdclass={ - 'install': InstallLocalPackagesForInstallation, - }) - - -if __name__ == '__main__': - main() diff --git a/system/stack/Android.bp b/system/stack/Android.bp index 155dcd4208..2d690133f3 100644 --- a/system/stack/Android.bp +++ b/system/stack/Android.bp @@ -72,9 +72,6 @@ cc_library_static { "packages/modules/Bluetooth/system/gd/hal", "packages/modules/Bluetooth/system/udrv/include", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ "a2dp/a2dp_aac.cc", "a2dp/a2dp_aac_decoder.cc", @@ -322,7 +319,6 @@ cc_library_static { "srvc/srvc_eng.cc", ], generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", "cxx-bridge-header", ], cflags: [ @@ -358,7 +354,6 @@ cc_defaults { host_supported: true, defaults: [ "bluetooth_cflags", - "bluetooth_flatbuffer_bundler_defaults", ], include_dirs: [ "packages/modules/Bluetooth/system", @@ -442,6 +437,7 @@ cc_fuzz { static_libs: [ "bluetooth_flags_c_lib", "libbluetooth-types", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-platform-protos-lite", ], @@ -460,14 +456,11 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", "packages/modules/Bluetooth/system/stack/test/common", "packages/modules/Bluetooth/system/stack/test/rfcomm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -516,12 +509,9 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -571,12 +561,9 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -620,12 +607,9 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -661,12 +645,9 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -711,12 +692,9 @@ cc_fuzz { ], include_dirs: [ "external/flatbuffers/include", - "external/rust/crates/quiche/deps/boringssl/src/include", + "external/rust/android-crates-io/crates/quiche/deps/boringssl/src/include", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothPacketSources", ":TestCommonMockFunctions", @@ -740,6 +718,7 @@ cc_fuzz { "libbluetooth_l2cap_pdl", "libbluetooth_log", "libbluetooth_smp_pdl", + "libbt-common", "libbt-platform-protos-lite", ], shared_libs: [ @@ -807,7 +786,6 @@ cc_test { "bluetooth_flags_c_lib_for_test", "libFraunhoferAAC", "libbase", - "libbluetooth-dumpsys", "libbluetooth-for-tests", "libbluetooth-types", "libbluetooth_core_rs", @@ -909,9 +887,6 @@ cc_test { "test/rfcomm/stack_rfcomm_test_utils.cc", "test/rfcomm/stack_rfcomm_test_utils_test.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], shared_libs: [ "libcrypto", "libcutils", @@ -957,7 +932,6 @@ cc_test { cc_test { name: "net_test_stack_smp", defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -998,9 +972,6 @@ cc_test { "smp/smp_utils.cc", "test/stack_smp_test.cc", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], shared_libs: [ "libaconfig_storage_read_api_cc", "libcrypto", @@ -1285,7 +1256,6 @@ cc_test { "avdt/avdt_l2c.cc", "avdt/avdt_scb.cc", "avdt/avdt_scb_act.cc", - "test/common/mock_btu_layer.cc", "test/common/mock_stack_avdt_msg.cc", "test/stack_avdtp_test.cc", ], @@ -1517,6 +1487,7 @@ cc_test { ":TestMockMainShim", ":TestMockRustFfi", ":TestMockStackBtm", + ":TestMockStackConnMgr", ":TestMockStackL2cap", ":TestMockStackMetrics", "gatt/gatt_db.cc", @@ -1544,6 +1515,7 @@ cc_test { "libbluetooth_core_rs_bridge", "libbluetooth_crypto_toolbox", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-common", "libbt-platform-protos-lite", @@ -1572,7 +1544,6 @@ cc_test { unit_test: true, }, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -1581,9 +1552,6 @@ cc_test { "include", "test/common", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], include_dirs: [ "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", @@ -1634,7 +1602,6 @@ cc_test { unit_test: true, }, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -1651,9 +1618,6 @@ cc_test { "packages/modules/Bluetooth/system/bta", "packages/modules/Bluetooth/system/gd", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":TestCommonMainHandler", ":TestCommonMockFunctions", @@ -1663,7 +1627,6 @@ cc_test { ":TestMockStackL2capInterface", ":TestStubOsi", "eatt/eatt.cc", - "test/common/mock_btif_storage.cc", "test/common/mock_btm_api_layer.cc", "test/common/mock_gatt_layer.cc", "test/common/mock_l2cap_layer.cc", @@ -1692,6 +1655,7 @@ cc_test { "libprotobuf-cpp-lite", "libstatslog_bt", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], target: { android: { @@ -1712,7 +1676,6 @@ cc_test { unit_test: true, }, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -1725,9 +1688,6 @@ cc_test { "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothHalSources_hci_host", ":BluetoothHalSources_ranging_host", @@ -1930,6 +1890,7 @@ cc_test { "libbase", "libbluetooth-types", "libbluetooth_crypto_toolbox", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-common", "libbt-platform-protos-lite", @@ -1981,9 +1942,6 @@ cc_test { "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":TestCommonMockFunctions", ":TestMockBta", @@ -2007,6 +1965,7 @@ cc_test { "libbase", "libbluetooth-types", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-common", "libbt-platform-protos-lite", @@ -2018,6 +1977,7 @@ cc_test { "liblog", "libosi", "server_configurable_flags", + "libaconfig_storage_read_api_cc", ], aidl: { libs: ["bluetooth_constants"], @@ -2057,9 +2017,6 @@ cc_test { "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":OsiCompatSources", ":TestCommonMainHandler", @@ -2076,6 +2033,7 @@ cc_test { ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackL2cap", + ":TestMockStackMetrics", ":TestMockStackSdp", ":TestMockStackSmp", "ais/ais_ble.cc", @@ -2150,7 +2108,6 @@ cc_test { test_suites: ["general-tests"], host_supported: true, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -2163,9 +2120,6 @@ cc_test { "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":OsiCompatSources", ":TestCommonMainHandler", @@ -2250,7 +2204,6 @@ cc_test { test_suites: ["general-tests"], host_supported: true, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -2263,9 +2216,6 @@ cc_test { "packages/modules/Bluetooth/system/gd", "packages/modules/Bluetooth/system/stack/btm", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":OsiCompatSources", ":TestCommonMainHandler", @@ -2298,6 +2248,7 @@ cc_test { "libbluetooth-types", "libbluetooth_core_rs_bridge", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-common", "libbt-platform-protos-lite", @@ -2348,7 +2299,6 @@ cc_test { unit_test: true, }, defaults: [ - "bluetooth_flatbuffer_bundler_defaults", "fluoride_defaults", "mts_defaults", ], @@ -2361,9 +2311,6 @@ cc_test { "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], srcs: [ ":BluetoothHalSources_hci_host", ":BluetoothHalSources_ranging_host", @@ -2479,6 +2426,7 @@ cc_test { "libbase", "libbluetooth-types", "libbluetooth_gd", + "libbluetooth_hci_pdl", "libbluetooth_log", "libbt-common", "libbt-platform-protos-lite", diff --git a/system/stack/a2dp/a2dp_sbc.cc b/system/stack/a2dp/a2dp_sbc.cc index cac37e686e..f04fe13134 100644 --- a/system/stack/a2dp/a2dp_sbc.cc +++ b/system/stack/a2dp/a2dp_sbc.cc @@ -65,7 +65,11 @@ typedef struct { /* SBC Source codec capabilities */ static const tA2DP_SBC_CIE a2dp_sbc_source_caps = { - (A2DP_SBC_IE_SAMP_FREQ_44), /* samp_freq */ +#ifdef TARGET_FLOSS + (A2DP_SBC_IE_SAMP_FREQ_48 | A2DP_SBC_IE_SAMP_FREQ_44), /* samp_freq */ +#else + (A2DP_SBC_IE_SAMP_FREQ_44), /* samp_freq */ +#endif (A2DP_SBC_IE_CH_MD_MONO | A2DP_SBC_IE_CH_MD_JOINT), /* ch_mode */ (A2DP_SBC_IE_BLOCKS_16 | A2DP_SBC_IE_BLOCKS_12 | A2DP_SBC_IE_BLOCKS_8 | A2DP_SBC_IE_BLOCKS_4), /* block_len */ @@ -92,7 +96,11 @@ static const tA2DP_SBC_CIE a2dp_sbc_sink_caps = { /* Default SBC codec configuration */ const tA2DP_SBC_CIE a2dp_sbc_default_config = { - A2DP_SBC_IE_SAMP_FREQ_44, /* samp_freq */ +#ifdef TARGET_FLOSS + (A2DP_SBC_IE_SAMP_FREQ_48), /* samp_freq */ +#else + (A2DP_SBC_IE_SAMP_FREQ_44), /* samp_freq */ +#endif A2DP_SBC_IE_CH_MD_JOINT, /* ch_mode */ A2DP_SBC_IE_BLOCKS_16, /* block_len */ A2DP_SBC_IE_SUBBAND_8, /* num_subbands */ diff --git a/system/stack/acl/btm_pm.cc b/system/stack/acl/btm_pm.cc index 1914f30a38..15e0e51f28 100644 --- a/system/stack/acl/btm_pm.cc +++ b/system/stack/acl/btm_pm.cc @@ -31,7 +31,6 @@ #include "main/shim/entry.h" #define LOG_TAG "bt_btm_pm" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -103,9 +102,9 @@ static void send_sniff_subrating(uint16_t handle, const RawAddress& addr, uint16 btsnd_hcic_sniff_sub_rate(handle, max_lat, min_rmt_to, min_loc_to); BTM_LogHistory(kBtmLogTag, addr, "Sniff subrating", - base::StringPrintf("max_latency:%.2f peer_timeout:%.2f local_timeout:%.2f", - ticks_to_seconds(max_lat), ticks_to_seconds(min_rmt_to), - ticks_to_seconds(min_loc_to))); + std::format("max_latency:{:.2f} peer_timeout:{:.2f} local_timeout:{:.2f}", + ticks_to_seconds(max_lat), ticks_to_seconds(min_rmt_to), + ticks_to_seconds(min_loc_to))); } static tBTM_STATUS btm_pm_snd_md_req(uint16_t handle, uint8_t pm_id, int link_ind, @@ -555,9 +554,8 @@ static tBTM_STATUS btm_pm_snd_md_req(uint16_t handle, uint8_t pm_id, int link_in log::info("Switching from {}[0x{:02x}] to {}[0x{:02x}]", power_mode_state_text(p_cb->state), p_cb->state, power_mode_state_text(md_res.mode), md_res.mode); BTM_LogHistory(kBtmLogTag, p_cb->bda_, "Power mode change", - base::StringPrintf("%s[0x%02x] ==> %s[0x%02x]", - power_mode_state_text(p_cb->state).c_str(), p_cb->state, - power_mode_state_text(md_res.mode).c_str(), md_res.mode)); + std::format("{}[0x{:02x}] ==> {}[0x{:02x}]", power_mode_state_text(p_cb->state), + p_cb->state, power_mode_state_text(md_res.mode), md_res.mode)); switch (md_res.mode) { case BTM_PM_MD_ACTIVE: diff --git a/system/stack/ais/ais_ble.cc b/system/stack/ais/ais_ble.cc index 879797ed34..a4e4a65549 100644 --- a/system/stack/ais/ais_ble.cc +++ b/system/stack/ais/ais_ble.cc @@ -28,9 +28,6 @@ #include "stack/include/gatt_api.h" #include "types/bluetooth/uuid.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using bluetooth::Uuid; using bluetooth::log::error; using bluetooth::log::warn; @@ -104,7 +101,7 @@ void ais_request_cback(tCONN_ID conn_id, uint32_t trans_id, tGATTS_REQ_TYPE type * Returns void. * ******************************************************************************/ -void ais_attr_db_init(void) { +static void ais_attr_db_init(void) { if (!com::android::bluetooth::flags::android_os_identifier()) { return; } diff --git a/system/stack/avct/avct_api.cc b/system/stack/avct/avct_api.cc index 33d58126c0..4369d68d13 100644 --- a/system/stack/avct/avct_api.cc +++ b/system/stack/avct/avct_api.cc @@ -70,9 +70,6 @@ void AVCT_Register() { memset(&avct_cb, 0, sizeof(tAVCT_CB)); uint16_t sec = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT; - if (!com::android::bluetooth::flags::use_encrypt_req_for_av()) { - sec = BTA_SEC_AUTHENTICATE; - } /* register PSM with L2CAP */ if (!stack::l2cap::get_interface().L2CA_RegisterWithSecurity( diff --git a/system/stack/avct/avct_bcb_act.cc b/system/stack/avct/avct_bcb_act.cc index 68d10249e4..be2c90e43d 100644 --- a/system/stack/avct/avct_bcb_act.cc +++ b/system/stack/avct/avct_bcb_act.cc @@ -130,13 +130,8 @@ void avct_bcb_chnl_open(tAVCT_BCB* p_bcb, tAVCT_LCB_EVT* /* p_data */) { /* call l2cap connect req */ p_bcb->ch_state = AVCT_CH_CONN; - if (com::android::bluetooth::flags::use_encrypt_req_for_av()) { - p_bcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( - BT_PSM_AVCTP_BROWSE, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); - } else { - p_bcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( - BT_PSM_AVCTP_BROWSE, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE); - } + p_bcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( + BT_PSM_AVCTP_BROWSE, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); if (p_bcb->ch_lcid == 0) { /* if connect req failed, send ourselves close event */ tAVCT_LCB_EVT avct_lcb_evt; diff --git a/system/stack/avct/avct_lcb_act.cc b/system/stack/avct/avct_lcb_act.cc index 9985a6fcbb..7770cd9e4a 100644 --- a/system/stack/avct/avct_lcb_act.cc +++ b/system/stack/avct/avct_lcb_act.cc @@ -192,13 +192,8 @@ void avct_lcb_chnl_open(tAVCT_LCB* p_lcb, tAVCT_LCB_EVT* /* p_data */) { uint16_t result = AVCT_RESULT_FAIL; p_lcb->ch_state = AVCT_CH_CONN; - if (com::android::bluetooth::flags::use_encrypt_req_for_av()) { - p_lcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( - BT_PSM_AVCTP, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); - } else { - p_lcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( - BT_PSM_AVCTP, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE); - } + p_lcb->ch_lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( + BT_PSM_AVCTP, p_lcb->peer_addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); if (p_lcb->ch_lcid == 0) { /* if connect req failed, send ourselves close event */ tAVCT_LCB_EVT avct_lcb_evt; diff --git a/system/stack/avdt/avdt_ad.cc b/system/stack/avdt/avdt_ad.cc index fda9493690..938d4a4464 100644 --- a/system/stack/avdt/avdt_ad.cc +++ b/system/stack/avdt/avdt_ad.cc @@ -530,13 +530,8 @@ void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb, tAVDT_ROLE p_tbl->state = AVDT_AD_ST_CONN; /* call l2cap connect req */ - if (com::android::bluetooth::flags::use_encrypt_req_for_av()) { - lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( - AVDT_PSM, p_ccb->peer_addr, BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); - } else { - lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity(AVDT_PSM, p_ccb->peer_addr, - BTM_SEC_OUT_AUTHENTICATE); - } + lcid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity( + AVDT_PSM, p_ccb->peer_addr, BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); if (lcid == 0) { /* if connect req failed, call avdt_ad_tc_close_ind() */ diff --git a/system/stack/avdt/avdt_api.cc b/system/stack/avdt/avdt_api.cc index 207c090cd4..3a979e9c3a 100644 --- a/system/stack/avdt/avdt_api.cc +++ b/system/stack/avdt/avdt_api.cc @@ -104,9 +104,6 @@ void avdt_scb_transport_channel_timer_timeout(void* data) { ******************************************************************************/ void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) { uint16_t sec = BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT; - if (!com::android::bluetooth::flags::use_encrypt_req_for_av()) { - sec = BTA_SEC_AUTHENTICATE; - } /* register PSM with L2CAP */ if (!stack::l2cap::get_interface().L2CA_RegisterWithSecurity( AVDT_PSM, avdt_l2c_appl, true /* enable_snoop */, nullptr, kAvdtpMtu, 0, sec)) { @@ -981,7 +978,7 @@ void stack_debug_avdtp_api_dump(int fd) { continue; } dprintf(fd, "\n Channel control block: %zu peer: %s\n", i, - ADDRESS_TO_LOGGABLE_CSTR(ccb.peer_addr)); + ccb.peer_addr.ToRedactedStringForLogging().c_str()); dprintf(fd, " Allocated: %s\n", ccb.allocated ? "true" : "false"); dprintf(fd, " State: %d\n", ccb.state); dprintf(fd, " Link-layer opened: %s\n", ccb.ll_opened ? "true" : "false"); @@ -1010,7 +1007,8 @@ void stack_debug_avdtp_api_dump(int fd) { dprintf(fd, " Transport channel connect timer: %s\n", alarm_is_scheduled(scb.transport_channel_timer) ? "Scheduled" : "Not scheduled"); dprintf(fd, " Channel control block peer: %s\n", - (scb.p_ccb != nullptr) ? ADDRESS_TO_LOGGABLE_CSTR(scb.p_ccb->peer_addr) : "null"); + (scb.p_ccb != nullptr) ? scb.p_ccb->peer_addr.ToRedactedStringForLogging().c_str() + : "null"); dprintf(fd, " Allocated: %s\n", scb.allocated ? "true" : "false"); dprintf(fd, " In use: %s\n", scb.in_use ? "true" : "false"); dprintf(fd, " Role: 0x%x\n", scb.role); diff --git a/system/stack/avdt/avdt_scb_act.cc b/system/stack/avdt/avdt_scb_act.cc index 003f927c9c..e09cde8eeb 100644 --- a/system/stack/avdt/avdt_scb_act.cc +++ b/system/stack/avdt/avdt_scb_act.cc @@ -570,10 +570,9 @@ void avdt_scb_hdl_setconfig_cmd(AvdtpScb* p_scb, tAVDT_SCB_EVT* p_data) { // the codec type differs from the type of the SEP, or INVALID_CODEC_TYPE // if the codec type does not match the values defined by Assigned Numbers. if (local_codec_type != remote_codec_type) { - p_data->msg.hdr.err_code = - !com::android::bluetooth::flags::avdtp_error_codes() ? AVDTP_UNSUPPORTED_CONFIGURATION - : !A2DP_IsCodecTypeValid(remote_codec_type) ? A2DP_INVALID_CODEC_TYPE - : A2DP_NOT_SUPPORTED_CODEC_TYPE; + p_data->msg.hdr.err_code = !A2DP_IsCodecTypeValid(remote_codec_type) + ? A2DP_INVALID_CODEC_TYPE + : A2DP_NOT_SUPPORTED_CODEC_TYPE; p_data->msg.hdr.err_param = 0; avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), p_data->msg.hdr.sig_id, &p_data->msg); diff --git a/system/stack/bnep/bnep_utils.cc b/system/stack/bnep/bnep_utils.cc index 0740451fda..64ea5f2501 100644 --- a/system/stack/bnep/bnep_utils.cc +++ b/system/stack/bnep/bnep_utils.cc @@ -45,9 +45,6 @@ #include "types/bt_transport.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; using bluetooth::Uuid; @@ -56,8 +53,9 @@ using bluetooth::Uuid; /******************************************************************************/ static uint8_t* bnepu_init_hdr(BT_HDR* p_buf, uint16_t hdr_len, uint8_t pkt_type); -void bnepu_process_peer_multicast_filter_set(tBNEP_CONN* p_bcb, uint8_t* p_filters, uint16_t len); -void bnepu_send_peer_multicast_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code); +static void bnepu_process_peer_multicast_filter_set(tBNEP_CONN* p_bcb, uint8_t* p_filters, + uint16_t len); +static void bnepu_send_peer_multicast_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code); /******************************************************************************* * @@ -343,7 +341,7 @@ void bnepu_send_peer_our_multi_filters(tBNEP_CONN* p_bcb) { * Returns void * ******************************************************************************/ -void bnepu_send_peer_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code) { +static void bnepu_send_peer_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code) { BT_HDR* p_buf = (BT_HDR*)osi_malloc(BNEP_BUF_SIZE); uint8_t* p; @@ -1030,7 +1028,8 @@ void bnepu_process_multicast_filter_rsp(tBNEP_CONN* p_bcb, uint8_t* p_data) { * Returns void * ******************************************************************************/ -void bnepu_process_peer_multicast_filter_set(tBNEP_CONN* p_bcb, uint8_t* p_filters, uint16_t len) { +static void bnepu_process_peer_multicast_filter_set(tBNEP_CONN* p_bcb, uint8_t* p_filters, + uint16_t len) { uint16_t resp_code = BNEP_FILTER_CRL_OK; uint16_t num_filters, xx; uint8_t *p_temp_filters, null_bda[BD_ADDR_LEN] = {0, 0, 0, 0, 0, 0}; @@ -1106,7 +1105,7 @@ void bnepu_process_peer_multicast_filter_set(tBNEP_CONN* p_bcb, uint8_t* p_filte * Returns void * ******************************************************************************/ -void bnepu_send_peer_multicast_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code) { +static void bnepu_send_peer_multicast_filter_rsp(tBNEP_CONN* p_bcb, uint16_t response_code) { BT_HDR* p_buf = (BT_HDR*)osi_malloc(BNEP_BUF_SIZE); uint8_t* p; diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc index 6f6e42b216..4b9cc79e32 100644 --- a/system/stack/btm/btm_ble_gap.cc +++ b/system/stack/btm/btm_ble_gap.cc @@ -30,6 +30,7 @@ #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> +#include <bitset> #include <cstdint> #include <list> #include <memory> @@ -66,6 +67,7 @@ #include "stack/include/gattdefs.h" #include "stack/include/hci_error_code.h" #include "stack/include/inq_hci_link_interface.h" +#include "stack/rnr/remote_name_request.h" #include "types/ble_address_with_type.h" #include "types/raw_address.h" @@ -76,7 +78,6 @@ using namespace bluetooth; extern tBTM_CB btm_cb; -void btm_inq_remote_name_timer_timeout(void* data); void btm_ble_adv_filter_init(void); #define BTM_EXT_BLE_RMT_NAME_TIMEOUT_MS (30 * 1000) @@ -594,10 +595,10 @@ tBTM_STATUS BTM_BleObserve(bool start, uint8_t duration, tBTM_INQ_RESULTS_CB* p_ } else if (btm_cb.ble_ctr_cb.is_ble_observe_active()) { const uint64_t duration_timestamp = timestamper_in_milliseconds.GetTimestamp() - btm_cb.neighbor.le_observe.start_time_ms; - BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Le observe stopped", - base::StringPrintf("duration_s:%6.3f results:%-3lu", - (double)duration_timestamp / 1000.0, - (unsigned long)btm_cb.neighbor.le_observe.results)); + BTM_LogHistory( + kBtmLogTag, RawAddress::kEmpty, "Le observe stopped", + std::format("duration_s:{:6.3f} results:{:<3}", (double)duration_timestamp / 1000.0, + btm_cb.neighbor.le_observe.results)); status = tBTM_STATUS::BTM_CMD_STARTED; btm_ble_stop_observe(); } else { @@ -1464,15 +1465,25 @@ void btm_send_hci_set_scan_params(uint8_t scan_type, uint16_t scan_int, uint16_t uint8_t scan_phy, tBLE_ADDR_TYPE addr_type_own, uint8_t scan_filter_policy) { if (bluetooth::shim::GetController()->SupportsBleExtendedAdvertising()) { - scanning_phy_cfg phy_cfg; - phy_cfg.scan_type = scan_type; - phy_cfg.scan_int = scan_int; - phy_cfg.scan_win = scan_win; - if (com::android::bluetooth::flags::phy_to_native()) { + int phy_cnt = std::bitset<std::numeric_limits<uint8_t>::digits>(scan_phy).count(); + + scanning_phy_cfg phy_cfgs[phy_cnt]; + + for (int i = 0; i < phy_cnt; i++) { + phy_cfgs[i].scan_type = scan_type; + phy_cfgs[i].scan_int = scan_int; + phy_cfgs[i].scan_win = scan_win; + } + btsnd_hcic_ble_set_extended_scan_params(addr_type_own, scan_filter_policy, scan_phy, - &phy_cfg); + phy_cfgs); } else { + scanning_phy_cfg phy_cfg; + phy_cfg.scan_type = scan_type; + phy_cfg.scan_int = scan_int; + phy_cfg.scan_win = scan_win; + btsnd_hcic_ble_set_extended_scan_params(addr_type_own, scan_filter_policy, 1, &phy_cfg); } } else { @@ -2425,8 +2436,8 @@ static void btm_ble_stop_scan(void) { timestamper_in_milliseconds.GetTimestamp() - btm_cb.neighbor.le_legacy_scan.start_time_ms; BTM_LogHistory( kBtmLogTag, RawAddress::kEmpty, "Le legacy scan stopped", - base::StringPrintf("duration_s:%6.3f results:%-3lu", (double)duration_timestamp / 1000.0, - (unsigned long)btm_cb.neighbor.le_legacy_scan.results)); + std::format("duration_s:{:6.3f} results:{:<3}", (double)duration_timestamp / 1000.0, + btm_cb.neighbor.le_legacy_scan.results)); btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); btm_update_scanner_filter_policy(SP_ADV_ALL); @@ -2447,8 +2458,8 @@ void btm_ble_stop_inquiry(void) { timestamper_in_milliseconds.GetTimestamp() - btm_cb.neighbor.le_inquiry.start_time_ms; BTM_LogHistory( kBtmLogTag, RawAddress::kEmpty, "Le inquiry stopped", - base::StringPrintf("duration_s:%6.3f results:%-3lu", (double)duration_timestamp / 1000.0, - (unsigned long)btm_cb.neighbor.le_inquiry.results)); + std::format("duration_s:{:6.3f} results:{:<3}", (double)duration_timestamp / 1000.0, + btm_cb.neighbor.le_inquiry.results)); btm_cb.ble_ctr_cb.reset_ble_inquiry(); /* Cleanup anything remaining on index 0 */ diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc index 7ae82b335b..93d019cd08 100644 --- a/system/stack/btm/btm_ble_sec.cc +++ b/system/stack/btm/btm_ble_sec.cc @@ -20,7 +20,6 @@ #include "stack/btm/btm_ble_sec.h" #include <android_bluetooth_sysprop.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -265,7 +264,7 @@ void BTM_SecurityGrant(const RawAddress& bd_addr, tBTM_STATUS res) { (res == tBTM_STATUS::BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS; log::verbose("bd_addr:{}, res:{}", bd_addr, smp_status_text(res_smp)); BTM_LogHistory(kBtmLogTag, bd_addr, "Granted", - base::StringPrintf("passkey_status:%s", smp_status_text(res_smp).c_str())); + std::format("passkey_status:{}", smp_status_text(res_smp))); SMP_SecurityGrant(bd_addr, res_smp); } @@ -295,9 +294,8 @@ void BTM_BlePasskeyReply(const RawAddress& bd_addr, tBTM_STATUS res, uint32_t pa const tSMP_STATUS res_smp = (res == tBTM_STATUS::BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; BTM_LogHistory(kBtmLogTag, bd_addr, "Passkey reply", - base::StringPrintf("transport:%s authenticate_status:%s", - bt_transport_text(BT_TRANSPORT_LE).c_str(), - smp_status_text(res_smp).c_str())); + std::format("transport:{} authenticate_status:{}", + bt_transport_text(BT_TRANSPORT_LE), smp_status_text(res_smp))); p_dev_rec->sec_rec.sec_flags |= BTM_SEC_LE_AUTHENTICATED; SMP_PasskeyReply(bd_addr, res_smp, passkey); @@ -326,9 +324,8 @@ void BTM_BleConfirmReply(const RawAddress& bd_addr, tBTM_STATUS res) { (res == tBTM_STATUS::BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; BTM_LogHistory(kBtmLogTag, bd_addr, "Confirm reply", - base::StringPrintf("transport:%s numeric_comparison_authenticate_status:%s", - bt_transport_text(BT_TRANSPORT_LE).c_str(), - smp_status_text(res_smp).c_str())); + std::format("transport:{} numeric_comparison_authenticate_status:{}", + bt_transport_text(BT_TRANSPORT_LE), smp_status_text(res_smp))); p_dev_rec->sec_rec.sec_flags |= BTM_SEC_LE_AUTHENTICATED; SMP_ConfirmReply(bd_addr, res_smp); @@ -358,9 +355,8 @@ void BTM_BleOobDataReply(const RawAddress& bd_addr, tBTM_STATUS res, uint8_t len const tSMP_STATUS res_smp = (res == tBTM_STATUS::BTM_SUCCESS) ? SMP_SUCCESS : SMP_OOB_FAIL; BTM_LogHistory(kBtmLogTag, bd_addr, "Oob data reply", - base::StringPrintf("transport:%s authenticate_status:%s", - bt_transport_text(BT_TRANSPORT_LE).c_str(), - smp_status_text(res_smp).c_str())); + std::format("transport:{} authenticate_status:{}", + bt_transport_text(BT_TRANSPORT_LE), smp_status_text(res_smp))); p_dev_rec->sec_rec.sec_flags |= BTM_SEC_LE_AUTHENTICATED; SMP_OobDataReply(bd_addr, res_smp, len, p_data); @@ -387,7 +383,7 @@ void BTM_BleSecureConnectionOobDataReply(const RawAddress& bd_addr, uint8_t* p_c } BTM_LogHistory(kBtmLogTag, bd_addr, "Oob data reply", - base::StringPrintf("transport:%s", bt_transport_text(BT_TRANSPORT_LE).c_str())); + std::format("transport:{}", bt_transport_text(BT_TRANSPORT_LE))); p_dev_rec->sec_rec.sec_flags |= BTM_SEC_LE_AUTHENTICATED; @@ -1147,6 +1143,10 @@ tBTM_STATUS btm_ble_set_encryption(const RawAddress& bd_addr, tBTM_BLE_SEC_ACT s switch (sec_act) { case BTM_BLE_SEC_ENCRYPT: + if (p_rec->sec_rec.is_le_device_encrypted()) { + return tBTM_STATUS::BTM_SUCCESS; + } + if (link_role == HCI_ROLE_CENTRAL) { /* start link layer encryption using the security info stored */ cmd = btm_ble_start_encrypt(bd_addr, false, NULL); @@ -1407,6 +1407,42 @@ void btm_ble_ltk_request_reply(const RawAddress& bda, bool use_stk, const Octet1 btsnd_hcic_ble_ltk_req_reply(btm_sec_cb.enc_handle, p_rec->sec_rec.ble_keys.lltk); } +static void btm_ble_get_auth_req(const tBTM_SEC_DEV_REC* p_dev_rec, tBTM_LE_AUTH_REQ* p_auth_req) { + // If the device is bonded and we are trying to encrypt the link with it as a + // peripheral, then we need to ensure that the authentication requirements + // match what was agreed upon during bonding. + if (com::android::bluetooth::flags::peripheral_auth_req() && + btm_sec_cb.pairing_bda != p_dev_rec->bd_addr && + btm_sec_cb.pairing_bda != p_dev_rec->ble.pseudo_addr) { // Not pairing + if (!p_dev_rec->role_central && p_dev_rec->sec_rec.is_le_link_key_known() && + p_dev_rec->sec_rec.ble_keys.key_type != BTM_LE_KEY_NONE && + p_dev_rec->sec_rec.le_link == tSECURITY_STATE::AUTHENTICATING) { + // Trying to encrypt the link with already bonded device in peripheral role + if ((p_dev_rec->sec_rec.security_required & BTM_SEC_IN_MITM) || + p_dev_rec->sec_rec.ble_keys.sec_level == SMP_SEC_AUTHENTICATED) { + // Authentication required or existing bond record was authenticated + *p_auth_req |= BTM_LE_AUTH_REQ_MITM; + } else { + // No authentication required and no bond record + *p_auth_req &= ~BTM_LE_AUTH_REQ_MITM; + } + + // Request Secure Connections only if the remote device claim support earlier + if (p_dev_rec->SupportsSecureConnections()) { + *p_auth_req |= BTM_LE_AUTH_REQ_SC_ONLY; + } else { + *p_auth_req &= ~BTM_LE_AUTH_REQ_SC_ONLY; + } + return; + } + } + + /* Authentication requested? */ + if (p_dev_rec->sec_rec.security_required & BTM_SEC_IN_MITM) { + *p_auth_req |= BTM_LE_AUTH_REQ_MITM; + } +} + /******************************************************************************* * * Function btm_ble_io_capabilities_req @@ -1439,10 +1475,7 @@ static tBTM_STATUS btm_ble_io_capabilities_req(tBTM_SEC_DEV_REC* p_dev_rec, log::verbose("2:i_keys=0x{:x} r_keys=0x{:x} (bit 0-LTK 1-IRK 2-CSRK)", p_data->init_keys, p_data->resp_keys); - /* if authentication requires MITM protection, put on the mask */ - if (p_dev_rec->sec_rec.security_required & BTM_SEC_IN_MITM) { - p_data->auth_req |= BTM_LE_AUTH_REQ_MITM; - } + btm_ble_get_auth_req(p_dev_rec, &p_data->auth_req); if (!(p_data->auth_req & SMP_AUTH_BOND)) { log::verbose("Non bonding: No keys should be exchanged"); @@ -1555,13 +1588,22 @@ void btm_ble_connected(const RawAddress& bda, uint16_t handle, uint8_t /* enc_mo * ******************************************************************************/ void btm_ble_connection_established(const RawAddress& bda) { - if (!com::android::bluetooth::flags::name_discovery_for_le_pairing()) { + tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda); + if (p_dev_rec == nullptr) { + log::warn("No security record for {}", bda); return; } + // Encrypt the link if device is bonded + if (com::android::bluetooth::flags::le_enc_on_reconnection() && + p_dev_rec->sec_rec.is_le_link_key_known()) { + btm_ble_set_encryption(bda, BTM_BLE_SEC_ENCRYPT, + p_dev_rec->role_central ? HCI_ROLE_CENTRAL : HCI_ROLE_PERIPHERAL); + } + // Read device name if it is not known already, we may need it for pairing - tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda); - if (p_dev_rec != nullptr && !p_dev_rec->sec_rec.is_name_known()) { + if (com::android::bluetooth::flags::name_discovery_for_le_pairing() && + !p_dev_rec->sec_rec.is_name_known()) { btm_ble_read_remote_name(bda, nullptr); } @@ -1575,6 +1617,34 @@ void btm_ble_connection_established(const RawAddress& bda) { } } +static bool btm_ble_complete_evt_ignore(const tBTM_SEC_DEV_REC* p_dev_rec, + const tBTM_LE_EVT_DATA* p_data) { + if (!com::android::bluetooth::flags::bonded_device_smp_failure_handling()) { + return false; + } + // Encryption request in peripheral role results in SMP Security request. SMP may generate a + // SMP_COMPLT_EVT failure event cases like below: + // 1) Some central devices don't handle cross-over between encryption and SMP security request + // 2) Link may get disconnected after the SMP security request was sent. + if (p_data->complt.reason != SMP_SUCCESS && !p_dev_rec->role_central && + btm_sec_cb.pairing_bda != p_dev_rec->bd_addr && + btm_sec_cb.pairing_bda != p_dev_rec->ble.pseudo_addr && + p_dev_rec->sec_rec.is_le_link_key_known() && + p_dev_rec->sec_rec.ble_keys.key_type != BTM_LE_KEY_NONE) { + if (p_dev_rec->sec_rec.is_le_device_encrypted()) { + log::warn("Bonded device {} is already encrypted, ignoring SMP failure", p_dev_rec->bd_addr); + return true; + } else if (p_data->complt.reason == SMP_CONN_TOUT) { + log::warn("Bonded device {} disconnected while waiting for encryption, ignoring SMP failure", + p_dev_rec->bd_addr); + l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); + return true; + } + } + + return false; +} + static void btm_ble_user_confirmation_req(const RawAddress& bd_addr, tBTM_SEC_DEV_REC* p_dev_rec, tBTM_LE_EVT event, tBTM_LE_EVT_DATA* p_data) { p_dev_rec->sec_rec.sec_flags |= BTM_SEC_LE_AUTHENTICATED; @@ -1604,6 +1674,11 @@ static void btm_ble_consent_req(const RawAddress& bd_addr, tBTM_LE_EVT_DATA* p_d static void btm_ble_complete_evt(const RawAddress& bd_addr, tBTM_SEC_DEV_REC* p_dev_rec, tBTM_LE_EVT_DATA* p_data) { + + if (btm_ble_complete_evt_ignore(p_dev_rec, p_data)) { + return; + } + BTM_BLE_SEC_CALLBACK(BTM_LE_COMPLT_EVT, bd_addr, p_data); log::verbose("before update sec_level=0x{:x} sec_flags=0x{:x}", p_data->complt.sec_level, @@ -1865,7 +1940,7 @@ void BTM_BleSirkConfirmDeviceReply(const RawAddress& bd_addr, tBTM_STATUS res) { } BTM_LogHistory(kBtmLogTag, bd_addr, "SIRK confirmation", - base::StringPrintf("status:%s", smp_status_text(res_smp).c_str())); + std::format("status:{}", smp_status_text(res_smp))); SMP_SirkConfirmDeviceReply(bd_addr, res_smp); } diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc index 6712527fe7..4840a6d36e 100644 --- a/system/stack/btm/btm_dev.cc +++ b/system/stack/btm/btm_dev.cc @@ -192,10 +192,9 @@ bool BTM_SecDeleteDevice(const RawAddress& bd_addr) { /* Tell controller to get rid of the link key, if it has one stored */ BTM_DeleteStoredLinkKey(&bda, NULL); log::info("{} complete", bd_addr); - BTM_LogHistory( - kBtmLogTag, bd_addr, "Device removed", - base::StringPrintf("device_type:%s bond_type:%s", DeviceTypeText(device_type).c_str(), - bond_type_text(bond_type).c_str())); + BTM_LogHistory(kBtmLogTag, bd_addr, "Device removed", + std::format("device_type:{} bond_type:{}", DeviceTypeText(device_type), + bond_type_text(bond_type))); return true; } diff --git a/system/stack/btm/btm_inq.cc b/system/stack/btm/btm_inq.cc index 32debc62e1..b1ed1c6d03 100644 --- a/system/stack/btm/btm_inq.cc +++ b/system/stack/btm/btm_inq.cc @@ -98,9 +98,9 @@ void btm_log_history_scan_mode(uint8_t scan_mode) { } BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic updated", - base::StringPrintf("inquiry_scan_enable:%c page_scan_enable:%c", - (scan_mode & HCI_INQUIRY_SCAN_ENABLED) ? 'T' : 'F', - (scan_mode & HCI_PAGE_SCAN_ENABLED) ? 'T' : 'F')); + std::format("inquiry_scan_enable:{:c} page_scan_enable:{:c}", + (scan_mode & HCI_INQUIRY_SCAN_ENABLED) ? 'T' : 'F', + (scan_mode & HCI_PAGE_SCAN_ENABLED) ? 'T' : 'F')); scan_mode_cached_ = scan_mode; } @@ -563,12 +563,11 @@ void BTM_CancelInquiry(void) { const auto duration_ms = timestamper_in_milliseconds.GetTimestamp() - btm_cb.neighbor.classic_inquiry.start_time_ms; BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry canceled", - base::StringPrintf( - "duration_s:%6.3f results:%lu std:%u rssi:%u ext:%u", duration_ms / 1000.0, - (unsigned long)btm_cb.neighbor.classic_inquiry.results, - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD], - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI], - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED])); + std::format("duration_s:{:6.3f} results:{} std:{} rssi:{} ext:{}", + duration_ms / 1000.0, btm_cb.neighbor.classic_inquiry.results, + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD], + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI], + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED])); btm_cb.neighbor.classic_inquiry = {}; /* Only cancel if not in periodic mode, otherwise the caller should call @@ -710,9 +709,9 @@ tBTM_STATUS BTM_StartInquiry(tBTM_INQ_RESULTS_CB* p_results_cb, tBTM_CMPL_CB* p_ } BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry started", - base::StringPrintf("%s", (btm_cb.neighbor.classic_inquiry.start_time_ms == 0) - ? "" - : "ERROR Already in progress")); + std::format("{}", (btm_cb.neighbor.classic_inquiry.start_time_ms == 0) + ? "" + : "ERROR Already in progress")); const uint8_t inq_length = osi_property_get_int32(PROPERTY_INQ_LENGTH, BTIF_DM_DEFAULT_INQ_MAX_DURATION); @@ -1690,16 +1689,16 @@ void btm_process_inq_complete(tHCI_STATUS status, uint8_t mode) { .start_time_ms = btm_cb.neighbor.classic_inquiry.start_time_ms, }); const auto end_time_ms = timestamper_in_milliseconds.GetTimestamp(); - BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry complete", - base::StringPrintf( - "duration_s:%6.3f results:%lu inq_active:0x%02x std:%u rssi:%u " - "ext:%u status:%s", - (end_time_ms - btm_cb.neighbor.classic_inquiry.start_time_ms) / 1000.0, - (unsigned long)btm_cb.neighbor.classic_inquiry.results, inq_active, - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD], - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI], - btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED], - hci_error_code_text(status).c_str())); + BTM_LogHistory( + kBtmLogTag, RawAddress::kEmpty, "Classic inquiry complete", + std::format("duration_s:{:6.3f} results:{} inq_active:0x{:02x} std:{} rssi:{} ext:{} " + "status:{}", + (end_time_ms - btm_cb.neighbor.classic_inquiry.start_time_ms) / 1000.0, + btm_cb.neighbor.classic_inquiry.results, inq_active, + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD], + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI], + btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED], + hci_error_code_text(status))); btm_cb.neighbor.classic_inquiry.start_time_ms = 0; /* Clear the results callback if set */ diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h index cdda94f0a2..e99e99f557 100644 --- a/system/stack/btm/btm_iso_impl.h +++ b/system/stack/btm/btm_iso_impl.h @@ -133,8 +133,8 @@ struct iso_impl { IsCigKnown(cig_id) ? kIsoEventCigOnReconfigureCmpl : kIsoEventCigOnCreateCmpl; BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "CIG Create complete", - base::StringPrintf("cig_id:0x%02x, status: %s", evt.cig_id, - hci_status_code_text((tHCI_STATUS)(evt.status)).c_str())); + std::format("cig_id:0x{:02x}, status: {}", evt.cig_id, + hci_status_code_text((tHCI_STATUS)(evt.status)))); if (evt.status == HCI_SUCCESS) { log::assert_that(len >= (3) + (cis_cnt * sizeof(uint16_t)), "Invalid CIS count: {}", cis_cnt); @@ -188,8 +188,7 @@ struct iso_impl { cig_params.sdu_itv_mtos)); BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "CIG Create", - base::StringPrintf("cig_id:0x%02x, size: %d", cig_id, - static_cast<int>(cig_params.cis_cfgs.size()))); + std::format("cig_id:0x{:02x}, size: {}", cig_id, cig_params.cis_cfgs.size())); } void reconfigure_cig(uint8_t cig_id, struct iso_manager::cig_create_params cig_params) { @@ -213,8 +212,8 @@ struct iso_impl { STREAM_TO_UINT8(evt.cig_id, stream); BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "CIG Remove complete", - base::StringPrintf("cig_id:0x%02x, status: %s", evt.cig_id, - hci_status_code_text((tHCI_STATUS)(evt.status)).c_str())); + std::format("cig_id:0x{:02x}, status: {}", evt.cig_id, + hci_status_code_text((tHCI_STATUS)(evt.status)))); if (evt.status == HCI_SUCCESS) { auto cis_it = conn_hdl_to_cis_map_.cbegin(); @@ -247,7 +246,7 @@ struct iso_impl { btsnd_hcic_remove_cig(cig_id, base::BindOnce(&iso_impl::on_remove_cig, weak_factory_.GetWeakPtr())); BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "CIG Remove", - base::StringPrintf("cig_id:0x%02x (f:%d)", cig_id, force)); + std::format("cig_id:0x{:02x} (f:{})", cig_id, force)); } void on_status_establish_cis(struct iso_manager::cis_establish_params conn_params, @@ -272,8 +271,8 @@ struct iso_impl { cig_callbacks_->OnCisEvent(kIsoEventCisEstablishCmpl, &evt); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[evt.cis_conn_hdl], "Establish CIS failed ", - base::StringPrintf("handle:0x%04x, status: %s", evt.cis_conn_hdl, - hci_status_code_text((tHCI_STATUS)(status)).c_str())); + std::format("handle:0x{:04x}, status: {}", evt.cis_conn_hdl, + hci_status_code_text((tHCI_STATUS)(status)))); cis_hdl_to_addr.erase(evt.cis_conn_hdl); } } @@ -295,7 +294,7 @@ struct iso_impl { if (p_rec) { cis_hdl_to_addr[el.cis_conn_handle] = p_rec->ble.pseudo_addr; BTM_LogHistory(kBtmLogTag, p_rec->ble.pseudo_addr, "Establish CIS", - base::StringPrintf("handle:0x%04x", el.acl_conn_handle)); + std::format("handle:0x{:04x}", el.acl_conn_handle)); } } btsnd_hcic_create_cis(conn_params.conn_pairs.size(), conn_params.conn_pairs.data(), @@ -318,8 +317,8 @@ struct iso_impl { bluetooth::legacy::hci::GetInterface().Disconnect(cis_handle, static_cast<tHCI_STATUS>(reason)); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[cis_handle], "Disconnect CIS ", - base::StringPrintf("handle:0x%04x, reason:%s", cis_handle, - hci_reason_code_text((tHCI_REASON)(reason)).c_str())); + std::format("handle:0x{:04x}, reason:{}", cis_handle, + hci_reason_code_text((tHCI_REASON)(reason)))); } int get_number_of_active_iso() { @@ -344,8 +343,8 @@ struct iso_impl { } BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[conn_handle], "Setup data path complete", - base::StringPrintf("handle:0x%04x, status:%s", conn_handle, - hci_status_code_text((tHCI_STATUS)(status)).c_str())); + std::format("handle:0x{:04x}, status:{}", conn_handle, + hci_status_code_text((tHCI_STATUS)(status)))); if (status == HCI_SUCCESS) { iso->state_flags |= kStateFlagHasDataPathSet; @@ -374,9 +373,9 @@ struct iso_impl { path_params.controller_delay, std::move(path_params.codec_conf), base::BindOnce(&iso_impl::on_setup_iso_data_path, weak_factory_.GetWeakPtr())); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[conn_handle], "Setup data path", - base::StringPrintf("handle:0x%04x, dir:0x%02x, path_id:0x%02x, codec_id:0x%02x", - conn_handle, path_params.data_path_dir, - path_params.data_path_id, path_params.codec_id_format)); + std::format("handle:0x{:04x}, dir:0x{:02x}, path_id:0x{:02x}, codec_id:0x{:02x}", + conn_handle, path_params.data_path_dir, path_params.data_path_id, + path_params.codec_id_format)); } void on_remove_iso_data_path(uint8_t* stream, uint16_t len) { @@ -399,8 +398,8 @@ struct iso_impl { } BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[conn_handle], "Remove data path complete", - base::StringPrintf("handle:0x%04x, status:%s", conn_handle, - hci_status_code_text((tHCI_STATUS)(status)).c_str())); + std::format("handle:0x{:04x}, status:{}", conn_handle, + hci_status_code_text((tHCI_STATUS)(status)))); if (status == HCI_SUCCESS) { iso->state_flags &= ~kStateFlagHasDataPathSet; @@ -426,7 +425,7 @@ struct iso_impl { base::BindOnce(&iso_impl::on_remove_iso_data_path, weak_factory_.GetWeakPtr())); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[iso_handle], "Remove data path", - base::StringPrintf("handle:0x%04x, dir:0x%02x", iso_handle, data_path_dir)); + std::format("handle:0x{:04x}, dir:0x{:02x}", iso_handle, data_path_dir)); } void on_iso_link_quality_read(uint8_t* stream, uint16_t len) { @@ -565,8 +564,8 @@ struct iso_impl { log::assert_that(cis != nullptr, "No such cis: {}", evt.cis_conn_hdl); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[evt.cis_conn_hdl], "CIS established event", - base::StringPrintf("cis_handle:0x%04x status:%s", evt.cis_conn_hdl, - hci_error_code_text((tHCI_STATUS)(evt.status)).c_str())); + std::format("cis_handle:0x{:04x} status:{}", evt.cis_conn_hdl, + hci_error_code_text((tHCI_STATUS)(evt.status)))); STREAM_TO_UINT24(evt.cig_sync_delay, data); STREAM_TO_UINT24(evt.cis_sync_delay, data); @@ -607,8 +606,8 @@ struct iso_impl { log::info("flags: {}", cis->state_flags); BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[handle], "CIS disconnected", - base::StringPrintf("cis_handle:0x%04x, reason:%s", handle, - hci_error_code_text((tHCI_REASON)(reason)).c_str())); + std::format("cis_handle:0x{:04x}, reason:{}", handle, + hci_error_code_text((tHCI_REASON)(reason)))); cis_hdl_to_addr.erase(handle); if (cis->state_flags & kStateFlagIsConnected || cis->state_flags & kStateFlagIsCancelled) { diff --git a/system/stack/btm/btm_main.cc b/system/stack/btm/btm_main.cc index d07769b157..581b4d6445 100644 --- a/system/stack/btm/btm_main.cc +++ b/system/stack/btm/btm_main.cc @@ -81,7 +81,7 @@ static void btm_log_history(const std::string& tag, const char* addr, const std: void BTM_LogHistory(const std::string& tag, const RawAddress& bd_addr, const std::string& msg, const std::string& extra) { - btm_log_history(tag, ADDRESS_TO_LOGGABLE_CSTR(bd_addr), msg, extra); + btm_log_history(tag, bd_addr.ToRedactedStringForLogging().c_str(), msg, extra); } void BTM_LogHistory(const std::string& tag, const RawAddress& bd_addr, const std::string& msg) { @@ -90,7 +90,7 @@ void BTM_LogHistory(const std::string& tag, const RawAddress& bd_addr, const std void BTM_LogHistory(const std::string& tag, const tBLE_BD_ADDR& ble_bd_addr, const std::string& msg, const std::string& extra) { - btm_log_history(tag, ADDRESS_TO_LOGGABLE_CSTR(ble_bd_addr), msg, extra); + btm_log_history(tag, ble_bd_addr.ToRedactedStringForLogging().c_str(), msg, extra); } void BTM_LogHistory(const std::string& tag, const tBLE_BD_ADDR& ble_bd_addr, diff --git a/system/stack/btm/btm_sco.cc b/system/stack/btm/btm_sco.cc index 1aba506513..ecb0c4d734 100644 --- a/system/stack/btm/btm_sco.cc +++ b/system/stack/btm/btm_sco.cc @@ -27,7 +27,6 @@ #include "stack/btm/btm_sco.h" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -810,7 +809,7 @@ tBTM_STATUS BTM_CreateSco(const RawAddress* remote_bda, bool is_orig, uint16_t p log::debug("SCO connection successfully requested"); if (p->state == SCO_ST_CONNECTING) { BTM_LogHistory(kBtmLogTag, *remote_bda, "Connecting", - base::StringPrintf("local initiated acl:0x%04x", acl_handle)); + std::format("local initiated acl:0x{:04x}", acl_handle)); } return tBTM_STATUS::BTM_CMD_STARTED; } @@ -1005,7 +1004,7 @@ void btm_sco_connected(const RawAddress& bda, uint16_t hci_handle, tBTM_ESCO_DAT (p->state == SCO_ST_W4_CONN_RSP)) && (p->rem_bd_known) && (p->esco.data.bd_addr == bda)) { BTM_LogHistory(kBtmLogTag, bda, "Connection created", - base::StringPrintf("sco_idx:%hu handle:0x%04x ", xx, hci_handle)); + std::format("sco_idx:{} handle:0x{:04x}", xx, hci_handle)); power_telemetry::GetInstance().LogLinkDetails(hci_handle, bda, true, false); if (p->state == SCO_ST_LISTENING) { @@ -1017,7 +1016,7 @@ void btm_sco_connected(const RawAddress& bda, uint16_t hci_handle, tBTM_ESCO_DAT BTM_LogHistory( kBtmLogTag, bda, "Connection success", - base::StringPrintf("handle:0x%04x %s", hci_handle, (spt) ? "listener" : "initiator")); + std::format("handle:0x{:04x} {}", hci_handle, (spt) ? "listener" : "initiator")); log::debug("Connected SCO link handle:0x{:04x} peer:{}", hci_handle, bda); if (!btm_cb.sco_cb.esco_supported) { @@ -1080,19 +1079,17 @@ void btm_sco_create_command_status_failed(tHCI_STATUS hci_status) { (*p->p_disc_cb)(idx); BTM_LogHistory(kBtmLogTag, p->esco.data.bd_addr, "Connection failed", - base::StringPrintf( - "locally_initiated reason:%s", - hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)).c_str())); + std::format("locally_initiated reason:{}", + hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)))); return; } } log::warn("No context found for the SCO connection failed"); - BTM_LogHistory( - kBtmLogTag, RawAddress::kEmpty, "Connection failed", - base::StringPrintf("locally_initiated reason:%s", - hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)).c_str())); + BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Connection failed", + std::format("locally_initiated reason:{}", + hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)))); } /******************************************************************************* @@ -1133,9 +1130,8 @@ void btm_sco_connection_failed(tHCI_STATUS hci_status, const RawAddress& bda, ui (*p->p_disc_cb)(xx); } BTM_LogHistory(kBtmLogTag, bda, "Connection failed", - base::StringPrintf( - "locally_initiated reason:%s", - hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)).c_str())); + std::format("locally_initiated reason:{}", + hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)))); } else { log::debug("SCO terminating connection failed handle:0x{:04x} reason:{}", hci_handle, hci_error_code_text(hci_status)); @@ -1149,9 +1145,8 @@ void btm_sco_connection_failed(tHCI_STATUS hci_status, const RawAddress& bda, ui } } BTM_LogHistory(kBtmLogTag, bda, "Connection failed", - base::StringPrintf( - "remote_initiated reason:%s", - hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)).c_str())); + std::format("remote_initiated reason:{}", + hci_reason_code_text(static_cast<tHCI_REASON>(hci_status)))); } return; } @@ -1203,8 +1198,8 @@ tBTM_STATUS BTM_RemoveSco(uint16_t sco_inx) { log::debug("Disconnecting link sco_handle:0x{:04x} peer:{}", p->Handle(), p->esco.data.bd_addr); BTM_LogHistory(kBtmLogTag, p->esco.data.bd_addr, "Disconnecting", - base::StringPrintf("local initiated handle:0x%04x previous_state:%s", p->Handle(), - sco_state_text(old_state).c_str())); + std::format("local initiated handle:0x{:04x} previous_state:{}", p->Handle(), + sco_state_text(old_state))); return tBTM_STATUS::BTM_CMD_STARTED; } @@ -1287,9 +1282,9 @@ void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason) { p_sco->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ (*p_sco->p_disc_cb)(btm_cb.sco_cb.get_index(p_sco)); log::debug("Disconnected SCO link handle:{} reason:{}", hci_handle, hci_reason_code_text(reason)); - BTM_LogHistory(kBtmLogTag, bd_addr, "Disconnected", - base::StringPrintf("handle:0x%04x reason:%s", hci_handle, - hci_reason_code_text(reason).c_str())); + BTM_LogHistory( + kBtmLogTag, bd_addr, "Disconnected", + std::format("handle:0x{:04x} reason:{}", hci_handle, hci_reason_code_text(reason))); hfp_hal_interface::notify_sco_connection_change( bd_addr, /*is_connected=*/false, diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc index 8c6d057f53..7df5f2bd4a 100644 --- a/system/stack/btm/btm_sec.cc +++ b/system/stack/btm/btm_sec.cc @@ -28,7 +28,6 @@ #include <android_bluetooth_sysprop.h> #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -1155,7 +1154,7 @@ void BTM_ConfirmReqReply(tBTM_STATUS res, const RawAddress& bd_addr) { } BTM_LogHistory(kBtmLogTag, bd_addr, "Confirm reply", - base::StringPrintf("status:%s", btm_status_text(res).c_str())); + std::format("status:{}", btm_status_text(res))); btm_sec_cb.change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); @@ -2160,9 +2159,8 @@ tBTM_SEC_DEV_REC* btm_rnr_add_name_to_security_record(const RawAddress* p_bd_add p_bd_name = (const uint8_t*)kBtmBdNameEmpty; } - BTM_LogHistory( - kBtmLogTag, (p_bd_addr) ? *p_bd_addr : RawAddress::kEmpty, "RNR complete", - base::StringPrintf("hci_status:%s name:%s", hci_error_code_text(hci_status).c_str(), + BTM_LogHistory(kBtmLogTag, (p_bd_addr) ? *p_bd_addr : RawAddress::kEmpty, "RNR complete", + std::format("hci_status:{} name:{}", hci_error_code_text(hci_status), PRIVATE_NAME(reinterpret_cast<char const*>(p_bd_name)))); if (p_dev_rec == nullptr) { @@ -2217,7 +2215,7 @@ tBTM_SEC_DEV_REC* btm_rnr_add_name_to_security_record(const RawAddress* p_bd_add void btm_sec_rmt_name_request_complete(const RawAddress* p_bd_addr, const uint8_t* p_bd_name, tHCI_STATUS hci_status) { log::info("btm_sec_rmt_name_request_complete for {}", - p_bd_addr ? ADDRESS_TO_LOGGABLE_CSTR(*p_bd_addr) : "null"); + p_bd_addr ? p_bd_addr->ToRedactedStringForLogging() : "null"); if ((!p_bd_addr && !get_btm_client_interface().peer.BTM_IsAclConnectionUp( btm_sec_cb.connecting_bda, BT_TRANSPORT_BR_EDR)) || @@ -2231,9 +2229,8 @@ void btm_sec_rmt_name_request_complete(const RawAddress* p_bd_addr, const uint8_ if (p_dev_rec == nullptr) { log::warn( "Remote read request complete for unknown device peer:{} " - "pairing_state:{} " - "hci_status:{} name:{}", - (p_bd_addr) ? ADDRESS_TO_LOGGABLE_CSTR(*p_bd_addr) : "null", + "pairing_state:{} hci_status:{} name:{}", + p_bd_addr ? p_bd_addr->ToRedactedStringForLogging() : "null", tBTM_SEC_CB::btm_pair_state_descr(btm_sec_cb.pairing_state), hci_status_code_text(hci_status), reinterpret_cast<char const*>(p_bd_name)); return; @@ -3229,6 +3226,11 @@ void btm_sec_encrypt_change(uint16_t handle, tHCI_STATUS status, uint8_t encr_en if (transport == BT_TRANSPORT_LE) { key_size = p_dev_rec->sec_rec.ble_keys.key_size; + if (key_size == 0 && status == HCI_SUCCESS && encr_enable == HCI_ENCRYPT_MODE_ON) { + /* Only case when key size is 0 during successfull encryption is pairing - for this case look + * up the key size */ + key_size = SMP_GetPendingPairingKeySize(); + } } log::debug( @@ -3283,16 +3285,15 @@ void btm_sec_encrypt_change(uint16_t handle, tHCI_STATUS status, uint8_t encr_en kBtmLogTag, (transport == BT_TRANSPORT_LE) ? p_dev_rec->ble.pseudo_addr : p_dev_rec->bd_addr, (status == HCI_SUCCESS) ? "Encryption success" : "Encryption failed", - base::StringPrintf("status:%s transport:%s is_encrypted:%c", - hci_status_code_text(status).c_str(), - bt_transport_text(transport).c_str(), is_encrypted ? 'T' : 'F')); + std::format("status:{} transport:{} is_encrypted:{:c}", hci_status_code_text(status), + bt_transport_text(transport), is_encrypted ? 'T' : 'F')); log::debug("after update p_dev_rec->sec_rec.sec_flags=0x{:x}", p_dev_rec->sec_rec.sec_flags); btm_sec_check_pending_enc_req(p_dev_rec, transport, encr_enable); if (!from_key_refresh) { - bta_dm_on_encryption_change(bt_encryption_change_evt{p_dev_rec->ble.pseudo_addr, status, + bta_dm_on_encryption_change(bt_encryption_change_evt{p_dev_rec->bd_addr, status, (bool)encr_enable, key_size, transport, p_dev_rec->SupportsSecureConnections()}); } @@ -3730,10 +3731,10 @@ void btm_sec_connected(const RawAddress& bda, uint16_t handle, tHCI_STATUS statu l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, true); } log::info("Connection complete during pairing process peer:{}", bda); - BTM_LogHistory(kBtmLogTag, bda, "Dedicated bonding", - base::StringPrintf("Initiated:%c pairing_flag:0x%02x", - (is_pair_flags_we_started_dd) ? 'T' : 'F', - p_dev_rec->sec_rec.sec_flags)); + BTM_LogHistory( + kBtmLogTag, bda, "Dedicated bonding", + std::format("Initiated:{:c} pairing_flag:0x{:02x}", + (is_pair_flags_we_started_dd) ? 'T' : 'F', p_dev_rec->sec_rec.sec_flags)); } p_dev_rec->hci_handle = handle; @@ -4513,6 +4514,14 @@ tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec) { } if (start_auth) { + if (com::android::bluetooth::flags::ignore_auth_req_when_collision_timer_active() && + alarm_is_scheduled(btm_sec_cb.sec_collision_timer) && + (btm_sec_cb.p_collided_dev_rec->bd_addr == p_dev_rec->bd_addr)) { + log::debug( + "Security Manager: Authentication will be executed after collision " + "timer expired"); + return tBTM_STATUS::BTM_CMD_STARTED; + } log::debug("Security Manager: Start authentication"); /* @@ -4736,8 +4745,8 @@ void tBTM_SEC_CB::change_pairing_state(tBTM_PAIRING_STATE new_state) { if (pairing_state != new_state) { BTM_LogHistory(kBtmLogTag, btm_sec_cb.pairing_bda, "Pairing state changed", - base::StringPrintf("%s => %s", tBTM_SEC_CB::btm_pair_state_descr(pairing_state), - tBTM_SEC_CB::btm_pair_state_descr(new_state))); + std::format("{} => {}", tBTM_SEC_CB::btm_pair_state_descr(pairing_state), + tBTM_SEC_CB::btm_pair_state_descr(new_state))); } pairing_state = new_state; diff --git a/system/stack/btm/security_device_record.h b/system/stack/btm/security_device_record.h index d72e1c078b..94e48753d2 100644 --- a/system/stack/btm/security_device_record.h +++ b/system/stack/btm/security_device_record.h @@ -18,7 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -292,18 +291,13 @@ public: void increment_sign_counter(bool local); std::string ToString() const { - return base::StringPrintf( - "bredr_linkkey_known:%c,le_linkkey_known:%c," - "bond_type:%s," - "bredr_linkkey_type:%s," - "ble_enc_key_size:%d," - "bredr_authenticated:%c,le_authenticated:%c," - "16_digit_key_authenticated:%c," - "bredr_encrypted:%c,le_encrypted:%c", + return std::format( + "bredr_linkkey_known:{:c},le_linkkey_known:{:c},bond_type:{},bredr_linkkey_type:{},ble_" + "enc_key_size:{},bredr_authenticated:{:c},le_authenticated:{:c},16_digit_key_" + "authenticated:{:c},bredr_encrypted:{:c},le_encrypted:{:c}", is_link_key_known() ? 'T' : 'F', is_le_link_key_known() ? 'T' : 'F', - bond_type_text(bond_type).c_str(), linkkey_type_text(link_key_type).c_str(), - enc_key_size, is_device_authenticated() ? 'T' : 'F', - is_le_device_authenticated() ? 'T' : 'F', + bond_type_text(bond_type), linkkey_type_text(link_key_type), enc_key_size, + is_device_authenticated() ? 'T' : 'F', is_le_device_authenticated() ? 'T' : 'F', is_le_link_16_digit_key_authenticated() ? 'T' : 'F', is_device_encrypted() ? 'T' : 'F', is_le_device_encrypted() ? 'T' : 'F'); } @@ -331,13 +325,12 @@ public: bool SupportsSecureConnections() const { return remote_supports_secure_connections; } std::string ToString() const { - return base::StringPrintf( - "%s %6s cod:%s remote_info:%-14s sm4:0x%02x SecureConn:%c name:\"%s\"" - "sec_prop:%s", - ADDRESS_TO_LOGGABLE_CSTR(bd_addr), DeviceTypeText(device_type).c_str(), - dev_class_text(dev_class).c_str(), remote_version_info.ToString().c_str(), sm4, - (remote_supports_secure_connections) ? 'T' : 'F', - PRIVATE_NAME(reinterpret_cast<char const*>(sec_bd_name)), sec_rec.ToString().c_str()); + return std::format( + "{} {:6s} cod:{} remote_info:{:<14s} sm4:0x{:02x} SecureConn:{:c} " + "name:\"{}\" sec_prop:{}", + bd_addr, DeviceTypeText(device_type), dev_class_text(dev_class), + remote_version_info.ToString(), sm4, remote_supports_secure_connections ? 'T' : 'F', + PRIVATE_NAME(reinterpret_cast<char const*>(sec_bd_name)), sec_rec.ToString()); } public: diff --git a/system/stack/connection_manager/connection_manager.cc b/system/stack/connection_manager/connection_manager.cc index c29ac5d403..11d4637b56 100644 --- a/system/stack/connection_manager/connection_manager.cc +++ b/system/stack/connection_manager/connection_manager.cc @@ -27,7 +27,9 @@ #include <memory> #include <set> +#include "gd/hci/controller_interface.h" #include "main/shim/acl_api.h" +#include "main/shim/entry.h" #include "main/shim/le_scanning_manager.h" #include "os/logging/log_adapter.h" #include "osi/include/alarm.h" @@ -42,9 +44,6 @@ #define DIRECT_CONNECT_TIMEOUT (30 * 1000) /* 30 seconds */ -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; constexpr char kBtmLogTag[] = "TA"; @@ -62,8 +61,8 @@ static void alarm_closure_cb(void* p) { } // Periodic alarms are not supported, because we clean up data in callback -void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm, uint64_t interval_ms, - base::OnceClosure user_task) { +static void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm, + uint64_t interval_ms, base::OnceClosure user_task) { closure_data* data = new closure_data; data->posted_from = posted_from; data->user_task = std::move(user_task); @@ -108,17 +107,45 @@ bool is_anyone_connecting(const std::map<RawAddress, tAPPS_CONNECTING>::iterator !it->second.doing_targeted_announcements_conn.empty(); } +static bool accept_list_is_full() { + uint8_t accept_list_size = shim::GetController()->GetLeFilterAcceptListSize(); + + int num_entries = 0; + for (const auto& entry : bgconn_dev) { + if (entry.second.is_in_accept_list) { + num_entries++; + } + } + + if (num_entries >= accept_list_size) { + return true; + } + + return false; +} + } // namespace -/** background connection device from the list. Returns pointer to the device - * record, or nullptr if not found */ +/** Return all apps interested in device, or empty set if not found. */ std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& address) { log::debug("address={}", address); auto it = bgconn_dev.find(address); - return (it != bgconn_dev.end()) ? it->second.doing_bg_conn : std::set<tAPP_ID>(); + if (it == bgconn_dev.end()) { + return std::set<tAPP_ID>(); + } + + std::set<tAPP_ID> result; + result.insert(it->second.doing_bg_conn.begin(), it->second.doing_bg_conn.end()); + result.insert(it->second.doing_targeted_announcements_conn.begin(), + it->second.doing_targeted_announcements_conn.end()); + + for (const auto& entry : it->second.doing_direct_conn) { + result.insert(entry.first); + } + return result; } -bool IsTargetedAnnouncement(const uint8_t* p_eir, uint16_t eir_len) { +static bool IsTargetedAnnouncement(const uint8_t* p_eir, uint16_t eir_len) { const uint8_t* p_service_data = p_eir; uint8_t service_data_len = 0; @@ -186,7 +213,7 @@ static void target_announcement_observe_results_cb(tBTM_INQ_RESULTS* p_inq, cons do_in_main_thread(base::BindOnce(schedule_direct_connect_add, app_id, addr)); } -void target_announcements_filtering_set(bool enable) { +static void target_announcements_filtering_set(bool enable) { log::debug("enable {}", enable); BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, (enable ? "Start filtering" : "Stop filtering")); @@ -276,6 +303,12 @@ bool background_connect_add(uint8_t app_id, const RawAddress& address) { if (is_targeted_announcement_enabled) { log::debug("Targeted announcement enabled, do not add to AcceptList"); } else { + if (accept_list_is_full()) { + log::warn("accept list is full ({}), can't add {}", + shim::GetController()->GetLeFilterAcceptListSize(), address); + return false; + } + if (!bluetooth::shim::ACL_AcceptLeConnectionFrom(BTM_Sec_GetAddressWithType(address), false)) { log::warn("Failed to add device {} to accept list for app {}", address, @@ -431,7 +464,7 @@ void reset(bool after_reset) { } } -void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) { +static void wl_direct_connect_timeout_cb(uint8_t app_id, const RawAddress& address) { log::debug("app_id={}, address={}", static_cast<int>(app_id), address); on_connection_timed_out(app_id, address); @@ -458,57 +491,65 @@ static void find_in_device_record(const RawAddress& bd_addr, tBLE_BD_ADDR* addre return; } -bool create_le_connection(uint8_t /* id */, const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type) { +bool direct_connect_add(uint8_t app_id, const RawAddress& address, tBLE_ADDR_TYPE addr_type) { tBLE_BD_ADDR address_with_type{ .type = addr_type, - .bda = bd_addr, + .bda = address, }; - find_in_device_record(bd_addr, &address_with_type); - - log::debug("Creating le direct connection to:{} type:{} (initial type: {})", address_with_type, - AddressTypeText(address_with_type.type), AddressTypeText(addr_type)); + find_in_device_record(address, &address_with_type); if (address_with_type.type == BLE_ADDR_ANONYMOUS) { - log::warn( - "Creating le direct connection to:{}, address type 'anonymous' is " - "invalid", - address_with_type); + log::warn("Can't use anonymous address for connection: {}", address_with_type); return false; } - bluetooth::shim::ACL_AcceptLeConnectionFrom(address_with_type, - /* is_direct */ true); - return true; -} + log::debug("app_id=0x{:x}, address={} (initial type: {})", static_cast<int>(app_id), + address_with_type, AddressTypeText(addr_type)); -/** Add a device to the direct connection list. Returns true if device - * added to the list, false otherwise */ -bool direct_connect_add(uint8_t app_id, const RawAddress& address) { - log::debug("app_id={}, address={}", static_cast<int>(app_id), address); bool in_acceptlist = false; auto it = bgconn_dev.find(address); if (it != bgconn_dev.end()) { + const tAPPS_CONNECTING& info = it->second; // app already trying to connect to this particular device - if (it->second.doing_direct_conn.count(app_id)) { - log::info("direct connect attempt from app_id=0x{:x} already in progress", app_id); + if (info.doing_direct_conn.count(app_id)) { + log::info("attempt from app_id=0x{:x} to {} already in progress", app_id, address_with_type); return false; } + // This is to match existing GD connection manager behavior - if multiple apps try direct + // connect at same time, only 1st request is fully processed + if (!info.doing_direct_conn.empty()) { + log::info("app_id=0x{:x}: attempt from other app already in progress, will merge {}", app_id, + address_with_type); + return true; + } + // are we already in the acceptlist ? - if (it->second.is_in_accept_list) { - log::warn("Background connection attempt already in progress app_id={:x}", app_id); + if (info.is_in_accept_list) { + log::warn("background connect attempt already in progress app_id=0x{:x} {}", app_id, + address_with_type); in_acceptlist = true; } } if (!in_acceptlist) { - if (!bluetooth::shim::ACL_AcceptLeConnectionFrom(BTM_Sec_GetAddressWithType(address), true)) { + if (accept_list_is_full()) { + log::warn("accept list is full ({}), can't add {}", + shim::GetController()->GetLeFilterAcceptListSize(), address_with_type); + return false; + } + + if (!bluetooth::shim::ACL_AcceptLeConnectionFrom(address_with_type, true /* is_direct */)) { // if we can't add to acceptlist, turn parameters back to slow. - log::warn("Unable to add le device to acceptlist"); + log::warn("unable to add to acceptlist {}", address_with_type); return false; } bgconn_dev[address].is_in_accept_list = true; + } else { + // if already in accept list, we should just bump parameters up for direct + // connection. There is no API for that yet, so use API that's adding to accept list. + bluetooth::shim::ACL_AcceptLeConnectionFrom(address_with_type, true /* is_direct */); } // Setup a timer @@ -517,7 +558,6 @@ bool direct_connect_add(uint8_t app_id, const RawAddress& address) { base::BindOnce(&wl_direct_connect_timeout_cb, app_id, address)); bgconn_dev[address].doing_direct_conn.emplace(app_id, unique_alarm_ptr(timeout, &alarm_free)); - return true; } @@ -529,13 +569,13 @@ bool direct_connect_remove(uint8_t app_id, const RawAddress& address, bool conne log::debug("app_id={}, address={}", static_cast<int>(app_id), address); auto it = bgconn_dev.find(address); if (it == bgconn_dev.end()) { - log::warn("Unable to find background connection to remove peer:{}", address); + log::warn("unable to find entry to remove: {}", address); return false; } auto app_it = it->second.doing_direct_conn.find(app_id); if (app_it == it->second.doing_direct_conn.end()) { - log::warn("Unable to find direct connection to remove peer:{}", address); + log::warn("unable to find direct connection to remove: {}", address); return false; } @@ -547,13 +587,12 @@ bool direct_connect_remove(uint8_t app_id, const RawAddress& address, bool conne if (is_anyone_interested_to_use_accept_list(it)) { if (connection_timeout) { - /* In such case we need to add device back to allow list because, - * when connection timeout out, the lower layer removes device from - * the allow list. + /* In such case we need to add device back to allow list because, when connection timeout + * out, the lower layer removes device from the allow list. */ if (!bluetooth::shim::ACL_AcceptLeConnectionFrom(BTM_Sec_GetAddressWithType(address), - false)) { - log::warn("Failed to re-add device {} to accept list after connection timeout", address); + false /* is_direct */)) { + log::warn("Failed to re-add {} to accept list after connection timeout", address); } } return true; @@ -582,7 +621,7 @@ void dump(int fd) { for (const auto& entry : bgconn_dev) { // TODO: confirm whether we need to replace this dprintf(fd, "\n\t * %s:\t\tin_accept_list: %s\t cap_targeted_announcements: %s", - ADDRESS_TO_LOGGABLE_CSTR(entry.first), + entry.first.ToRedactedStringForLogging().c_str(), entry.second.is_in_accept_list ? "true" : "false", entry.second.doing_targeted_announcements_conn.empty() ? "false" : "true"); diff --git a/system/stack/connection_manager/connection_manager.h b/system/stack/connection_manager/connection_manager.h index 4d86eae0e3..0f029fbe60 100644 --- a/system/stack/connection_manager/connection_manager.h +++ b/system/stack/connection_manager/connection_manager.h @@ -37,10 +37,22 @@ namespace connection_manager { using tAPP_ID = uint8_t; -/* for background connection */ +/* Mark device as using targeted announcements. + * + * @return true if device added to the list, false otherwise */ bool background_connect_targeted_announcement_add(tAPP_ID app_id, const RawAddress& address); + +/* Add a background connect request. + * + * @return true if device added to the list, false otherwise */ bool background_connect_add(tAPP_ID app_id, const RawAddress& address); + +/* Remove a background connection request. + * + * @return true if the request is removed, false otherwise. + */ bool background_connect_remove(tAPP_ID app_id, const RawAddress& address); + bool remove_unconditional(const RawAddress& address); void reset(bool after_reset); @@ -50,15 +62,15 @@ void on_connection_complete(const RawAddress& address); std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& remote_bda); -/* create_le_connection is adding device directly to AclManager, and relying on it's "direct - * connect" implementation. - * direct_connect_add method is doing multiplexing of apps request, and - * sending the request to AclManager, but it lacks some extra checks and lookups. Currently these - * methods are exclusive, if you try to use both you will get some bad behavior. These should be - * merged into one. */ -bool create_le_connection(uint8_t /* id */, const RawAddress& bd_addr, - tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC); -bool direct_connect_add(tAPP_ID app_id, const RawAddress& address); +/* Add a direct connect request. + * + * @return true if device added to the list, false otherwise */ +bool direct_connect_add(tAPP_ID app_id, const RawAddress& address, + tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC); +/* Remove a direct connection request. + * + * @return true if the request is removed, false otherwise. + */ bool direct_connect_remove(tAPP_ID app_id, const RawAddress& address, bool connection_timeout = false); diff --git a/system/stack/eatt/eatt.h b/system/stack/eatt/eatt.h index 7a05f27b63..c47509def7 100644 --- a/system/stack/eatt/eatt.h +++ b/system/stack/eatt/eatt.h @@ -87,10 +87,12 @@ public: if (state == EattChannelState::EATT_CHANNEL_OPENED) { server_outstanding_cmd_ = tGATT_SR_CMD{}; char name[64]; - sprintf(name, "eatt_ind_ack_timer_%s_cid_0x%04x", ADDRESS_TO_LOGGABLE_CSTR(bda_), cid_); + sprintf(name, "eatt_ind_ack_timer_%s_cid_0x%04x", bda_.ToRedactedStringForLogging().c_str(), + cid_); ind_ack_timer_ = alarm_new(name); - sprintf(name, "eatt_ind_conf_timer_%s_cid_0x%04x", ADDRESS_TO_LOGGABLE_CSTR(bda_), cid_); + sprintf(name, "eatt_ind_conf_timer_%s_cid_0x%04x", + bda_.ToRedactedStringForLogging().c_str(), cid_); ind_confirmation_timer_ = alarm_new(name); } } @@ -99,6 +101,7 @@ public: void EattChannelSetTxMTU(uint16_t tx_mtu) { this->tx_mtu_ = std::min<uint16_t>(tx_mtu, EATT_MAX_TX_MTU); + this->tx_mtu_ = std::max<uint16_t>(tx_mtu, EATT_MIN_MTU_MPS); } }; diff --git a/system/stack/fuzzers/l2cap_fuzzer.cc b/system/stack/fuzzers/l2cap_fuzzer.cc index 1963983aef..004e5b0920 100644 --- a/system/stack/fuzzers/l2cap_fuzzer.cc +++ b/system/stack/fuzzers/l2cap_fuzzer.cc @@ -78,6 +78,9 @@ uint32_t GetSystemPropertyUint32Base(const std::string& /*property*/, uint32_t d int /*base*/) { return default_value; } +uint32_t GetSystemPropertyUint32(const std::string& /*property*/, uint32_t default_value) { + return default_value; +} } // namespace os namespace hal { @@ -97,8 +100,8 @@ void SnoopLogger::SetL2capChannelOpen(uint16_t, uint16_t, uint16_t, uint16_t, bo } // namespace bluetooth namespace connection_manager { -bool create_le_connection(uint8_t /* id */, const RawAddress& /* bd_addr */, - tBLE_ADDR_TYPE /* addr_type */) { +bool direct_connect_add(uint8_t /* id */, const RawAddress& /* bd_addr */, + tBLE_ADDR_TYPE /* addr_type */) { return true; } } // namespace connection_manager diff --git a/system/stack/fuzzers/rfcomm_fuzzer.cc b/system/stack/fuzzers/rfcomm_fuzzer.cc index 3f3afbfe40..1445156aec 100644 --- a/system/stack/fuzzers/rfcomm_fuzzer.cc +++ b/system/stack/fuzzers/rfcomm_fuzzer.cc @@ -36,12 +36,18 @@ #include "test/mock/mock_stack_btm_dev.h" #include "test/mock/mock_stack_l2cap_api.h" #include "test/mock/mock_stack_l2cap_ble.h" +#include "test/mock/mock_stack_l2cap_interface.h" #include "test/rfcomm/stack_rfcomm_test_utils.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" #pragma GCC diagnostic ignored "-Wunused-parameter" +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::Unused; + namespace bluetooth { namespace hal { class SnoopLogger; @@ -75,30 +81,35 @@ void port_event_cback(uint32_t code, uint16_t port_handle) { } class FakeBtStack { + NiceMock<bluetooth::testing::stack::l2cap::Mock> mock_l2cap_interface; + public: + NiceMock<bluetooth::rfcomm::MockRfcommCallback> mock_rfcomm_callback; + FakeBtStack() { - test::mock::stack_l2cap_api::L2CA_DataWrite.body = [](uint16_t lcid, BT_HDR* hdr) { - osi_free(hdr); - return tL2CAP_DW_RESULT::SUCCESS; - }; - test::mock::stack_l2cap_api::L2CA_ConnectReq.body = - [](uint16_t psm, const RawAddress& raw_address) { return kDummyCID; }; - - test::mock::stack_l2cap_api::L2CA_DisconnectReq.body = [](uint16_t) { return true; }; - test::mock::stack_l2cap_api::L2CA_Register.body = - [](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, bool enable_snoop, - tL2CAP_ERTM_INFO* p_ertm_info, uint16_t my_mtu, uint16_t required_remote_mtu, - uint16_t sec_level) { - appl_info = p_cb_info; - return psm; - }; + ON_CALL(mock_l2cap_interface, L2CA_DataWrite) + .WillByDefault([](Unused, BT_HDR* hdr) { + osi_free(hdr); + return tL2CAP_DW_RESULT::SUCCESS; + }); + ON_CALL(mock_l2cap_interface, L2CA_ConnectReq) + .WillByDefault([](Unused, Unused) { return kDummyCID; }); + ON_CALL(mock_l2cap_interface, L2CA_DisconnectReq) + .WillByDefault([](Unused) { return true; }); + ON_CALL(mock_l2cap_interface, L2CA_Register) + .WillByDefault([](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, Unused, Unused, + Unused, Unused, Unused) { + appl_info = p_cb_info; + return psm; + }); + bluetooth::testing::stack::l2cap::set_interface(&mock_l2cap_interface); + + rfcomm_callback = &mock_rfcomm_callback; } ~FakeBtStack() { - test::mock::stack_l2cap_api::L2CA_DataWrite = {}; - test::mock::stack_l2cap_api::L2CA_ConnectReq = {}; - test::mock::stack_l2cap_api::L2CA_DisconnectReq = {}; - test::mock::stack_l2cap_api::L2CA_Register = {}; + rfcomm_callback = nullptr; + bluetooth::testing::stack::l2cap::reset_interface(); } }; @@ -108,15 +119,6 @@ public: FakeBtStack fake_stack; }; -class Mocks { -public: - ::testing::NiceMock<bluetooth::rfcomm::MockRfcommCallback> mock_rfcomm_callback; - - Mocks() { rfcomm_callback = &mock_rfcomm_callback; } - - ~Mocks() { rfcomm_callback = nullptr; } -}; - } // namespace static int Cleanup(uint16_t* server_handle) { return RFCOMM_RemoveServer(*server_handle); } @@ -129,7 +131,7 @@ static int ServerInit(FuzzedDataProvider* fdp, uint16_t* server_handle) { auto uuid = fdp->ConsumeIntegral<uint16_t>(); int status = RFCOMM_CreateConnectionWithSecurity(uuid, scn, true, mtu, kDummyAddr, server_handle, - port_mgmt_cback, 0); + port_mgmt_cback, 0, RfcommCfgInfo{}); if (status != PORT_SUCCESS) { return status; } @@ -174,7 +176,7 @@ static int ClientInit(FuzzedDataProvider* fdp, uint16_t* client_handle) { auto uuid = fdp->ConsumeIntegral<uint16_t>(); int status = RFCOMM_CreateConnectionWithSecurity(uuid, scn, false, mtu, kDummyAddr, client_handle, - port_mgmt_cback, 0); + port_mgmt_cback, 0, RfcommCfgInfo{}); if (status != PORT_SUCCESS) { return status; } @@ -212,7 +214,6 @@ static void FuzzAsClient(FuzzedDataProvider* fdp) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { auto fakes = std::make_unique<Fakes>(); - auto mocks = std::make_unique<Mocks>(); FuzzedDataProvider fdp(data, size); diff --git a/system/stack/gap/gap_conn.cc b/system/stack/gap/gap_conn.cc index fd988c8fd3..0f8ff9b77f 100644 --- a/system/stack/gap/gap_conn.cc +++ b/system/stack/gap/gap_conn.cc @@ -55,7 +55,9 @@ typedef struct { uint8_t service_id; /* Used by BTM */ uint16_t gap_handle; /* GAP handle */ - uint16_t connection_id; /* L2CAP CID */ + uint16_t local_cid; /* Local L2CAP CID */ + uint16_t remote_cid; /* Remote L2CAP CID */ + uint16_t acl_handle; /* ACL handle */ bool rem_addr_specified; uint8_t chan_mode_mask; /* Supported channel modes (FCR) */ RawAddress rem_dev_address; @@ -211,7 +213,12 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is /* Configure L2CAP COC, if transport is LE */ if (transport == BT_TRANSPORT_LE) { - p_ccb->local_coc_cfg.credits = L2CA_LeCreditDefault(); + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349376 + p_ccb->local_coc_cfg.credits = + (p_ccb->cfg.init_credit_present) ? p_ccb->cfg.init_credit : L2CA_LeCreditDefault(); + } else { + p_ccb->local_coc_cfg.credits = L2CA_LeCreditDefault(); + } p_ccb->local_coc_cfg.mtu = p_cfg->mtu; uint16_t max_mps = bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_; @@ -284,7 +291,7 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is cid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity(p_ccb->psm, *p_rem_bda, security); if (cid != 0) { - p_ccb->connection_id = cid; + p_ccb->local_cid = cid; return p_ccb->gap_handle; } log::warn("Unable to initiate connection peer:{} psm:{} transport:{}", *p_rem_bda, p_ccb->psm, @@ -295,7 +302,7 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is cid = stack::l2cap::get_interface().L2CA_ConnectLECocReq(p_ccb->psm, *p_rem_bda, &p_ccb->local_coc_cfg, security); if (cid != 0) { - p_ccb->connection_id = cid; + p_ccb->local_cid = cid; return p_ccb->gap_handle; } log::warn("Unable to initiate connection peer:{} psm:{} transport:{}", *p_rem_bda, p_ccb->psm, @@ -329,14 +336,14 @@ uint16_t GAP_ConnClose(uint16_t gap_handle) { /* Check if we have a connection ID */ if (p_ccb->con_state != GAP_CCB_STATE_LISTENING) { if (p_ccb->transport == BT_TRANSPORT_LE) { - if (!stack::l2cap::get_interface().L2CA_DisconnectLECocReq(p_ccb->connection_id)) { + if (!stack::l2cap::get_interface().L2CA_DisconnectLECocReq(p_ccb->local_cid)) { log::warn("Unable to request L2CAP disconnect le_coc peer:{} cid:{}", - p_ccb->rem_dev_address, p_ccb->connection_id); + p_ccb->rem_dev_address, p_ccb->local_cid); } } else { - if (!stack::l2cap::get_interface().L2CA_DisconnectReq(p_ccb->connection_id)) { + if (!stack::l2cap::get_interface().L2CA_DisconnectReq(p_ccb->local_cid)) { log::warn("Unable to request L2CAP disconnect peer:{} cid:{}", p_ccb->rem_dev_address, - p_ccb->connection_id); + p_ccb->local_cid); } } } @@ -451,9 +458,9 @@ static bool gap_try_write_queued_data(tGAP_CCB* p_ccb) { while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) { tL2CAP_DW_RESULT status; if (p_ccb->transport == BT_TRANSPORT_LE) { - status = stack::l2cap::get_interface().L2CA_LECocDataWrite(p_ccb->connection_id, p_buf); + status = stack::l2cap::get_interface().L2CA_LECocDataWrite(p_ccb->local_cid, p_buf); } else { - status = stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->connection_id, p_buf); + status = stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->local_cid, p_buf); } if (status == tL2CAP_DW_RESULT::CONGESTED) { @@ -576,7 +583,67 @@ uint16_t GAP_ConnGetL2CAPCid(uint16_t gap_handle) { return 0; } - return p_ccb->connection_id; + return p_ccb->local_cid; +} + +/******************************************************************************* + * + * Function GAP_GetLeChannelInfo + * + * Description This function is called to get LE L2CAP channel information + * by the gap handle. All OUT parameters must NOT be nullptr. + * + * Parameters: handle - Handle of the port returned in the Open + * remote_mtu - OUT remote L2CAP MTU + * local_mps - OUT local L2CAP COC MPS + * remote_mps - OUT remote L2CAP COC MPS + * local_credit - OUT local L2CAP COC credit + * remote_credit - OUT remote L2CAP COC credit + * local_cid - OUT local L2CAP CID + * remote_cid - OUT remote L2CAP CID + * acl_handle - OUT ACL handle + * + * Returns true if request accepted + * + ******************************************************************************/ +bool GAP_GetLeChannelInfo(uint16_t gap_handle, uint16_t* remote_mtu, uint16_t* local_mps, + uint16_t* remote_mps, uint16_t* local_credit, uint16_t* remote_credit, + uint16_t* local_cid, uint16_t* remote_cid, uint16_t* acl_handle) { + tGAP_CCB* p_ccb = gap_find_ccb_by_handle(gap_handle); + if (p_ccb == NULL || p_ccb->transport != BT_TRANSPORT_LE || + p_ccb->con_state != GAP_CCB_STATE_CONNECTED) { + return false; + } + + *remote_mtu = p_ccb->peer_coc_cfg.mtu; + *local_mps = p_ccb->local_coc_cfg.mps; + *remote_mps = p_ccb->peer_coc_cfg.mps; + *local_credit = p_ccb->local_coc_cfg.credits; + *remote_credit = p_ccb->peer_coc_cfg.credits; + *local_cid = p_ccb->local_cid; + *remote_cid = p_ccb->remote_cid; + *acl_handle = p_ccb->acl_handle; + return true; +} + +/******************************************************************************* + * + * Function GAP_IsTransportLe + * + * Description This function returns if the transport is LE by the gap handle. + * + * Parameters: handle - Handle of the port returned in the Open + * + * Returns true if transport is LE, else false + * + ******************************************************************************/ +bool GAP_IsTransportLe(uint16_t gap_handle) { + tGAP_CCB* p_ccb = gap_find_ccb_by_handle(gap_handle); + if (p_ccb == NULL || p_ccb->transport != BT_TRANSPORT_LE || + p_ccb->con_state != GAP_CCB_STATE_CONNECTED) { + return false; + } + return true; } /******************************************************************************* @@ -649,7 +716,7 @@ static void gap_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid, uint1 /* Save the BD Address and Channel ID. */ p_ccb->rem_dev_address = bd_addr; - p_ccb->connection_id = l2cap_cid; + p_ccb->local_cid = l2cap_cid; if (p_ccb->transport == BT_TRANSPORT_LE) { /* get the remote coc configuration */ @@ -683,12 +750,16 @@ static void gap_checks_con_flags(tGAP_CCB* p_ccb) { tGAP_CB_DATA cb_data; uint16_t l2cap_remote_cid; if (com::android::bluetooth::flags::bt_socket_api_l2cap_cid() && - stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->connection_id, + stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->local_cid, &l2cap_remote_cid)) { - cb_data.l2cap_cids.local_cid = p_ccb->connection_id; + cb_data.l2cap_cids.local_cid = p_ccb->local_cid; cb_data.l2cap_cids.remote_cid = l2cap_remote_cid; cb_data_ptr = &cb_data; } + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3367197 + stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->local_cid, &p_ccb->remote_cid); + stack::l2cap::get_interface().L2CA_GetAclHandle(p_ccb->local_cid, &p_ccb->acl_handle); + } p_ccb->con_state = GAP_CCB_STATE_CONNECTED; p_ccb->p_callback(p_ccb->gap_handle, GAP_EVT_CONN_OPENED, cb_data_ptr); @@ -926,7 +997,7 @@ static void gap_congestion_ind(uint16_t lcid, bool is_congested) { * Function gap_find_ccb_by_cid * * Description This function searches the CCB table for an entry with the - * passed CID. + * passed local CID. * * Returns the CCB address, or NULL if not found. * @@ -937,7 +1008,7 @@ static tGAP_CCB* gap_find_ccb_by_cid(uint16_t cid) { /* Look through each connection control block */ for (xx = 0, p_ccb = conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) { - if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->connection_id == cid)) { + if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->local_cid == cid)) { return p_ccb; } } diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc index 3f2c7136a9..19ea3a3090 100644 --- a/system/stack/gatt/gatt_api.cc +++ b/system/stack/gatt/gatt_api.cc @@ -33,6 +33,7 @@ #include "internal_include/bt_target.h" #include "internal_include/stack_config.h" +#include "main/shim/helpers.h" #include "os/system_properties.h" #include "osi/include/allocator.h" #include "stack/arbiter/acl_arbiter.h" @@ -47,6 +48,7 @@ #include "stack/include/l2cap_interface.h" #include "stack/include/l2cdefs.h" #include "stack/include/sdp_api.h" +#include "stack/include/stack_metrics_logging.h" #include "types/bluetooth/uuid.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -1472,6 +1474,8 @@ bool GATT_Connect(tGATT_IF gatt_if, const RawAddress& bd_addr, tBLE_ADDR_TYPE ad return true; } + log_le_connection_lifecycle(ToGdAddress(bd_addr), true /* is_connect */, is_direct); + bool ret = false; if (is_direct) { log::debug("Starting direct connect gatt_if={} address={} transport={}", gatt_if, bd_addr, @@ -1490,7 +1494,7 @@ bool GATT_Connect(tGATT_IF gatt_if, const RawAddress& bd_addr, tBLE_ADDR_TYPE ad log::warn("{} already added to gatt_if {} direct conn list", bd_addr, gatt_if); } - ret = connection_manager::create_le_connection(gatt_if, bd_addr, addr_type); + ret = connection_manager::direct_connect_add(gatt_if, bd_addr, addr_type); } } else { @@ -1619,6 +1623,9 @@ tGATT_STATUS GATT_Disconnect(tCONN_ID conn_id) { return GATT_ILLEGAL_PARAMETER; } + log_le_connection_lifecycle(ToGdAddress(p_tcb->peer_bda), true /* is_connect */, + false /* is_direct */); + tGATT_IF gatt_if = gatt_get_gatt_if(conn_id); gatt_update_app_use_link_flag(gatt_if, p_tcb, false, true); return GATT_SUCCESS; diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h index 3999ec4638..db0d0da9ca 100644 --- a/system/stack/gatt/gatt_int.h +++ b/system/stack/gatt/gatt_int.h @@ -20,7 +20,6 @@ #define GATT_INT_H #include <base/functional/bind.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <deque> @@ -64,7 +63,7 @@ inline std::string gatt_security_action_text(const tGATT_SEC_ACTION& action) { CASE_RETURN_TEXT(GATT_SEC_ENCRYPT_MITM); CASE_RETURN_TEXT(GATT_SEC_ENC_PENDING); default: - return base::StringPrintf("UNKNOWN[%hhu]", action); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(action)); } } @@ -248,7 +247,7 @@ inline std::string gatt_channel_state_text(const tGATT_CH_STATE& state) { CASE_RETURN_TEXT(GATT_CH_CFG); CASE_RETURN_TEXT(GATT_CH_OPEN); default: - return base::StringPrintf("UNKNOWN[%hhu]", state); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(state)); } } @@ -473,8 +472,7 @@ inline std::string EpochMillisToString(uint64_t time_ms) { struct tm tm; localtime_r(&time_sec, &tm); std::string s = bluetooth::common::StringFormatTime(kTimeFormatString, tm); - return base::StringPrintf("%s.%03u", s.c_str(), - static_cast<unsigned int>(time_ms % MillisPerSecond)); + return std::format("{}.{:03}", s, time_ms % MillisPerSecond); } } // namespace @@ -484,9 +482,8 @@ struct tTCB_STATE_HISTORY { tGATT_CH_STATE state; std::string holders_info; std::string ToString() const { - return base::StringPrintf("%s, %s, state: %s, %s", ADDRESS_TO_LOGGABLE_CSTR(address), - bt_transport_text(transport).c_str(), - gatt_channel_state_text(state).c_str(), holders_info.c_str()); + return std::format("{}, {}, state: {}, {}", address, bt_transport_text(transport), + gatt_channel_state_text(state), holders_info); } }; diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc index e530d9fd5e..0d8d51d846 100644 --- a/system/stack/gatt/gatt_main.cc +++ b/system/stack/gatt/gatt_main.cc @@ -235,7 +235,7 @@ static bool gatt_connect(const RawAddress& rem_bda, tBLE_ADDR_TYPE addr_type, tG } p_tcb->att_lcid = L2CAP_ATT_CID; - return connection_manager::create_le_connection(gatt_if, rem_bda, addr_type); + return connection_manager::direct_connect_add(gatt_if, rem_bda, addr_type); } /******************************************************************************* diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc index aad7b1f89e..49c1ae452d 100644 --- a/system/stack/gatt/gatt_sr.cc +++ b/system/stack/gatt/gatt_sr.cc @@ -162,6 +162,13 @@ static void build_read_multi_rsp(tGATT_SR_CMD* p_cmd, uint16_t mtu) { uint8_t* p; bool is_overflow = false; + // We need at least one extra byte for the opcode + if (mtu == 0) { + log::error("Invalid MTU"); + p_cmd->status = GATT_ILLEGAL_PARAMETER; + return; + } + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; BT_HDR* p_buf = (BT_HDR*)osi_calloc(len); p_buf->offset = L2CAP_MIN_OFFSET; @@ -423,8 +430,19 @@ static void gatt_process_exec_write_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op } } } else { /* nothing needs to be executed , send response now */ - log::error("gatt_process_exec_write_req: no prepare write pending"); - gatt_send_error_rsp(tcb, cid, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, false); + log::warn("gatt_process_exec_write_req: no prepare write pending"); + if (com::android::bluetooth::flags::fix_execute_write_no_pending()) { + uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid); + BT_HDR* p_buf = + attp_build_sr_msg(tcb, GATT_RSP_EXEC_WRITE, (tGATT_SR_MSG*)NULL, payload_size); + if (p_buf != NULL) { + attp_send_sr_msg(tcb, cid, p_buf); + } else { + gatt_send_error_rsp(tcb, cid, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, false); + } + } else { + gatt_send_error_rsp(tcb, cid, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, false); + } } } @@ -761,6 +779,11 @@ static void gatts_process_primary_service_req(tGATT_TCB& tcb, uint16_t cid, uint uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid); + // This can happen if the channel is already closed. + if (payload_size == 0) { + return; + } + uint16_t msg_len = (uint16_t)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET); BT_HDR* p_msg = (BT_HDR*)osi_calloc(msg_len); reason = gatt_build_primary_service_rsp(p_msg, tcb, cid, op_code, s_hdl, e_hdl, p_data, value); @@ -793,6 +816,12 @@ static void gatts_process_find_info(tGATT_TCB& tcb, uint16_t cid, uint8_t op_cod } uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid); + + // This can happen if the channel is already closed. + if (payload_size == 0) { + return; + } + uint16_t buf_len = (uint16_t)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET); BT_HDR* p_msg = (BT_HDR*)osi_calloc(buf_len); @@ -938,6 +967,11 @@ static void gatts_process_read_by_type_req(tGATT_TCB& tcb, uint16_t cid, uint8_t uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid); + // This can happen if the channel is already closed. + if (payload_size == 0) { + return; + } + size_t msg_len = sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET; BT_HDR* p_msg = (BT_HDR*)osi_calloc(msg_len); uint8_t* p = (uint8_t*)(p_msg + 1) + L2CAP_MIN_OFFSET; @@ -1085,6 +1119,11 @@ static void gatts_process_read_req(tGATT_TCB& tcb, uint16_t cid, tGATT_SRV_LIST_ uint8_t* p_data) { uint16_t payload_size = gatt_tcb_get_payload_size(tcb, cid); + // This can happen if the channel is already closed. + if (payload_size == 0) { + return; + } + size_t buf_len = sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET; uint16_t offset = 0; diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc index 73880e3cd4..88ecfe2d53 100644 --- a/system/stack/gatt/gatt_utils.cc +++ b/system/stack/gatt/gatt_utils.cc @@ -23,7 +23,6 @@ ******************************************************************************/ #define LOG_TAG "gatt_utils" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -473,7 +472,7 @@ void gatt_tcb_dump(int fd) { if (p_tcb->in_use) { in_use_cnt++; stream << " id: " << +p_tcb->tcb_idx - << " address: " << ADDRESS_TO_LOGGABLE_STR(p_tcb->peer_bda) + << " address: " << p_tcb->peer_bda.ToRedactedStringForLogging() << " transport: " << bt_transport_text(p_tcb->transport) << " ch_state: " << gatt_channel_state_text(p_tcb->ch_state) << ", " << gatt_tcb_get_holders_info_string(p_tcb) << "\n"; diff --git a/system/stack/hid/hid_conn.h b/system/stack/hid/hid_conn.h index 1611db4ffe..92175fcd26 100644 --- a/system/stack/hid/hid_conn.h +++ b/system/stack/hid/hid_conn.h @@ -25,7 +25,6 @@ #ifndef HID_CONN_H #define HID_CONN_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include "macros.h" @@ -56,7 +55,7 @@ typedef struct hid_conn { CASE_RETURN_TEXT(HID_CONN_STATE_DISCONNECTING); CASE_RETURN_TEXT(HID_CONN_STATE_SECURITY); default: - return base::StringPrintf("UNKNOWN[%hhu]", state); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(state)); } } diff --git a/system/stack/hid/hidh_api.cc b/system/stack/hid/hidh_api.cc index a02af75063..97d811ed5b 100644 --- a/system/stack/hid/hidh_api.cc +++ b/system/stack/hid/hidh_api.cc @@ -49,9 +49,6 @@ #include "types/bluetooth/uuid.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; using namespace bluetooth::legacy::stack::sdp; using bluetooth::Uuid; @@ -95,7 +92,7 @@ tHID_STATUS HID_HostGetSDPRecord(const RawAddress& addr, tSDP_DISCOVERY_DB* p_db } } -void hidh_get_str_attr(tSDP_DISC_REC* p_rec, uint16_t attr_id, uint16_t max_len, char* str) { +static void hidh_get_str_attr(tSDP_DISC_REC* p_rec, uint16_t attr_id, uint16_t max_len, char* str) { tSDP_DISC_ATTR* p_attr; uint16_t name_len; diff --git a/system/stack/hid/hidh_conn.cc b/system/stack/hid/hidh_conn.cc index cc2a480b57..83bb039173 100644 --- a/system/stack/hid/hidh_conn.cc +++ b/system/stack/hid/hidh_conn.cc @@ -23,7 +23,6 @@ ******************************************************************************/ #include <base/functional/callback.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> #include <string.h> @@ -203,10 +202,9 @@ static void hidh_l2cif_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid tHID_CONN* p_hcon = &hh_cb.devices[i].conn; - BTM_LogHistory( - kBtmLogTag, hh_cb.devices[i].addr, "Connect request", - base::StringPrintf("%s state:%s", (psm == HID_PSM_CONTROL) ? "control" : "interrupt", - hid_conn::state_text(p_hcon->conn_state).c_str())); + BTM_LogHistory(kBtmLogTag, hh_cb.devices[i].addr, "Connect request", + std::format("{} state:{}", (psm == HID_PSM_CONTROL) ? "control" : "interrupt", + hid_conn::state_text(p_hcon->conn_state))); /* Check we are in the correct state for this */ if (psm == HID_PSM_INTERRUPT) { @@ -360,10 +358,9 @@ static void hidh_l2cif_connect_cfm(uint16_t l2cap_cid, tL2CAP_CONN result) { } else { p_hcon->conn_state = HID_CONN_STATE_CONFIG; } - BTM_LogHistory( - kBtmLogTag, hh_cb.devices[dhandle].addr, "Configuring", - base::StringPrintf("control:0x%04x interrupt:0x%04x state:%s", p_hcon->ctrl_cid, - p_hcon->intr_cid, hid_conn::state_text(p_hcon->conn_state).c_str())); + BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Configuring", + std::format("control:0x{:04x} interrupt:0x{:04x} state:{}", p_hcon->ctrl_cid, + p_hcon->intr_cid, hid_conn::state_text(p_hcon->conn_state))); return; } @@ -469,10 +466,9 @@ static void hidh_l2cif_config_cfm(uint16_t l2cap_cid, uint16_t /* initiator */, hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL); - BTM_LogHistory( - kBtmLogTag, hh_cb.devices[dhandle].addr, "Connected", - base::StringPrintf("control:0x%04x interrupt:0x%04x state:%s", p_hcon->ctrl_cid, - p_hcon->intr_cid, hid_conn::state_text(p_hcon->conn_state).c_str())); + BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Connected", + std::format("control:0x{:04x} interrupt:0x{:04x} state:{}", p_hcon->ctrl_cid, + p_hcon->intr_cid, hid_conn::state_text(p_hcon->conn_state))); } } @@ -506,9 +502,9 @@ static void hidh_l2cif_disconnect_ind(uint16_t l2cap_cid, bool ack_needed) { log::verbose("HID-Host Rcvd L2CAP disc, CID: 0x{:x}", l2cap_cid); p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; - BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Disconnecting", - base::StringPrintf("%s channel", - (l2cap_cid == p_hcon->ctrl_cid) ? "control" : "interrupt")); + BTM_LogHistory( + kBtmLogTag, hh_cb.devices[dhandle].addr, "Disconnecting", + std::format("{} channel", (l2cap_cid == p_hcon->ctrl_cid) ? "control" : "interrupt")); if (l2cap_cid == p_hcon->ctrl_cid) { p_hcon->ctrl_cid = 0; diff --git a/system/stack/include/avdt_api.h b/system/stack/include/avdt_api.h index 88ad0661d0..b20f75c4f2 100644 --- a/system/stack/include/avdt_api.h +++ b/system/stack/include/avdt_api.h @@ -25,7 +25,6 @@ #ifndef AVDT_API_H #define AVDT_API_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -69,7 +68,7 @@ inline std::string avdt_result_text(const tAVDT_RESULT& result) { CASE_RETURN_TEXT(AVDT_BUSY); CASE_RETURN_TEXT(AVDT_WRITE_FAIL); default: - return base::StringPrintf("UNKNOWN[%hu]", result); + return std::format("UNKNOWN[{}]", static_cast<uint16_t>(result)); } } diff --git a/system/stack/include/btm_api_types.h b/system/stack/include/btm_api_types.h index 2c2ccf211d..59fde54dad 100644 --- a/system/stack/include/btm_api_types.h +++ b/system/stack/include/btm_api_types.h @@ -19,8 +19,6 @@ #ifndef BTM_API_TYPES_H #define BTM_API_TYPES_H -#include <base/strings/stringprintf.h> - #include <cstdint> #include <string> diff --git a/system/stack/include/btm_sec_api_types.h b/system/stack/include/btm_sec_api_types.h index d04974f259..3721ef302e 100644 --- a/system/stack/include/btm_sec_api_types.h +++ b/system/stack/include/btm_sec_api_types.h @@ -18,7 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -64,7 +63,7 @@ inline std::string security_mode_text(const tSECURITY_MODE& security_mode) { case BTM_SEC_MODE_SC: return std::string("secure connections only"); default: - return base::StringPrintf("UNKNOWN[%hhu]", security_mode); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(security_mode)); } } @@ -137,7 +136,7 @@ inline std::string linkkey_type_text(const int linkkey_type) { case BTM_LKEY_TYPE_AUTH_COMB_P_256: return std::string("AUTH_COMB_P_256"); default: - return base::StringPrintf("UNKNOWN[0x%02x]", linkkey_type); + return std::format("UNKNOWN[0x{:02x}]", linkkey_type); } } @@ -196,7 +195,7 @@ inline std::string sp_evt_to_text(const tBTM_SP_EVT evt) { CASE_RETURN_TEXT(BTM_SP_RMT_OOB_EVT); } - return base::StringPrintf("UNKNOWN[%hhu]", evt); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(evt)); } enum : uint8_t { @@ -223,7 +222,7 @@ inline std::string io_capabilities_text(const tBTM_IO_CAP& io_caps) { case BTM_IO_CAP_KBDISP: return std::string("Keyboard-Display"); default: - return base::StringPrintf("UNKNOWN[%hhu]", io_caps); + return std::format("UNKNOWN[{}]", io_caps); } } @@ -442,7 +441,7 @@ inline std::string ble_evt_to_text(const tBTM_LE_EVT evt) { CASE_RETURN_TEXT(BTM_LE_ADDR_ASSOC_EVT); } - return base::StringPrintf("UNKNOWN[%hhu]", evt); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(evt)); } enum : uint8_t { @@ -521,7 +520,7 @@ inline std::string bond_type_text(const tBTM_BOND_TYPE& bond_type) { CASE_RETURN_TEXT(BOND_TYPE_PERSISTENT); CASE_RETURN_TEXT(BOND_TYPE_TEMPORARY); default: - return base::StringPrintf("UNKNOWN[%hhu]", bond_type); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(bond_type)); } } diff --git a/system/stack/include/btm_status.h b/system/stack/include/btm_status.h index a33a4b6a87..b3b6be698c 100644 --- a/system/stack/include/btm_status.h +++ b/system/stack/include/btm_status.h @@ -16,8 +16,6 @@ #pragma once -#include <base/strings/stringprintf.h> - #include <cstdint> #include "macros.h" @@ -94,7 +92,7 @@ inline std::string btm_status_text(const tBTM_STATUS& status) { CASE_RETURN_TEXT(tBTM_STATUS::BTM_NOT_ENCRYPTED); CASE_RETURN_TEXT(tBTM_STATUS::BTM_INSUFFICIENT_ENCRYPT_KEY_SIZE); default: - return base::StringPrintf("UNKNOWN[%hhu]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } diff --git a/system/stack/include/gap_api.h b/system/stack/include/gap_api.h index e4a185eefe..f216b12f3b 100644 --- a/system/stack/include/gap_api.h +++ b/system/stack/include/gap_api.h @@ -226,6 +226,43 @@ uint16_t GAP_ConnGetL2CAPCid(uint16_t gap_handle); /******************************************************************************* * + * Function GAP_GetLeChannelInfo + * + * Description This function is called to get LE L2CAP channel information + * by the gap handle. All OUT parameters must NOT be nullptr. + * + * Parameters: handle - Handle of the port returned in the Open + * remote_mtu - OUT remote L2CAP MTU + * local_mps - OUT local L2CAP COC MPS + * remote_mps - OUT remote L2CAP COC MPS + * local_credit - OUT local L2CAP COC credit + * remote_credit - OUT remote L2CAP COC credit + * local_cid - OUT local L2CAP CID + * remote_cid - OUT remote L2CAP CID + * acl_handle - OUT ACL handle + * + * Returns true if request accepted + * + ******************************************************************************/ +bool GAP_GetLeChannelInfo(uint16_t gap_handle, uint16_t* remote_mtu, uint16_t* local_mps, + uint16_t* remote_mps, uint16_t* local_credit, uint16_t* remote_credit, + uint16_t* local_cid, uint16_t* remote_cid, uint16_t* acl_handle); + +/******************************************************************************* + * + * Function GAP_IsTransportLe + * + * Description This function returns if the transport is LE by the gap handle. + * + * Parameters: handle - Handle of the port returned in the Open + * + * Returns true if transport is LE, else false + * + ******************************************************************************/ +bool GAP_IsTransportLe(uint16_t gap_handle); + +/******************************************************************************* + * * Function GAP_Init * * Description Initializes the control blocks used by GAP. diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h index 3b751e10cf..3073a6e145 100644 --- a/system/stack/include/gatt_api.h +++ b/system/stack/include/gatt_api.h @@ -18,7 +18,6 @@ #ifndef GATT_API_H #define GATT_API_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -137,7 +136,7 @@ inline std::string gatt_status_text(const tGATT_STATUS& status) { CASE_RETURN_TEXT(GATT_PRC_IN_PROGRESS); CASE_RETURN_TEXT(GATT_OUT_OF_RANGE); default: - return base::StringPrintf("UNKNOWN[%hhu]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } @@ -293,7 +292,7 @@ inline std::string gatt_disconnection_reason_text(const tGATT_DISCONN_REASON& re CASE_RETURN_TEXT(BTA_GATT_CONN_NONE); CASE_RETURN_TEXT(GATT_CONN_TERMINATED_POWER_OFF); default: - return base::StringPrintf("UNKNOWN[%hu]", reason); + return std::format("UNKNOWN[{}]", static_cast<uint16_t>(reason)); } } diff --git a/system/stack/include/hci_error_code.h b/system/stack/include/hci_error_code.h index 3d4b50c745..2d70e00010 100644 --- a/system/stack/include/hci_error_code.h +++ b/system/stack/include/hci_error_code.h @@ -16,7 +16,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <hardware/bluetooth.h> @@ -121,7 +120,7 @@ inline std::string hci_error_code_text(const tHCI_ERROR_CODE& error_code) { CASE_RETURN_TEXT(HCI_ERR_LIMIT_REACHED); CASE_RETURN_TEXT(HCI_ERR_CANCELLED_BY_LOCAL_HOST); default: - return base::StringPrintf("UNKNOWN[0x%02hx]", error_code); + return std::format("UNKNOWN[0x{:02x}]", static_cast<uint8_t>(error_code)); } } diff --git a/system/stack/include/hiddefs.h b/system/stack/include/hiddefs.h index 583c302d1f..47bd6f9561 100644 --- a/system/stack/include/hiddefs.h +++ b/system/stack/include/hiddefs.h @@ -25,7 +25,6 @@ #ifndef HIDDEFS_H #define HIDDEFS_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include "internal_include/bt_target.h" @@ -80,7 +79,7 @@ inline std::string hid_status_text(const tHID_STATUS& status) { CASE_RETURN_TEXT(HID_ERR_GATT); CASE_RETURN_TEXT(HID_ERR_INVALID); default: - return base::StringPrintf("UNKNOWN[%hhu]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } diff --git a/system/stack/include/l2cap_interface.h b/system/stack/include/l2cap_interface.h index cfd88153f7..b1551d2a80 100644 --- a/system/stack/include/l2cap_interface.h +++ b/system/stack/include/l2cap_interface.h @@ -638,6 +638,7 @@ public: ******************************************************************************/ virtual void L2CA_Consolidate(const RawAddress& identity_addr, const RawAddress& rpa) = 0; virtual tHCI_ROLE L2CA_GetBleConnRole(const RawAddress& bd_addr) = 0; + virtual uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr) = 0; /******************************************************************************* ** @@ -916,6 +917,40 @@ public: ** *******************************************************************************/ virtual bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid) = 0; + + /******************************************************************************* + ** + ** Function L2CA_GetAclHandle + ** + ** Description Given a local channel identifier, |lcid|, this function + ** returns the handle of the corresponding ACL connection, |acl_handle|. If + ** |lcid| is not known or is invalid, this function returns false and does not + ** modify the value pointed at by |acl_handle|. + ** + ** Parameters: lcid: Local CID + ** acl_handle: Pointer to ACL handle must NOT be nullptr + ** + ** Returns true if acl_handle lookup was successful + ** + ******************************************************************************/ + virtual bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) = 0; + + /******************************************************************************* + ** + ** Function L2CA_GetLocalMtu + ** + ** Description Given a local channel identifier, |lcid|, this function + ** returns the L2CAP local mtu, |local_mtu|. If + ** |lcid| is not known or is invalid, this function returns false and does not + ** modify the value pointed at by |local_mtu|. + ** + ** Parameters: lcid: Local CID + ** local_mtu: Pointer to L2CAP local mtu must NOT be nullptr + ** + ** Returns true if local_mtu lookup was successful + ** + ******************************************************************************/ + virtual bool L2CA_GetLocalMtu(uint16_t lcid, uint16_t* local_mtu) = 0; }; Interface& get_interface(); diff --git a/system/stack/include/l2cap_types.h b/system/stack/include/l2cap_types.h index 311dab7528..d2dd74bad7 100644 --- a/system/stack/include/l2cap_types.h +++ b/system/stack/include/l2cap_types.h @@ -129,6 +129,8 @@ typedef struct { uint8_t fcs; /* '0' if desire is to bypass FCS, otherwise '1' */ bool ext_flow_spec_present; tHCI_EXT_FLOW_SPEC ext_flow_spec; + bool init_credit_present; + uint16_t init_credit; uint16_t flags; /* bit 0: 0-no continuation, 1-continuation */ } tL2CAP_CFG_INFO; diff --git a/system/stack/include/l2cdefs.h b/system/stack/include/l2cdefs.h index 88ed7a76d3..be101711da 100644 --- a/system/stack/include/l2cdefs.h +++ b/system/stack/include/l2cdefs.h @@ -487,6 +487,7 @@ inline std::string l2cap_cfg_result_text(const tL2CAP_CFG_RESULT& result) { */ #define L2CAP_SDU_LENGTH_MAX (8080 + 26 - (L2CAP_MIN_OFFSET + 6)) constexpr uint16_t L2CAP_SDU_LENGTH_LE_MAX = 0xffff; +constexpr uint16_t L2CAP_SDU_LENGTH_LE_MIN = 23; /* SAR bits in the control word */ diff --git a/system/stack/include/pan_api.h b/system/stack/include/pan_api.h index 9246f2aa39..a4e10279cf 100644 --- a/system/stack/include/pan_api.h +++ b/system/stack/include/pan_api.h @@ -24,7 +24,6 @@ #ifndef PAN_API_H #define PAN_API_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -58,9 +57,9 @@ typedef uint8_t tPAN_ROLE; inline const std::string pan_role_to_text(const tPAN_ROLE& role) { - return base::StringPrintf("%c%c%c[0x%x]", (role & PAN_ROLE_CLIENT) ? 'C' : '.', - (role & PAN_ROLE_GROUP) ? 'G' : '.', - (role & PAN_ROLE_NAP_SERVER) ? 'N' : '.', role); + return std::format("{:c}{:c}{:c}[0x{:x}]", (role & PAN_ROLE_CLIENT) ? 'C' : '.', + (role & PAN_ROLE_GROUP) ? 'G' : '.', (role & PAN_ROLE_NAP_SERVER) ? 'N' : '.', + role); } /***************************************************************************** @@ -116,7 +115,7 @@ inline const std::string pan_result_text(const tPAN_RESULT& result) { CASE_RETURN_TEXT(PAN_FAILURE); CASE_RETURN_TEXT(PAN_HOTSPOT_DISABLED); default: - return base::StringPrintf("UNKNOWN[%hhu]", result); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(result)); } } diff --git a/system/stack/include/port_api.h b/system/stack/include/port_api.h index e2986740f1..3a9fd7e4b3 100644 --- a/system/stack/include/port_api.h +++ b/system/stack/include/port_api.h @@ -179,6 +179,17 @@ inline std::string port_result_text(const tPORT_RESULT& result) { RETURN_UNKNOWN_TYPE_STRING(tPORT_RESULT, result); } +/* Define a structure to hold the configuration parameters. Since the + * parameters are optional, for each parameter there is a boolean to + * use to signify its presence or absence. + */ +struct RfcommCfgInfo { + bool init_credit_present; + uint16_t init_credit; + bool rx_mtu_present; + uint16_t rx_mtu; +}; + namespace std { template <> struct formatter<tPORT_RESULT> : enum_formatter<tPORT_RESULT> {}; @@ -192,9 +203,9 @@ typedef void(tPORT_MGMT_CALLBACK)(const tPORT_RESULT code, uint16_t port_handle) /******************************************************************************* * - * Function RFCOMM_CreateConnection + * Function RFCOMM_CreateConnectionWithSecurity * - * Description RFCOMM_CreateConnection is used from the application to + * Description RFCOMM_CreateConnectionWithSecurity is used from the application to * establish a serial port connection to the peer device, * or allow RFCOMM to accept a connection from the peer * application. @@ -210,6 +221,9 @@ typedef void(tPORT_MGMT_CALLBACK)(const tPORT_RESULT code, uint16_t port_handle) * p_handle - OUT pointer to the handle. * p_mgmt_callback - pointer to callback function to receive * connection up/down events. + * sec_mask - bitmask of BTM_SEC_* values indicating the + * minimum security requirements for this + * cfg - optional configurations for the connection * Notes: * * Server can call this function with the same scn parameter multiple times if @@ -225,7 +239,7 @@ typedef void(tPORT_MGMT_CALLBACK)(const tPORT_RESULT code, uint16_t port_handle) uint16_t mtu, const RawAddress& bd_addr, uint16_t* p_handle, tPORT_MGMT_CALLBACK* p_mgmt_callback, - uint16_t sec_mask); + uint16_t sec_mask, RfcommCfgInfo cfg); /******************************************************************************* * @@ -483,4 +497,30 @@ void RFCOMM_Init(void); ******************************************************************************/ [[nodiscard]] int PORT_GetSecurityMask(uint16_t handle, uint16_t* sec_mask); +/******************************************************************************* + * + * Function PORT_GetChannelInfo + * + * Description This function is called to get RFCOMM channel information + * by the handle of the port. All OUT parameters must NOT be nullptr. + * + * Parameters: handle - Handle of the port returned in the Open + * local_mtu - OUT local L2CAP MTU + * remote_mtu - OUT remote L2CAP MTU + * local_credit - OUT local RFCOMM credit + * remote_credit - OUT remote RFCOMM credit + * local_cid - OUT local L2CAP CID + * remote_cid - OUT remote L2CAP CID + * dlci - OUT dlci + * max_frame_size- OUT max frame size for RFCOMM + * acl_handle - OUT ACL handle + * mux_initiator - OUT is initiator of the RFCOMM multiplexer control channel + * + ******************************************************************************/ +[[nodiscard]] int PORT_GetChannelInfo(uint16_t handle, uint16_t* local_mtu, uint16_t* remote_mtu, + uint16_t* local_credit, uint16_t* remote_credit, + uint16_t* local_cid, uint16_t* remote_cid, uint16_t* dlci, + uint16_t* max_frame_size, uint16_t* acl_handle, + bool* mux_initiator); + #endif /* PORT_API_H */ diff --git a/system/stack/include/rfcdefs.h b/system/stack/include/rfcdefs.h index 628fc77c45..35be4076d6 100644 --- a/system/stack/include/rfcdefs.h +++ b/system/stack/include/rfcdefs.h @@ -45,6 +45,11 @@ #define RFCOMM_DEFAULT_MTU 127 /* + * The minimum allowed MTU should be 23 according to the RFCOMM specs + */ +#define RFCOMM_MIN_MTU 23 + +/* * RFCOMM buffer sizes */ #define RFCOMM_CMD_BUF_SIZE BT_SMALL_BUFFER_SIZE // command packet buffer size diff --git a/system/stack/include/sdp_api.h b/system/stack/include/sdp_api.h index f639b107f7..a23ba0502d 100644 --- a/system/stack/include/sdp_api.h +++ b/system/stack/include/sdp_api.h @@ -19,7 +19,6 @@ #pragma once #include <base/functional/callback_forward.h> -#include <base/strings/stringprintf.h> #include <cstdint> diff --git a/system/stack/include/sdp_status.h b/system/stack/include/sdp_status.h index cebcd9697c..363b9c09bd 100644 --- a/system/stack/include/sdp_status.h +++ b/system/stack/include/sdp_status.h @@ -18,7 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -82,7 +81,7 @@ inline std::string sdp_status_text(const tSDP_STATUS& status) { CASE_RETURN_TEXT(tSDP_STATUS::SDP_DB_FULL); CASE_RETURN_TEXT(tSDP_STATUS::SDP_CANCEL); default: - return base::StringPrintf("UNKNOWN[%hu]", status); + return std::format("UNKNOWN[{}]", static_cast<uint16_t>(status)); } } const auto sdp_result_text = sdp_status_text; diff --git a/system/stack/include/sdpdefs.h b/system/stack/include/sdpdefs.h index 600bd44fb4..f4f4dba79e 100644 --- a/system/stack/include/sdpdefs.h +++ b/system/stack/include/sdpdefs.h @@ -25,8 +25,7 @@ #ifndef SDP_DEFS_H #define SDP_DEFS_H -#include <base/strings/stringprintf.h> - +#include <format> #include <string> #include "macros.h" @@ -122,7 +121,7 @@ inline std::string sdp_attr_id_text(const unsigned& id) { CASE_RETURN_TEXT(ATTR_ID_SERVICE_DESCRIPTION); CASE_RETURN_TEXT(ATTR_ID_PROVIDER_NAME); default: - return base::StringPrintf("SDP_ATTR_ID:[%u]", id); + return std::format("SDP_ATTR_ID:[{}]", id); } } @@ -152,7 +151,7 @@ inline std::string sdp_attr_type_text(const unsigned& type) { CASE_RETURN_TEXT(DATA_ELE_ALT_DESC_TYPE); CASE_RETURN_TEXT(URL_DESC_TYPE); default: - return base::StringPrintf("UNKNOWN[%u]", type); + return std::format("UNKNOWN[{}]", type); } } @@ -180,7 +179,7 @@ inline std::string sdp_attr_size_text(const unsigned& size) { CASE_RETURN_TEXT(SIZE_IN_NEXT_WORD); CASE_RETURN_TEXT(SIZE_IN_NEXT_LONG); default: - return base::StringPrintf("UNKNOWN[%u]", size); + return std::format("UNKNOWN[{}]", size); } } diff --git a/system/stack/include/smp_api.h b/system/stack/include/smp_api.h index 6957c71875..291eca99d1 100644 --- a/system/stack/include/smp_api.h +++ b/system/stack/include/smp_api.h @@ -200,6 +200,15 @@ void SMP_ClearLocScOobData(); ******************************************************************************/ void SMP_SirkConfirmDeviceReply(const RawAddress& bd_addr, uint8_t res); +/******************************************************************************* + * + * Function SMP_SirkConfirmDeviceReply + * + * Description Returns the key size of peinding LE pairing + * + ******************************************************************************/ +uint16_t SMP_GetPendingPairingKeySize(); + // Called when LTK request is received from controller. bool smp_proc_ltk_request(const RawAddress& bda); diff --git a/system/stack/include/smp_api_types.h b/system/stack/include/smp_api_types.h index 2a27416acd..db575f0804 100644 --- a/system/stack/include/smp_api_types.h +++ b/system/stack/include/smp_api_types.h @@ -19,7 +19,6 @@ #ifndef SMP_API_TYPES_H #define SMP_API_TYPES_H -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> diff --git a/system/stack/include/smp_status.h b/system/stack/include/smp_status.h index 5c8b6f16f4..86670a5161 100644 --- a/system/stack/include/smp_status.h +++ b/system/stack/include/smp_status.h @@ -18,7 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -91,7 +90,7 @@ inline std::string smp_status_text(const tSMP_STATUS& status) { CASE_RETURN_TEXT(SMP_SIRK_DEVICE_INVALID); CASE_RETURN_TEXT(SMP_USER_CANCELLED); default: - return base::StringPrintf("UNKNOWN[%hhu]", status); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(status)); } } diff --git a/system/stack/include/stack_metrics_logging.h b/system/stack/include/stack_metrics_logging.h index 912d5a5a91..dda02a0d1d 100644 --- a/system/stack/include/stack_metrics_logging.h +++ b/system/stack/include/stack_metrics_logging.h @@ -21,6 +21,8 @@ #include <cstdint> +#include "hci/address.h" +#include "hci/hci_packets.h" #include "types/raw_address.h" void log_classic_pairing_event(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, @@ -55,3 +57,10 @@ void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_reque int codec_type); void log_le_pairing_fail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing); + +void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason); + +void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add); + +void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct); diff --git a/system/stack/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h index 5ed3403cff..dd1dbf52e6 100644 --- a/system/stack/l2cap/internal/l2c_api.h +++ b/system/stack/l2cap/internal/l2c_api.h @@ -705,6 +705,7 @@ void L2CA_LockBleConnParamsForProfileConnection(const RawAddress& rem_bda, bool ******************************************************************************/ void L2CA_Consolidate(const RawAddress& identity_addr, const RawAddress& rpa); [[nodiscard]] tHCI_ROLE L2CA_GetBleConnRole(const RawAddress& bd_addr); +[[nodiscard]] uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr); void L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interval, uint16_t floor_interval); @@ -777,3 +778,37 @@ void L2CA_SetMediaStreamChannel(uint16_t local_media_cid, bool status); ** *******************************************************************************/ [[nodiscard]] bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid); + +/******************************************************************************* +** +** Function L2CA_GetAclHandle +** +** Description Given a local channel identifier, |lcid|, this function +** returns the handle of the corresponding ACL connection, |acl_handle|. If +** |lcid| is not known or is invalid, this function returns false and does not +** modify the value pointed at by |acl_handle|. +** +** Parameters: lcid: Local CID +** acl_handle: Pointer to ACL handle must NOT be nullptr +** +** Returns true if acl_handle lookup was successful +** +******************************************************************************/ +[[nodiscard]] bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle); + +/******************************************************************************* + ** + ** Function L2CA_GetLocalMtu + ** + ** Description Given a local channel identifier, |lcid|, this function + ** returns the L2CAP local mtu, |local_mtu|. If + ** |lcid| is not known or is invalid, this function returns false and does not + ** modify the value pointed at by |local_mtu|. + ** + ** Parameters: lcid: Local CID + ** local_mtu: Pointer to L2CAP local mtu must NOT be nullptr + ** + ** Returns true if local_mtu lookup was successful + ** + ******************************************************************************/ +[[nodiscard]] bool L2CA_GetLocalMtu(uint16_t lcid, uint16_t* local_mtu); diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc index 2b67132edb..d8d2eb0023 100644 --- a/system/stack/l2cap/l2c_api.cc +++ b/system/stack/l2cap/l2c_api.cc @@ -27,7 +27,6 @@ #include "stack/l2cap/l2c_api.h" #include <base/location.h> -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -1731,6 +1730,65 @@ bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid return ret; } +/******************************************************************************* + * + * Function L2CA_GetAclHandle + * + * Description Given a local channel identifier, |lcid|, this function + * returns the bound ACL handle, |acl_handle|. If |acl_handle| + * is not known or is invalid, this function returns false and + * does not modify the value pointed at by |acl_handle|. + * + * Parameters: lcid: Local CID + * rcid: Pointer to ACL handle must NOT be nullptr + * + * Return value: true if acl_handle lookup was successful + * + ******************************************************************************/ +bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) { + log::assert_that(acl_handle != nullptr, "assert failed: acl_handle != nullptr"); + + tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(nullptr, lcid); + if (p_ccb == nullptr) { + log::error("No CCB for CID:0x{:04x}", lcid); + return false; + } + uint16_t handle = p_ccb->p_lcb->Handle(); + if (handle == HCI_INVALID_HANDLE) { + log::error("Invalid ACL handle"); + return false; + } + *acl_handle = handle; + return true; +} + +/******************************************************************************* + ** + ** Function L2CA_GetLocalMtu + ** + ** Description Given a local channel identifier, |lcid|, this function + ** returns the L2CAP local mtu, |local_mtu|. If + ** |lcid| is not known or is invalid, this function returns false and does not + ** modify the value pointed at by |local_mtu|. + ** + ** Parameters: lcid: Local CID + ** local_mtu: Pointer to L2CAP local mtu must NOT be nullptr + ** + ** Returns true if local_mtu lookup was successful + ** + ******************************************************************************/ +bool L2CA_GetLocalMtu(uint16_t lcid, uint16_t* local_mtu) { + log::assert_that(local_mtu != nullptr, "assert failed: local_mtu != nullptr"); + + tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(nullptr, lcid); + if (p_ccb == nullptr) { + log::error("No CCB for CID:0x{:04x}", lcid); + return false; + } + *local_mtu = p_ccb->p_rcb->my_mtu; + return true; +} + using namespace bluetooth; #define DUMPSYS_TAG "shim::legacy::l2cap" diff --git a/system/stack/l2cap/l2c_api.h b/system/stack/l2cap/l2c_api.h index f645803dad..1ff09ffaf5 100644 --- a/system/stack/l2cap/l2c_api.h +++ b/system/stack/l2cap/l2c_api.h @@ -106,6 +106,7 @@ public: void L2CA_LockBleConnParamsForServiceDiscovery(const RawAddress& bd_addr, bool lock) override; void L2CA_LockBleConnParamsForProfileConnection(const RawAddress& bd_addr, bool lock) override; [[nodiscard]] tHCI_ROLE L2CA_GetBleConnRole(const RawAddress& bd_addr) override; + [[nodiscard]] uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr) override; [[nodiscard]] bool L2CA_SetLeGattTimeout(const RawAddress& bd_addr, uint16_t idle_tout) override; [[nodiscard]] bool L2CA_MarkLeLinkAsActive(const RawAddress& bd_addr) override; [[nodiscard]] bool L2CA_GetPeerLECocConfig(uint16_t lcid, tL2CAP_LE_CFG_INFO* peer_cfg) override; @@ -133,6 +134,10 @@ public: void L2CA_SetMediaStreamChannel(uint16_t local_media_cid, bool status) override; [[nodiscard]] bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid) override; + + [[nodiscard]] bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) override; + + [[nodiscard]] bool L2CA_GetLocalMtu(uint16_t lcid, uint16_t* local_mtu) override; }; } // namespace l2cap diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc index 9d69e5ab36..2983c5c071 100644 --- a/system/stack/l2cap/l2c_ble.cc +++ b/system/stack/l2cap/l2c_ble.cc @@ -24,7 +24,6 @@ #define LOG_TAG "l2c_ble" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -34,6 +33,7 @@ #include "btif/include/core_callbacks.h" #include "btif/include/stack_manager_t.h" +#include "common/le_conn_params.h" #include "hci/controller_interface.h" #include "hci/hci_interface.h" #include "internal_include/bt_target.h" @@ -89,6 +89,15 @@ hci_role_t L2CA_GetBleConnRole(const RawAddress& bd_addr) { return p_lcb->LinkRole(); } +uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr) { + tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + if (p_lcb == nullptr) { + log::error("lcb for {} is not available", bd_addr); + return 0; + } + return p_lcb->ConnInterval(); +} + /******************************************************************************* * * Function l2cble_notify_le_connection @@ -108,10 +117,10 @@ void l2cble_notify_le_connection(const RawAddress& bda) { if (get_btm_client_interface().peer.BTM_IsAclConnectionUp(bda, BT_TRANSPORT_LE) && p_lcb->link_state != LST_CONNECTED) { /* update link status */ + p_lcb->link_state = LST_CONNECTED; // TODO Move this back into acl layer btm_establish_continue_from_address(bda, BT_TRANSPORT_LE); - /* update l2cap link status and send callback */ - p_lcb->link_state = LST_CONNECTED; + /* send callback */ l2cu_process_fixed_chnl_resp(p_lcb); } @@ -174,9 +183,26 @@ bool l2cble_conn_comp(uint16_t handle, tHCI_ROLE role, const RawAddress& bda, /* update link parameter, set peripheral link as non-spec default upon link up */ p_lcb->min_interval = p_lcb->max_interval = conn_interval; + p_lcb->SetConnInterval(conn_interval); p_lcb->timeout = conn_timeout; p_lcb->latency = conn_latency; p_lcb->conn_update_mask = L2C_BLE_NOT_DEFAULT_PARAM; + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + uint16_t min_conn_interval_aggressive = LeConnectionParameters::GetMinConnIntervalAggressive(); + uint16_t max_conn_interval_aggressive = LeConnectionParameters::GetMaxConnIntervalAggressive(); + + stack::l2cap::get_interface().L2CA_AdjustConnectionIntervals( + &min_conn_interval_aggressive, &max_conn_interval_aggressive, BTM_BLE_CONN_INT_MIN); + + bool is_aggressive_initial_param = conn_interval <= max_conn_interval_aggressive; + log::info("conn_interval={}, max_conn_interval_aggressive={}, is_aggressive_initial_param={}", + conn_interval, max_conn_interval_aggressive, is_aggressive_initial_param); + + if (is_aggressive_initial_param) { + p_lcb->conn_update_mask |= L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } + } + p_lcb->conn_update_blocked_by_profile_connection = false; p_lcb->conn_update_blocked_by_service_discovery = false; @@ -328,6 +354,9 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { p_lcb->latency = latency; p_lcb->timeout = timeout; p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + p_lcb->conn_update_mask &= ~L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } l2cble_start_conn_update(p_lcb); } @@ -777,11 +806,21 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { p_ccb->p_rcb = p_rcb; p_ccb->remote_cid = rcid; - p_ccb->local_conn_cfg.mtu = L2CAP_SDU_LENGTH_LE_MAX; - p_ccb->local_conn_cfg.mps = - bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_; - p_ccb->local_conn_cfg.credits = L2CA_LeCreditDefault(); - p_ccb->remote_credit_count = L2CA_LeCreditDefault(); + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349377 + p_ccb->local_conn_cfg.mtu = p_rcb->coc_cfg.mtu; + p_ccb->local_conn_cfg.mps = p_rcb->coc_cfg.mps; + } else { + p_ccb->local_conn_cfg.mtu = L2CAP_SDU_LENGTH_LE_MAX; + p_ccb->local_conn_cfg.mps = + bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_; + } + if (com::android::bluetooth::flags::socket_settings_api()) { // Added with aosp/3349376 + p_ccb->local_conn_cfg.credits = p_rcb->coc_cfg.credits; + p_ccb->remote_credit_count = p_rcb->coc_cfg.credits; + } else { + p_ccb->local_conn_cfg.credits = L2CA_LeCreditDefault(); + p_ccb->remote_credit_count = L2CA_LeCreditDefault(); + } p_ccb->peer_conn_cfg.mtu = mtu; p_ccb->peer_conn_cfg.mps = mps; @@ -925,7 +964,7 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { /** This function is to initiate a direct connection. Returns true if connection * initiated, false otherwise. */ bool l2cble_create_conn(tL2C_LCB* p_lcb) { - if (!connection_manager::create_le_connection(CONN_MGR_ID_L2CAP, p_lcb->remote_bd_addr)) { + if (!connection_manager::direct_connect_add(CONN_MGR_ID_L2CAP, p_lcb->remote_bd_addr)) { return false; } @@ -1149,7 +1188,7 @@ void l2cble_process_data_length_change_event(uint16_t handle, uint16_t tx_data_l "{}", p_lcb->remote_bd_addr, p_lcb->tx_data_len, tx_data_len); BTM_LogHistory(kBtmLogTag, p_lcb->remote_bd_addr, "LE Data length change", - base::StringPrintf("tx_octets:%hu => %hu", p_lcb->tx_data_len, tx_data_len)); + std::format("tx_octets:{} => {}", p_lcb->tx_data_len, tx_data_len)); p_lcb->tx_data_len = tx_data_len; } else { log::debug( diff --git a/system/stack/l2cap/l2c_ble_conn_params.cc b/system/stack/l2cap/l2c_ble_conn_params.cc index 1e9d4ba7ab..47c19f3a3c 100644 --- a/system/stack/l2cap/l2c_ble_conn_params.cc +++ b/system/stack/l2cap/l2c_ble_conn_params.cc @@ -26,7 +26,9 @@ #define LOG_TAG "l2c_ble_conn_params" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> +#include "common/le_conn_params.h" #include "hci/controller_interface.h" #include "hci/event_checkers.h" #include "hci/hci_interface.h" @@ -34,6 +36,7 @@ #include "internal_include/stack_config.h" #include "main/shim/acl_api.h" #include "main/shim/entry.h" +#include "osi/include/properties.h" #include "stack/btm/btm_dev.h" #include "stack/include/acl_api.h" #include "stack/include/btm_ble_api_types.h" @@ -85,6 +88,10 @@ bool L2CA_UpdateBleConnParams(const RawAddress& rem_bda, uint16_t min_int, uint1 p_lcb->latency = latency; p_lcb->timeout = timeout; p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + p_lcb->conn_update_mask &= ~L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } + p_lcb->min_ce_len = min_ce_len; p_lcb->max_ce_len = max_ce_len; @@ -117,7 +124,15 @@ void L2CA_LockBleConnParamsForServiceDiscovery(const RawAddress& rem_bda, bool l if (lock == p_lcb->conn_update_blocked_by_service_discovery) { log::warn("{} service discovery already locked/unlocked conn params: {}", rem_bda, lock); - return; + + if (!lock && com::android::bluetooth::flags::initial_conn_params_p1() && + (p_lcb->conn_update_mask & L2C_BLE_AGGRESSIVE_INITIAL_PARAM)) { + p_lcb->conn_update_mask &= ~L2C_BLE_NOT_DEFAULT_PARAM; + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + log::info("Service discovery is skipped. Relaxing connection parameters."); + } else { + return; + } } p_lcb->conn_update_blocked_by_service_discovery = lock; @@ -217,11 +232,41 @@ void l2cble_start_conn_update(tL2C_LCB* p_lcb) { /* application requests to disable parameters update. If parameters are already updated, lets set them up to what has been requested during connection establishement */ - if (p_lcb->conn_update_mask & L2C_BLE_NOT_DEFAULT_PARAM && - /* current connection interval is greater than default min */ - p_lcb->min_interval > BTM_BLE_CONN_INT_MIN) { - /* use 7.5 ms as fast connection parameter, 0 peripheral latency */ - min_conn_int = max_conn_int = BTM_BLE_CONN_INT_MIN; + if (p_lcb->conn_update_mask & L2C_BLE_NOT_DEFAULT_PARAM) { + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + min_conn_int = LeConnectionParameters::GetMinConnIntervalAggressive(); + max_conn_int = LeConnectionParameters::GetMaxConnIntervalAggressive(); + log::info("min_conn_int={}, max_conn_int={}", min_conn_int, max_conn_int); + + if (p_lcb->conn_update_mask & L2C_BLE_AGGRESSIVE_INITIAL_PARAM) { + // Usually, we can use the same aggressive connection parameters for service discovery. + // However when hearing aid is being used, the connection intervals may need to be + // adjusted. + uint16_t adjusted_min_conn_int = min_conn_int; + uint16_t adjusted_max_conn_int = max_conn_int; + + stack::l2cap::get_interface().L2CA_AdjustConnectionIntervals( + &adjusted_min_conn_int, &adjusted_max_conn_int, BTM_BLE_CONN_INT_MIN); + + log::info("adjusted_min_conn_int={}, adjusted_max_conn_int={}", adjusted_min_conn_int, + adjusted_max_conn_int); + + if ((adjusted_min_conn_int == min_conn_int) && (adjusted_max_conn_int == max_conn_int)) { + log::info("No need to update connection parameters."); + p_lcb->conn_update_mask &= ~L2C_BLE_NOT_DEFAULT_PARAM; + p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM; + return; + } + } + } else { + if (p_lcb->min_interval <= BTM_BLE_CONN_INT_MIN) { + // Skip updating connection parameters for service discovery if we are already + // using default minimum interval. + return; + } + /* use 7.5 ms as fast connection parameter, 0 peripheral latency */ + min_conn_int = max_conn_int = BTM_BLE_CONN_INT_MIN; + } stack::l2cap::get_interface().L2CA_AdjustConnectionIntervals(&min_conn_int, &max_conn_int, BTM_BLE_CONN_INT_MIN); @@ -250,6 +295,15 @@ void l2cble_start_conn_update(tL2C_LCB* p_lcb) { if (p_lcb->IsLinkRoleCentral() || (bluetooth::shim::GetController()->SupportsBleConnectionParametersRequest() && acl_peer_supports_ble_connection_parameters_request(p_lcb->remote_bd_addr))) { + if (com::android::bluetooth::flags::initial_conn_params_p1() && + (p_lcb->conn_update_mask & L2C_BLE_AGGRESSIVE_INITIAL_PARAM)) { + log::info("Relaxing aggressive initial connection parameters. addr={}", + p_lcb->remote_bd_addr); + p_lcb->min_interval = LeConnectionParameters::GetMinConnIntervalRelaxed(); + p_lcb->max_interval = LeConnectionParameters::GetMaxConnIntervalRelaxed(); + p_lcb->conn_update_mask &= ~L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } + acl_ble_connection_parameters_request(p_lcb->Handle(), p_lcb->min_interval, p_lcb->max_interval, p_lcb->latency, p_lcb->timeout, p_lcb->min_ce_len, p_lcb->max_ce_len); @@ -275,7 +329,7 @@ void l2cble_start_conn_update(tL2C_LCB* p_lcb) { * Returns void * ******************************************************************************/ -void l2cble_process_conn_update_evt(uint16_t handle, uint8_t status, uint16_t /* interval */, +void l2cble_process_conn_update_evt(uint16_t handle, uint8_t status, uint16_t interval, uint16_t /* latency */, uint16_t /* timeout */) { log::verbose(""); @@ -285,7 +339,7 @@ void l2cble_process_conn_update_evt(uint16_t handle, uint8_t status, uint16_t /* log::warn("Invalid handle: {}", handle); return; } - + p_lcb->SetConnInterval(interval); p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING; if (status != HCI_SUCCESS) { @@ -321,6 +375,9 @@ void l2cble_process_rc_param_request_evt(uint16_t handle, uint16_t int_min, uint p_lcb->max_interval = int_max; p_lcb->latency = latency; p_lcb->timeout = timeout; + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + p_lcb->conn_update_mask &= ~L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } /* if update is enabled, always accept connection parameter update */ if ((p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) == 0) { @@ -370,6 +427,9 @@ void l2cble_use_preferred_conn_params(const RawAddress& bda) { p_lcb->max_interval = p_dev_rec->conn_params.max_conn_int; p_lcb->timeout = p_dev_rec->conn_params.supervision_tout; p_lcb->latency = p_dev_rec->conn_params.peripheral_latency; + if (com::android::bluetooth::flags::initial_conn_params_p1()) { + p_lcb->conn_update_mask &= ~L2C_BLE_AGGRESSIVE_INITIAL_PARAM; + } acl_ble_connection_parameters_request(p_lcb->Handle(), p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, diff --git a/system/stack/l2cap/l2c_int.h b/system/stack/l2cap/l2c_int.h index 0077cab5bf..639cee872e 100644 --- a/system/stack/l2cap/l2c_int.h +++ b/system/stack/l2cap/l2c_int.h @@ -23,7 +23,6 @@ ******************************************************************************/ #pragma once -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <stdbool.h> @@ -95,7 +94,7 @@ inline std::string channel_state_text(const tL2C_CHNL_STATE& state) { CASE_RETURN_TEXT(CST_W4_L2CAP_DISCONNECT_RSP); CASE_RETURN_TEXT(CST_W4_L2CA_DISCONNECT_RSP); default: - return base::StringPrintf("UNKNOWN[%d]", state); + return std::format("UNKNOWN[{}]", static_cast<int>(state)); } } @@ -395,6 +394,8 @@ enum tCONN_UPDATE_MASK : uint8_t { L2C_BLE_UPDATE_PENDING = (1u << 2), /* not using default connection parameters */ L2C_BLE_NOT_DEFAULT_PARAM = (1u << 3), + /* Aggressive initial connection parameters are used */ + L2C_BLE_AGGRESSIVE_INITIAL_PARAM = (1u << 4), }; /* Define a link control block. There is one link control block between @@ -429,6 +430,7 @@ public: private: tHCI_ROLE link_role_{HCI_ROLE_CENTRAL}; /* Central or peripheral */ + uint16_t conn_interval_; public: tHCI_ROLE LinkRole() const { return link_role_; } @@ -436,6 +438,8 @@ public: bool IsLinkRolePeripheral() const { return link_role_ == HCI_ROLE_PERIPHERAL; } void SetLinkRoleAsCentral() { link_role_ = HCI_ROLE_CENTRAL; } void SetLinkRoleAsPeripheral() { link_role_ = HCI_ROLE_PERIPHERAL; } + uint16_t ConnInterval() const { return conn_interval_; } + void SetConnInterval(uint16_t conn_interval) { conn_interval_ = conn_interval; } uint8_t signal_id; /* Signalling channel id */ uint8_t cur_echo_id; /* Current id value for echo request */ diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc index c04379bf89..c18ba8c5a9 100644 --- a/system/stack/l2cap/l2c_utils.cc +++ b/system/stack/l2cap/l2c_utils.cc @@ -3331,6 +3331,9 @@ void l2cu_send_peer_ble_credit_based_conn_res(tL2C_CCB* p_ccb, tL2CAP_LE_RESULT_ p = (uint8_t*)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + log::verbose("local cid: {}, mtu: {}, mps: {}, initial credits: {}", p_ccb->local_cid, + p_ccb->local_conn_cfg.mtu, p_ccb->local_conn_cfg.mps, p_ccb->local_conn_cfg.credits); + UINT16_TO_STREAM(p, p_ccb->local_cid); /* Local CID */ UINT16_TO_STREAM(p, p_ccb->local_conn_cfg.mtu); /* MTU */ UINT16_TO_STREAM(p, p_ccb->local_conn_cfg.mps); /* MPS */ diff --git a/system/stack/l2cap/l2cap_api.cc b/system/stack/l2cap/l2cap_api.cc index 94466c90e2..0752c995c0 100644 --- a/system/stack/l2cap/l2cap_api.cc +++ b/system/stack/l2cap/l2cap_api.cc @@ -214,6 +214,11 @@ void bluetooth::stack::l2cap::Impl::L2CA_Consolidate(const RawAddress& identity_ return ::L2CA_GetBleConnRole(bd_addr); } +[[nodiscard]] uint16_t bluetooth::stack::l2cap::Impl::L2CA_GetBleConnInterval( + const RawAddress& bd_addr) { + return ::L2CA_GetBleConnInterval(bd_addr); +} + void bluetooth::stack::l2cap::Impl::L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interval, uint16_t floor_interval) { @@ -257,3 +262,13 @@ void bluetooth::stack::l2cap::Impl::L2CA_SetMediaStreamChannel(uint16_t local_me uint16_t* rcid) { return ::L2CA_GetRemoteChannelId(lcid, rcid); } + +[[nodiscard]] bool bluetooth::stack::l2cap::Impl::L2CA_GetAclHandle(uint16_t lcid, + uint16_t* acl_handle) { + return ::L2CA_GetAclHandle(lcid, acl_handle); +} + +[[nodiscard]] bool bluetooth::stack::l2cap::Impl::L2CA_GetLocalMtu(uint16_t lcid, + uint16_t* local_mtu) { + return ::L2CA_GetLocalMtu(lcid, local_mtu); +} diff --git a/system/stack/metrics/stack_metrics_logging.cc b/system/stack/metrics/stack_metrics_logging.cc index 4a6326f2ee..ffe7a43488 100644 --- a/system/stack/metrics/stack_metrics_logging.cc +++ b/system/stack/metrics/stack_metrics_logging.cc @@ -81,3 +81,16 @@ void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_reque void log_le_pairing_fail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing) { bluetooth::shim::LogMetricLePairingFail(raw_address, failure_reason, is_outgoing); } + +void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason) { + bluetooth::shim::LogMetricLeConnectionStatus(address, is_connect, reason); +} + +void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add) { + bluetooth::shim::LogMetricLeDeviceInAcceptList(address, is_add); +} + +void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct) { + bluetooth::shim::LogMetricLeConnectionLifecycle(address, is_connect, is_direct); +} diff --git a/system/stack/mmc/main.cc b/system/stack/mmc/main.cc index 75de58852e..3f24e7851c 100644 --- a/system/stack/mmc/main.cc +++ b/system/stack/mmc/main.cc @@ -19,7 +19,6 @@ #include <base/files/file_util.h> #include <base/logging.h> #include <base/run_loop.h> -#include <base/strings/stringprintf.h> #include <base/task/single_thread_task_executor.h> #include <bluetooth/log.h> #include <sys/syslog.h> @@ -44,8 +43,7 @@ const int kSyslogCritical = LOG_CRIT; static bool MessageHandler(int severity, const char* file, int line, size_t message_start, const std::string& message) { - const auto str = - base::StringPrintf("%s:%d - %s", file, line, message.substr(message_start).c_str()); + const auto str = std::format("{}:{} - {}", file, line, message.substr(message_start)); switch (severity) { case logging::LOGGING_INFO: diff --git a/system/stack/pan/pan_api.cc b/system/stack/pan/pan_api.cc index 0d6d17f368..432a071ec7 100644 --- a/system/stack/pan/pan_api.cc +++ b/system/stack/pan/pan_api.cc @@ -27,7 +27,6 @@ #include "stack/include/pan_api.h" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <cstdint> @@ -231,8 +230,7 @@ tPAN_RESULT PAN_SetRole(uint8_t role, std::string p_user_name, std::string p_nap pan_cb.role = role; log::verbose("PAN role set to: {}", role); - BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Role change", - base::StringPrintf("role:0x%x", role)); + BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Role change", std::format("role:0x{:x}", role)); return PAN_SUCCESS; } @@ -687,7 +685,7 @@ void PAN_Dumpsys(int fd) { if (pcb->con_state == PAN_STATE_IDLE) { continue; } - LOG_DUMPSYS(fd, " Id:%d peer:%s", i, ADDRESS_TO_LOGGABLE_CSTR(pcb->rem_bda)); + LOG_DUMPSYS(fd, " Id:%d peer:%s", i, pcb->rem_bda.ToRedactedStringForLogging().c_str()); LOG_DUMPSYS(fd, " rx_packets:%-5lu rx_octets:%-8lu rx_errors:%-5lu rx_drops:%-5lu", (unsigned long)pcb->read.packets, (unsigned long)pcb->read.octets, (unsigned long)pcb->read.errors, (unsigned long)pcb->read.drops); diff --git a/system/stack/pan/pan_main.cc b/system/stack/pan/pan_main.cc index f4e7a65226..49afce8324 100644 --- a/system/stack/pan/pan_main.cc +++ b/system/stack/pan/pan_main.cc @@ -25,7 +25,6 @@ #define LOG_TAG "pan" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <string.h> // memset diff --git a/system/stack/rfcomm/port_api.cc b/system/stack/rfcomm/port_api.cc index 6786e88d02..d483a99d07 100644 --- a/system/stack/rfcomm/port_api.cc +++ b/system/stack/rfcomm/port_api.cc @@ -26,8 +26,8 @@ #include "stack/include/port_api.h" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <cstdint> @@ -97,6 +97,7 @@ const char kBtmLogTag[] = "RFCOMM"; * connection up/down events. * sec_mask - bitmask of BTM_SEC_* values indicating the * minimum security requirements for this + * cfg - optional configurations for the connection *connection Notes: * * Server can call this function with the same scn parameter multiple times if @@ -110,7 +111,8 @@ const char kBtmLogTag[] = "RFCOMM"; ******************************************************************************/ int RFCOMM_CreateConnectionWithSecurity(uint16_t uuid, uint8_t scn, bool is_server, uint16_t mtu, const RawAddress& bd_addr, uint16_t* p_handle, - tPORT_MGMT_CALLBACK* p_mgmt_callback, uint16_t sec_mask) { + tPORT_MGMT_CALLBACK* p_mgmt_callback, uint16_t sec_mask, + RfcommCfgInfo cfg) { *p_handle = 0; if ((scn == 0) || (scn > RFCOMM_MAX_SCN)) { @@ -198,6 +200,16 @@ int RFCOMM_CreateConnectionWithSecurity(uint16_t uuid, uint8_t scn, bool is_serv p_port->mtu = rfcomm_mtu; } + // Set the optional configuration for future use when the server or client negotiates the + // parameters with the peer device. + if (com::android::bluetooth::flags::socket_settings_api()) { + p_port->rfc_cfg_info = cfg; + // Update the local mtu with the optional configuration if set by the app + if (p_port->rfc_cfg_info.rx_mtu_present) { + p_port->mtu = p_port->rfc_cfg_info.rx_mtu; + } + } + // Other states // server doesn't need to release port when closing if (is_server) { @@ -219,15 +231,13 @@ int RFCOMM_CreateConnectionWithSecurity(uint16_t uuid, uint8_t scn, bool is_serv // If this is not initiator of the connection need to just wait if (p_port->is_server) { - BTM_LogHistory( - kBtmLogTag, bd_addr, "Server started", - base::StringPrintf("handle:%hu scn:%hhu dlci:%hhu mtu:%hu", *p_handle, scn, dlci, mtu)); + BTM_LogHistory(kBtmLogTag, bd_addr, "Server started", + std::format("handle:{} scn:{} dlci:{} mtu:{}", *p_handle, scn, dlci, mtu)); return PORT_SUCCESS; } - BTM_LogHistory( - kBtmLogTag, bd_addr, "Connection opened", - base::StringPrintf("handle:%hu scn:%hhu dlci:%hhu mtu:%hu", *p_handle, scn, dlci, mtu)); + BTM_LogHistory(kBtmLogTag, bd_addr, "Connection opened", + std::format("handle:{} scn:{} dlci:{} mtu:{}", *p_handle, scn, dlci, mtu)); // Open will be continued after security checks are passed return port_open_continue(p_port); @@ -306,10 +316,9 @@ int RFCOMM_RemoveConnection(uint16_t handle) { const RawAddress bd_addr = (p_port->rfc.p_mcb) ? (p_port->rfc.p_mcb->bd_addr) : (RawAddress::kEmpty); - BTM_LogHistory( - kBtmLogTag, bd_addr, "Connection closed", - base::StringPrintf("handle:%hu scn:%hhu dlci:%hhu is_server:%s", handle, p_port->scn, - p_port->dlci, p_port->is_server ? "true" : "false")); + BTM_LogHistory(kBtmLogTag, bd_addr, "Connection closed", + std::format("handle:{} scn:{} dlci:{} is_server:{}", handle, p_port->scn, + p_port->dlci, p_port->is_server)); p_port->state = PORT_CONNECTION_STATE_CLOSING; @@ -345,10 +354,9 @@ int RFCOMM_RemoveServer(uint16_t handle) { const RawAddress bd_addr = (p_port->rfc.p_mcb) ? (p_port->rfc.p_mcb->bd_addr) : (RawAddress::kEmpty); - BTM_LogHistory( - kBtmLogTag, bd_addr, "Server stopped", - base::StringPrintf("handle:%hu scn:%hhu dlci:%hhu is_server:%s", handle, p_port->scn, - p_port->dlci, p_port->is_server ? "true" : "false")); + BTM_LogHistory(kBtmLogTag, bd_addr, "Server stopped", + std::format("handle:{} scn:{} dlci:{} is_server:{}", handle, p_port->scn, + p_port->dlci, p_port->is_server)); /* this port will be deallocated after closing */ p_port->keep_port_handle = false; @@ -1194,3 +1202,51 @@ int PORT_GetSecurityMask(uint16_t handle, uint16_t* sec_mask) { *sec_mask = p_port->sec_mask; return PORT_SUCCESS; } + +int PORT_GetChannelInfo(uint16_t handle, uint16_t* local_mtu, uint16_t* remote_mtu, + uint16_t* local_credit, uint16_t* remote_credit, uint16_t* local_cid, + uint16_t* remote_cid, uint16_t* dlci, uint16_t* max_frame_size, + uint16_t* acl_handle, bool* mux_initiator) { + log::verbose("PORT_GetChannelInfo() handle:{}", handle); + + tPORT* p_port = get_port_from_handle(handle); + if (p_port == nullptr) { + log::error("Unable to get RFCOMM port control block bad handle:{}", handle); + return PORT_BAD_HANDLE; + } + + if (!p_port->in_use || (p_port->state == PORT_CONNECTION_STATE_CLOSED)) { + return PORT_NOT_OPENED; + } + + if (p_port->line_status) { + return PORT_LINE_ERR; + } + + uint16_t rcid, ahandle, lmtu; + if (!stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_port->rfc.p_mcb->lcid, &rcid)) { + log::error("L2CA_GetRemoteChannelId failed, local cid: {}", p_port->rfc.p_mcb->lcid); + return PORT_PEER_FAILED; + } + + if (!stack::l2cap::get_interface().L2CA_GetAclHandle(p_port->rfc.p_mcb->lcid, &ahandle)) { + log::error("L2CA_GetAclHandle failed, local cid: {}", p_port->rfc.p_mcb->lcid); + return PORT_PEER_FAILED; + } + + if (!stack::l2cap::get_interface().L2CA_GetLocalMtu(p_port->rfc.p_mcb->lcid, &lmtu)) { + log::error("L2CA_GetLocalMtu failed, local cid: {}", p_port->rfc.p_mcb->lcid); + return PORT_PEER_FAILED; + } + *local_mtu = lmtu; + *remote_mtu = p_port->rfc.p_mcb->peer_l2cap_mtu + RFCOMM_MIN_OFFSET + 1; + *local_credit = p_port->credit_rx; + *remote_credit = p_port->credit_tx; + *local_cid = p_port->rfc.p_mcb->lcid; + *remote_cid = rcid; + *dlci = p_port->dlci; + *max_frame_size = p_port->mtu; + *acl_handle = ahandle; + *mux_initiator = p_port->rfc.p_mcb->is_initiator; + return PORT_SUCCESS; +} diff --git a/system/stack/rfcomm/port_int.h b/system/stack/rfcomm/port_int.h index 8fcbf845fd..94d9f4cc8b 100644 --- a/system/stack/rfcomm/port_int.h +++ b/system/stack/rfcomm/port_int.h @@ -205,6 +205,8 @@ typedef struct { uint16_t keep_mtu; /* Max MTU that port can receive by server */ uint16_t sec_mask; /* Bitmask of security requirements for this port */ /* see the BTM_SEC_* values in btm_api_types.h */ + RfcommCfgInfo rfc_cfg_info; /* store optional rfc configure info for incoming */ + /* connection while connecting */ } tPORT; /* Define the PORT/RFCOMM control structure diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc index 1f8ba0b5ad..01c8270869 100644 --- a/system/stack/rfcomm/port_rfc.cc +++ b/system/stack/rfcomm/port_rfc.cc @@ -27,6 +27,7 @@ #include <base/functional/callback.h> #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> #include <cstdint> @@ -344,7 +345,13 @@ void PORT_ParNegInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint8_t cl, uin /* Set convergence layer and number of credits (k) */ our_cl = RFCOMM_PN_CONV_LAYER_CBFC_R; - our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + if (com::android::bluetooth::flags::socket_settings_api()) { + our_k = (p_port->rfc_cfg_info.init_credit_present) ? p_port->rfc_cfg_info.init_credit + : (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max + : RFCOMM_K_MAX; + } else { + our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + } p_port->credit_rx = our_k; } else { /* must not be using credit based flow control; use TS 7.10 */ diff --git a/system/stack/rfcomm/rfc_mx_fsm.cc b/system/stack/rfcomm/rfc_mx_fsm.cc index c9890023d5..c2a00bdfa5 100644 --- a/system/stack/rfcomm/rfc_mx_fsm.cc +++ b/system/stack/rfcomm/rfc_mx_fsm.cc @@ -346,7 +346,7 @@ void rfc_mx_sm_sabme_wait_ua(tRFC_MCB* p_mcb, tRFC_MX_EVENT event, void* /* p_da case RFC_MX_EVENT_DM: rfc_timer_stop(p_mcb); - FALLTHROUGH_INTENDED; /* FALLTHROUGH */ + [[fallthrough]]; case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ diff --git a/system/stack/rfcomm/rfc_port_if.cc b/system/stack/rfcomm/rfc_port_if.cc index c6c37cc11c..0973f0370a 100644 --- a/system/stack/rfcomm/rfc_port_if.cc +++ b/system/stack/rfcomm/rfc_port_if.cc @@ -26,6 +26,7 @@ #define LOG_TAG "rfcomm" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <cstdint> #include <unordered_map> @@ -149,7 +150,14 @@ void RFCOMM_ParameterNegotiationRequest(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t /* Set convergence layer and number of credits (k) */ if (flow == PORT_FC_CREDIT) { cl = RFCOMM_PN_CONV_LAYER_CBFC_I; - k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + + if (com::android::bluetooth::flags::socket_settings_api()) { + k = (p_port->rfc_cfg_info.init_credit_present) ? p_port->rfc_cfg_info.init_credit + : (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max + : RFCOMM_K_MAX; + } else { + k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + } p_port->credit_rx = k; } else { cl = RFCOMM_PN_CONV_LAYER_TYPE_1; diff --git a/system/stack/rfcomm/rfc_ts_frames.cc b/system/stack/rfcomm/rfc_ts_frames.cc index e9df718116..495d0aea08 100644 --- a/system/stack/rfcomm/rfc_ts_frames.cc +++ b/system/stack/rfcomm/rfc_ts_frames.cc @@ -35,9 +35,6 @@ #include "stack/rfcomm/port_int.h" #include "stack/rfcomm/rfc_int.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; /******************************************************************************* @@ -378,7 +375,7 @@ void rfc_send_rls(tRFC_MCB* p_mcb, uint8_t dlci, bool is_command, uint8_t status * Description This function sends Non Supported Command Response. * ******************************************************************************/ -void rfc_send_nsc(tRFC_MCB* p_mcb) { +static void rfc_send_nsc(tRFC_MCB* p_mcb) { uint8_t* p_data; BT_HDR* p_buf = (BT_HDR*)osi_malloc(RFCOMM_CMD_BUF_SIZE); diff --git a/system/stack/rnr/remote_name_request.cc b/system/stack/rnr/remote_name_request.cc index ed6801a855..bd0fcbce44 100644 --- a/system/stack/rnr/remote_name_request.cc +++ b/system/stack/rnr/remote_name_request.cc @@ -27,16 +27,12 @@ #include "stack/btm/security_device_record.h" #include "stack/include/btm_client_interface.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" +using namespace bluetooth; extern tBTM_CB btm_cb; -using namespace bluetooth; tBTM_STATUS btm_ble_read_remote_name(const RawAddress& remote_bda, tBTM_NAME_CMPL_CB* p_cb); bool btm_ble_cancel_remote_name(const RawAddress& remote_bda); -void btm_ble_read_remote_name_cmpl(bool status, const RawAddress& bda, uint16_t length, - char* p_name); bool BTM_SecAddRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) { int i; @@ -80,7 +76,7 @@ bool BTM_IsRemoteNameKnown(const RawAddress& bd_addr, tBT_TRANSPORT /* transport * Returns void * ******************************************************************************/ -void btm_inq_rmt_name_failed_cancelled(void) { +static void btm_inq_rmt_name_failed_cancelled(void) { log::error("remname_active={}", btm_cb.rnr.remname_active); if (btm_cb.rnr.remname_active) { @@ -123,8 +119,8 @@ static uint16_t get_clock_offset_from_storage(const RawAddress& remote_bda) { : 0; } -tBTM_STATUS btm_initiate_rem_name(const RawAddress& remote_bda, uint64_t timeout_ms, - tBTM_NAME_CMPL_CB* p_cb) { +static tBTM_STATUS btm_initiate_rem_name(const RawAddress& remote_bda, uint64_t timeout_ms, + tBTM_NAME_CMPL_CB* p_cb) { /*** Make sure the device is ready ***/ if (!get_btm_client_interface().local.BTM_IsDeviceUp()) { return tBTM_STATUS::BTM_WRONG_MODE; diff --git a/system/stack/rnr/remote_name_request.h b/system/stack/rnr/remote_name_request.h index 6df83680dc..356f24ca49 100644 --- a/system/stack/rnr/remote_name_request.h +++ b/system/stack/rnr/remote_name_request.h @@ -151,6 +151,8 @@ tBTM_STATUS BTM_CancelRemoteDeviceName(void); void btm_process_remote_name(const RawAddress* bda, const BD_NAME bdn, uint16_t /* evt_len */, tHCI_STATUS hci_status); +void btm_inq_remote_name_timer_timeout(void* data); + namespace bluetooth { namespace stack { namespace rnr { diff --git a/system/stack/sdp/sdp_api.cc b/system/stack/sdp/sdp_api.cc index 093dddde82..2380b76043 100644 --- a/system/stack/sdp/sdp_api.cc +++ b/system/stack/sdp/sdp_api.cc @@ -26,7 +26,6 @@ #include "stack/include/sdp_api.h" -#include <base/strings/stringprintf.h> #include <bluetooth/log.h> #include <string.h> diff --git a/system/stack/sdp/sdp_discovery.cc b/system/stack/sdp/sdp_discovery.cc index 183c3e7193..d3284892c8 100644 --- a/system/stack/sdp/sdp_discovery.cc +++ b/system/stack/sdp/sdp_discovery.cc @@ -25,6 +25,7 @@ #define LOG_TAG "stack::sdp" #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <cstdint> @@ -636,6 +637,16 @@ static void process_service_search_attr_rsp(tCONN_CB* p_ccb, uint8_t* p_reply, uint8_t* p; uint16_t bytes_left = SDP_DATA_BUF_SIZE; + /* If we don't have a valid discovery database, we can't do anything. */ + if (com::android::bluetooth::flags::btsec_check_valid_discovery_database() && + p_ccb->p_db == NULL) { + log::warn( + "Attempted continuation or first time request with invalid discovery " + "database"); + sdp_disconnect(p_ccb, tSDP_STATUS::SDP_INVALID_CONT_STATE); + return; + } + p_msg->offset = L2CAP_MIN_OFFSET; p = p_start = (uint8_t*)(p_msg + 1) + L2CAP_MIN_OFFSET; diff --git a/system/stack/sdp/sdpint.h b/system/stack/sdp/sdpint.h index e831065798..07d2a1e12a 100644 --- a/system/stack/sdp/sdpint.h +++ b/system/stack/sdp/sdpint.h @@ -25,7 +25,6 @@ #pragma once #include <base/functional/callback.h> -#include <base/strings/stringprintf.h> #include <cstdint> #include <string> @@ -220,7 +219,7 @@ inline std::string sdp_disc_wait_text(const tSDP_DISC_WAIT& state) { CASE_RETURN_TEXT(SDP_DISC_WAIT_SEARCH_ATTR); CASE_RETURN_TEXT(SDP_DISC_WAIT_CANCEL); default: - return base::StringPrintf("UNKNOWN[%d]", state); + return std::format("UNKNOWN[{}]", state); } } diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc index 777bb012a6..c625217c7c 100644 --- a/system/stack/smp/smp_act.cc +++ b/system/stack/smp/smp_act.cc @@ -269,7 +269,7 @@ void smp_send_pair_fail(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { if (p_cb->status <= SMP_MAX_FAIL_RSN_PER_SPEC && p_cb->status != SMP_SUCCESS) { log::error("Pairing failed smp_status:{}", smp_status_text(p_cb->status)); BTM_LogHistory(kBtmLogTag, p_cb->pairing_bda, "Pairing failed", - base::StringPrintf("smp_status:%s", smp_status_text(p_cb->status).c_str())); + std::format("smp_status:{}", smp_status_text(p_cb->status))); smp_send_cmd(SMP_OPCODE_PAIRING_FAILED, p_cb); p_cb->wait_for_authorization_complete = true; } @@ -1220,8 +1220,8 @@ void smp_sirk_verify(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { smp_int_data.status = SMP_SIRK_DEVICE_INVALID; BTM_LogHistory(kBtmLogTag, p_cb->pairing_bda, "SIRK verification", - base::StringPrintf("Verification failed, smp_status:%s", - smp_status_text(smp_int_data.status).c_str())); + std::format("Verification failed, smp_status:{}", + smp_status_text(smp_int_data.status))); smp_sm_event(p_cb, SMP_SIRK_DEVICE_VALID_EVT, &smp_int_data); @@ -1235,7 +1235,7 @@ void smp_sirk_verify(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { /* There is no member validator callback - device is by default valid */ if (callback_rc == tBTM_STATUS::BTM_SUCCESS_NO_SECURITY) { BTM_LogHistory(kBtmLogTag, p_cb->pairing_bda, "SIRK verification", - base::StringPrintf("Device validated due to no security")); + std::format("Device validated due to no security")); tSMP_INT_DATA smp_int_data; smp_int_data.status = SMP_SUCCESS; diff --git a/system/stack/smp/smp_api.cc b/system/stack/smp/smp_api.cc index 4485bf3fcb..0d66b0a064 100644 --- a/system/stack/smp/smp_api.cc +++ b/system/stack/smp/smp_api.cc @@ -525,3 +525,8 @@ void SMP_SirkConfirmDeviceReply(const RawAddress& bd_addr, uint8_t res) { smp_sm_event(p_cb, SMP_SIRK_DEVICE_VALID_EVT, &smp_int_data); } } + +uint16_t SMP_GetPendingPairingKeySize() { + tSMP_CB* p_cb = &smp_cb; + return p_cb->loc_enc_size; +}
\ No newline at end of file diff --git a/system/stack/smp/smp_int.h b/system/stack/smp/smp_int.h index 7725bd7e6d..438fe1e8eb 100644 --- a/system/stack/smp/smp_int.h +++ b/system/stack/smp/smp_int.h @@ -105,7 +105,7 @@ inline std::string smp_opcode_text(const tSMP_OPCODE opcode) { CASE_RETURN_TEXT(SMP_OPCODE_PAIR_KEYPR_NOTIF); CASE_RETURN_TEXT(SMP_OPCODE_PAIR_COMMITM); default: - return base::StringPrintf("UNKNOWN[%hhu]", opcode); + return std::format("UNKNOWN[{}]", static_cast<uint8_t>(opcode)); } } diff --git a/system/stack/smp/smp_main.cc b/system/stack/smp/smp_main.cc index 26635cf4d8..cfc8df3725 100644 --- a/system/stack/smp/smp_main.cc +++ b/system/stack/smp/smp_main.cc @@ -916,9 +916,9 @@ void smp_set_state(tSMP_STATE state) { log::debug("State change: {}({})==>{}({})", smp_get_state_name(smp_cb.state), smp_cb.state, smp_get_state_name(state), state); if (smp_cb.state != state) { - BTM_LogHistory(kBtmLogTag, smp_cb.pairing_ble_bd_addr, "Security state changed", - base::StringPrintf("%s => %s", smp_get_state_name(smp_cb.state), - smp_get_state_name(state))); + BTM_LogHistory( + kBtmLogTag, smp_cb.pairing_ble_bd_addr, "Security state changed", + std::format("{} => {}", smp_get_state_name(smp_cb.state), smp_get_state_name(state))); } smp_cb.state = state; } else { diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc index 2c4f43746b..81b005a78c 100644 --- a/system/stack/smp/smp_utils.cc +++ b/system/stack/smp/smp_utils.cc @@ -997,7 +997,7 @@ void smp_proc_pairing_cmpl(tSMP_CB* p_cb) { "sec_level:0x{:0x}", p_cb->pairing_bda, smp_status_text(evt_data.cmplt.reason), evt_data.cmplt.sec_level); BTM_LogHistory(kBtmLogTag, pairing_bda, "Pairing failed", - base::StringPrintf("reason:%s", smp_status_text(evt_data.cmplt.reason).c_str())); + std::format("reason:{}", smp_status_text(evt_data.cmplt.reason))); } // Log pairing complete event diff --git a/system/stack/srvc/srvc_dis.cc b/system/stack/srvc/srvc_dis.cc index 38751c653c..2a0014e5c1 100644 --- a/system/stack/srvc/srvc_dis.cc +++ b/system/stack/srvc/srvc_dis.cc @@ -32,9 +32,6 @@ #include "types/bluetooth/uuid.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; static const uint16_t dis_attr_uuid[] = { @@ -120,7 +117,7 @@ static void dis_gatt_c_read_dis_value_cmpl(tCONN_ID conn_id) { * Returns void * ******************************************************************************/ -bool dis_gatt_c_read_dis_req(tCONN_ID conn_id) { +static bool dis_gatt_c_read_dis_req(tCONN_ID conn_id) { tGATT_READ_PARAM param; memset(¶m, 0, sizeof(tGATT_READ_PARAM)); diff --git a/system/stack/test/a2dp/mock_bta_av_codec.cc b/system/stack/test/a2dp/mock_bta_av_codec.cc index c261bebfe9..6c0a80d0da 100644 --- a/system/stack/test/a2dp/mock_bta_av_codec.cc +++ b/system/stack/test/a2dp/mock_bta_av_codec.cc @@ -15,8 +15,6 @@ */ #include "a2dp_codec_api.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#include "btif/include/btif_av_co.h" A2dpCodecConfig* bta_av_get_a2dp_current_codec(void) { return nullptr; } diff --git a/system/stack/test/btm/stack_btm_dm_inq_db_test.cc b/system/stack/test/btm/stack_btm_dm_inq_db_test.cc index e86e7eaced..7f5e27f26f 100644 --- a/system/stack/test/btm/stack_btm_dm_inq_db_test.cc +++ b/system/stack/test/btm/stack_btm_dm_inq_db_test.cc @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <base/strings/stringprintf.h> #include <gtest/gtest.h> #include <stdlib.h> @@ -118,7 +117,7 @@ protected: void setup_thread() { for (size_t i = 0; i < kNumberOfThreads; i++) { - std::string name = base::StringPrintf("thread:%zu", i); + std::string name = std::format("thread:{}", i); threads[i] = thread_new(name.c_str()); } } diff --git a/system/stack/test/btm/stack_btm_inq_test.cc b/system/stack/test/btm/stack_btm_inq_test.cc index 160ff6124f..70219a92c2 100644 --- a/system/stack/test/btm/stack_btm_inq_test.cc +++ b/system/stack/test/btm/stack_btm_inq_test.cc @@ -36,9 +36,6 @@ #include "test/mock/mock_main_shim_entry.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - extern tBTM_CB btm_cb; using bluetooth::common::ContextualCallback; @@ -129,11 +126,12 @@ public: MockBtmInquiryCallbacks* inquiry_callback_ptr = nullptr; -void btm_inq_results_cb(tBTM_INQ_RESULTS* p_inq_results, const uint8_t* p_eir, uint16_t eir_len) { +static void btm_inq_results_cb(tBTM_INQ_RESULTS* p_inq_results, const uint8_t* p_eir, + uint16_t eir_len) { inquiry_callback_ptr->btm_inq_results_cb(p_inq_results, p_eir, eir_len); } -void btm_inq_cmpl_cb(void* p1) { inquiry_callback_ptr->btm_inq_cmpl_cb(p1); } +static void btm_inq_cmpl_cb(void* p1) { inquiry_callback_ptr->btm_inq_cmpl_cb(p1); } class BtmDeviceInquiryTest : public BtmInqTest { protected: diff --git a/system/stack/test/btm/stack_btm_sec_test.cc b/system/stack/test/btm/stack_btm_sec_test.cc index daa6bfbfb4..3bfac8572d 100644 --- a/system/stack/test/btm/stack_btm_sec_test.cc +++ b/system/stack/test/btm/stack_btm_sec_test.cc @@ -226,7 +226,7 @@ TEST_F(StackBtmSecTest, btm_oob_data_text) { for (const auto& data : datas) { ASSERT_STREQ(data.second.c_str(), btm_oob_data_text(data.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%hhu]", std::numeric_limits<std::uint8_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<std::uint8_t>::max()); ASSERT_STREQ( unknown.c_str(), btm_oob_data_text(static_cast<tBTM_OOB_DATA>(std::numeric_limits<std::uint8_t>::max())) @@ -242,7 +242,7 @@ TEST_F(StackBtmSecTest, bond_type_text) { for (const auto& data : datas) { ASSERT_STREQ(data.second.c_str(), bond_type_text(data.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%hhu]", std::numeric_limits<std::uint8_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<std::uint8_t>::max()); ASSERT_STREQ(unknown.c_str(), bond_type_text(static_cast<tBTM_BOND_TYPE>(std::numeric_limits<std::uint8_t>::max())) .c_str()); diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc index c308911bce..7471d4faca 100644 --- a/system/stack/test/btm/stack_btm_test.cc +++ b/system/stack/test/btm/stack_btm_test.cc @@ -32,15 +32,13 @@ #include "stack/include/acl_hci_link_interface.h" #include "stack/include/btm_client_interface.h" #include "stack/l2cap/l2c_int.h" +#include "stack/rnr/remote_name_request.h" #include "stack/test/btm/btm_test_fixtures.h" #include "test/common/mock_functions.h" #include "test/mock/mock_legacy_hci_interface.h" #include "test/mock/mock_main_shim_entry.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using ::testing::_; using ::testing::Each; using ::testing::Eq; @@ -50,11 +48,11 @@ extern tBTM_CB btm_cb; tL2C_CB l2cb; -void btm_inq_remote_name_timer_timeout(void*) {} - const std::string kSmpOptions("mock smp options"); const std::string kBroadcastAudioConfigOptions("mock broadcast audio config options"); +void btm_inq_remote_name_timer_timeout(void*) {} + namespace { using testing::Return; diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc index 3cb550b04d..40453f703d 100644 --- a/system/stack/test/btm_iso_test.cc +++ b/system/stack/test/btm_iso_test.cc @@ -27,14 +27,12 @@ #include "stack/btm/btm_dev.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_types.h" +#include "stack/include/btm_log_history.h" #include "stack/include/hci_error_code.h" #include "stack/include/hcidefs.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_main_shim_hci_layer.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using bluetooth::hci::IsoManager; using testing::_; using testing::AnyNumber; @@ -448,14 +446,14 @@ protected: void CleanupIsoManager() override { /* DO NOTHING */ } }; -bool operator==(const EXT_CIS_CFG& x, const EXT_CIS_CFG& y) { +static bool operator==(const EXT_CIS_CFG& x, const EXT_CIS_CFG& y) { return (x.cis_id == y.cis_id) && (x.max_sdu_size_mtos == y.max_sdu_size_mtos) && (x.max_sdu_size_stom == y.max_sdu_size_stom) && (x.phy_mtos == y.phy_mtos) && (x.phy_stom == y.phy_stom) && (x.rtn_mtos == y.rtn_mtos) && (x.rtn_stom == y.rtn_stom); } -bool operator==(const struct bluetooth::hci::iso_manager::cig_create_params& x, - const struct bluetooth::hci::iso_manager::cig_create_params& y) { +static bool operator==(const struct bluetooth::hci::iso_manager::cig_create_params& x, + const struct bluetooth::hci::iso_manager::cig_create_params& y) { return (x.sdu_itv_mtos == y.sdu_itv_mtos) && (x.sdu_itv_stom == y.sdu_itv_stom) && (x.sca == y.sca) && (x.packing == y.packing) && (x.framing == y.framing) && (x.max_trans_lat_stom == y.max_trans_lat_stom) && @@ -463,8 +461,8 @@ bool operator==(const struct bluetooth::hci::iso_manager::cig_create_params& x, std::is_permutation(x.cis_cfgs.begin(), x.cis_cfgs.end(), y.cis_cfgs.begin()); } -bool operator==(const struct bluetooth::hci::iso_manager::big_create_params& x, - const struct bluetooth::hci::iso_manager::big_create_params& y) { +static bool operator==(const struct bluetooth::hci::iso_manager::big_create_params& x, + const struct bluetooth::hci::iso_manager::big_create_params& y) { return (x.adv_handle == y.adv_handle) && (x.num_bis == y.num_bis) && (x.sdu_itv == y.sdu_itv) && (x.max_sdu_size == y.max_sdu_size) && (x.max_transport_latency == y.max_transport_latency) && (x.rtn == y.rtn) && diff --git a/system/stack/test/common/mock_btif_storage.cc b/system/stack/test/common/mock_btif_storage.cc deleted file mode 100644 index bf51e41c63..0000000000 --- a/system/stack/test/common/mock_btif_storage.cc +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ******************************************************************************/ - -#include "mock_btif_storage.h" - -#include "stack/include/btm_sec_api_types.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - -static bluetooth::manager::MockBtifStorageInterface* btif_storage_interface = nullptr; - -void bluetooth::manager::SetMockBtifStorageInterface( - MockBtifStorageInterface* mock_btif_storage_interface) { - btif_storage_interface = mock_btif_storage_interface; -} - -void btif_storage_load_bonded_eatt(void) { btif_storage_interface->LoadBondedEatt(); } diff --git a/system/stack/test/common/mock_btif_storage.h b/system/stack/test/common/mock_btif_storage.h deleted file mode 100644 index cc7da1cf7d..0000000000 --- a/system/stack/test/common/mock_btif_storage.h +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** - * - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ******************************************************************************/ -#pragma once - -#include <gmock/gmock.h> - -namespace bluetooth { -namespace manager { - -class BtifStorageInterface { -public: - virtual void LoadBondedEatt(void) = 0; - virtual ~BtifStorageInterface() = default; -}; - -class MockBtifStorageInterface : public BtifStorageInterface { -public: - MOCK_METHOD0(LoadBondedEatt, void(void)); -}; - -/** - * Set the {@link MockBifStorageInterface} for testing - * - * @param mock_btif_storage_interface pointer to mock btm security - * internal interface, could be null - */ -void SetMockBtifStorageInterface(MockBtifStorageInterface* mock_btif_storage_interface); - -} // namespace manager -} // namespace bluetooth diff --git a/system/stack/test/common/mock_btm_api_layer.cc b/system/stack/test/common/mock_btm_api_layer.cc index 735d6be806..83d969dc72 100644 --- a/system/stack/test/common/mock_btm_api_layer.cc +++ b/system/stack/test/common/mock_btm_api_layer.cc @@ -17,8 +17,8 @@ #include "mock_btm_api_layer.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#include "stack/btm/btm_ble_sec.h" +#include "stack/btm/btm_sec.h" static bluetooth::manager::MockBtmApiInterface* btm_api_interface = nullptr; diff --git a/system/stack/test/common/mock_btm_layer.cc b/system/stack/test/common/mock_btm_layer.cc index 129b58ce18..cde4cab07f 100644 --- a/system/stack/test/common/mock_btm_layer.cc +++ b/system/stack/test/common/mock_btm_layer.cc @@ -18,13 +18,11 @@ #include "mock_btm_layer.h" +#include "stack/include/acl_api.h" #include "stack/include/btm_client_interface.h" #include "stack/include/rfcdefs.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - static bluetooth::manager::MockBtmSecurityInternalInterface* btm_security_internal_interface = nullptr; diff --git a/system/stack/test/common/mock_btu_layer.cc b/system/stack/test/common/mock_btu_layer.cc deleted file mode 100644 index 26ab4b58c5..0000000000 --- a/system/stack/test/common/mock_btu_layer.cc +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** - * - * Copyright 2018 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 "common/message_loop_thread.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - -bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; } diff --git a/system/stack/test/common/mock_l2cap_layer.cc b/system/stack/test/common/mock_l2cap_layer.cc index b61b80c567..8383cead45 100644 --- a/system/stack/test/common/mock_l2cap_layer.cc +++ b/system/stack/test/common/mock_l2cap_layer.cc @@ -23,9 +23,6 @@ #include "stack/l2cap/l2c_int.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - static bluetooth::l2cap::MockL2capInterface* l2cap_interface = nullptr; void bluetooth::l2cap::SetMockInterface(MockL2capInterface* mock_l2cap_interface) { @@ -47,16 +44,6 @@ uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& bd_addr) { bool L2CA_DisconnectReq(uint16_t cid) { return l2cap_interface->DisconnectRequest(cid); } -bool L2CA_DisconnectRsp(uint16_t cid) { return l2cap_interface->DisconnectResponse(cid); } - -bool L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) { - return l2cap_interface->ConfigRequest(cid, p_cfg); -} - -bool L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) { - return l2cap_interface->ConfigResponse(cid, p_cfg); -} - tL2CAP_DW_RESULT L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) { return l2cap_interface->DataWrite(cid, p_data); } @@ -72,6 +59,10 @@ tHCI_ROLE L2CA_GetBleConnRole(const RawAddress& bd_addr) { return to_hci_role(l2cap_interface->GetBleConnRole(bd_addr)); } +uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr) { + return l2cap_interface->GetBleConnInterval(bd_addr); +} + std::vector<uint16_t> L2CA_ConnectCreditBasedReq(uint16_t psm, const RawAddress& bd_addr, tL2CAP_LE_CFG_INFO* p_cfg) { return l2cap_interface->ConnectCreditBasedReq(psm, bd_addr, p_cfg); diff --git a/system/stack/test/common/mock_l2cap_layer.h b/system/stack/test/common/mock_l2cap_layer.h index 6c4f3ed4e7..1b069fce14 100644 --- a/system/stack/test/common/mock_l2cap_layer.h +++ b/system/stack/test/common/mock_l2cap_layer.h @@ -71,6 +71,7 @@ public: uint16_t(uint16_t psm, const tL2CAP_APPL_INFO& cb_info, uint16_t sec_level)); MOCK_METHOD1(DeregisterLECoc, void(uint16_t psm)); MOCK_METHOD1(GetBleConnRole, uint8_t(const RawAddress& bd_addr)); + MOCK_METHOD1(GetBleConnInterval, uint16_t(const RawAddress& bd_addr)); MOCK_METHOD5(ConnectCreditBasedRsp, bool(const RawAddress& p_bd_addr, uint8_t id, std::vector<uint16_t>& lcids, tL2CAP_LE_RESULT_CODE result, tL2CAP_LE_CFG_INFO* p_cfg)); diff --git a/system/stack/test/common/mock_stack_avdt_msg.cc b/system/stack/test/common/mock_stack_avdt_msg.cc index 22a5afce90..7accae9435 100644 --- a/system/stack/test/common/mock_stack_avdt_msg.cc +++ b/system/stack/test/common/mock_stack_avdt_msg.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "mock_stack_avdt_msg.h" + #include <vector> #include "stack/avdt/avdt_int.h" @@ -25,9 +27,6 @@ * Need to consider more sophisticated existing methods. */ -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - static std::vector<uint8_t> _rsp_sig_ids{}; void avdt_msg_send_rsp(AvdtpCcb* /*p_ccb*/, uint8_t sig_id, tAVDT_MSG* /*p_params*/) { diff --git a/system/stack/test/connection_manager_test.cc b/system/stack/test/connection_manager_test.cc index cbb7b3ff35..8c99963279 100644 --- a/system/stack/test/connection_manager_test.cc +++ b/system/stack/test/connection_manager_test.cc @@ -9,13 +9,19 @@ #include <memory> +#include "gd/hci/controller_interface_mock.h" +#include "main/shim/acl_api.h" +#include "main/shim/entry.h" +#include "main/shim/le_scanning_manager.h" #include "osi/include/alarm.h" #include "osi/test/alarm_mock.h" #include "security_device_record.h" +#include "stack/btm/btm_dev.h" +#include "stack/btm/internal/btm_api.h" #include "stack/btm/neighbor_inquiry.h" - -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#include "stack/include/btm_ble_api.h" +#include "stack/include/btm_log_history.h" +#include "stack/l2cap/internal/l2c_api.h" using testing::_; using testing::DoAll; @@ -69,6 +75,13 @@ void ACL_IgnoreLeConnectionFrom(const tBLE_BD_ADDR& address) { void ACL_IgnoreAllLeConnections() { return localAcceptlistMock->AcceptlistClear(); } +testing::NiceMock<bluetooth::hci::testing::MockControllerInterface> controller; + +hci::ControllerInterface* GetController() { + ON_CALL(controller, GetLeFilterAcceptListSize).WillByDefault(Return(128)); + return &controller; +} + } // namespace shim } // namespace bluetooth @@ -86,7 +99,7 @@ void set_target_announcements_filter(bool /*enable*/) {} } // namespace bluetooth bool L2CA_ConnectFixedChnl(uint16_t /*fixed_cid*/, const RawAddress& /*bd_addr*/) { return false; } -uint16_t BTM_GetHCIConnHandle(RawAddress const&, unsigned char) { return 0xFFFF; } +uint16_t BTM_GetHCIConnHandle(RawAddress const&, tBT_TRANSPORT) { return 0xFFFF; } namespace connection_manager { class BleConnectionManager : public testing::Test { @@ -258,9 +271,10 @@ TEST_F(BleConnectionManager, test_app_unregister) { */ EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address1, true)).WillOnce(Return(true)); - EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address2, false)).WillOnce(Return(true)); EXPECT_TRUE(direct_connect_add(CLIENT1, address1)); + EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address2, false)).WillOnce(Return(true)); EXPECT_TRUE(background_connect_add(CLIENT1, address2)); + EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address2, true)).WillOnce(Return(true)); EXPECT_TRUE(direct_connect_add(CLIENT2, address2)); Mock::VerifyAndClearExpectations(localAcceptlistMock.get()); @@ -270,6 +284,8 @@ TEST_F(BleConnectionManager, test_app_unregister) { EXPECT_CALL(*localAcceptlistMock, AcceptlistRemove(address2)).Times(1); on_app_deregistered(CLIENT2); + + Mock::VerifyAndClearExpectations(localAcceptlistMock.get()); } /** Verify adding device to both direct connection and background connection. */ diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc index 437a2f13ef..31ceba5060 100644 --- a/system/stack/test/eatt/eatt_test.cc +++ b/system/stack/test/eatt/eatt_test.cc @@ -27,7 +27,6 @@ #include "stack/include/bt_hdr.h" #include "stack/include/bt_psm_types.h" #include "stack/include/l2cdefs.h" -#include "stack/test/common/mock_btif_storage.h" #include "stack/test/common/mock_btm_api_layer.h" #include "stack/test/common/mock_eatt.h" #include "stack/test/common/mock_gatt_layer.h" @@ -36,9 +35,6 @@ #include "test/mock/mock_stack_l2cap_interface.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using testing::_; using testing::DoAll; using testing::MockFunction; @@ -59,7 +55,6 @@ extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_; /* Needed for testing context */ static tGATT_TCB test_tcb; -void btif_storage_add_eatt_supported(const RawAddress& /*addr*/) { return; } void gatt_consolidate(const RawAddress& /*identity_addr*/, const RawAddress& /*rpa*/) {} void gatt_data_process(tGATT_TCB& /*tcb*/, uint16_t /*cid*/, BT_HDR* /*p_buf*/) { return; } tGATT_TCB* gatt_find_tcb_by_addr(const RawAddress& /*bda*/, tBT_TRANSPORT /*transport*/) { @@ -227,7 +222,6 @@ protected: EXPECT_CALL(controller_, GetLeBufferSize).WillRepeatedly(Return(le_buffer_size_)); bluetooth::l2cap::SetMockInterface(&l2cap_interface_); bluetooth::manager::SetMockBtmApiInterface(&btm_api_interface_); - bluetooth::manager::SetMockBtifStorageInterface(&btif_storage_interface_); bluetooth::gatt::SetMockGattInterface(&gatt_interface_); bluetooth::hci::testing::mock_controller_ = &controller_; @@ -237,8 +231,6 @@ protected: EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_RegisterLECoc(BT_PSM_EATT, _, _, _)) .WillOnce(DoAll(SaveArg<1>(&l2cap_app_info_), ::testing::ReturnArg<0>())); - ON_CALL(btif_storage_interface_, LoadBondedEatt).WillByDefault([]() { return; }); - hci_role_ = HCI_ROLE_CENTRAL; EXPECT_CALL(l2cap_interface_, LeCreditDefault()).WillRepeatedly(DoAll(Return(0xfff))); @@ -265,7 +257,6 @@ protected: bluetooth::gatt::SetMockGattInterface(nullptr); bluetooth::l2cap::SetMockInterface(nullptr); bluetooth::testing::stack::l2cap::reset_interface(); - bluetooth::manager::SetMockBtifStorageInterface(nullptr); bluetooth::manager::SetMockBtmApiInterface(nullptr); bluetooth::hci::testing::mock_controller_ = nullptr; @@ -274,7 +265,6 @@ protected: tL2CAP_APPL_INFO reg_info_; - bluetooth::manager::MockBtifStorageInterface btif_storage_interface_; bluetooth::manager::MockBtmApiInterface btm_api_interface_; bluetooth::l2cap::MockL2capInterface l2cap_interface_; bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_; diff --git a/system/stack/test/fuzzers/Android.bp b/system/stack/test/fuzzers/Android.bp index af2ad8623c..30ad5a5092 100644 --- a/system/stack/test/fuzzers/Android.bp +++ b/system/stack/test/fuzzers/Android.bp @@ -38,7 +38,6 @@ cc_defaults { "android.system.suspend.control-V1-ndk", "bluetooth_flags_c_lib", "libFraunhoferAAC", - "libbluetooth-dumpsys", "libbluetooth-types", "libbluetooth_core_rs", "libbluetooth_crypto_toolbox", diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc index fcd758640f..a7b9082824 100644 --- a/system/stack/test/gatt/mock_gatt_utils_ref.cc +++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc @@ -14,52 +14,45 @@ * limitations under the License. */ +#include "stack/connection_manager/connection_manager.h" #include "stack/gatt/gatt_int.h" #include "stack/include/bt_hdr.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" -#pragma GCC diagnostic ignored "-Wunused-parameter" - -/** stack/connection_manager/connection_manager.cc */ -namespace connection_manager { -bool background_connect_remove(uint8_t app_id, const RawAddress& address) { return false; } -bool direct_connect_remove(uint8_t app_id, const RawAddress& address, bool connection_timeout) { - return false; -} -bool is_background_connection(const RawAddress& address) { return false; } -} // namespace connection_manager - /** stack/gatt/att_protocol.cc */ -BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg, - uint16_t payload_size) { +BT_HDR* attp_build_sr_msg(tGATT_TCB& /*tcb*/, uint8_t /*op_code*/, tGATT_SR_MSG* /*p_msg*/, + uint16_t /*payload_size*/) { return nullptr; } -tGATT_STATUS attp_send_cl_confirmation_msg(tGATT_TCB& tcb, uint16_t cid) { return GATT_SUCCESS; } -tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t op_code, - tGATT_CL_MSG* p_msg) { +tGATT_STATUS attp_send_cl_confirmation_msg(tGATT_TCB& /*tcb*/, uint16_t /*cid*/) { + return GATT_SUCCESS; +} +tGATT_STATUS attp_send_cl_msg(tGATT_TCB& /*tcb*/, tGATT_CLCB* /*p_clcb*/, uint8_t /*op_code*/, + tGATT_CL_MSG* /*p_msg*/) { + return GATT_SUCCESS; +} +tGATT_STATUS attp_send_sr_msg(tGATT_TCB& /*tcb*/, uint16_t /*cid*/, BT_HDR* /*p_msg*/) { return GATT_SUCCESS; } -tGATT_STATUS attp_send_sr_msg(tGATT_TCB& tcb, uint16_t cid, BT_HDR* p_msg) { return GATT_SUCCESS; } /** stack/gatt/gatt_attr.cc */ -void gatt_sr_init_cl_status(tGATT_TCB& tcb) {} +void gatt_sr_init_cl_status(tGATT_TCB& /*tcb*/) {} /** stack/gatt/gatt_cl.cc */ -void gatt_act_discovery(tGATT_CLCB* p_clcb) {} +void gatt_act_discovery(tGATT_CLCB* /*p_clcb*/) {} /** stack/gatt/gatt_main.cc */ -void gatt_update_app_use_link_flag(tGATT_IF gatt_if, tGATT_TCB* p_tcb, bool is_add, - bool check_acl_link) {} +void gatt_update_app_use_link_flag(tGATT_IF /*gatt_if*/, tGATT_TCB* /*p_tcb*/, bool /*is_add*/, + bool /*check_acl_link*/) {} void gatts_proc_srv_chg_ind_ack(tGATT_TCB) {} -bool gatt_disconnect(tGATT_TCB* p_tcb) { return false; } -void gatt_cancel_connect(const RawAddress& bd_addr, tBT_TRANSPORT transport) {} -tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB* p_tcb) { return GATT_CH_CLOSE; } -void gatt_set_ch_state(tGATT_TCB* p_tcb, tGATT_CH_STATE ch_state) {} +bool gatt_disconnect(tGATT_TCB* /*p_tcb*/) { return false; } +void gatt_cancel_connect(const RawAddress& /*bd_addr*/, tBT_TRANSPORT /*transport*/) {} +tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB* /*p_tcb*/) { return GATT_CH_CLOSE; } +void gatt_set_ch_state(tGATT_TCB* /*p_tcb*/, tGATT_CH_STATE /*ch_state*/) {} /** stack/gatt/gatt_sr.cc */ -uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t handle) { +uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& /*tcb*/, uint16_t /*cid*/, uint8_t /*op_code*/, + uint16_t /*handle*/) { return 0x0000; } -void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) {} +void gatt_dequeue_sr_cmd(tGATT_TCB& /*tcb*/, uint16_t /*cid*/) {} diff --git a/system/stack/test/gatt/stack_gatt_test.cc b/system/stack/test/gatt/stack_gatt_test.cc index 96f248af2e..71928440a4 100644 --- a/system/stack/test/gatt/stack_gatt_test.cc +++ b/system/stack/test/gatt/stack_gatt_test.cc @@ -217,7 +217,7 @@ TEST_F(StackGattTest, gatt_status_text) { ASSERT_STREQ(status.second.c_str(), gatt_status_text(status.first).c_str()); } // Typical max value is already classified so use arbitrary unused one. - auto unknown = base::StringPrintf("UNKNOWN[%hhu]", 0xfc); + auto unknown = std::format("UNKNOWN[{}]", 0xfc); ASSERT_STREQ(unknown.c_str(), gatt_status_text(static_cast<tGATT_STATUS>(0xfc)).c_str()); } diff --git a/system/stack/test/hci/stack_hci_test.cc b/system/stack/test/hci/stack_hci_test.cc index 93d1de3f16..c489f40d95 100644 --- a/system/stack/test/hci/stack_hci_test.cc +++ b/system/stack/test/hci/stack_hci_test.cc @@ -172,7 +172,7 @@ TEST_F(StackHciTest, hci_error_code_text) { for (const auto& error : errors) { ASSERT_STREQ(error.second.c_str(), hci_error_code_text(error.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[0x%02hx]", std::numeric_limits<std::uint8_t>::max()); + auto unknown = std::format("UNKNOWN[0x{:02x}]", std::numeric_limits<std::uint8_t>::max()); ASSERT_STREQ(unknown.c_str(), hci_error_code_text( static_cast<tHCI_ERROR_CODE>(std::numeric_limits<std::uint8_t>::max())) diff --git a/system/stack/test/hid/stack_hid_test.cc b/system/stack/test/hid/stack_hid_test.cc index 7fc527ac2b..641d75101e 100644 --- a/system/stack/test/hid/stack_hid_test.cc +++ b/system/stack/test/hid/stack_hid_test.cc @@ -19,13 +19,12 @@ #include "common/message_loop_thread.h" #include "stack/hid/hidh_int.h" +#include "stack/include/acl_api.h" #include "stack/include/hci_error_code.h" +#include "stack/include/main_thread.h" #include "test/common/mock_functions.h" #include "test/mock/mock_stack_l2cap_interface.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; } tHCI_REASON btm_get_acl_disc_reason_code(void) { return HCI_SUCCESS; } diff --git a/system/stack/test/rfcomm/stack_rfcomm_test.cc b/system/stack/test/rfcomm/stack_rfcomm_test.cc index c15bd8fb55..a39597c0b2 100644 --- a/system/stack/test/rfcomm/stack_rfcomm_test.cc +++ b/system/stack/test/rfcomm/stack_rfcomm_test.cc @@ -34,12 +34,9 @@ #include "stack/test/rfcomm/stack_rfcomm_test_utils.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; -std::string DumpByteBufferToString(uint8_t* p_data, size_t len) { +static std::string DumpByteBufferToString(uint8_t* p_data, size_t len) { std::stringstream str; str.setf(std::ios_base::hex, std::ios::basefield); str.setf(std::ios_base::uppercase); @@ -51,14 +48,14 @@ std::string DumpByteBufferToString(uint8_t* p_data, size_t len) { return str.str(); } -std::string DumpBtHdrToString(BT_HDR* p_hdr) { +static std::string DumpBtHdrToString(BT_HDR* p_hdr) { uint8_t* p_hdr_data = p_hdr->data + p_hdr->offset; return DumpByteBufferToString(p_hdr_data, p_hdr->len); } -void PrintTo(BT_HDR* value, ::std::ostream* os) { *os << DumpBtHdrToString(value); } +static void PrintTo(BT_HDR* value, ::std::ostream* os) { *os << DumpBtHdrToString(value); } -void PrintTo(tL2CAP_CFG_INFO* value, ::std::ostream* os) { +static void PrintTo(tL2CAP_CFG_INFO* value, ::std::ostream* os) { *os << DumpByteBufferToString((uint8_t*)value, sizeof(tL2CAP_CFG_INFO)); } @@ -136,7 +133,8 @@ public: uint16_t* server_handle) { log::verbose("Step 1"); ASSERT_EQ(RFCOMM_CreateConnectionWithSecurity(uuid, scn, true, mtu, RawAddress::kAny, - server_handle, management_callback, 0), + server_handle, management_callback, 0, + RfcommCfgInfo{}), PORT_SUCCESS); ASSERT_EQ(PORT_SetEventMaskAndCallback(*server_handle, PORT_EV_RXCHAR, event_callback), PORT_SUCCESS); @@ -261,9 +259,10 @@ public: EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(uih_pn_channel_3))) .WillOnce(Return(tL2CAP_DW_RESULT::SUCCESS)); } - ASSERT_EQ(RFCOMM_CreateConnectionWithSecurity(uuid, scn, false, mtu, peer_bd_addr, - client_handle, management_callback, 0), - PORT_SUCCESS); + ASSERT_EQ( + RFCOMM_CreateConnectionWithSecurity(uuid, scn, false, mtu, peer_bd_addr, client_handle, + management_callback, 0, RfcommCfgInfo{}), + PORT_SUCCESS); ASSERT_EQ(PORT_SetEventMaskAndCallback(*client_handle, PORT_EV_RXCHAR, event_callback), PORT_SUCCESS); osi_free(uih_pn_channel_3); @@ -644,7 +643,7 @@ TEST_F(StackRfcommTest, DISABLED_TestConnectionCollision) { // Prepare a server port int status = RFCOMM_CreateConnectionWithSecurity(test_uuid, test_server_scn, true, test_mtu, RawAddress::kAny, &server_handle, - port_mgmt_cback_0, 0); + port_mgmt_cback_0, 0, RfcommCfgInfo{}); ASSERT_EQ(status, PORT_SUCCESS); status = PORT_SetEventMaskAndCallback(server_handle, PORT_EV_RXCHAR, port_event_cback_0); ASSERT_EQ(status, PORT_SUCCESS); @@ -655,9 +654,9 @@ TEST_F(StackRfcommTest, DISABLED_TestConnectionCollision) { EXPECT_CALL(l2cap_interface_, ConnectRequest(BT_PSM_RFCOMM, test_address)) .Times(1) .WillOnce(Return(old_lcid)); - status = - RFCOMM_CreateConnectionWithSecurity(test_uuid, test_peer_scn, false, test_mtu, - test_address, &client_handle_1, port_mgmt_cback_1, 0); + status = RFCOMM_CreateConnectionWithSecurity(test_uuid, test_peer_scn, false, test_mtu, + test_address, &client_handle_1, port_mgmt_cback_1, 0, + RfcommCfgInfo{}); ASSERT_EQ(status, PORT_SUCCESS); status = PORT_SetEventMaskAndCallback(client_handle_1, PORT_EV_RXCHAR, port_event_cback_1); ASSERT_EQ(status, PORT_SUCCESS); diff --git a/system/stack/test/rfcomm/stack_rfcomm_test_utils.cc b/system/stack/test/rfcomm/stack_rfcomm_test_utils.cc index f11639bc0f..41ff6f7569 100644 --- a/system/stack/test/rfcomm/stack_rfcomm_test_utils.cc +++ b/system/stack/test/rfcomm/stack_rfcomm_test_utils.cc @@ -25,9 +25,6 @@ #include "stack/rfcomm/rfc_int.h" #include "stack_test_packet_utils.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - namespace bluetooth { namespace rfcomm { @@ -57,7 +54,7 @@ uint8_t GetControlField(bool pf, uint8_t frame_type) { return static_cast<uint8_t>(control.to_ulong()); } -uint8_t GetFrameTypeFromControlField(uint8_t control_field) { +static uint8_t GetFrameTypeFromControlField(uint8_t control_field) { return static_cast<uint8_t>(control_field & ~(0b10000)); } diff --git a/system/stack/test/sdp/stack_sdp_test.cc b/system/stack/test/sdp/stack_sdp_test.cc index a60f808b4b..314ea02334 100644 --- a/system/stack/test/sdp/stack_sdp_test.cc +++ b/system/stack/test/sdp/stack_sdp_test.cc @@ -36,9 +36,6 @@ #define BT_DEFAULT_BUFFER_SIZE (4096 + 16) #endif -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using ::testing::_; using ::testing::DoAll; using ::testing::Invoke; @@ -130,7 +127,7 @@ TEST_F(StackSdpInitTest, sdp_service_search_request) { ASSERT_EQ(p_ccb->con_state, tSDP_STATE::IDLE); } -tCONN_CB* find_ccb(uint16_t cid, tSDP_STATE state) { +static tCONN_CB* find_ccb(uint16_t cid, tSDP_STATE state) { uint16_t xx; tCONN_CB* p_ccb; @@ -188,7 +185,7 @@ TEST_F(StackSdpInitTest, sdp_service_search_request_queuing) { ASSERT_EQ(p_ccb2->con_state, tSDP_STATE::IDLE); } -void sdp_callback(const RawAddress& /* bd_addr */, tSDP_RESULT result) { +static void sdp_callback(const RawAddress& /* bd_addr */, tSDP_RESULT result) { if (result == tSDP_STATUS::SDP_SUCCESS) { ASSERT_TRUE(SDP_ServiceSearchRequest(addr, sdp_db, nullptr)); } @@ -243,7 +240,7 @@ TEST_F(StackSdpInitTest, sdp_disc_wait_text) { for (const auto& state : states) { ASSERT_STREQ(state.second.c_str(), sdp_disc_wait_text(state.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%d]", std::numeric_limits<uint8_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max()); ASSERT_STREQ(unknown.c_str(), sdp_disc_wait_text(static_cast<tSDP_DISC_WAIT>(std::numeric_limits<uint8_t>::max())) .c_str()); @@ -277,7 +274,7 @@ TEST_F(StackSdpInitTest, sdp_flags_text) { for (const auto& flag : flags) { ASSERT_STREQ(flag.second.c_str(), sdp_flags_text(flag.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%hhu]", std::numeric_limits<uint8_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max()); ASSERT_STREQ( unknown.c_str(), sdp_flags_text(static_cast<tSDP_DISC_WAIT>(std::numeric_limits<uint8_t>::max())).c_str()); @@ -315,7 +312,7 @@ TEST_F(StackSdpInitTest, sdp_status_text) { for (const auto& stat : status) { ASSERT_STREQ(stat.second.c_str(), sdp_status_text(stat.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%hu]", std::numeric_limits<uint16_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<uint16_t>::max()); ASSERT_STREQ( unknown.c_str(), sdp_status_text(static_cast<tSDP_STATUS>(std::numeric_limits<uint16_t>::max())).c_str()); diff --git a/system/stack/test/sdp/stack_sdp_utils_test.cc b/system/stack/test/sdp/stack_sdp_utils_test.cc index 5a6b1fc9a1..e6a9ea9007 100644 --- a/system/stack/test/sdp/stack_sdp_utils_test.cc +++ b/system/stack/test/sdp/stack_sdp_utils_test.cc @@ -22,6 +22,7 @@ #include "btif/include/btif_storage.h" #include "btif/include/stack_manager_t.h" #include "device/include/interop.h" +#include "device/include/interop_config.h" #include "mock_btif_config.h" #include "osi/include/allocator.h" #include "profile/avrcp/avrcp_config.h" @@ -49,9 +50,6 @@ #define HFP_PROFILE_MINOR_VERSION_6 0x06 #define HFP_PROFILE_MINOR_VERSION_7 0x07 -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - static int L2CA_ConnectReqWithSecurity_cid = 0x42; static RawAddress addr = RawAddress({0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6}); static tSDP_DISCOVERY_DB* sdp_db = nullptr; @@ -64,9 +62,6 @@ using ::testing::ReturnArg; using ::testing::SaveArg; using ::testing::SetArrayArgument; -void sdp_callback(const RawAddress& bd_addr, tSDP_RESULT result); -tCONN_CB* find_ccb(uint16_t cid, uint8_t state); - namespace { // convenience mock class IopMock { @@ -178,32 +173,32 @@ tSDP_ATTRIBUTE hfp_attr = { .type = 0, }; -void set_hfp_attr(uint32_t len, uint16_t id, uint16_t uuid) { +static void set_hfp_attr(uint32_t len, uint16_t id, uint16_t uuid) { hfp_attr.value_ptr[4] = uuid; hfp_attr.len = len; hfp_attr.id = id; } -void set_avrcp_feat_attr(uint32_t len, uint16_t id, uint16_t feature) { +static void set_avrcp_feat_attr(uint32_t len, uint16_t id, uint16_t feature) { UINT16_TO_BE_FIELD(avrc_feat_value, feature); avrcp_feat_attr.len = len; avrcp_feat_attr.id = id; } -void set_avrcp_attr(uint32_t len, uint16_t id, uint16_t uuid, uint16_t version) { +static void set_avrcp_attr(uint32_t len, uint16_t id, uint16_t uuid, uint16_t version) { UINT16_TO_BE_FIELD(avrc_value + 3, uuid); UINT16_TO_BE_FIELD(avrc_value + 6, version); avrcp_attr.len = len; avrcp_attr.id = id; } -uint16_t get_avrc_target_version(tSDP_ATTRIBUTE* p_attr) { +static uint16_t get_avrc_target_version(tSDP_ATTRIBUTE* p_attr) { uint8_t* p_version = p_attr->value_ptr + 6; uint16_t version = (((uint16_t)(*(p_version))) << 8) + ((uint16_t)(*((p_version) + 1))); return version; } -uint16_t get_avrc_target_feature(tSDP_ATTRIBUTE* p_attr) { +static uint16_t get_avrc_target_feature(tSDP_ATTRIBUTE* p_attr) { uint8_t* p_feature = p_attr->value_ptr; uint16_t feature = (((uint16_t)(*(p_feature))) << 8) + ((uint16_t)(*((p_feature) + 1))); return feature; diff --git a/system/stack/test/stack_avdtp_test.cc b/system/stack/test/stack_avdtp_test.cc index 8c9372ec93..676718a22a 100644 --- a/system/stack/test/stack_avdtp_test.cc +++ b/system/stack/test/stack_avdtp_test.cc @@ -24,10 +24,13 @@ #include "osi/include/allocator.h" #include "stack/avdt/avdt_int.h" #include "stack/include/avdt_api.h" +#include "stack/include/main_thread.h" #include "stack/test/common/mock_stack_avdt_msg.h" #include "test/common/mock_functions.h" #include "types/raw_address.h" +bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; } + class StackAvdtpTest : public ::testing::Test { protected: StackAvdtpTest() = default; diff --git a/system/stack/test/stack_rnr_test.cc b/system/stack/test/stack_rnr_test.cc index e2b0b3cb73..b2c4523e6c 100644 --- a/system/stack/test/stack_rnr_test.cc +++ b/system/stack/test/stack_rnr_test.cc @@ -27,13 +27,6 @@ #include "test/mock/mock_osi_thread.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - -extern tBTM_CB btm_cb; - -bool ble_evt_type_is_connectable(uint16_t /* evt_type */) { return true; } - tBTM_CB btm_cb; namespace { diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc index c7de45814c..41d61e738b 100644 --- a/system/stack/test/stack_smp_test.cc +++ b/system/stack/test/stack_smp_test.cc @@ -35,39 +35,36 @@ #include "types/hci_role.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using testing::StrEq; tBTM_CB btm_cb; const std::string kSmpOptions("mock smp options"); const std::string kBroadcastAudioConfigOptions("mock broadcast audio config options"); -bool get_pts_avrcp_test(void) { return false; } -bool get_pts_secure_only_mode(void) { return false; } -bool get_pts_conn_updates_disabled(void) { return false; } -bool get_pts_crosskey_sdp_disable(void) { return false; } -const std::string* get_pts_smp_options(void) { return &kSmpOptions; } -int get_pts_smp_failure_case(void) { return 123; } -bool get_pts_force_eatt_for_notifications(void) { return false; } -bool get_pts_connect_eatt_unconditionally(void) { return false; } -bool get_pts_connect_eatt_before_encryption(void) { return false; } -bool get_pts_unencrypt_broadcast(void) { return false; } -bool get_pts_eatt_peripheral_collision_support(void) { return false; } -bool get_pts_use_eatt_for_all_services(void) { return false; } -bool get_pts_force_le_audio_multiple_contexts_metadata(void) { return false; } -bool get_pts_l2cap_ecoc_upper_tester(void) { return false; } -int get_pts_l2cap_ecoc_min_key_size(void) { return -1; } -int get_pts_l2cap_ecoc_initial_chan_cnt(void) { return -1; } -bool get_pts_l2cap_ecoc_connect_remaining(void) { return false; } -int get_pts_l2cap_ecoc_send_num_of_sdu(void) { return -1; } -bool get_pts_l2cap_ecoc_reconfigure(void) { return false; } -const std::string* get_pts_broadcast_audio_config_options(void) { +static bool get_pts_avrcp_test(void) { return false; } +static bool get_pts_secure_only_mode(void) { return false; } +static bool get_pts_conn_updates_disabled(void) { return false; } +static bool get_pts_crosskey_sdp_disable(void) { return false; } +static const std::string* get_pts_smp_options(void) { return &kSmpOptions; } +static int get_pts_smp_failure_case(void) { return 123; } +static bool get_pts_force_eatt_for_notifications(void) { return false; } +static bool get_pts_connect_eatt_unconditionally(void) { return false; } +static bool get_pts_connect_eatt_before_encryption(void) { return false; } +static bool get_pts_unencrypt_broadcast(void) { return false; } +static bool get_pts_eatt_peripheral_collision_support(void) { return false; } +static bool get_pts_use_eatt_for_all_services(void) { return false; } +static bool get_pts_force_le_audio_multiple_contexts_metadata(void) { return false; } +static bool get_pts_l2cap_ecoc_upper_tester(void) { return false; } +static int get_pts_l2cap_ecoc_min_key_size(void) { return -1; } +static int get_pts_l2cap_ecoc_initial_chan_cnt(void) { return -1; } +static bool get_pts_l2cap_ecoc_connect_remaining(void) { return false; } +static int get_pts_l2cap_ecoc_send_num_of_sdu(void) { return -1; } +static bool get_pts_l2cap_ecoc_reconfigure(void) { return false; } +static const std::string* get_pts_broadcast_audio_config_options(void) { return &kBroadcastAudioConfigOptions; } -bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } -config_t* get_all(void) { return nullptr; } +static bool get_pts_le_audio_disable_ases_before_stopping(void) { return false; } +static config_t* get_all(void) { return nullptr; } const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; } stack_config_t mock_stack_config{ @@ -127,21 +124,7 @@ const stack_config_t* stack_config_get_interface(void) { return &mock_stack_conf * MSB on the right. */ -Octet16 smp_gen_p1_4_confirm(tSMP_CB* p_cb, tBLE_ADDR_TYPE remote_bd_addr_type); - -Octet16 smp_gen_p2_4_confirm(tSMP_CB* p_cb, const RawAddress& remote_bda); - -tSMP_STATUS smp_calculate_confirm(tSMP_CB* p_cb, const Octet16& rand, Octet16* output); - -void dump_uint128(const Octet16& a, char* buffer) { - for (unsigned int i = 0; i < OCTET16_LEN; ++i) { - snprintf(buffer, 3, "%02x", a[i]); - buffer += 2; - } - *buffer = '\0'; -} - -void dump_uint128_reverse(const Octet16& a, char* buffer) { +static void dump_uint128_reverse(const Octet16& a, char* buffer) { for (int i = (int)(OCTET16_LEN - 1); i >= 0; --i) { snprintf(buffer, 3, "%02x", a[i]); buffer += 2; @@ -149,22 +132,6 @@ void dump_uint128_reverse(const Octet16& a, char* buffer) { *buffer = '\0'; } -void print_uint128(const Octet16& a) { - for (unsigned int i = 0; i < OCTET16_LEN; ++i) { - printf("%02x", a[i]); - } - printf("\n"); -} - -Octet16 parse_uint128(const char* input) { - Octet16 output{0}; - for (unsigned int count = 0; count < OCTET16_LEN; count++) { - sscanf(input, "%2hhx", &output[count]); - input += 2; - } - return output; -} - class SmpCalculateConfirmTest : public testing::Test { protected: tSMP_CB p_cb_; @@ -422,7 +389,7 @@ TEST(SmpStatusText, smp_status_text) { for (const auto& stat : status) { ASSERT_STREQ(stat.second.c_str(), smp_status_text(stat.first).c_str()); } - auto unknown = base::StringPrintf("UNKNOWN[%hhu]", std::numeric_limits<uint8_t>::max()); + auto unknown = std::format("UNKNOWN[{}]", std::numeric_limits<uint8_t>::max()); ASSERT_STREQ( unknown.c_str(), smp_status_text(static_cast<tSMP_STATUS>(std::numeric_limits<uint8_t>::max())).c_str()); diff --git a/system/test/Android.bp b/system/test/Android.bp index d4a1a767eb..c5a3b0c2d6 100644 --- a/system/test/Android.bp +++ b/system/test/Android.bp @@ -628,7 +628,10 @@ cc_defaults { target: { android: { test_config_template: ":BluetoothTestConfigTemplate", - test_suites: ["mts-bluetooth"], + test_suites: [ + "mts-bluetooth", + "mts-bt", + ], }, }, compile_multilib: "both", diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp index ec61ea012f..09a08f8fec 100644 --- a/system/test/headless/Android.bp +++ b/system/test/headless/Android.bp @@ -82,7 +82,6 @@ cc_binary { "lib-bt-packets-base", "libFraunhoferAAC", "libbase", - "libbluetooth-dumpsys", "libbluetooth-types", "libbluetooth_core_rs", "libbluetooth_crypto_toolbox", diff --git a/system/test/headless/bt_stack_info.cc b/system/test/headless/bt_stack_info.cc index 300dbf62b7..f711dbf312 100644 --- a/system/test/headless/bt_stack_info.cc +++ b/system/test/headless/bt_stack_info.cc @@ -49,11 +49,10 @@ void BtStackInfo::DumpsysLite() { LOG_CONSOLE("jni_pid:%u", jni_pid_); int fd = STDIN_FILENO; - const char** arguments = nullptr; connection_manager::dump(fd); PAN_Dumpsys(fd); DumpsysHid(fd); DumpsysBtaDm(fd); - bluetooth::shim::Dump(fd, arguments); + bluetooth::shim::Dump(fd); } diff --git a/system/test/mock/mock_bta_jv_api.cc b/system/test/mock/mock_bta_jv_api.cc index cf7fd4645e..ec913b5b01 100644 --- a/system/test/mock/mock_bta_jv_api.cc +++ b/system/test/mock/mock_bta_jv_api.cc @@ -76,14 +76,14 @@ tBTA_JV_STATUS BTA_JvRfcommClose(uint32_t /* handle */, uint32_t /* rfcomm_slot_ tBTA_JV_STATUS BTA_JvRfcommConnect(tBTA_SEC /* sec_mask */, uint8_t /* remote_scn */, const RawAddress& /* peer_bd_addr */, tBTA_JV_RFCOMM_CBACK* /* p_cback */, - uint32_t /* rfcomm_slot_id */) { + uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */) { inc_func_call_count(__func__); return tBTA_JV_STATUS::SUCCESS; } tBTA_JV_STATUS BTA_JvRfcommStartServer(tBTA_SEC /* sec_mask */, uint8_t /* local_scn */, uint8_t /* max_session */, tBTA_JV_RFCOMM_CBACK* /* p_cback */, - uint32_t /* rfcomm_slot_id */) { + uint32_t /* rfcomm_slot_id */, RfcommCfgInfo /* cfg */) { inc_func_call_count(__func__); return tBTA_JV_STATUS::SUCCESS; } diff --git a/system/test/mock/mock_btif_sock_l2cap.cc b/system/test/mock/mock_btif_sock_l2cap.cc new file mode 100644 index 0000000000..41f19f8cd0 --- /dev/null +++ b/system/test/mock/mock_btif_sock_l2cap.cc @@ -0,0 +1,66 @@ +/* + * 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. + */ +/* + * Generated mock file from original source file + * Functions generated:13 + * + * mockcify.pl ver 0.7.1 + */ + +// Mock include file to share data between tests and mock +#include "test/mock/mock_btif_sock_l2cap.h" + +#include <cstdint> + +#include "test/common/mock_functions.h" + +// TODO(b/369381361) Enfore -Wmissing-prototypes +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +// Original usings + +// Mocked internal structures, if any + +namespace test { +namespace mock { +namespace btif_sock_l2cap { + +// Function state capture and return values, if needed +struct on_btsocket_l2cap_close on_btsocket_l2cap_close; +struct on_btsocket_l2cap_opened_complete on_btsocket_l2cap_opened_complete; + +} // namespace btif_sock_l2cap +} // namespace mock +} // namespace test + +// Mocked function return values, if any +namespace test { +namespace mock { +namespace btif_sock_l2cap {} // namespace btif_sock_l2cap +} // namespace mock +} // namespace test + +// Mocked functions, if any +void on_btsocket_l2cap_close(uint64_t socket_id) { + inc_func_call_count(__func__); + test::mock::btif_sock_l2cap::on_btsocket_l2cap_close(socket_id); +} +void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success) { + inc_func_call_count(__func__); + test::mock::btif_sock_l2cap::on_btsocket_l2cap_opened_complete(socket_id, success); +} +// Mocked functions complete +// END mockcify generation diff --git a/system/test/mock/mock_btif_sock_l2cap.h b/system/test/mock/mock_btif_sock_l2cap.h new file mode 100644 index 0000000000..f883f8bf1f --- /dev/null +++ b/system/test/mock/mock_btif_sock_l2cap.h @@ -0,0 +1,63 @@ +/* + * 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. + */ +#pragma once + +/* + * Generated mock file from original source file + * Functions generated:13 + * + * mockcify.pl ver 0.7.1 + */ + +#include <cstdint> +#include <functional> + +// Original included files, if any +#include <cstring> + +// Original usings + +// Mocked compile conditionals, if any + +namespace test { +namespace mock { +namespace btif_sock_l2cap { + +// Shared state between mocked functions and tests +// Name: on_btsocket_l2cap_close +// Params: uint64_t socket_id +// Return: void +struct on_btsocket_l2cap_close { + std::function<void(uint64_t socket_id)> body{[](uint64_t /* socket_id */) {}}; + void operator()(uint64_t socket_id) { body(socket_id); } +}; +extern struct on_btsocket_l2cap_close on_btsocket_l2cap_close; + +// Name: on_btsocket_l2cap_opened_complete +// Params: uint64_t socket_id, bool success +// Return: void +struct on_btsocket_l2cap_opened_complete { + std::function<void(uint64_t socket_id, bool success)> body{ + [](uint64_t /* socket_id */, bool /* success */) {}}; + void operator()(uint64_t socket_id, bool success) { body(socket_id, success); } +}; +extern struct on_btsocket_l2cap_opened_complete on_btsocket_l2cap_opened_complete; + +} // namespace btif_sock_l2cap +} // namespace mock +} // namespace test + +// END mockcify generation diff --git a/system/test/mock/mock_btif_sock_rfc.cc b/system/test/mock/mock_btif_sock_rfc.cc index 49b5b117fa..6ca7512418 100644 --- a/system/test/mock/mock_btif_sock_rfc.cc +++ b/system/test/mock/mock_btif_sock_rfc.cc @@ -49,6 +49,8 @@ struct btsock_rfc_disconnect btsock_rfc_disconnect; struct btsock_rfc_init btsock_rfc_init; struct btsock_rfc_listen btsock_rfc_listen; struct btsock_rfc_signaled btsock_rfc_signaled; +struct on_btsocket_rfc_close on_btsocket_rfc_close; +struct on_btsocket_rfc_opened_complete on_btsocket_rfc_opened_complete; } // namespace btif_sock_rfc } // namespace mock @@ -120,5 +122,13 @@ void btsock_rfc_signaled(int fd, int flags, uint32_t id) { inc_func_call_count(__func__); test::mock::btif_sock_rfc::btsock_rfc_signaled(fd, flags, id); } +void on_btsocket_rfc_close(uint64_t socket_id) { + inc_func_call_count(__func__); + test::mock::btif_sock_rfc::on_btsocket_rfc_close(socket_id); +} +void on_btsocket_rfc_opened_complete(uint64_t socket_id, bool success) { + inc_func_call_count(__func__); + test::mock::btif_sock_rfc::on_btsocket_rfc_opened_complete(socket_id, success); +} // Mocked functions complete // END mockcify generation diff --git a/system/test/mock/mock_btif_sock_rfc.h b/system/test/mock/mock_btif_sock_rfc.h index fa0bfe4373..59f296f016 100644 --- a/system/test/mock/mock_btif_sock_rfc.h +++ b/system/test/mock/mock_btif_sock_rfc.h @@ -173,6 +173,25 @@ struct btsock_rfc_signaled { }; extern struct btsock_rfc_signaled btsock_rfc_signaled; +// Name: on_btsocket_rfc_close +// Params: uint64_t socket_id +// Return: void +struct on_btsocket_rfc_close { + std::function<void(uint64_t socket_id)> body{[](uint64_t /* socket_id */) {}}; + void operator()(uint64_t socket_id) { body(socket_id); } +}; +extern struct on_btsocket_rfc_close on_btsocket_rfc_close; + +// Name: on_btsocket_rfc_opened_complete +// Params: uint64_t socket_id, bool success +// Return: void +struct on_btsocket_rfc_opened_complete { + std::function<void(uint64_t socket_id, bool success)> body{ + [](uint64_t /* socket_id */, bool /* success */) {}}; + void operator()(uint64_t socket_id, bool success) { body(socket_id, success); } +}; +extern struct on_btsocket_rfc_opened_complete on_btsocket_rfc_opened_complete; + } // namespace btif_sock_rfc } // namespace mock } // namespace test diff --git a/system/test/mock/mock_main_shim_acl.cc b/system/test/mock/mock_main_shim_acl.cc index 831be689d6..bd27e6927f 100644 --- a/system/test/mock/mock_main_shim_acl.cc +++ b/system/test/mock/mock_main_shim_acl.cc @@ -47,7 +47,7 @@ void DumpsysNeighbor(int /* fd */) { inc_func_call_count(__func__); } void shim::Acl::Dump(int /* fd */) const { inc_func_call_count(__func__); } shim::Acl::Acl(os::Handler* /* handler */, const acl_interface_t& acl_interface, - uint8_t /* max_acceptlist_size */, uint8_t /* max_address_resolution_size */) + uint8_t /* max_address_resolution_size */) : acl_interface_(acl_interface) { inc_func_call_count(__func__); } diff --git a/system/test/mock/mock_main_shim_dumpsys.cc b/system/test/mock/mock_main_shim_dumpsys.cc index 77b3bfc06a..16e3a83eed 100644 --- a/system/test/mock/mock_main_shim_dumpsys.cc +++ b/system/test/mock/mock_main_shim_dumpsys.cc @@ -20,13 +20,12 @@ */ #include "main/shim/dumpsys.h" -#include "shim/dumpsys.h" #include "test/common/mock_functions.h" void bluetooth::shim::RegisterDumpsysFunction(const void* /* token */, DumpsysFunction /* func */) { inc_func_call_count(__func__); } -void bluetooth::shim::Dump(int /* fd */, const char** /* args */) { inc_func_call_count(__func__); } +void bluetooth::shim::Dump(int /* fd */) { inc_func_call_count(__func__); } void bluetooth::shim::UnregisterDumpsysFunction(const void* /* token */) { inc_func_call_count(__func__); } diff --git a/system/test/mock/mock_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc index 72054dad81..291cb0c0fd 100644 --- a/system/test/mock/mock_main_shim_entry.cc +++ b/system/test/mock/mock_main_shim_entry.cc @@ -20,6 +20,7 @@ #include "hci/hci_interface.h" #include "hci/le_advertising_manager_mock.h" #include "hci/le_scanning_manager_mock.h" +#include "lpp/lpp_offload_interface_mock.h" #include "main/shim/entry.h" #include "os/handler.h" #include "storage/storage_module.h" @@ -30,7 +31,6 @@ namespace testing { MockAclManager* mock_acl_manager_{nullptr}; MockControllerInterface* mock_controller_{nullptr}; -shim::Dumpsys* shim_dumpsys_ = {}; HciInterface* mock_hci_layer_{nullptr}; os::Handler* mock_gd_shim_handler_{nullptr}; MockLeAdvertisingManager* mock_le_advertising_manager_{nullptr}; @@ -40,11 +40,14 @@ MockDistanceMeasurementManager* mock_distance_measurement_manager_{nullptr}; } // namespace testing } // namespace hci +namespace lpp::testing { +MockLppOffloadInterface* mock_lpp_offload_interface_{nullptr}; +} // namespace lpp::testing + class Dumpsys; namespace shim { -Dumpsys* GetDumpsys() { return hci::testing::shim_dumpsys_; } hci::AclManager* GetAclManager() { return hci::testing::mock_acl_manager_; } hci::ControllerInterface* GetController() { return hci::testing::mock_controller_; } hci::HciInterface* GetHciLayer() { return hci::testing::mock_hci_layer_; } @@ -59,6 +62,9 @@ storage::StorageModule* GetStorage() { return nullptr; } metrics::CounterMetrics* GetCounterMetrics() { return nullptr; } hci::MsftExtensionManager* GetMsftExtensionManager() { return nullptr; } hci::RemoteNameRequestModule* GetRemoteNameRequest() { return nullptr; } +lpp::LppOffloadInterface* GetLppOffloadManager() { + return lpp::testing::mock_lpp_offload_interface_; +} } // namespace shim } // namespace bluetooth diff --git a/system/test/mock/mock_main_shim_entry.h b/system/test/mock/mock_main_shim_entry.h index cde3f778f7..d82342e1a6 100644 --- a/system/test/mock/mock_main_shim_entry.h +++ b/system/test/mock/mock_main_shim_entry.h @@ -22,7 +22,6 @@ #include "hci/hci_interface.h" #include "hci/le_advertising_manager_mock.h" #include "hci/le_scanning_manager_mock.h" -#include "shim/dumpsys.h" namespace bluetooth { namespace hci { @@ -30,7 +29,6 @@ namespace testing { extern MockAclManager* mock_acl_manager_; extern MockControllerInterface* mock_controller_; -extern std::function<shim::Dumpsys*()> shim_dumpsys_; extern HciInterface* mock_hci_layer_; extern os::Handler* mock_gd_shim_handler_; extern MockLeAdvertisingManager* mock_le_advertising_manager_; diff --git a/system/test/mock/mock_main_shim_metrics_api.cc b/system/test/mock/mock_main_shim_metrics_api.cc index ffe5a46f15..841cd759a9 100644 --- a/system/test/mock/mock_main_shim_metrics_api.cc +++ b/system/test/mock/mock_main_shim_metrics_api.cc @@ -55,6 +55,9 @@ struct LogMetricClassicPairingEvent LogMetricClassicPairingEvent; struct LogMetricSdpAttribute LogMetricSdpAttribute; struct LogMetricSocketConnectionState LogMetricSocketConnectionState; struct LogMetricManufacturerInfo LogMetricManufacturerInfo; +struct LogMetricLeConnectionStatus LogMetricLeConnectionStatus; +struct LogMetricLeDeviceInAcceptList LogMetricLeDeviceInAcceptList; +struct LogMetricLeConnectionLifecycle LogMetricLeConnectionLifecycle; } // namespace main_shim_metrics_api } // namespace mock @@ -190,4 +193,18 @@ bool bluetooth::shim::CountCounterMetrics(int32_t /* key */, int64_t /* count */ inc_func_call_count(__func__); return false; } +void bluetooth::shim::LogMetricLeConnectionStatus(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason) { + inc_func_call_count(__func__); + test::mock::main_shim_metrics_api::LogMetricLeConnectionStatus(address, is_connect, reason); +} +void bluetooth::shim::LogMetricLeDeviceInAcceptList(bluetooth::hci::Address address, bool is_add) { + inc_func_call_count(__func__); + test::mock::main_shim_metrics_api::LogMetricLeDeviceInAcceptList(address, is_add); +} +void bluetooth::shim::LogMetricLeConnectionLifecycle(bluetooth::hci::Address address, + bool is_connect, bool is_direct) { + inc_func_call_count(__func__); + test::mock::main_shim_metrics_api::LogMetricLeConnectionLifecycle(address, is_connect, is_direct); +} // END mockcify generation diff --git a/system/test/mock/mock_main_shim_metrics_api.h b/system/test/mock/mock_main_shim_metrics_api.h index 401b5a05ef..f5f177b5f4 100644 --- a/system/test/mock/mock_main_shim_metrics_api.h +++ b/system/test/mock/mock_main_shim_metrics_api.h @@ -28,6 +28,8 @@ // Original included files, if any // #include <frameworks/proto_logging/stats/enums/bluetooth/le/enums.pb.h> +#include "hci/address.h" +#include "hci/hci_packets.h" #include "os/metrics.h" #include "types/raw_address.h" @@ -312,6 +314,42 @@ struct LogMetricManufacturerInfo { } }; extern struct LogMetricManufacturerInfo LogMetricManufacturerInfo; +// Name: LogMetricLeConnectionStatus +// Params: bluetooth::hci::Address address, bool is_connect, bluetooth::hci::ErrorCode reason +// Returns: void +struct LogMetricLeConnectionStatus { + std::function<void(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason)> + body{[](bluetooth::hci::Address /* address */, bool /* is_connect */, + bluetooth::hci::ErrorCode /* reason */) {}}; + void operator()(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason) { + body(address, is_connect, reason); + } +}; +extern struct LogMetricLeConnectionStatus LogMetricLeConnectionStatus; +// Name: LogMetricLeDeviceInAcceptList +// Params: bluetooth::hci::Address address, bool is_add +// Returns: void +struct LogMetricLeDeviceInAcceptList { + std::function<void(bluetooth::hci::Address address, bool is_add)> body{ + [](bluetooth::hci::Address /* address */, bool /* is_add */) {}}; + void operator()(bluetooth::hci::Address address, bool is_add) { body(address, is_add); } +}; +extern struct LogMetricLeDeviceInAcceptList LogMetricLeDeviceInAcceptList; + +// Name: LogMetricLeConnectionLifecycle +// Params: bluetooth::hci::Address address, bool is_connect, bool is_direct +// Returns: void +struct LogMetricLeConnectionLifecycle { + std::function<void(bluetooth::hci::Address address, bool is_connect, bool is_direct)> body{ + [](bluetooth::hci::Address /* address */, bool /* is_connect */, bool /* is_direct */) { + }}; + void operator()(bluetooth::hci::Address address, bool is_connect, bool is_direct) { + body(address, is_connect, is_direct); + } +}; +extern struct LogMetricLeConnectionLifecycle LogMetricLeConnectionLifecycle; } // namespace main_shim_metrics_api } // namespace mock diff --git a/system/test/mock/mock_main_shim_stack.cc b/system/test/mock/mock_main_shim_stack.cc index 04543301ee..03ec09a1b3 100644 --- a/system/test/mock/mock_main_shim_stack.cc +++ b/system/test/mock/mock_main_shim_stack.cc @@ -37,17 +37,9 @@ void Stack::Stop() {} bool Stack::IsRunning() { return stack_thread_ != nullptr; } -StackManager* Stack::GetStackManager() { return nullptr; } - -const StackManager* Stack::GetStackManager() const { return nullptr; } - Acl* Stack::GetAcl() { return testing::acl_; } os::Handler* Stack::GetHandler() { return stack_handler_; } -bool Stack::IsDumpsysModuleStarted() const { return false; } - -bool Stack::LockForDumpsys(std::function<void()> /* dumpsys_callback */) { return false; } - } // namespace shim } // namespace bluetooth diff --git a/system/test/mock/mock_stack_connection_manager.cc b/system/test/mock/mock_stack_connection_manager.cc index 97cd160854..5f4d045e63 100644 --- a/system/test/mock/mock_stack_connection_manager.cc +++ b/system/test/mock/mock_stack_connection_manager.cc @@ -44,13 +44,8 @@ bool connection_manager::background_connect_remove(uint8_t /* app_id */, return false; } -bool connection_manager::create_le_connection(uint8_t /* id */, const RawAddress& /* bd_addr */, - tBLE_ADDR_TYPE /* addr_type */) { - inc_func_call_count(__func__); - return false; -} - -bool connection_manager::direct_connect_add(uint8_t /* app_id */, const RawAddress& /* address */) { +bool connection_manager::direct_connect_add(uint8_t /* app_id */, const RawAddress& /* address */, + tBLE_ADDR_TYPE /* addr_type */) { inc_func_call_count(__func__); return false; } diff --git a/system/test/mock/mock_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn.cc index 19034a9fec..07193c9130 100644 --- a/system/test/mock/mock_stack_gap_conn.cc +++ b/system/test/mock/mock_stack_gap_conn.cc @@ -61,4 +61,16 @@ uint16_t GAP_ConnWriteData(uint16_t /* gap_handle */, BT_HDR* /* msg */) { inc_func_call_count(__func__); return 0; } +bool GAP_GetLeChannelInfo(uint16_t /* gap_handle */, uint16_t* /*remote_mtu */, + uint16_t* /* local_mps */, uint16_t* /* remote_mps */, + uint16_t* /* local_credit */, uint16_t* /* remote_credit */, + uint16_t* /* local_cid */, uint16_t* /* remote_cid */, + uint16_t* /* acl_handle */) { + inc_func_call_count(__func__); + return false; +} +bool GAP_IsTransportLe(uint16_t /* gap_handle */) { + inc_func_call_count(__func__); + return false; +} void GAP_Init(void) { inc_func_call_count(__func__); } diff --git a/system/test/mock/mock_stack_hidh.cc b/system/test/mock/mock_stack_hidh.cc index 15b16a4ae8..607fa16753 100644 --- a/system/test/mock/mock_stack_hidh.cc +++ b/system/test/mock/mock_stack_hidh.cc @@ -27,9 +27,6 @@ #include "test/common/mock_functions.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - tHID_STATUS HID_HostAddDev(const RawAddress& /* addr */, uint16_t /* attr_mask */, uint8_t* /* handle */) { inc_func_call_count(__func__); @@ -70,7 +67,3 @@ tHID_STATUS HID_HostWriteDev(uint8_t /* dev_handle */, uint8_t /* t_type */, uin return HID_SUCCESS; } void HID_HostInit(void) { inc_func_call_count(__func__); } -void hidh_get_str_attr(tSDP_DISC_REC* /* p_rec */, uint16_t /* attr_id */, uint16_t /* max_len */, - char* /* str */) { - inc_func_call_count(__func__); -} diff --git a/system/test/mock/mock_stack_l2cap_api.cc b/system/test/mock/mock_stack_l2cap_api.cc index b8f3f4e227..de117f06a9 100644 --- a/system/test/mock/mock_stack_l2cap_api.cc +++ b/system/test/mock/mock_stack_l2cap_api.cc @@ -75,6 +75,8 @@ struct L2CA_SetMediaStreamChannel L2CA_SetMediaStreamChannel; struct L2CA_isMediaChannel L2CA_isMediaChannel; struct L2CA_LeCreditDefault L2CA_LeCreditDefault; struct L2CA_LeCreditThreshold L2CA_LeCreditThreshold; +struct L2CA_GetAclHandle L2CA_GetAclHandle; +struct L2CA_GetLocalMtu L2CA_GetLocalMtu; } // namespace stack_l2cap_api } // namespace mock @@ -250,6 +252,14 @@ uint16_t L2CA_LeCreditThreshold() { inc_func_call_count(__func__); return test::mock::stack_l2cap_api::L2CA_LeCreditThreshold(); } +bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) { + inc_func_call_count(__func__); + return test::mock::stack_l2cap_api::L2CA_GetAclHandle(lcid, acl_handle); +} +bool L2CA_GetLocalMtu(uint16_t lcid, uint16_t* local_mtu) { + inc_func_call_count(__func__); + return test::mock::stack_l2cap_api::L2CA_GetLocalMtu(lcid, local_mtu); +} // END mockcify generation diff --git a/system/test/mock/mock_stack_l2cap_api.h b/system/test/mock/mock_stack_l2cap_api.h index 40d746ecd3..3f2e6dc72f 100644 --- a/system/test/mock/mock_stack_l2cap_api.h +++ b/system/test/mock/mock_stack_l2cap_api.h @@ -457,6 +457,26 @@ struct L2CA_LeCreditThreshold { }; extern struct L2CA_LeCreditThreshold L2CA_LeCreditThreshold; +// Name: L2CA_GetAclHandle +// Params: uint16_t lcid, uint16_t* acl_handle +// Returns: bool +struct L2CA_GetAclHandle { + std::function<bool(uint16_t lcid, uint16_t* acl_handle)> body{ + [](uint16_t /* lcid */, uint16_t* /* acl_handle */) { return false; }}; + bool operator()(uint16_t lcid, uint16_t* acl_handle) { return body(lcid, acl_handle); } +}; +extern struct L2CA_GetAclHandle L2CA_GetAclHandle; + +// Name: L2CA_GetLocalMtu +// Params: uint16_t lcid, uint16_t* local_mtu +// Returns: bool +struct L2CA_GetLocalMtu { + std::function<bool(uint16_t lcid, uint16_t* local_mtu)> body{ + [](uint16_t /* lcid */, uint16_t* /* local_mtu */) { return false; }}; + bool operator()(uint16_t lcid, uint16_t* local_mtu) { return body(lcid, local_mtu); } +}; +extern struct L2CA_GetLocalMtu L2CA_GetLocalMtu; + } // namespace stack_l2cap_api } // namespace mock } // namespace test diff --git a/system/test/mock/mock_stack_l2cap_ble.cc b/system/test/mock/mock_stack_l2cap_ble.cc index 7dcd2f6bf0..6224446eb0 100644 --- a/system/test/mock/mock_stack_l2cap_ble.cc +++ b/system/test/mock/mock_stack_l2cap_ble.cc @@ -44,6 +44,7 @@ struct L2CA_LockBleConnParamsForServiceDiscovery L2CA_LockBleConnParamsForServic struct L2CA_LockBleConnParamsForProfileConnection L2CA_LockBleConnParamsForProfileConnection; struct L2CA_ConsolidateParams L2CA_ConsolidateParams; struct L2CA_GetBleConnRole L2CA_GetBleConnRole; +struct L2CA_GetBleConnInterval L2CA_GetBleConnInterval; struct l2cble_notify_le_connection l2cble_notify_le_connection; struct l2cble_conn_comp l2cble_conn_comp; struct l2cble_process_conn_update_evt l2cble_process_conn_update_evt; @@ -93,6 +94,10 @@ hci_role_t L2CA_GetBleConnRole(const RawAddress& bd_addr) { inc_func_call_count(__func__); return test::mock::stack_l2cap_ble::L2CA_GetBleConnRole(bd_addr); } +uint16_t L2CA_GetBleConnInterval(const RawAddress& bd_addr) { + inc_func_call_count(__func__); + return test::mock::stack_l2cap_ble::L2CA_GetBleConnInterval(bd_addr); +} void l2cble_notify_le_connection(const RawAddress& bda) { inc_func_call_count(__func__); test::mock::stack_l2cap_ble::l2cble_notify_le_connection(bda); diff --git a/system/test/mock/mock_stack_l2cap_ble.h b/system/test/mock/mock_stack_l2cap_ble.h index c5c3e45a1d..913a04f5e2 100644 --- a/system/test/mock/mock_stack_l2cap_ble.h +++ b/system/test/mock/mock_stack_l2cap_ble.h @@ -89,6 +89,15 @@ struct L2CA_GetBleConnRole { hci_role_t operator()(const RawAddress& bd_addr) { return body(bd_addr); } }; extern struct L2CA_GetBleConnRole L2CA_GetBleConnRole; +// Name: L2CA_GetBleConnInterval +// Params: const RawAddress& bd_addr +// Returns: uint16_t +struct L2CA_GetBleConnInterval { + std::function<uint16_t(const RawAddress& bd_addr)> body{ + [](const RawAddress& /* bd_addr */) { return 24; }}; + uint16_t operator()(const RawAddress& bd_addr) { return body(bd_addr); } +}; +extern struct L2CA_GetBleConnInterval L2CA_GetBleConnInterval; // Name: l2cble_notify_le_connection // Params: const RawAddress& bda // Returns: void diff --git a/system/test/mock/mock_stack_l2cap_interface.h b/system/test/mock/mock_stack_l2cap_interface.h index c87bf164bb..a5cd536777 100644 --- a/system/test/mock/mock_stack_l2cap_interface.h +++ b/system/test/mock/mock_stack_l2cap_interface.h @@ -99,6 +99,7 @@ public: MOCK_METHOD(void, L2CA_LockBleConnParamsForProfileConnection, (const RawAddress& bd_addr, bool lock)); MOCK_METHOD(tHCI_ROLE, L2CA_GetBleConnRole, (const RawAddress& bd_addr)); + MOCK_METHOD(uint16_t, L2CA_GetBleConnInterval, (const RawAddress& bd_addr)); MOCK_METHOD(bool, L2CA_SetLeGattTimeout, (const RawAddress& bd_addr, uint16_t idle_tout)); MOCK_METHOD(bool, L2CA_MarkLeLinkAsActive, (const RawAddress& bd_addr)); MOCK_METHOD(bool, L2CA_GetPeerLECocConfig, (uint16_t lcid, tL2CAP_LE_CFG_INFO* peer_cfg)); @@ -106,6 +107,8 @@ public: MOCK_METHOD(uint16_t, L2CA_LeCreditThreshold, ()); MOCK_METHOD(void, L2CA_Consolidate, (const RawAddress& identity_addr, const RawAddress& rpa)); + MOCK_METHOD(bool, L2CA_GetAclHandle, (uint16_t lcid, uint16_t* acl_handle)); + MOCK_METHOD(bool, L2CA_GetLocalMtu, (uint16_t lcid, uint16_t* local_mtu)); // Disconnect methods an active connection for both BR/EDR and BLE MOCK_METHOD(bool, L2CA_DisconnectReq, (uint16_t cid)); diff --git a/system/test/mock/mock_stack_metrics_logging.cc b/system/test/mock/mock_stack_metrics_logging.cc index 76ddee2e0b..f2336668e1 100644 --- a/system/test/mock/mock_stack_metrics_logging.cc +++ b/system/test/mock/mock_stack_metrics_logging.cc @@ -52,6 +52,9 @@ struct log_manufacturer_info log_manufacturer_info; struct log_counter_metrics log_counter_metrics; struct log_hfp_audio_packet_loss_stats log_hfp_audio_packet_loss_stats; struct log_mmc_transcode_rtt_stats log_mmc_transcode_rtt_stats; +struct log_le_connection_status log_le_connection_status; +struct log_le_device_in_accept_list log_le_device_in_accept_list; +struct log_le_connection_lifecycle log_le_connection_lifecycle; } // namespace stack_metrics_logging } // namespace mock @@ -133,4 +136,20 @@ void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_reque test::mock::stack_metrics_logging::log_mmc_transcode_rtt_stats(maximum_rtt, mean_rtt, num_requests, codec_type); } + +void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason) { + inc_func_call_count(__func__); + test::mock::stack_metrics_logging::log_le_connection_status(address, is_connect, reason); +} + +void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add) { + inc_func_call_count(__func__); + test::mock::stack_metrics_logging::log_le_device_in_accept_list(address, is_add); +} + +void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct) { + inc_func_call_count(__func__); + test::mock::stack_metrics_logging::log_le_connection_lifecycle(address, is_connect, is_direct); +} // END mockcify generation diff --git a/system/test/mock/mock_stack_metrics_logging.h b/system/test/mock/mock_stack_metrics_logging.h index beb3650056..413cbea629 100644 --- a/system/test/mock/mock_stack_metrics_logging.h +++ b/system/test/mock/mock_stack_metrics_logging.h @@ -27,6 +27,8 @@ #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> #include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> +#include "hci/address.h" +#include "hci/hci_packets.h" #include "types/raw_address.h" // Mocked compile conditionals, if any @@ -199,6 +201,39 @@ struct log_mmc_transcode_rtt_stats { } }; extern struct log_mmc_transcode_rtt_stats log_mmc_transcode_rtt_stats; + +// Name: log_le_connection_status +struct log_le_connection_status { + std::function<void(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason)> + body{[](bluetooth::hci::Address /* address */, bool /* is_connect */, + bluetooth::hci::ErrorCode /* reason */) {}}; + void operator()(bluetooth::hci::Address address, bool is_connect, + bluetooth::hci::ErrorCode reason) { + body(address, is_connect, reason); + } +}; +extern struct log_le_connection_status log_le_connection_status; + +// Name: log_le_device_in_accept_list +struct log_le_device_in_accept_list { + std::function<void(bluetooth::hci::Address address, bool is_add)> body{ + [](bluetooth::hci::Address /* address */, bool /* is_add */) {}}; + void operator()(bluetooth::hci::Address address, bool is_add) { body(address, is_add); } +}; +extern struct log_le_device_in_accept_list log_le_device_in_accept_list; + +// Name: log_le_connection_lifecycle +struct log_le_connection_lifecycle { + std::function<void(bluetooth::hci::Address address, bool is_connect, bool is_direct)> body{ + [](bluetooth::hci::Address /* address */, bool /* is_connect */, bool /* is_direct */) { + }}; + void operator()(bluetooth::hci::Address address, bool is_connect, bool is_direct) { + body(address, is_connect, is_direct); + } +}; +extern struct log_le_device_in_accept_list log_le_device_in_accept_list; + } // namespace stack_metrics_logging } // namespace mock } // namespace test diff --git a/system/test/mock/mock_stack_rfcomm_port_api.cc b/system/test/mock/mock_stack_rfcomm_port_api.cc index e3dffbbf48..b87a752689 100644 --- a/system/test/mock/mock_stack_rfcomm_port_api.cc +++ b/system/test/mock/mock_stack_rfcomm_port_api.cc @@ -78,7 +78,7 @@ int RFCOMM_CreateConnectionWithSecurity(uint16_t /* uuid */, uint8_t /* scn */, bool /* is_server */, uint16_t /* mtu */, const RawAddress& /* bd_addr */, uint16_t* /* p_handle */, tPORT_MGMT_CALLBACK* /* p_mgmt_callback */, - uint16_t /* sec_mask */) { + uint16_t /* sec_mask */, RfcommCfgInfo /* cfg */) { inc_func_call_count(__func__); return 0; } @@ -101,6 +101,15 @@ int PORT_GetSecurityMask(uint16_t /* handle */, uint16_t* /* sec_mask */) { inc_func_call_count(__func__); return 0; } +int PORT_GetChannelInfo(uint16_t /* handle */, uint16_t* /* local_mtu */, + uint16_t* /* remote_mtu */, uint16_t* /* local_credit */, + uint16_t* /* remote_credit */, uint16_t* /* local_cid */, + uint16_t* /* remote_cid */, uint16_t* /* dlci */, + uint16_t* /* max_frame_size */, uint16_t* /* acl_handle */, + bool* /* mux_initiator */) { + inc_func_call_count(__func__); + return 0; +} void RFCOMM_Init(void) { inc_func_call_count(__func__); } bool PORT_IsCollisionDetected(RawAddress /* bd_addr */) { inc_func_call_count(__func__); diff --git a/system/test/mock/mock_stack_smp_api.cc b/system/test/mock/mock_stack_smp_api.cc index 9540022e93..e637bbf3ac 100644 --- a/system/test/mock/mock_stack_smp_api.cc +++ b/system/test/mock/mock_stack_smp_api.cc @@ -69,3 +69,8 @@ void SMP_ClearLocScOobData() { inc_func_call_count(__func__); } void SMP_SirkConfirmDeviceReply(const RawAddress& /* bd_addr */, uint8_t /* res */) { inc_func_call_count(__func__); } + +uint16_t SMP_GetPendingPairingKeySize() { + inc_func_call_count(__func__); + return 0; +}
\ No newline at end of file diff --git a/system/test/mock/mock_stack_srvc_dis.cc b/system/test/mock/mock_stack_srvc_dis.cc index fc301ea0a1..9a7a3ac884 100644 --- a/system/test/mock/mock_stack_srvc_dis.cc +++ b/system/test/mock/mock_stack_srvc_dis.cc @@ -22,22 +22,16 @@ #include <cstdint> #include "stack/include/srvc_api.h" +#include "stack/srvc/srvc_dis_int.h" #include "stack/srvc/srvc_eng_int.h" #include "test/common/mock_functions.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - bool DIS_ReadDISInfo(const RawAddress& /* peer_bda */, tDIS_READ_CBACK* /* p_cback */, tDIS_ATTR_MASK /* mask */) { inc_func_call_count(__func__); return false; } -bool dis_gatt_c_read_dis_req(uint16_t /* conn_id */) { - inc_func_call_count(__func__); - return false; -} void dis_c_cmpl_cback(tSRVC_CLCB* /* p_clcb */, tGATTC_OPTYPE /* op */, tGATT_STATUS /* status */, tGATT_CL_COMPLETE* /* p_data */) { inc_func_call_count(__func__); diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp index 98307ada71..ac4a5e3b24 100644 --- a/system/test/suite/Android.bp +++ b/system/test/suite/Android.bp @@ -65,7 +65,6 @@ cc_defaults { "lib-bt-packets-avrcp", "lib-bt-packets-base", "libFraunhoferAAC", - "libbluetooth-dumpsys", "libbluetooth-types", "libbluetooth_core_rs", "libbluetooth_core_rs_bridge", @@ -142,9 +141,6 @@ cc_test { "libbinder", "server_configurable_flags", ], - generated_headers: [ - "BluetoothGeneratedDumpsysDataSchema_h", - ], target: { android: { static_libs: [ diff --git a/system/test/suite/gatt/gatt_test.cc b/system/test/suite/gatt/gatt_test.cc index b99dbf77f8..b992d2eab8 100644 --- a/system/test/suite/gatt/gatt_test.cc +++ b/system/test/suite/gatt/gatt_test.cc @@ -32,14 +32,6 @@ void RegisterClientCallback(int status, int clientIf, const bluetooth::Uuid& /*a semaphore_post(instance->register_client_callback_sem_); } -void ScanResultCallback(uint16_t /*ble_evt_type*/, uint8_t /*addr_type*/, RawAddress* /*bda*/, - uint8_t /*ble_primary_phy*/, uint8_t /*ble_secondary_phy*/, - uint8_t /*ble_advertising_sid*/, int8_t /*ble_tx_power*/, int8_t /*rssi*/, - uint16_t /*ble_periodic_adv_int*/, std::vector<uint8_t> /*adv_data*/, - RawAddress* /*original_bda*/) { - semaphore_post(instance->scan_result_callback_sem_); -} - // GATT server callbacks void RegisterServerCallback(int status, int server_if, const bluetooth::Uuid& /*uuid*/) { instance->status_ = status; @@ -69,10 +61,6 @@ void ServiceDeletedCallback(int status, int server_if, int srvc_handle) { semaphore_post(instance->service_deleted_callback_sem_); } -static const btgatt_scanner_callbacks_t scanner_callbacks = { - .scan_result_cb = ScanResultCallback, -}; - static const btgatt_client_callbacks_t client_callbacks = { .register_client_cb = RegisterClientCallback, }; @@ -88,7 +76,6 @@ static const btgatt_callbacks_t callbacks = { sizeof(btgatt_callbacks_t), &client_callbacks, &server_callbacks, - &scanner_callbacks, }; void GattTest::SetUp() { diff --git a/system/test/suite/gatt/gatt_test.h b/system/test/suite/gatt/gatt_test.h index 6385e6637d..85c2d34ee3 100644 --- a/system/test/suite/gatt/gatt_test.h +++ b/system/test/suite/gatt/gatt_test.h @@ -73,7 +73,6 @@ protected: // Semaphores used to wait for specific callback execution. Each callback // has its own semaphore associated with it btsemaphore register_client_callback_sem_; - btsemaphore scan_result_callback_sem_; btsemaphore listen_callback_sem_; btsemaphore register_server_callback_sem_; diff --git a/system/types/Android.bp b/system/types/Android.bp index 4201bafca4..00a0de546d 100644 --- a/system/types/Android.bp +++ b/system/types/Android.bp @@ -10,7 +10,6 @@ package { cc_library_headers { name: "libbluetooth-types-header", - visibility: ["//visibility:public"], export_include_dirs: ["./"], vendor_available: true, host_supported: true, @@ -37,7 +36,6 @@ cc_library_static { /* we export all classes, so change default visibility, instead of having EXPORT_SYMBOL on each class*/ "-fvisibility=default", ], - visibility: ["//visibility:public"], host_supported: true, srcs: [ "bluetooth/uuid.cc", diff --git a/system/types/remote_version_type.h b/system/types/remote_version_type.h index 307a9ef102..0c6cab3b86 100644 --- a/system/types/remote_version_type.h +++ b/system/types/remote_version_type.h @@ -18,8 +18,6 @@ #pragma once -#include <base/strings/stringprintf.h> - #include <cstdint> #include <string> @@ -29,8 +27,7 @@ struct tREMOTE_VERSION_INFO { uint16_t manufacturer{0}; bool valid{false}; std::string ToString() const { - return (valid) ? base::StringPrintf("%02hhu-%05hu-%05hu", lmp_version, lmp_subversion, - manufacturer) + return (valid) ? std::format("{:02}-{:05}-{:05}", lmp_version, lmp_subversion, manufacturer) : std::string("UNKNOWN"); } }; |