diff options
49 files changed, 983 insertions, 958 deletions
@@ -8,8 +8,8 @@ okamil@google.com #{LAST_RESORT_SUGGESTION} # Per-file ownership -# Build files / test_config / presubmit / preupload -per-file PREUPLOAD.cfg,TEST_MAPPING,*.bp,*.xml=file:/OWNERS_build +# Build files / test_config / presubmit / preupload / linter file +per-file PREUPLOAD.cfg,TEST_MAPPING,*.bp,*.xml,pyrightconfig.json=file:/OWNERS_build # ChromeOS team owns Linux build files # - build.py is used for Linux build diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp index b4cf5eb326..050fc7b1a5 100644 --- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp @@ -1020,7 +1020,7 @@ int hal_util_load_bt_library(const bt_interface_t** interface) { } static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest, jboolean isCommonCriteriaMode, - int configCompareResult, jboolean isAtvDevice, jstring userDataDirectory) { + int configCompareResult, jboolean isAtvDevice) { std::unique_lock<std::shared_timed_mutex> lock(jniObjMutex); log::verbose(""); @@ -1035,14 +1035,10 @@ static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest, jboolean isCo return JNI_FALSE; } - const char* user_data_directory = env->GetStringUTFChars(userDataDirectory, NULL); - int ret = sBluetoothInterface->init(&sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0, isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, - nullptr, isAtvDevice == JNI_TRUE ? 1 : 0, user_data_directory); - - env->ReleaseStringUTFChars(userDataDirectory, user_data_directory); + isAtvDevice == JNI_TRUE ? 1 : 0); if (ret != BT_STATUS_SUCCESS) { log::error("Error while setting the callbacks: {}", ret); @@ -2225,7 +2221,7 @@ static jboolean restoreFilterAcceptListNative(JNIEnv* /* env */, jobject /* obj int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { const JNINativeMethod methods[] = { - {"initNative", "(ZZIZLjava/lang/String;)Z", reinterpret_cast<void*>(initNative)}, + {"initNative", "(ZZIZ)Z", reinterpret_cast<void*>(initNative)}, {"cleanupNative", "()V", reinterpret_cast<void*>(cleanupNative)}, {"enableNative", "()Z", reinterpret_cast<void*>(enableNative)}, {"disableNative", "()Z", reinterpret_cast<void*>(disableNative)}, 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 63d6bb23cf..2f95d06703 100644 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java @@ -27,6 +27,7 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeriphe import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; import static com.android.bluetooth.flags.Flags.leaudioBroadcastMonitorSourceSyncStatus; import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper; +import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -158,6 +159,7 @@ public class BassClientService extends ProfileService { new HashMap<>(); private final PriorityQueue<SourceSyncRequest> mSourceSyncRequestsQueue = new PriorityQueue<>(sSourceSyncRequestComparator); + private final Map<Integer, Integer> mSyncFailureCounter = new HashMap<>(); private final Map<Integer, Integer> mBisDiscoveryCounterMap = new HashMap<>(); private final List<AddSourceData> mPendingSourcesToAdd = new ArrayList<>(); @@ -280,12 +282,14 @@ public class BassClientService extends ProfileService { } private static class SourceSyncRequest { - private ScanResult mScanResult; - private boolean mHasPriority; + private final ScanResult mScanResult; + private final boolean mHasPriority; + private final int mSyncFailureCounter; - SourceSyncRequest(ScanResult scanResult, boolean hasPriority) { + SourceSyncRequest(ScanResult scanResult, boolean hasPriority, int syncFailureCounter) { this.mScanResult = scanResult; this.mHasPriority = hasPriority; + this.mSyncFailureCounter = syncFailureCounter; } public ScanResult getScanResult() { @@ -300,6 +304,10 @@ public class BassClientService extends ProfileService { return mHasPriority; } + public int getFailsCounter() { + return mSyncFailureCounter; + } + @Override public String toString() { return "SourceSyncRequest{" @@ -307,6 +315,8 @@ public class BassClientService extends ProfileService { + mScanResult + ", mHasPriority=" + mHasPriority + + ", mSyncFailureCounter=" + + mSyncFailureCounter + '}'; } } @@ -319,6 +329,9 @@ public class BassClientService extends ProfileService { return -1; } else if (!ssr1.hasPriority() && ssr2.hasPriority()) { return 1; + } else if (leaudioSortScansToSyncByFails() + && (ssr1.getFailsCounter() != ssr2.getFailsCounter())) { + return Integer.compare(ssr1.getFailsCounter(), ssr2.getFailsCounter()); } else { return Integer.compare(ssr2.getRssi(), ssr1.getRssi()); } @@ -1046,9 +1059,8 @@ public class BassClientService extends ProfileService { if (isPlaying) { stopBigMonitoring(broadcastId, false); } else if (!mPausedBroadcastIds.containsKey(broadcastId)) { - ScanResult scanRes = getCachedBroadcast(broadcastId); - if (scanRes != null) { - addSelectSourceRequest(scanRes, true); + if (mCachedBroadcasts.containsKey(broadcastId)) { + addSelectSourceRequest(broadcastId, true); mPausedBroadcastIds.put(broadcastId, PauseType.SINK_UNKNOWN); logPausedBroadcastsAndSinks(); mHandler.removeMessages(MESSAGE_BIG_CHECK_START); @@ -1842,11 +1854,11 @@ public class BassClientService extends ProfileService { "Broadcast Source Found: Broadcast ID: " + broadcastId); if (broadcastId != BassConstants.INVALID_BROADCAST_ID - && mCachedBroadcasts.get(broadcastId) == null) { + && !mCachedBroadcasts.containsKey(broadcastId)) { log("selectBroadcastSource: broadcastId " + broadcastId); mCachedBroadcasts.put(broadcastId, result); if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { - addSelectSourceRequest(result, false); + addSelectSourceRequest(broadcastId, false); } else { synchronized (mStateMachines) { for (BassClientStateMachine sm : @@ -1866,15 +1878,13 @@ public class BassClientService extends ProfileService { informConnectedDeviceAboutScanOffloadStop(); } }; + mSyncFailureCounter.clear(); mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT); if (leaudioBroadcastResyncHelper()) { // Sync to the broadcasts already synced with sinks Set<Integer> syncedBroadcasts = getExternalBroadcastsActiveOnSinks(); for (int syncedBroadcast : syncedBroadcasts) { - ScanResult scanRes = getCachedBroadcast(syncedBroadcast); - if (scanRes != null) { - addSelectSourceRequest(scanRes, true); - } + addSelectSourceRequest(syncedBroadcast, true); } } // when starting scan, clear the previously cached broadcast scan results @@ -1972,10 +1982,7 @@ public class BassClientService extends ProfileService { Integer broadcastId = entry.getKey(); PauseType pauseType = entry.getValue(); if (pauseType != PauseType.HOST_INTENTIONAL) { - ScanResult scanRes = getCachedBroadcast(broadcastId); - if (scanRes != null) { - addSelectSourceRequest(scanRes, true); - } + addSelectSourceRequest(broadcastId, true); } } } @@ -1985,6 +1992,7 @@ public class BassClientService extends ProfileService { private void clearAllSyncData() { log("clearAllSyncData"); mSourceSyncRequestsQueue.clear(); + mSyncFailureCounter.clear(); mPendingSourcesToAdd.clear(); cancelActiveSync(null); @@ -2106,6 +2114,10 @@ public class BassClientService extends ProfileService { } } stopBigMonitoring(broadcastId, false); + synchronized (mSourceSyncRequestsQueue) { + int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; + mSyncFailureCounter.put(broadcastId, failsCounter); + } synchronized (mSearchScanCallbackLock) { // Clear from cache to make possible sync again (only during active searching) if (mSearchScanCallback != null) { @@ -2180,6 +2192,10 @@ public class BassClientService extends ProfileService { mCallbacks.notifySourceLost(broadcastId); } stopBigMonitoring(broadcastId, false); + synchronized (mSourceSyncRequestsQueue) { + int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; + mSyncFailureCounter.put(broadcastId, failsCounter); + } } clearAllDataForSyncHandle(syncHandle); // Clear from cache to make possible sync again (only during active searching) @@ -2464,30 +2480,35 @@ public class BassClientService extends ProfileService { return broadcastName; } - void addSelectSourceRequest(ScanResult scanRes, boolean hasPriority) { + void addSelectSourceRequest(int broadcastId, boolean hasPriority) { sEventLogger.logd( TAG, - "Add Select Broadcast Source, result: " - + scanRes + "Add Select Broadcast Source, broadcastId: " + + broadcastId + ", hasPriority: " + hasPriority); - if (scanRes == null) { - Log.e(TAG, "addSelectSourceRequest: Error bad parameters: scanRes cannot be null"); - return; - } + ScanResult scanRes = getCachedBroadcast(broadcastId); + if (scanRes != null) { + ScanRecord scanRecord = scanRes.getScanRecord(); + if (scanRecord == null) { + log("addSelectSourceRequest: ScanRecord empty"); + return; + } - ScanRecord scanRecord = scanRes.getScanRecord(); - if (scanRecord == null) { - log("addSelectSourceRequest: ScanRecord empty"); - return; - } + synchronized (mSourceSyncRequestsQueue) { + if (!mSyncFailureCounter.containsKey(broadcastId)) { + mSyncFailureCounter.put(broadcastId, 0); + } + mSourceSyncRequestsQueue.add( + new SourceSyncRequest( + scanRes, hasPriority, mSyncFailureCounter.get(broadcastId))); + } - synchronized (mSourceSyncRequestsQueue) { - mSourceSyncRequestsQueue.add(new SourceSyncRequest(scanRes, hasPriority)); + handleSelectSourceRequest(); + } else { + log("addSelectSourceRequest: ScanResult empty"); } - - handleSelectSourceRequest(); } @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 - Fix BASS annotation @@ -2732,7 +2753,7 @@ public class BassClientService extends ProfileService { mPendingSourcesToAdd.add( new AddSourceData(sink, sourceMetadata, isGroupOp)); if (!alreadyAdded) { - addSelectSourceRequest(getCachedBroadcast(broadcastId), true); + addSelectSourceRequest(broadcastId, true); } } } else { diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java index 87cd3536a4..4a5bb81267 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java @@ -64,15 +64,13 @@ public class AdapterNativeInterface { boolean startRestricted, boolean isCommonCriteriaMode, int configCompareResult, - boolean isAtvDevice, - String userDataDirectory) { + boolean isAtvDevice) { mJniCallbacks = new JniCallbacks(service, adapterProperties); return initNative( startRestricted, isCommonCriteriaMode, configCompareResult, - isAtvDevice, - userDataDirectory); + isAtvDevice); } void cleanup() { @@ -295,8 +293,7 @@ public class AdapterNativeInterface { boolean startRestricted, boolean isCommonCriteriaMode, int configCompareResult, - boolean isAtvDevice, - String userDataDirectory); + boolean isAtvDevice); private native void cleanupNative(); diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java index 448c9aa855..728bad66cf 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -977,6 +977,7 @@ class AdapterProperties { mName = name; if (Flags.getNameAndAddressAsCallback()) { mService.updateAdapterName(mName); + break; } intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName); diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 19c8aee207..563950078d 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -700,8 +700,7 @@ public class AdapterService extends Service { mUserManager.isGuestUser(), isCommonCriteriaMode, configCompareResult, - isAtvDevice, - getApplicationInfo().dataDir); + isAtvDevice); mNativeAvailable = true; // Load the name and address mNativeInterface.getAdapterProperty(AbstractionLayer.BT_PROPERTY_BDADDR); diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java index a2693764b8..c48e8b8e3c 100644 --- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -1381,10 +1381,6 @@ public class RemoteDevices { Log.i(TAG, "keyMissingCallback device: " + bluetoothDevice); if (getBondState(bluetoothDevice) == BluetoothDevice.BOND_BONDED) { - if (!Flags.keyMissingBroadcast()) { - Log.d(TAG, "flag not set - don't send key missing broadcast"); - return; - } Intent intent = new Intent(BluetoothDevice.ACTION_KEY_MISSING) .putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice) diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index 9015774554..1f7593fa2c 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattService.java +++ b/android/app/src/com/android/bluetooth/gatt/GattService.java @@ -145,7 +145,7 @@ public class GattService extends ProfileService { private static final Integer GATT_MTU_MAX = 517; private static final Map<String, Integer> EARLY_MTU_EXCHANGE_PACKAGES = - Map.of("com.teslamotors.tesla", GATT_MTU_MAX); + Map.of("com.teslamotors", GATT_MTU_MAX); @VisibleForTesting static final int GATT_CLIENT_LIMIT_PER_APP = 32; @@ -2161,14 +2161,19 @@ public class GattService extends ProfileService { // Some applications expect MTU to be exchanged immediately on connections String packageName = attributionSource.getPackageName(); - if (packageName != null && EARLY_MTU_EXCHANGE_PACKAGES.containsKey(packageName)) { - preferredMtu = EARLY_MTU_EXCHANGE_PACKAGES.get(packageName); - Log.i( - TAG, - "Early MTU exchange preference (" - + preferredMtu - + ") requested for " - + packageName); + if (packageName != null) { + for (Map.Entry<String, Integer> entry : EARLY_MTU_EXCHANGE_PACKAGES.entrySet()) { + if (packageName.contains(entry.getKey())) { + preferredMtu = entry.getValue(); + Log.i( + TAG, + "Early MTU exchange preference (" + + preferredMtu + + ") requested for " + + packageName); + break; + } + } } mNativeInterface.gattClientConnect( 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 b48c278e57..3ba383a656 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 @@ -2377,35 +2377,11 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_withSameBroadcastId() { - byte[] scanRecord = getScanRecord(TEST_BROADCAST_ID); - - ScanResult scanResult1 = - new ScanResult( - mSourceDevice, - 0, - 0, - 0, - 0, - 0, - TEST_RSSI, - 0, - ScanRecord.parseFromBytes(scanRecord), - 0); - ScanResult scanResult2 = - new ScanResult( - mSourceDevice2, - 0, - 0, - 0, - 0, - 0, - TEST_RSSI, - 0, - ScanRecord.parseFromBytes(scanRecord), - 0); + prepareConnectedDeviceGroup(); + startSearchingForSources(); // First selectSource - mBassClientService.addSelectSourceRequest(scanResult1, false); + onScanResult(mSourceDevice, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( @@ -2414,7 +2390,7 @@ public class BassClientServiceTest { onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // Second selectSource with the same broadcast id - mBassClientService.addSelectSourceRequest(scanResult2, false); + onScanResult(mSourceDevice2, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( @@ -2491,7 +2467,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); - mBassClientService.addSelectSourceRequest(scanResult, false); + prepareConnectedDeviceGroup(); + startSearchingForSources(); + mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); @@ -2591,7 +2569,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); - mBassClientService.addSelectSourceRequest(scanResult, false); + prepareConnectedDeviceGroup(); + startSearchingForSources(); + mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); @@ -2667,7 +2647,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); - mBassClientService.addSelectSourceRequest(scanResult, false); + prepareConnectedDeviceGroup(); + startSearchingForSources(); + mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); @@ -3318,7 +3300,7 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) - public void testSelectSource_orderOfSyncRegistering() { + public void testSelectSource_orderOfSyncRegisteringByPriorityAndRssi() { final BluetoothDevice device1 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); @@ -3441,20 +3423,42 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord7), 0); + prepareConnectedDeviceGroup(); + startSearchingForSources(); + // Added and executed immidiatelly as no other in queue - mBassClientService.addSelectSourceRequest(scanResult1, false); + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue with worst rssi - mBassClientService.addSelectSourceRequest(scanResult2, false); + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue with best rssi - mBassClientService.addSelectSourceRequest(scanResult3, false); + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); // Added to queue with medium rssi - mBassClientService.addSelectSourceRequest(scanResult4, false); - // Added to queue with priority and worst rssi - mBassClientService.addSelectSourceRequest(scanResult5, true); - // Added to queue with priority and best rssi - mBassClientService.addSelectSourceRequest(scanResult6, true); - // Added to queue with priority and medium rssi - mBassClientService.addSelectSourceRequest(scanResult7, true); + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult4); + // Added to queue with worst rssi (increase priority after all) + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult5); + // Added to queue with best rssi (increase priority after all) + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult6); + // Added to queue with medium rssi (increase priority after all) + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult7); + + // Increase priority of last 3 of them + mBassClientService.addSelectSourceRequest(broadcastId5, true); + mBassClientService.addSelectSourceRequest(broadcastId6, true); + mBassClientService.addSelectSourceRequest(broadcastId7, true); ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); mInOrderMethodProxy @@ -3556,6 +3560,215 @@ public class BassClientServiceTest { } @Test + @EnableFlags({ + Flags.FLAG_LEAUDIO_SORT_SCANS_TO_SYNC_BY_FAILS, + Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE + }) + public void testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter() { + final BluetoothDevice device1 = + mBluetoothAdapter.getRemoteLeDevice( + "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); + final BluetoothDevice device2 = + mBluetoothAdapter.getRemoteLeDevice( + "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM); + final BluetoothDevice device3 = + mBluetoothAdapter.getRemoteLeDevice( + "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM); + final int broadcastId1 = 1111; + final int broadcastId2 = 2222; + final int broadcastId3 = 3333; + + byte[] scanRecord1 = getScanRecord(broadcastId1); + byte[] scanRecord2 = getScanRecord(broadcastId2); + byte[] scanRecord3 = getScanRecord(broadcastId3); + + ScanResult scanResult1 = + new ScanResult( + device1, + 0, + 0, + 0, + 0, + 0, + TEST_RSSI + 10, + 0, + ScanRecord.parseFromBytes(scanRecord1), + 0); + ScanResult scanResult2 = + new ScanResult( + device2, + 0, + 0, + 0, + 0, + 0, + TEST_RSSI + 9, + 0, + ScanRecord.parseFromBytes(scanRecord2), + 0); + ScanResult scanResult3 = + new ScanResult( + device3, + 0, + 0, + 0, + 0, + 0, + TEST_RSSI, + 0, + ScanRecord.parseFromBytes(scanRecord3), + 0); + + prepareConnectedDeviceGroup(); + startSearchingForSources(); + + // Test using onSyncEstablishedFailed + + // Added and executed immidiatelly as no other in queue, high rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); + // Added to queue, medium rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); + // Added to queue, low rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); + + ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId1); + + onSyncEstablishedFailed(device1, TEST_SYNC_HANDLE); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId2); + + // Added to queue again, high rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); + + onSyncEstablishedFailed(device2, TEST_SYNC_HANDLE + 1); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId3); + + onSyncEstablished(device3, TEST_SYNC_HANDLE + 2); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId1); + + // Restart searching clears the mSyncFailureCounter + mBassClientService.stopSearchingForSources(); + mInOrderMethodProxy + .verify(mMethodProxy, times(2)) + .periodicAdvertisingManagerUnregisterSync(any(), any()); + startSearchingForSources(); + + // Test using onSyncLost + + // Added and executed immidiatelly as no other in queue, high rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); + // Added to queue, medium rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); + // Added to queue, low rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); + + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId1); + + onSyncEstablished(device1, TEST_SYNC_HANDLE); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId2); + onSyncLost(); + + // Added to queue again, high rssi + mCallbackCaptor + .getValue() + .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); + + onSyncEstablished(device2, TEST_SYNC_HANDLE + 1); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); + assertThat( + BassUtils.parseBroadcastId( + resultCaptor + .getValue() + .getScanRecord() + .getServiceData() + .get(BassConstants.BAAS_UUID))) + .isEqualTo(broadcastId3); + } + + @Test @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_invalidActiveSource() { final int testSyncHandle = 0; diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java index 726dc14fa5..1ca22bec3c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java @@ -16,9 +16,18 @@ package com.android.bluetooth.hfpclient; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; +import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE; +import static android.bluetooth.BluetoothProfile.EXTRA_STATE; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.content.pm.PackageManager.FEATURE_WATCH; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; + import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.AT_OK; import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.ENTER_PRIVATE_MODE; import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER; @@ -35,24 +44,22 @@ import android.bluetooth.BluetoothAssignedNumbers; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHeadsetClientCall; -import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothSinkAudioPolicy; import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.media.AudioManager; import android.os.Bundle; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.test.TestLooper; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Pair; -import androidx.test.espresso.intent.matcher.IntentMatchers; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.LargeTest; -import androidx.test.filters.MediumTest; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -62,17 +69,16 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.RemoteDevices; import com.android.bluetooth.hfp.HeadsetService; +import org.hamcrest.Matcher; import org.hamcrest.core.AllOf; -import org.hamcrest.core.IsInstanceOf; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.hamcrest.MockitoHamcrest; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -80,14 +86,17 @@ import org.mockito.junit.MockitoRule; import java.util.List; import java.util.Set; -/** Test cases for {@link HeadsetClientStateMachine}. */ -@LargeTest +@SmallTest @RunWith(AndroidJUnit4.class) public class HeadsetClientStateMachineTest { - private BluetoothAdapter mAdapter; - private HandlerThread mHandlerThread; + private final Context mTargetContext = InstrumentationRegistry.getTargetContext(); + private final BluetoothAdapter mAdapter = + mTargetContext.getSystemService(BluetoothManager.class).getAdapter(); + private final BluetoothDevice mTestDevice = TestUtils.getTestDevice(mAdapter, 42); + private TestHeadsetClientStateMachine mHeadsetClientStateMachine; - private BluetoothDevice mTestDevice; + private InOrder mInOrder; + private TestLooper mTestLooper; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -102,250 +111,163 @@ public class HeadsetClientStateMachineTest { @Mock private PackageManager mPackageManager; @Mock private NativeInterface mNativeInterface; - private static final int STANDARD_WAIT_MILLIS = 1000; - private static final int QUERY_CURRENT_CALLS_WAIT_MILLIS = 2000; - private static final int QUERY_CURRENT_CALLS_TEST_WAIT_MILLIS = - QUERY_CURRENT_CALLS_WAIT_MILLIS * 3 / 2; - private static final int TIMEOUT_MS = 1000; - @Before public void setUp() throws Exception { - // Setup mocks and test assets + mInOrder = inOrder(mHeadsetClientService); + // Set a valid volume - when(mAudioManager.getStreamVolume(anyInt())).thenReturn(2); - when(mAudioManager.getStreamMaxVolume(anyInt())).thenReturn(10); - when(mAudioManager.getStreamMinVolume(anyInt())).thenReturn(1); - when(mHeadsetClientService.getAudioManager()).thenReturn(mAudioManager); - when(mHeadsetClientService.getResources()).thenReturn(mMockHfpResources); - when(mHeadsetClientService.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); - when(mMockHfpResources.getBoolean(R.bool.hfp_clcc_poll_during_call)).thenReturn(true); - when(mMockHfpResources.getInteger(R.integer.hfp_clcc_poll_interval_during_call)) - .thenReturn(2000); + doReturn(2).when(mAudioManager).getStreamVolume(anyInt()); + doReturn(10).when(mAudioManager).getStreamMaxVolume(anyInt()); + doReturn(1).when(mAudioManager).getStreamMinVolume(anyInt()); + + doReturn(mAudioManager).when(mHeadsetClientService).getAudioManager(); + doReturn(mMockHfpResources).when(mHeadsetClientService).getResources(); + doReturn(mPackageManager).when(mHeadsetClientService).getPackageManager(); + doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetClientService).getConnectionPolicy(any()); + + doReturn(true).when(mMockHfpResources).getBoolean(eq(R.bool.hfp_clcc_poll_during_call)); + doReturn(2000) + .when(mMockHfpResources) + .getInteger(eq(R.integer.hfp_clcc_poll_interval_during_call)); doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices(); doReturn(true).when(mNativeInterface).sendAndroidAt(anyObject(), anyString()); - // This line must be called to make sure relevant objects are initialized properly - mAdapter = BluetoothAdapter.getDefaultAdapter(); - // Get a device for testing - mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); - - // Setup thread and looper - mHandlerThread = new HandlerThread("HeadsetClientStateMachineTestHandlerThread"); - mHandlerThread.start(); - // Manage looper execution in main test thread explicitly to guarantee timing consistency + mTestLooper = new TestLooper(); mHeadsetClientStateMachine = new TestHeadsetClientStateMachine( mAdapterService, mHeadsetClientService, mHeadsetService, - mHandlerThread.getLooper(), + mTestLooper.getLooper(), mNativeInterface); mHeadsetClientStateMachine.start(); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + mTestLooper.dispatchAll(); } @After public void tearDown() throws Exception { - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + mTestLooper.dispatchAll(); mHeadsetClientStateMachine.allowConnect = null; mHeadsetClientStateMachine.doQuit(); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - mHandlerThread.quit(); + mTestLooper.dispatchAll(); verifyNoMoreInteractions(mHeadsetService); } /** Test that default state is disconnected */ - @SmallTest @Test public void testDefaultDisconnectedState() { - Assert.assertEquals( - mHeadsetClientStateMachine.getConnectionState(null), - BluetoothProfile.STATE_DISCONNECTED); + assertThat(mHeadsetClientStateMachine.getConnectionState(null)) + .isEqualTo(STATE_DISCONNECTED); } /** Test that an incoming connection with low priority is rejected */ - @MediumTest @Test public void testIncomingPriorityReject() { - // Return false for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + doReturn(CONNECTION_POLICY_FORBIDDEN) + .when(mHeadsetClientService) + .getConnectionPolicy(any()); // Inject an event for when incoming connection is requested StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; connStCh.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, connStCh); + sendMessage(StackEvent.STACK_EVENT, connStCh); // Verify that only DISCONNECTED -> DISCONNECTED broadcast is fired - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS)) - .sendBroadcastMultiplePermissions( - MockitoHamcrest.argThat( - AllOf.allOf( - IntentMatchers.hasAction( - BluetoothHeadsetClient - .ACTION_CONNECTION_STATE_CHANGED), - IntentMatchers.hasExtra( - BluetoothProfile.EXTRA_STATE, - BluetoothProfile.STATE_DISCONNECTED), - IntentMatchers.hasExtra( - BluetoothProfile.EXTRA_PREVIOUS_STATE, - BluetoothProfile.STATE_DISCONNECTED))), - any(String[].class), - any(BroadcastOptions.class)); - // Check we are in disconnected state still. - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Disconnected.class)); + verifySendBroadcastMultiplePermissions( + hasAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED), + hasExtra(EXTRA_STATE, STATE_DISCONNECTED), + hasExtra(EXTRA_PREVIOUS_STATE, STATE_DISCONNECTED)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); } /** Test that an incoming connection with high priority is accepted */ - @MediumTest @Test public void testIncomingPriorityAccept() { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - // Inject an event for when incoming connection is requested StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; connStCh.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, connStCh); + sendMessage(StackEvent.STACK_EVENT, connStCh); // Verify that one connection state broadcast is executed - ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS)) - .sendBroadcastMultiplePermissions( - intentArgument1.capture(), - any(String[].class), - any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTING, - intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); - // Check we are in connecting state now. - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connecting.class); // Send a message to trigger SLC connection StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; slcEvent.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, slcEvent); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, slcEvent); setUpAndroidAt(false); // Verify that one connection state broadcast is executed - ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(2)) - .sendBroadcastMultiplePermissions( - intentArgument2.capture(), - any(String[].class), - any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTED, - intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - // Check we are in connecting state now. - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); } /** Test that an incoming connection that times out */ - @MediumTest @Test public void testIncomingTimeout() { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - // Inject an event for when incoming connection is requested StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; connStCh.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, connStCh); - - // Verify that one connection state broadcast is executed - ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS)) - .sendBroadcastMultiplePermissions( - intentArgument1.capture(), - any(String[].class), - any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTING, - intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - - // Check we are in connecting state now. - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class)); + sendMessage(StackEvent.STACK_EVENT, connStCh); // Verify that one connection state broadcast is executed - ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); - verify( - mHeadsetClientService, - timeout(HeadsetClientStateMachine.CONNECTING_TIMEOUT_MS * 2).times(2)) - .sendBroadcastMultiplePermissions( - intentArgument2.capture(), - any(String[].class), - any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_DISCONNECTED, - intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - - // Check we are in connecting state now. - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Disconnected.class)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connecting.class); + + // Trigger timeout + mTestLooper.moveTimeForward(HeadsetClientStateMachine.CONNECTING_TIMEOUT_MS); + mTestLooper.dispatchAll(); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); } + private boolean processAndroidSlcCommand(String command) { + return mHeadsetClientStateMachine.processAndroidSlcCommand(command, mTestDevice); + } + @Test public void testProcessAndroidSlcCommand() { initToConnectedState(); // True on correct AT command and BluetothDevice - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (SINKAUDIOPOLICY)", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand("+ANDROID: ()", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (,,,)", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE)", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE,1,2,3),(1,2,3)", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand("+ANDROID: 123", mTestDevice)); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand("+ANDROID: ", mTestDevice)); + assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY)")).isTrue(); + assertThat(processAndroidSlcCommand("+ANDROID: ()")).isTrue(); + assertThat(processAndroidSlcCommand("+ANDROID: (,,,)")).isTrue(); + assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE)")).isTrue(); + assertThat( + processAndroidSlcCommand( + "+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE,1,2,3),(1,2,3)")) + .isTrue(); + assertThat(processAndroidSlcCommand("+ANDROID: 123")).isTrue(); + assertThat(processAndroidSlcCommand("+ANDROID: ")).isTrue(); // False on incorrect AT command format - Assert.assertFalse( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID= (SINKAUDIOPOLICY)", mTestDevice)); - Assert.assertFalse( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "RANDOM ^%$# STRING", mTestDevice)); - Assert.assertFalse(mHeadsetClientStateMachine.processAndroidSlcCommand("", mTestDevice)); + assertThat(processAndroidSlcCommand("+ANDROID= (SINKAUDIOPOLICY)")).isFalse(); + assertThat(processAndroidSlcCommand("RANDOM ^%$# STRING")).isFalse(); + assertThat(processAndroidSlcCommand("")).isFalse(); // False on incorrect BluetoothDevice - Assert.assertFalse( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (SINKAUDIOPOLICY)", - mAdapter.getRemoteDevice("05:04:01:02:03:00"))); + assertThat( + mHeadsetClientStateMachine.processAndroidSlcCommand( + "+ANDROID: (SINKAUDIOPOLICY)", + mAdapter.getRemoteDevice("05:04:01:02:03:00"))) + .isFalse(); } @Test @@ -353,82 +275,45 @@ public class HeadsetClientStateMachineTest { initToConnectedState(); mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); - Assert.assertFalse( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "RANDOM ^%$# STRING", mTestDevice)); - Assert.assertEquals( - BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, - mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()); + assertThat(processAndroidSlcCommand("RANDOM ^%$# STRING")).isFalse(); + assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) + .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); - Assert.assertFalse( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID= (SINKAUDIOPOLICY)", mTestDevice)); - Assert.assertEquals( - BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, - mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()); + assertThat(processAndroidSlcCommand("+ANDROID= (SINKAUDIOPOLICY)")).isFalse(); + assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) + .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); - Assert.assertTrue( - mHeadsetClientStateMachine.processAndroidSlcCommand( - "+ANDROID: (SINKAUDIOPOLICY)", mTestDevice)); - Assert.assertEquals( - BluetoothStatusCodes.FEATURE_SUPPORTED, - mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()); + assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY)")).isTrue(); + assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) + .isEqualTo(BluetoothStatusCodes.FEATURE_SUPPORTED); } /** Test that In Band Ringtone information is relayed from phone. */ - @LargeTest @Test - @FlakyTest public void testInBandRingtone() { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - - Assert.assertEquals(false, mHeadsetClientStateMachine.getInBandRing()); + assertThat(mHeadsetClientStateMachine.getInBandRing()).isFalse(); // Inject an event for when incoming connection is requested StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; connStCh.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, connStCh); - - int expectedBroadcastIndex = 1; - int expectedBroadcastMultiplePermissionsIndex = 1; + sendMessage(StackEvent.STACK_EVENT, connStCh); // Verify that one connection state broadcast is executed - ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify( - mHeadsetClientService, - timeout(STANDARD_WAIT_MILLIS) - .times(expectedBroadcastMultiplePermissionsIndex++)) - .sendBroadcastMultiplePermissions( - intentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTING, - intentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); // Send a message to trigger SLC connection StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; slcEvent.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, slcEvent); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, slcEvent); setUpAndroidAt(false); - verify( - mHeadsetClientService, - timeout(STANDARD_WAIT_MILLIS) - .times(expectedBroadcastMultiplePermissionsIndex++)) - .sendBroadcastMultiplePermissions( - intentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); - - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTED, - intentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); @@ -440,25 +325,15 @@ public class HeadsetClientStateMachineTest { StackEvent eventInBandRing = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE); eventInBandRing.valueInt = 1; eventInBandRing.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, eventInBandRing); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex++)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); - Assert.assertEquals( - 1, - intentArgument - .getValue() - .getIntExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, -1)); - Assert.assertEquals(true, mHeadsetClientStateMachine.getInBandRing()); + sendMessage(StackEvent.STACK_EVENT, eventInBandRing); + verifySendBroadcast(hasExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 1)); + assertThat(mHeadsetClientStateMachine.getInBandRing()).isTrue(); // Simulate a new incoming phone call StackEvent eventCallStatusUpdated = new StackEvent(StackEvent.EVENT_TYPE_CLIP); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, eventCallStatusUpdated); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - verify( - mHeadsetClientService, - timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex - 1)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); + sendMessage(StackEvent.STACK_EVENT, eventCallStatusUpdated); + mInOrder.verify(mHeadsetClientService, never()) + .sendBroadcast(any(Intent.class), anyString(), any(Bundle.class)); // Provide information about the new call StackEvent eventIncomingCall = new StackEvent(StackEvent.EVENT_TYPE_CURRENT_CALLS); @@ -469,73 +344,58 @@ public class HeadsetClientStateMachineTest { eventIncomingCall.valueString = "5551212"; // phone number eventIncomingCall.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, eventIncomingCall); - verify( - mHeadsetClientService, - timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex - 1)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); + sendMessage(StackEvent.STACK_EVENT, eventIncomingCall); + mInOrder.verify(mHeadsetClientService, never()) + .sendBroadcast(any(Intent.class), anyString(), any(Bundle.class)); // Signal that the complete list of calls was received. StackEvent eventCommandStatus = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); eventCommandStatus.valueInt = AT_OK; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, eventCommandStatus); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - verify( - mHeadsetClientService, - timeout(QUERY_CURRENT_CALLS_TEST_WAIT_MILLIS) - .times(expectedBroadcastIndex++)) + sendMessage(StackEvent.STACK_EVENT, eventCommandStatus); + + ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); + mInOrder.verify(mHeadsetClientService) .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); // Verify that the new call is being registered with the inBandRing flag set. - Assert.assertEquals( - true, - ((HfpClientCall) - intentArgument - .getValue() - .getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL)) - .isInBandRing()); + HfpClientCall clientCall = + (HfpClientCall) + intentArgument + .getValue() + .getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL); + assertThat(clientCall.isInBandRing()).isTrue(); // Disable In Band Ring and verify state gets propagated. eventInBandRing.valueInt = 0; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, eventInBandRing); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex++)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); - Assert.assertEquals( - 0, - intentArgument - .getValue() - .getIntExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, -1)); - Assert.assertEquals(false, mHeadsetClientStateMachine.getInBandRing()); + sendMessage(StackEvent.STACK_EVENT, eventInBandRing); + verifySendBroadcast(hasExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 0)); + assertThat(mHeadsetClientStateMachine.getInBandRing()).isFalse(); } /** Test that wearables use {@code BluetoothHeadsetClientCall} in intent. */ @Test public void testWearablesUseBluetoothHeadsetClientCallInIntent() { // Specify the watch form factor when package manager is asked - when(mPackageManager.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); + doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_WATCH); // Skip over the Android AT commands to test this code path doReturn(false).when(mNativeInterface).sendAndroidAt(anyObject(), anyString()); - // Return true for connection policy to allow connections - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - // Send an incoming connection event StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.device = mTestDevice; event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); + sendMessage(StackEvent.STACK_EVENT, event); // Send a message to trigger service level connection using the required ECS feature event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.device = mTestDevice; event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; event.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); + sendMessage(StackEvent.STACK_EVENT, event); // Dial a phone call, which will fail as @{code dial} method is not specified in @{code // mNativeInterface} mock and trigger a call state changed broadcast - mHeadsetClientStateMachine.sendMessage( + sendMessage( HeadsetClientStateMachine.DIAL_NUMBER, new HfpClientCall( mTestDevice, @@ -546,73 +406,54 @@ public class HeadsetClientStateMachineTest { false, false)); - // Wait for processing - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - // Verify the broadcast ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, times(1)) + mInOrder.verify(mHeadsetClientService) .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); // Verify that the parcelable extra has a legacy {@code BluetoothHeadsetClientCall} type for // wearables. - Assert.assertThat( - intentArgument.getValue().getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL), - IsInstanceOf.instanceOf(BluetoothHeadsetClientCall.class)); + Object clientCall = + (Object) + intentArgument + .getValue() + .getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL); + assertThat(clientCall).isInstanceOf(BluetoothHeadsetClientCall.class); // To satisfy the @After verification verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); } /* Utility function to simulate HfpClient is connected. */ - private int setUpHfpClientConnection(int startBroadcastIndex) { + private void setUpHfpClientConnection() { // Trigger an incoming connection is requested StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; connStCh.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, connStCh); - ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(startBroadcastIndex)) - .sendBroadcastMultiplePermissions( - intentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTING, - intentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - startBroadcastIndex++; - return startBroadcastIndex; + sendMessage(StackEvent.STACK_EVENT, connStCh); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); } /* Utility function to simulate SLC connection. */ - private int setUpServiceLevelConnection(int startBroadcastIndex) { - return setUpServiceLevelConnection(startBroadcastIndex, false); + private void setUpServiceLevelConnection() { + setUpServiceLevelConnection(false); } - private int setUpServiceLevelConnection(int startBroadcastIndex, boolean androidAtSupported) { + private void setUpServiceLevelConnection(boolean androidAtSupported) { // Trigger SLC connection StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; slcEvent.valueInt2 |= HeadsetClientHalConstants.PEER_FEAT_HF_IND; slcEvent.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, slcEvent); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, slcEvent); setUpAndroidAt(androidAtSupported); - ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(startBroadcastIndex)) - .sendBroadcastMultiplePermissions( - intentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); - Assert.assertEquals( - BluetoothProfile.STATE_CONNECTED, - intentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); - - startBroadcastIndex++; - return startBroadcastIndex; } /** @@ -626,48 +467,36 @@ public class HeadsetClientStateMachineTest { StackEvent unknownEvt = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); unknownEvt.valueString = "+ANDROID: (SINKAUDIOPOLICY)"; unknownEvt.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, unknownEvt); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, unknownEvt); // receive CMD_RESULT OK after the Android AT command from remote StackEvent cmdResEvt = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); cmdResEvt.valueInt = StackEvent.CMD_RESULT_TYPE_OK; cmdResEvt.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, cmdResEvt); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, cmdResEvt); - Assert.assertEquals( - BluetoothStatusCodes.FEATURE_SUPPORTED, - mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()); + assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) + .isEqualTo(BluetoothStatusCodes.FEATURE_SUPPORTED); } else { // receive CMD_RESULT CME_ERROR due to remote not supporting Android AT StackEvent cmdResEvt = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); cmdResEvt.valueInt = StackEvent.CMD_RESULT_TYPE_CME_ERROR; cmdResEvt.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, cmdResEvt); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, cmdResEvt); - Assert.assertEquals( - BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, - mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()); + assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) + .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); } } /* Utility function: supported AT command should lead to native call */ private void runSupportedVendorAtCommand(String atCommand, int vendorId) { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); - Message msg = - mHeadsetClientStateMachine.obtainMessage( - HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); - mHeadsetClientStateMachine.sendMessage(msg); + sendMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); - verify(mNativeInterface, timeout(STANDARD_WAIT_MILLIS).times(1)) + verify(mNativeInterface) .sendATCmd( mTestDevice, HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_VENDOR_SPECIFIC_CMD, @@ -677,7 +506,6 @@ public class HeadsetClientStateMachineTest { } /** Test: supported vendor specific command: set operation */ - @LargeTest @Test public void testSupportedVendorAtCommandSet() { int vendorId = BluetoothAssignedNumbers.APPLE; @@ -686,7 +514,6 @@ public class HeadsetClientStateMachineTest { } /** Test: supported vendor specific command: read operation */ - @LargeTest @Test public void testSupportedVendorAtCommandRead() { int vendorId = BluetoothAssignedNumbers.APPLE; @@ -696,24 +523,15 @@ public class HeadsetClientStateMachineTest { /* utility function: unsupported vendor specific command shall be filtered. */ public void runUnsupportedVendorAtCommand(String atCommand, int vendorId) { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + sendMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); - Message msg = - mHeadsetClientStateMachine.obtainMessage( - HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); - mHeadsetClientStateMachine.sendMessage(msg); - - verify(mNativeInterface, timeout(STANDARD_WAIT_MILLIS).times(0)) - .sendATCmd(any(), anyInt(), anyInt(), anyInt(), any()); + verify(mNativeInterface, never()).sendATCmd(any(), anyInt(), anyInt(), anyInt(), any()); } /** Test: unsupported vendor specific command shall be filtered: bad command code */ - @LargeTest @Test public void testUnsupportedVendorAtCommandBadCode() { String atCommand = "+XAAPL=ABCD-1234-0100,100"; @@ -722,7 +540,6 @@ public class HeadsetClientStateMachineTest { } /** Test: unsupported vendor specific command shall be filtered: no back to back command */ - @LargeTest @Test public void testUnsupportedVendorAtCommandBackToBack() { String atCommand = "+XAPL=ABCD-1234-0100,100; +XAPL=ab"; @@ -735,44 +552,25 @@ public class HeadsetClientStateMachineTest { */ private void runSupportedVendorEvent( int vendorId, String vendorEventCode, String vendorEventArgument) { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - int expectedBroadcastIndex = 1; - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); // Simulate a known event arrive String vendorEvent = vendorEventCode + vendorEventArgument; StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); event.device = mTestDevice; event.valueString = vendorEvent; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); + sendMessage(StackEvent.STACK_EVENT, event); // Validate broadcast intent - ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); - Assert.assertEquals( - BluetoothHeadsetClient.ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT, - intentArgument.getValue().getAction()); - Assert.assertEquals( - vendorId, - intentArgument.getValue().getIntExtra(BluetoothHeadsetClient.EXTRA_VENDOR_ID, -1)); - Assert.assertEquals( - vendorEventCode, - intentArgument - .getValue() - .getStringExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_CODE)); - Assert.assertEquals( - vendorEvent, - intentArgument - .getValue() - .getStringExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_FULL_ARGS)); + verifySendBroadcast( + hasAction(BluetoothHeadsetClient.ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT), + hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_ID, vendorId), + hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_CODE, vendorEventCode), + hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_FULL_ARGS, vendorEvent)); } /** Test: supported vendor specific response: response to read command */ - @LargeTest @Test public void testSupportedVendorEventReadResponse() { final int vendorId = BluetoothAssignedNumbers.APPLE; @@ -782,7 +580,6 @@ public class HeadsetClientStateMachineTest { } /** Test: supported vendor specific response: response to test command */ - @LargeTest @Test public void testSupportedVendorEventTestResponse() { final int vendorId = BluetoothAssignedNumbers.APPLE; @@ -794,18 +591,15 @@ public class HeadsetClientStateMachineTest { /* Utility test function: unsupported vendor specific response shall be filtered out*/ public void runUnsupportedVendorEvent( int vendorId, String vendorEventCode, String vendorEventArgument) { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); // Simulate an unknown event arrive String vendorEvent = vendorEventCode + vendorEventArgument; StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); event.device = mTestDevice; event.valueString = vendorEvent; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); + sendMessage(StackEvent.STACK_EVENT, event); // Validate no broadcast intent verify(mHeadsetClientService, atMost(2)) @@ -813,7 +607,6 @@ public class HeadsetClientStateMachineTest { } /** Test unsupported vendor response: bad read response */ - @LargeTest @Test public void testUnsupportedVendorEventBadReadResponse() { final int vendorId = BluetoothAssignedNumbers.APPLE; @@ -823,7 +616,6 @@ public class HeadsetClientStateMachineTest { } /** Test unsupported vendor response: bad test response */ - @LargeTest @Test public void testUnsupportedVendorEventBadTestResponse() { final int vendorId = BluetoothAssignedNumbers.APPLE; @@ -833,80 +625,57 @@ public class HeadsetClientStateMachineTest { } /** Test voice recognition state change broadcast. */ - @MediumTest @Test public void testVoiceRecognitionStateChange() { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); doReturn(true).when(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class)); doReturn(true).when(mNativeInterface).stopVoiceRecognition(any(BluetoothDevice.class)); - int expectedBroadcastIndex = 1; - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); // Simulate a voice recognition start - mHeadsetClientStateMachine.sendMessage(VOICE_RECOGNITION_START); + sendMessage(VOICE_RECOGNITION_START); // Signal that the complete list of actions was received. StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); event.device = mTestDevice; event.valueInt = AT_OK; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); + sendMessage(StackEvent.STACK_EVENT, event); - expectedBroadcastIndex = - verifyVoiceRecognitionBroadcast( - expectedBroadcastIndex, HeadsetClientHalConstants.VR_STATE_STARTED); + verifySendBroadcast( + hasAction(BluetoothHeadsetClient.ACTION_AG_EVENT), + hasExtra( + BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, + HeadsetClientHalConstants.VR_STATE_STARTED)); // Simulate a voice recognition stop - mHeadsetClientStateMachine.sendMessage(VOICE_RECOGNITION_STOP); + sendMessage(VOICE_RECOGNITION_STOP); // Signal that the complete list of actions was received. event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); event.device = mTestDevice; event.valueInt = AT_OK; - mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event); - - verifyVoiceRecognitionBroadcast( - expectedBroadcastIndex, HeadsetClientHalConstants.VR_STATE_STOPPED); - } + sendMessage(StackEvent.STACK_EVENT, event); - private int verifyVoiceRecognitionBroadcast(int expectedBroadcastIndex, int expectedState) { - // Validate broadcast intent - ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(expectedBroadcastIndex)) - .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); - Assert.assertEquals( - BluetoothHeadsetClient.ACTION_AG_EVENT, intentArgument.getValue().getAction()); - int state = - intentArgument - .getValue() - .getIntExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, -1); - Assert.assertEquals(expectedState, state); - return expectedBroadcastIndex + 1; + verifySendBroadcast( + hasAction(BluetoothHeadsetClient.ACTION_AG_EVENT), + hasExtra( + BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, + HeadsetClientHalConstants.VR_STATE_STOPPED)); } /** Test send BIEV command */ - @MediumTest @Test public void testSendBIEVCommand() { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); int indicator_id = 2; int indicator_value = 50; - Message msg = mHeadsetClientStateMachine.obtainMessage(HeadsetClientStateMachine.SEND_BIEV); - msg.arg1 = indicator_id; - msg.arg2 = indicator_value; - - mHeadsetClientStateMachine.sendMessage(msg); + sendMessage(HeadsetClientStateMachine.SEND_BIEV, indicator_id, indicator_value); - verify(mNativeInterface, timeout(STANDARD_WAIT_MILLIS).times(1)) + verify(mNativeInterface) .sendATCmd( mTestDevice, HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_BIEV, @@ -918,17 +687,12 @@ public class HeadsetClientStateMachineTest { /** * Test state machine shall try to send AT+BIEV command to AG to update an init battery level. */ - @MediumTest @Test public void testSendBatteryUpdateIndicatorWhenConnect() { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); - - verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(1)).updateBatteryLevel(); + verify(mHeadsetClientService).updateBatteryLevel(); } @Test @@ -938,7 +702,7 @@ public class HeadsetClientStateMachineTest { BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); - verify(mHeadsetClientService).sendBroadcast(any(), any(), any()); + mInOrder.verify(mHeadsetClientService).sendBroadcast(any(), any(), any()); } @Test @@ -948,8 +712,8 @@ public class HeadsetClientStateMachineTest { mTestDevice, 0, HfpClientCall.CALL_STATE_WAITING, "1", false, false, false); mHeadsetClientStateMachine.mCalls.put(0, call); - Assert.assertEquals( - mHeadsetClientStateMachine.callsInState(HfpClientCall.CALL_STATE_WAITING), 1); + assertThat(mHeadsetClientStateMachine.callsInState(HfpClientCall.CALL_STATE_WAITING)) + .isEqualTo(1); } @Test @@ -965,7 +729,7 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.enterPrivateMode(0); Pair expectedPair = new Pair<Integer, Object>(ENTER_PRIVATE_MODE, call); - Assert.assertEquals(mHeadsetClientStateMachine.mQueuedActions.peek(), expectedPair); + assertThat(mHeadsetClientStateMachine.mQueuedActions.peek()).isEqualTo(expectedPair); } @Test @@ -985,14 +749,14 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.explicitCallTransfer(); Pair expectedPair = new Pair<Integer, Object>(EXPLICIT_CALL_TRANSFER, 0); - Assert.assertEquals(mHeadsetClientStateMachine.mQueuedActions.peek(), expectedPair); + assertThat(mHeadsetClientStateMachine.mQueuedActions.peek()).isEqualTo(expectedPair); } @Test public void testSetAudioRouteAllowed() { mHeadsetClientStateMachine.setAudioRouteAllowed(true); - Assert.assertTrue(mHeadsetClientStateMachine.getAudioRouteAllowed()); + assertThat(mHeadsetClientStateMachine.getAudioRouteAllowed()).isTrue(); // Case 1: if remote is not supported // Expect: Should not send +ANDROID to remote @@ -1020,11 +784,10 @@ public class HeadsetClientStateMachineTest { @Test public void testGetAudioState_withCurrentDeviceNull() { - Assert.assertNull(mHeadsetClientStateMachine.mCurrentDevice); + assertThat(mHeadsetClientStateMachine.mCurrentDevice).isNull(); - Assert.assertEquals( - mHeadsetClientStateMachine.getAudioState(mTestDevice), - BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); + assertThat(mHeadsetClientStateMachine.getAudioState(mTestDevice)) + .isEqualTo(BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); } @Test @@ -1033,7 +796,7 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.mAudioState = audioState; mHeadsetClientStateMachine.mCurrentDevice = mTestDevice; - Assert.assertEquals(mHeadsetClientStateMachine.getAudioState(mTestDevice), audioState); + assertThat(mHeadsetClientStateMachine.getAudioState(mTestDevice)).isEqualTo(audioState); } @Test @@ -1045,7 +808,7 @@ public class HeadsetClientStateMachineTest { int[] states = new int[1]; states[0] = HfpClientCall.CALL_STATE_ACTIVE; - Assert.assertEquals(mHeadsetClientStateMachine.getCall(states), call); + assertThat(mHeadsetClientStateMachine.getCall(states)).isEqualTo(call); } @Test @@ -1057,52 +820,48 @@ public class HeadsetClientStateMachineTest { int[] states = new int[1]; states[0] = HfpClientCall.CALL_STATE_ACTIVE; - Assert.assertNull(mHeadsetClientStateMachine.getCall(states)); + assertThat(mHeadsetClientStateMachine.getCall(states)).isNull(); } @Test public void testGetConnectionState_withNullDevice() { - Assert.assertEquals( - mHeadsetClientStateMachine.getConnectionState(null), - BluetoothProfile.STATE_DISCONNECTED); + assertThat(mHeadsetClientStateMachine.getConnectionState(null)) + .isEqualTo(STATE_DISCONNECTED); } @Test public void testGetConnectionState_withNonNullDevice() { mHeadsetClientStateMachine.mCurrentDevice = mTestDevice; - Assert.assertEquals( - mHeadsetClientStateMachine.getConnectionState(mTestDevice), - BluetoothProfile.STATE_DISCONNECTED); + assertThat(mHeadsetClientStateMachine.getConnectionState(mTestDevice)) + .isEqualTo(STATE_DISCONNECTED); } @Test public void testGetConnectionStateFromAudioState() { - Assert.assertEquals( - HeadsetClientStateMachine.getConnectionStateFromAudioState( - BluetoothHeadsetClient.STATE_AUDIO_CONNECTED), - BluetoothAdapter.STATE_CONNECTED); - Assert.assertEquals( - HeadsetClientStateMachine.getConnectionStateFromAudioState( - BluetoothHeadsetClient.STATE_AUDIO_CONNECTING), - BluetoothAdapter.STATE_CONNECTING); - Assert.assertEquals( - HeadsetClientStateMachine.getConnectionStateFromAudioState( - BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED), - BluetoothAdapter.STATE_DISCONNECTED); + assertThat( + HeadsetClientStateMachine.getConnectionStateFromAudioState( + BluetoothHeadsetClient.STATE_AUDIO_CONNECTED)) + .isEqualTo(BluetoothAdapter.STATE_CONNECTED); + assertThat( + HeadsetClientStateMachine.getConnectionStateFromAudioState( + BluetoothHeadsetClient.STATE_AUDIO_CONNECTING)) + .isEqualTo(BluetoothAdapter.STATE_CONNECTING); + assertThat( + HeadsetClientStateMachine.getConnectionStateFromAudioState( + BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED)) + .isEqualTo(BluetoothAdapter.STATE_DISCONNECTED); int invalidAudioState = 3; - Assert.assertEquals( - HeadsetClientStateMachine.getConnectionStateFromAudioState(invalidAudioState), - BluetoothAdapter.STATE_DISCONNECTED); + assertThat(HeadsetClientStateMachine.getConnectionStateFromAudioState(invalidAudioState)) + .isEqualTo(BluetoothAdapter.STATE_DISCONNECTED); } @Test public void testGetCurrentAgEvents() { Bundle bundle = mHeadsetClientStateMachine.getCurrentAgEvents(); - Assert.assertEquals( - bundle.getString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO), - mHeadsetClientStateMachine.mSubscriberInfo); + assertThat(bundle.getString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO)) + .isEqualTo(mHeadsetClientStateMachine.mSubscriberInfo); } @Test @@ -1110,30 +869,30 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_3WAY; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC; Set<Integer> features = mHeadsetClientStateMachine.getCurrentAgFeatures(); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.PEER_FEAT_3WAY)); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)); + assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_3WAY)).isTrue(); + assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)).isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_VREC; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL; features = mHeadsetClientStateMachine.getCurrentAgFeatures(); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.PEER_FEAT_VREC)); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL)); + assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_VREC)).isTrue(); + assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL)).isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_REJECT; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL_ACC; features = mHeadsetClientStateMachine.getCurrentAgFeatures(); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.PEER_FEAT_REJECT)); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)); + assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_REJECT)).isTrue(); + assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)).isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_ECC; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE; features = mHeadsetClientStateMachine.getCurrentAgFeatures(); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.PEER_FEAT_ECC)); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE)); + assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_ECC)).isTrue(); + assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE)).isTrue(); mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH; features = mHeadsetClientStateMachine.getCurrentAgFeatures(); - Assert.assertTrue(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)); + assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)).isTrue(); } @Test @@ -1141,37 +900,42 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_3WAY; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC; Bundle bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); - Assert.assertTrue(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING)); - Assert.assertTrue( - bundle.getBoolean( - BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL)); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING)) + .isTrue(); + assertThat( + bundle.getBoolean( + BluetoothHeadsetClient + .EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL)) + .isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_VREC; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL; bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); - Assert.assertTrue( - bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION)); - Assert.assertTrue( - bundle.getBoolean( - BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL)); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION)) + .isTrue(); + assertThat( + bundle.getBoolean( + BluetoothHeadsetClient + .EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL)) + .isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_REJECT; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL_ACC; bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); - Assert.assertTrue(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL)); - Assert.assertTrue( - bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT)); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL)).isTrue(); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT)) + .isTrue(); mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_ECC; mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE; bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); - Assert.assertTrue(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC)); - Assert.assertTrue(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE)); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC)).isTrue(); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE)).isTrue(); mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH; bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); - Assert.assertTrue( - bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH)); + assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH)) + .isTrue(); } @Test @@ -1183,91 +947,41 @@ public class HeadsetClientStateMachineTest { List<HfpClientCall> currentCalls = mHeadsetClientStateMachine.getCurrentCalls(); - Assert.assertEquals(currentCalls.get(0), call); + assertThat(currentCalls.get(0)).isEqualTo(call); + } + + private void assertName(int message, String message_name) { + assertThat(HeadsetClientStateMachine.getMessageName(message)).isEqualTo(message_name); } @Test public void testGetMessageName() { - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(StackEvent.STACK_EVENT), "STACK_EVENT"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.CONNECT), - "CONNECT"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.DISCONNECT), - "DISCONNECT"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.CONNECT_AUDIO), - "CONNECT_AUDIO"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.DISCONNECT_AUDIO), - "DISCONNECT_AUDIO"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(VOICE_RECOGNITION_START), - "VOICE_RECOGNITION_START"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(VOICE_RECOGNITION_STOP), - "VOICE_RECOGNITION_STOP"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.SET_MIC_VOLUME), - "SET_MIC_VOLUME"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.SET_SPEAKER_VOLUME), - "SET_SPEAKER_VOLUME"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.DIAL_NUMBER), - "DIAL_NUMBER"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.ACCEPT_CALL), - "ACCEPT_CALL"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.REJECT_CALL), - "REJECT_CALL"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.HOLD_CALL), - "HOLD_CALL"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.TERMINATE_CALL), - "TERMINATE_CALL"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(ENTER_PRIVATE_MODE), "ENTER_PRIVATE_MODE"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.SEND_DTMF), - "SEND_DTMF"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(EXPLICIT_CALL_TRANSFER), - "EXPLICIT_CALL_TRANSFER"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.DISABLE_NREC), - "DISABLE_NREC"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND), - "SEND_VENDOR_AT_COMMAND"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.SEND_BIEV), - "SEND_BIEV"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.QUERY_CURRENT_CALLS), - "QUERY_CURRENT_CALLS"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.QUERY_OPERATOR_NAME), - "QUERY_OPERATOR_NAME"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(HeadsetClientStateMachine.SUBSCRIBER_INFO), - "SUBSCRIBER_INFO"); - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName( - HeadsetClientStateMachine.CONNECTING_TIMEOUT), - "CONNECTING_TIMEOUT"); + assertName(StackEvent.STACK_EVENT, "STACK_EVENT"); + assertName(HeadsetClientStateMachine.CONNECT, "CONNECT"); + assertName(HeadsetClientStateMachine.DISCONNECT, "DISCONNECT"); + assertName(HeadsetClientStateMachine.CONNECT_AUDIO, "CONNECT_AUDIO"); + assertName(HeadsetClientStateMachine.DISCONNECT_AUDIO, "DISCONNECT_AUDIO"); + assertName(VOICE_RECOGNITION_START, "VOICE_RECOGNITION_START"); + assertName(VOICE_RECOGNITION_STOP, "VOICE_RECOGNITION_STOP"); + assertName(HeadsetClientStateMachine.SET_MIC_VOLUME, "SET_MIC_VOLUME"); + assertName(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, "SET_SPEAKER_VOLUME"); + assertName(HeadsetClientStateMachine.DIAL_NUMBER, "DIAL_NUMBER"); + assertName(HeadsetClientStateMachine.ACCEPT_CALL, "ACCEPT_CALL"); + assertName(HeadsetClientStateMachine.REJECT_CALL, "REJECT_CALL"); + assertName(HeadsetClientStateMachine.HOLD_CALL, "HOLD_CALL"); + assertName(HeadsetClientStateMachine.TERMINATE_CALL, "TERMINATE_CALL"); + assertName(ENTER_PRIVATE_MODE, "ENTER_PRIVATE_MODE"); + assertName(HeadsetClientStateMachine.SEND_DTMF, "SEND_DTMF"); + assertName(EXPLICIT_CALL_TRANSFER, "EXPLICIT_CALL_TRANSFER"); + assertName(HeadsetClientStateMachine.DISABLE_NREC, "DISABLE_NREC"); + assertName(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, "SEND_VENDOR_AT_COMMAND"); + assertName(HeadsetClientStateMachine.SEND_BIEV, "SEND_BIEV"); + assertName(HeadsetClientStateMachine.QUERY_CURRENT_CALLS, "QUERY_CURRENT_CALLS"); + assertName(HeadsetClientStateMachine.QUERY_OPERATOR_NAME, "QUERY_OPERATOR_NAME"); + assertName(HeadsetClientStateMachine.SUBSCRIBER_INFO, "SUBSCRIBER_INFO"); + assertName(HeadsetClientStateMachine.CONNECTING_TIMEOUT, "CONNECTING_TIMEOUT"); int unknownMessageInt = 54; - Assert.assertEquals( - HeadsetClientStateMachine.getMessageName(unknownMessageInt), - "UNKNOWN(" + unknownMessageInt + ")"); + assertName(unknownMessageInt, "UNKNOWN(" + unknownMessageInt + ")"); } /** @@ -1276,12 +990,8 @@ public class HeadsetClientStateMachineTest { */ @Test public void testAndroidAtRemoteNotSupported_StateTransition_setAudioPolicy() { - // Setup connection state machine to be in connected state - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2); + setUpHfpClientConnection(); + setUpServiceLevelConnection(); BluetoothSinkAudioPolicy dummyAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build(); mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); @@ -1289,15 +999,11 @@ public class HeadsetClientStateMachineTest { .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); } - @SmallTest @Test public void testSetGetCallAudioPolicy() { - // Return true for priority. - when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))) - .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); - setUpHfpClientConnection(1); - setUpServiceLevelConnection(2, true); + setUpHfpClientConnection(); + setUpServiceLevelConnection(true); BluetoothSinkAudioPolicy dummyAudioPolicy = new BluetoothSinkAudioPolicy.Builder() @@ -1312,19 +1018,19 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); verify(mNativeInterface, never()) .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,2,1"); - Assert.assertEquals(0, mHeadsetClientStateMachine.mQueuedActions.size()); + assertThat(mHeadsetClientStateMachine.mQueuedActions).isEmpty(); // Test setAudioPolicy mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(true); mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,2,1"); - Assert.assertEquals(1, mHeadsetClientStateMachine.mQueuedActions.size()); + assertThat(mHeadsetClientStateMachine.mQueuedActions.size()).isEqualTo(1); mHeadsetClientStateMachine.mQueuedActions.clear(); // Test if fail to sendAndroidAt doReturn(false).when(mNativeInterface).sendAndroidAt(anyObject(), anyString()); mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); - Assert.assertEquals(0, mHeadsetClientStateMachine.mQueuedActions.size()); + assertThat(mHeadsetClientStateMachine.mQueuedActions).isEmpty(); } @Test @@ -1335,8 +1041,7 @@ public class HeadsetClientStateMachineTest { // Check if set default policy when Connecting -> Connected // The testing sys prop is 0. It is ok to check if set audio policy while leaving connecting // state. - verify(mNativeInterface, times(1)) - .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); + verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); // Check if won't set default policy when AudioOn -> Connected // Transit to AudioOn @@ -1344,26 +1049,19 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); // Back to Connected event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); - - verify(mNativeInterface, times(1)) - .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); + + verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); } @Test @@ -1373,10 +1071,9 @@ public class HeadsetClientStateMachineTest { @Test public void testProcessDisconnectMessage_onDisconnectedState() { - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertEquals( - STATE_DISCONNECTED, mHeadsetClientStateMachine.getConnectionState(mTestDevice)); + sendMessage(HeadsetClientStateMachine.DISCONNECT); + assertThat(mHeadsetClientStateMachine.getConnectionState(mTestDevice)) + .isEqualTo(STATE_DISCONNECTED); } @Test @@ -1417,11 +1114,11 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( HeadsetClientStateMachine.CONNECT)) .isFalse(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertTrue( - mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( - HeadsetClientStateMachine.CONNECT)); + sendMessage(HeadsetClientStateMachine.CONNECT); + assertThat( + mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( + HeadsetClientStateMachine.CONNECT)) + .isTrue(); } @Test @@ -1442,11 +1139,9 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connecting.class); } @Test @@ -1455,11 +1150,9 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connecting.class); } @Test @@ -1470,11 +1163,9 @@ public class HeadsetClientStateMachineTest { event.device = mTestDevice; assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) .isFalse(); - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertTrue( - mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) + .isTrue(); } @Test @@ -1483,11 +1174,9 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); event.valueInt = StackEvent.CMD_RESULT_TYPE_OK; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connecting.class); } @Test @@ -1498,22 +1187,19 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); event.valueString = atCommand; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); } @Test public void testProcessConnectTimeoutMessage_onConnectingState() { initToConnectingState(); - Message msg = + sendMessageAndVerifyTransition( mHeadsetClientStateMachine.obtainMessage( - HeadsetClientStateMachine.CONNECTING_TIMEOUT); - sendMessageAndVerifyTransition(msg, HeadsetClientStateMachine.Disconnected.class); + HeadsetClientStateMachine.CONNECTING_TIMEOUT), + HeadsetClientStateMachine.Disconnected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); } @@ -1525,9 +1211,7 @@ public class HeadsetClientStateMachineTest { event.device = mTestDevice; assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) .isFalse(); - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(StackEvent.STACK_EVENT, event); assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) .isTrue(); } @@ -1535,42 +1219,36 @@ public class HeadsetClientStateMachineTest { @Test public void testProcessConnectMessage_onConnectedState() { initToConnectedState(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + sendMessage(HeadsetClientStateMachine.CONNECT); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); } @Test public void testProcessDisconnectMessage_onConnectedState() { initToConnectedState(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); verify(mNativeInterface).disconnect(any(BluetoothDevice.class)); } @Test public void testProcessConnectAudioMessage_onConnectedState() { initToConnectedState(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); verify(mNativeInterface).connectAudio(any(BluetoothDevice.class)); } @Test public void testProcessDisconnectAudioMessage_onConnectedState() { initToConnectedState(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); } @Test public void testProcessVoiceRecognitionStartMessage_onConnectedState() { initToConnectedState(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); verify(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class)); } @@ -1581,19 +1259,17 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( HeadsetClientStateMachine.DISCONNECT)) .isFalse(); - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertTrue( - mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( - HeadsetClientStateMachine.DISCONNECT)); + sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); + assertThat( + mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( + HeadsetClientStateMachine.DISCONNECT)) + .isTrue(); } @Test public void testProcessDisconnectAudioMessage_onAudioOnState() { initToAudioOnState(); - mHeadsetClientStateMachine.sendMessage( - HeadsetClientStateMachine.DISCONNECT_AUDIO, mTestDevice); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO, mTestDevice); verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); } @@ -1606,52 +1282,42 @@ public class HeadsetClientStateMachineTest { mHeadsetClientStateMachine.mCalls.put(0, call); int[] states = new int[1]; states[0] = HfpClientCall.CALL_STATE_ACTIVE; - mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.HOLD_CALL, mTestDevice); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); + sendMessage(HeadsetClientStateMachine.HOLD_CALL, mTestDevice); verify(mNativeInterface).handleCallAction(any(BluetoothDevice.class), anyInt(), eq(0)); } @Test public void testProcessStackEvent_ConnectionStateChanged_onAudioOnState() { initToAudioOnState(); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Disconnected.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); } @Test public void testProcessStackEvent_AudioStateChanged_onAudioOnState() { initToAudioOnState(); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); } @Test public void testProcessStackEvent_CodecSelection_onConnectedState() { initToConnectedState(); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); // Trigger a MSBC codec stack event. Expect to mAudioWbs = true. mHeadsetClientStateMachine.mAudioWbs = false; @@ -1659,11 +1325,9 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertTrue(mHeadsetClientStateMachine.mAudioWbs); - Assert.assertFalse(mHeadsetClientStateMachine.mAudioSWB); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.mAudioWbs).isTrue(); + assertThat(mHeadsetClientStateMachine.mAudioSWB).isFalse(); // Trigger a LC3 codec stack event. Expect to mAudioSWB = true. mHeadsetClientStateMachine.mAudioWbs = false; @@ -1671,11 +1335,9 @@ public class HeadsetClientStateMachineTest { event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_LC3; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertFalse(mHeadsetClientStateMachine.mAudioWbs); - Assert.assertTrue(mHeadsetClientStateMachine.mAudioSWB); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.mAudioWbs).isFalse(); + assertThat(mHeadsetClientStateMachine.mAudioSWB).isTrue(); } /** @@ -1701,12 +1363,9 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); event.valueString = atCommand; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.Connected.class); verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); } @@ -1716,23 +1375,58 @@ public class HeadsetClientStateMachineTest { StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED; event.device = mTestDevice; - mHeadsetClientStateMachine.sendMessage( - mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event)); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), - IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class)); + sendMessage(StackEvent.STACK_EVENT, event); + assertThat(mHeadsetClientStateMachine.getCurrentState()) + .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); } - private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) { - Mockito.clearInvocations(mHeadsetClientService); - mHeadsetClientStateMachine.sendMessage(msg); - // Verify that one connection state broadcast is executed - verify(mHeadsetClientService, timeout(TIMEOUT_MS)) + private void verifySendBroadcastMultiplePermissions(Matcher<Intent>... matchers) { + mInOrder.verify(mHeadsetClientService) .sendBroadcastMultiplePermissions( - any(Intent.class), any(String[].class), any(BroadcastOptions.class)); - Assert.assertThat( - mHeadsetClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type)); + MockitoHamcrest.argThat(AllOf.allOf(matchers)), + any(String[].class), + any(BroadcastOptions.class)); + } + + private void verifySendBroadcast(Matcher<Intent>... matchers) { + mInOrder.verify(mHeadsetClientService) + .sendBroadcast( + MockitoHamcrest.argThat(AllOf.allOf(matchers)), + anyString(), + any(Bundle.class)); + } + + private void sendMessage(Message msg) { + mHeadsetClientStateMachine.sendMessage(msg); + mTestLooper.dispatchAll(); + } + + private void sendMessage(int what) { + sendMessage(mHeadsetClientStateMachine.obtainMessage(what)); + } + + private void sendMessage(int what, Object obj) { + sendMessage(mHeadsetClientStateMachine.obtainMessage(what, obj)); + } + + private void sendMessage(int what, int arg1, int arg2) { + sendMessage(mHeadsetClientStateMachine.obtainMessage(what, arg1, arg2)); + } + + private void sendMessage(int what, int arg1, int arg2, Object obj) { + sendMessage(mHeadsetClientStateMachine.obtainMessage(what, arg1, arg2, obj)); + } + + private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) { + int previousState = mHeadsetClientStateMachine.getConnectionState(mTestDevice); + + sendMessage(msg); + + int newState = mHeadsetClientStateMachine.getConnectionState(mTestDevice); + verifySendBroadcastMultiplePermissions( + hasExtra(EXTRA_PREVIOUS_STATE, previousState), hasExtra(EXTRA_STATE, newState)); + + assertThat(mHeadsetClientStateMachine.getCurrentState()).isInstanceOf(type); } public static class TestHeadsetClientStateMachine extends HeadsetClientStateMachine { diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java index 54674b1d14..1e837f3d9a 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapActivityTest.java @@ -84,7 +84,12 @@ public class BluetoothPbapActivityTest { if (mActivityScenario != null) { // Workaround for b/159805732. Without this, test hangs for 45 seconds. Thread.sleep(1_000); - mActivityScenario.close(); + try { + mActivityScenario.close(); + } catch (Exception e) { + // Ignore exception: Sometimes the state does not reach "DESTROYED", + // however this should not affect our test. + } } enableActivity(false); BluetoothMethodProxy.setInstanceForTesting(null); diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java index dbbb53e9d8..b5d098695a 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java @@ -18,6 +18,8 @@ package com.android.bluetooth.pbap; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -31,6 +33,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Message; import android.os.UserManager; import android.os.test.TestLooper; @@ -97,6 +100,10 @@ public class BluetoothPbapServiceTest { mService = new BluetoothPbapService(mAdapterService, mNotificationManager); mService.start(); mService.setAvailable(true); + + PackageManager pm = mTargetContext.getPackageManager(); + assumeNotNull(pm); + assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)); } @After diff --git a/android/pandora/mmi2grpc/pyrightconfig.json b/android/pandora/mmi2grpc/pyrightconfig.json deleted file mode 100644 index fab9f6135d..0000000000 --- a/android/pandora/mmi2grpc/pyrightconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "typeCheckingMode": "strict", - "useLibraryCodeForTypes": true, - "verboseOutput": false, - "reportMissingTypeStubs": false, - "reportUnknownLambdaType": false, - "reportImportCycles": false, - "reportPrivateUsage": false, - "extraPaths": [ - "../../../pandora/server", - "../../../../../../out/soong/.intermediates/external/pandora/bt-test-interfaces/python/pandora-python-gen-src/gen/", - "../../../../../../out/soong/.intermediates/packages/modules/Bluetooth/pandora/interfaces/python/pandora_experimental-python-gen-src/gen/" - ] -} diff --git a/android/pandora/server/src/Hap.kt b/android/pandora/server/src/Hap.kt index 92fb7bbab6..c4a6f84805 100644 --- a/android/pandora/server/src/Hap.kt +++ b/android/pandora/server/src/Hap.kt @@ -156,8 +156,7 @@ class Hap(val context: Context) : HAPImplBase(), Closeable { ) { grpcUnary<GetPresetRecordResponse>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) - - Log.i(TAG, "getPresetRecord device=${device.address} index=${request.index}") + Log.i(TAG, "getPresetRecord($device, ${request.index})") val presetInfo: BluetoothHapPresetInfo? = bluetoothHapClient.getPresetInfo(device, request.index) @@ -211,8 +210,7 @@ class Hap(val context: Context) : HAPImplBase(), Closeable { ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) - - Log.i(TAG, "writePresetName index=${request.index} name=${request.name}") + Log.i(TAG, "writePresetName($device, ${request.index}, ${request.name})") bluetoothHapClient.setPresetName(device, request.index, request.name) @@ -226,8 +224,7 @@ class Hap(val context: Context) : HAPImplBase(), Closeable { ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) - - Log.i(TAG, "SetActivePreset") + Log.i(TAG, "SetActivePreset($device, ${request.index})") bluetoothHapClient.selectPreset(device, request.index) @@ -241,8 +238,7 @@ class Hap(val context: Context) : HAPImplBase(), Closeable { ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) - - Log.i(TAG, "setNextPreset") + Log.i(TAG, "setNextPreset($device)") bluetoothHapClient.switchToNextPreset(device) @@ -256,8 +252,7 @@ class Hap(val context: Context) : HAPImplBase(), Closeable { ) { grpcUnary<Empty>(scope, responseObserver) { val device = request.connection.toBluetoothDevice(bluetoothAdapter) - - Log.i(TAG, "setPreviousPreset") + Log.i(TAG, "setPreviousPreset($device)") bluetoothHapClient.switchToPreviousPreset(device) diff --git a/android/pandora/test/hap_test.py b/android/pandora/test/hap_test.py index f382cd8601..ec1fd40f89 100644 --- a/android/pandora/test/hap_test.py +++ b/android/pandora/test/hap_test.py @@ -18,6 +18,7 @@ from bumble.gatt import GATT_HEARING_ACCESS_SERVICE, GATT_AUDIO_STREAM_CONTROL_S from bumble.profiles import hap from bumble.profiles.hap import DynamicPresets, HearingAccessService, HearingAidFeatures, HearingAidType, IndependentPresets, PresetRecord, PresetSynchronizationSupport, WritablePresetsSupport +from pandora_experimental.os_grpc_aio import Os as OsAio from pandora_experimental.gatt_grpc_aio import GATT from pandora_experimental.hap_grpc_aio import HAP from pandora_experimental.hap_pb2 import PresetRecord as grpcPresetRecord # type: ignore @@ -84,6 +85,8 @@ class HapTest(base_test.BaseTestClass): @asynchronous async def setup_test(self) -> None: await asyncio.gather(self.dut.reset(), self.ref_left.reset()) + self.logcat = OsAio(channel=self.dut.aio.channel) + await self.logcat.Log("setup test") self.hap_grpc = HAP(channel=self.dut.aio.channel) device_features = HearingAidFeatures(HearingAidType.MONAURAL_HEARING_AID, PresetSynchronizationSupport.PRESET_SYNCHRONIZATION_IS_NOT_SUPPORTED, @@ -162,6 +165,7 @@ class HapTest(base_test.BaseTestClass): @asynchronous async def test_get_features(self) -> None: + await self.logcat.Log("test_get_features") dut_connection_to_ref = await self.setupHapConnection() features = hap.HearingAidFeatures_from_bytes( @@ -170,6 +174,7 @@ class HapTest(base_test.BaseTestClass): @asynchronous async def test_get_preset(self) -> None: + await self.logcat.Log("test_get_preset") dut_connection_to_ref = await self.setupHapConnection() await self.assertIdentiqPresetInDutAndRef(dut_connection_to_ref) diff --git a/android/pandora/test/pyrightconfig.json b/android/pandora/test/pyrightconfig.json deleted file mode 100644 index ea458b5c64..0000000000 --- a/android/pandora/test/pyrightconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "typeCheckingMode": "strict", - "useLibraryCodeForTypes": true, - "verboseOutput": false, - "reportMissingTypeStubs": false, - "reportUnknownLambdaType": false, - "reportImportCycles": false, - "reportPrivateUsage": false, - "extraPaths": [ - "../../../pandora/server", - "../../../../../../external/pandora/avatar", - "../../../../../../external/python/bumble", - "../../../../../../external/python/mobly", - "../../../../../../external/python/pyee", - "../../../../../../external/python/portpicker/src", - "../../../../../../out/soong/.intermediates/external/pandora/bt-test-interfaces/python/pandora-python-gen-src/gen/", - "../../../../../../out/soong/.intermediates/packages/modules/Bluetooth/pandora/interfaces/python/pandora_experimental-python-gen-src/gen/" - ] -}
\ No newline at end of file diff --git a/flags/framework.aconfig b/flags/framework.aconfig index c2f1f51e81..7834bfd524 100644 --- a/flags/framework.aconfig +++ b/flags/framework.aconfig @@ -28,14 +28,6 @@ flag { } flag { - name: "key_missing_broadcast" - is_exported: true - namespace: "bluetooth" - description: "Broadcast when remote device it lost bond" - bug: "311447399" -} - -flag { name: "identity_address_null_if_not_known" namespace: "bluetooth" description: "Return null for identity address if identity address is not known" diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 0a65d636c1..8fb6d6940a 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -225,7 +225,7 @@ package android.bluetooth { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_ACCESS_CANCEL = "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_ACCESS_REPLY = "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_ACCESS_REQUEST = "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; - field @FlaggedApi("com.android.bluetooth.flags.key_missing_broadcast") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_KEY_MISSING = "android.bluetooth.device.action.KEY_MISSING"; + field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_KEY_MISSING = "android.bluetooth.device.action.KEY_MISSING"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_SWITCH_BUFFER_SIZE = "android.bluetooth.device.action.SWITCH_BUFFER_SIZE"; diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index 206056a92e..23b85060dc 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -302,7 +302,6 @@ public final class BluetoothDevice implements Parcelable, Attributable { * * @hide */ - @FlaggedApi(Flags.FLAG_KEY_MISSING_BROADCAST) @SuppressLint("ActionValue") @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000000..e2b62afdf1 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,34 @@ +{ + "typeCheckingMode": "strict", + "useLibraryCodeForTypes": true, + "verboseOutput": false, + "reportMissingTypeStubs": false, + "reportUnknownLambdaType": false, + "reportImportCycles": false, + "reportPrivateUsage": false, + "executionEnvironments": [ + { + "root": "android/pandora/test", + "extraPaths": ["pandora/server"] + }, + { + "root": "android/pandora/mmi2grpc" + }, + { + "root": "pandora/server/bumble_experimental" + }, + { + "root": "framework/tests/bumble/src/bumble_server.py", + "extraPaths": ["pandora/server"] + } + ], + "extraPaths": [ + "../../../external/pandora/avatar", + "../../../external/python/bumble", + "../../../external/python/mobly", + "../../../external/python/pyee", + "../../../external/python/portpicker/src", + "../../../out/soong/.intermediates/external/pandora/bt-test-interfaces/python/pandora-python-gen-src/gen/", + "../../../out/soong/.intermediates/packages/modules/Bluetooth/pandora/interfaces/python/pandora_experimental-python-gen-src/gen/" + ] +} diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java index 6c0f74c635..b59ab9de45 100644 --- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java @@ -16,6 +16,7 @@ package com.android.server.bluetooth; +import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON; import static android.bluetooth.BluetoothAdapter.STATE_BLE_TURNING_OFF; import static android.bluetooth.BluetoothAdapter.STATE_BLE_TURNING_ON; @@ -254,6 +255,13 @@ class BluetoothManagerService { } mName = name; Log.v(TAG, "storeName(" + mName + "): Success"); + mContext.sendBroadcastAsUser( + new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED) + .putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, name) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT), + UserHandle.ALL, + BLUETOOTH_CONNECT, + getTempAllowlistBroadcastOptions()); } private void storeAddress(String address) { diff --git a/sysprop/Android.bp b/sysprop/Android.bp index c2e71a97c8..1b72eb4cb7 100644 --- a/sysprop/Android.bp +++ b/sysprop/Android.bp @@ -11,6 +11,7 @@ sysprop_library { "ble.sysprop", "bta.sysprop", "device_id.sysprop", + "gap.sysprop", "hfp.sysprop", ], property_owner: "Platform", diff --git a/sysprop/BUILD.gn b/sysprop/BUILD.gn index 09126c8df0..407b7a88fc 100644 --- a/sysprop/BUILD.gn +++ b/sysprop/BUILD.gn @@ -7,6 +7,7 @@ sysprop("libcom.android.sysprop.bluetooth") { "ble.sysprop", "bta.sysprop", "device_id.sysprop", + "gap.sysprop", "hfp.sysprop", ] deps = [ "//bt/floss/android-base:android-base" ] diff --git a/sysprop/exported_include/android_bluetooth_sysprop.h b/sysprop/exported_include/android_bluetooth_sysprop.h index 50553ecc5b..8d5122d8e2 100644 --- a/sysprop/exported_include/android_bluetooth_sysprop.h +++ b/sysprop/exported_include/android_bluetooth_sysprop.h @@ -21,4 +21,5 @@ #include <ble.sysprop.h> #include <bta.sysprop.h> #include <device_id.sysprop.h> +#include <gap.sysprop.h> #include <hfp.sysprop.h> diff --git a/sysprop/gap.sysprop b/sysprop/gap.sysprop new file mode 100644 index 0000000000..e9f7e00a3c --- /dev/null +++ b/sysprop/gap.sysprop @@ -0,0 +1,12 @@ +module: "android.sysprop.bluetooth.Gap" +owner: Platform + +prop { + api_name: "min_key_size" + type: Integer + scope: Internal + access: Readonly + prop_name: "bluetooth.gap.min_key_size" +} + + diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc index f22870f900..455309e60a 100644 --- a/system/bta/dm/bta_dm_disc.cc +++ b/system/bta/dm/bta_dm_disc.cc @@ -31,8 +31,10 @@ #include "bta/dm/bta_dm_disc_int.h" #include "bta/include/bta_gatt_api.h" +#include "btif/include/btif_storage.h" #include "common/circular_buffer.h" #include "common/strings.h" +#include "device/include/interop.h" #include "internal_include/bt_target.h" #include "main/shim/dumpsys.h" #include "os/logging/log_adapter.h" @@ -276,8 +278,17 @@ static void bta_dm_disc_result(tBTA_DM_SVC_RES& disc_result) { bta_dm_discovery_cb.service_search_cbacks.on_service_discovery_results(r.bd_addr, r.uuids, r.result); } else { + char remote_name[BD_NAME_LEN] = ""; bta_dm_discovery_cb.transports &= ~BT_TRANSPORT_LE; - GAP_BleReadPeerPrefConnParams(bta_dm_discovery_cb.peer_bdaddr); + if (btif_storage_get_stored_remote_name(bta_dm_discovery_cb.peer_bdaddr, remote_name) && + interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, remote_name)) { + // Some devices provide PPCP values that are incompatible with the device-side firmware. + log::info("disable PPCP read: interop matched name {} address {}", remote_name, + bta_dm_discovery_cb.peer_bdaddr); + } else { + log::info("reading PPCP"); + GAP_BleReadPeerPrefConnParams(bta_dm_discovery_cb.peer_bdaddr); + } bta_dm_discovery_cb.service_search_cbacks.on_gatt_results(bta_dm_discovery_cb.peer_bdaddr, disc_result.gatt_uuids, diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc index 636951cb8c..95eb0a7337 100644 --- a/system/bta/gatt/bta_gattc_act.cc +++ b/system/bta/gatt/bta_gattc_act.cc @@ -297,7 +297,7 @@ void bta_gattc_deregister(tBTA_GATTC_RCB* p_clreg) { /* close all CLCB related to this app */ if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->p_rcb != p_clreg) { + if (!p_clcb->in_use || p_clcb->p_rcb != p_clreg) { continue; } p_clreg->dereg_pending = true; @@ -1504,7 +1504,8 @@ void bta_gattc_process_api_refresh(const RawAddress& remote_bda) { tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[0]; if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { - if (p_clcb_i->p_srcb == p_srvc_cb) { + if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srvc_cb) { + p_clcb = p_clcb_i.get(); found = true; break; } @@ -1576,7 +1577,7 @@ static bool bta_gattc_process_srvc_chg_ind(tCONN_ID conn_id, tBTA_GATTC_RCB* p_c if (p_clcb == NULL || (p_clcb && p_clcb->p_q_cmd != NULL)) { if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { - if (p_clcb_i->p_srcb == p_srcb && p_clcb_i->p_q_cmd == NULL) { + if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srcb && p_clcb_i->p_q_cmd == NULL) { p_clcb = p_clcb_i.get(); break; } diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc index 606566827a..8b0870f55f 100644 --- a/system/bta/gatt/bta_gattc_utils.cc +++ b/system/bta/gatt/bta_gattc_utils.cc @@ -145,7 +145,7 @@ tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_cif(uint8_t client_if, const RawAddress& tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_conn_id(tCONN_ID conn_id) { if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->bta_conn_id == conn_id) { + if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { return p_clcb.get(); } } @@ -960,6 +960,9 @@ void bta_gatt_client_dump(int fd) { if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { stream << " ->clcb (dynamic)\n"; for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (!p_clcb->in_use) { + continue; + } entry_count++; stream << " conn_id: " << loghex(p_clcb->bta_conn_id) << " address: " << ADDRESS_TO_LOGGABLE_STR(p_clcb->bda) diff --git a/system/bta/has/has_client.cc b/system/bta/has/has_client.cc index fddb26cd65..03f75a72e9 100644 --- a/system/bta/has/has_client.cc +++ b/system/bta/has/has_client.cc @@ -1394,6 +1394,12 @@ private: break; } + if (!device.has_presets.contains(nt.index)) { + log::error("Unknown preset. Notification is discarded: {}", nt); + device.has_journal_.Append(HasJournalRecord(nt)); + device.ctp_notifications_.pop_front(); + continue; + } auto preset = device.has_presets.extract(nt.index).value(); auto new_props = preset.GetProperties(); @@ -1560,7 +1566,15 @@ private: /* Get the active preset value */ auto* pp = value; - STREAM_TO_UINT8(device->currently_active_preset, pp); + uint8_t active_preset_index; + STREAM_TO_UINT8(active_preset_index, pp); + if (active_preset_index != 0 && device->isGattServiceValid() && + !device->has_presets.contains(active_preset_index)) { + log::error("Unknown preset {}. Active preset change is discarded", active_preset_index); + device->has_journal_.Append(HasJournalRecord(active_preset_index, false)); + return; + } + device->currently_active_preset = active_preset_index; if (device->isGattServiceValid()) { btif_storage_set_leaudio_has_active_preset(device->addr, device->currently_active_preset); @@ -1573,7 +1587,9 @@ private: MarkDeviceValidIfInInitialDiscovery(*device); if (device->isGattServiceValid()) { - if (!pending_group_operation_timeouts_.empty()) { + if (pending_group_operation_timeouts_.empty()) { + callbacks_->OnActivePresetSelected(device->addr, device->currently_active_preset); + } else { for (auto it = pending_group_operation_timeouts_.rbegin(); it != pending_group_operation_timeouts_.rend(); ++it) { auto& group_op_coordinator = it->second; @@ -1609,9 +1625,6 @@ private: break; } } - - } else { - callbacks_->OnActivePresetSelected(device->addr, device->currently_active_preset); } } } diff --git a/system/bta/ras/ras_client.cc b/system/bta/ras/ras_client.cc index 8d5cd3b473..093dfe0265 100644 --- a/system/bta/ras/ras_client.cc +++ b/system/bta/ras/ras_client.cc @@ -20,6 +20,8 @@ #include "bta/include/bta_gatt_api.h" #include "bta/include/bta_ras_api.h" #include "bta/ras/ras_types.h" +#include "gd/hci/controller_interface.h" +#include "main/shim/entry.h" #include "os/logging/log_adapter.h" #include "stack/include/bt_types.h" #include "stack/include/btm_ble_addr.h" @@ -100,6 +102,11 @@ public: }; void Initialize() override { + auto controller = bluetooth::shim::GetController(); + if (controller && !controller->SupportsBleChannelSounding()) { + log::info("controller does not support channel sounding."); + return; + } BTA_GATTC_AppRegister( [](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { if (instance && p_data) { diff --git a/system/bta/ras/ras_server.cc b/system/bta/ras/ras_server.cc index e6720a440c..a676deb6c4 100644 --- a/system/bta/ras/ras_server.cc +++ b/system/bta/ras/ras_server.cc @@ -23,8 +23,10 @@ #include "bta/include/bta_gatt_api.h" #include "bta/include/bta_ras_api.h" #include "bta/ras/ras_types.h" +#include "gd/hci/controller_interface.h" #include "gd/hci/uuid.h" #include "gd/os/rand.h" +#include "main/shim/entry.h" #include "os/logging/log_adapter.h" #include "stack/include/bt_types.h" #include "stack/include/btm_ble_addr.h" @@ -74,11 +76,15 @@ public: uint16_t last_overwritten_procedure_ = 0; }; - void Initialize() { + void Initialize() override { + auto controller = bluetooth::shim::GetController(); + if (controller && !controller->SupportsBleChannelSounding()) { + log::info("controller does not support channel sounding."); + return; + } Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>()); app_uuid_ = uuid; log::info("Register server with uuid:{}", app_uuid_.ToString()); - BTA_GATTS_AppRegister( app_uuid_, [](tBTA_GATTS_EVT event, tBTA_GATTS* p_data) { diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc index aeeb450a21..8438dee760 100644 --- a/system/btif/src/bluetooth.cc +++ b/system/btif/src/bluetooth.cc @@ -435,9 +435,7 @@ int GetAdapterIndex() { return 0; } // Unsupported outside of FLOSS #endif static int init(bt_callbacks_t* callbacks, bool start_restricted, bool is_common_criteria_mode, - int config_compare_result, const char** /* init_flags */, bool is_atv, - const char* user_data_directory) { - (void)user_data_directory; + int config_compare_result, bool is_atv) { log::info( "start restricted = {} ; common criteria mode = {}, config compare " "result = {}", diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc index 38c45cc78e..b9004718dc 100644 --- a/system/btif/src/btif_a2dp_source.cc +++ b/system/btif/src/btif_a2dp_source.cc @@ -1013,18 +1013,10 @@ static bool btif_a2dp_source_enqueue_callback(BT_HDR* p_buf, size_t frames_n, log::warn("Cannot read RSSI: status {}", status); } - // Intel controllers don't handle ReadFailedContactCounter very well, it - // sends back Hardware Error event which will crash the daemon. So - // temporarily disable this for Floss. - // TODO(b/249876976): Intel controllers to handle this command correctly. - // And if the need for disabling metrics-related HCI call grows, consider - // creating a framework to avoid ifdefs. -#ifndef TARGET_FLOSS status = BTM_ReadFailedContactCounter(peer_bda, btm_read_failed_contact_counter_cb); if (status != tBTM_STATUS::BTM_CMD_STARTED) { log::warn("Cannot read Failed Contact Counter: status {}", status); } -#endif status = BTM_ReadTxPower(peer_bda, BT_TRANSPORT_BR_EDR, btm_read_tx_power_cb); if (status != tBTM_STATUS::BTM_CMD_STARTED) { diff --git a/system/conf/interop_database.conf b/system/conf/interop_database.conf index ba5c679b22..c7827c41af 100644 --- a/system/conf/interop_database.conf +++ b/system/conf/interop_database.conf @@ -194,6 +194,9 @@ Motorola Keyboard KZ500 v122 = Name_Based [INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS] BSMBB09DS = Name_Based ELECOM = Name_Based +Dexcom = Name_Based +DXCM = Name_Based +DX0 = Name_Based # Disable role switch for headsets/car-kits # Some car kits allow role switch but when DUT initiates role switch diff --git a/system/device/test/interop_test.cc b/system/device/test/interop_test.cc index e0c44b1cb7..c27801f208 100644 --- a/system/device/test/interop_test.cc +++ b/system/device/test/interop_test.cc @@ -365,6 +365,9 @@ TEST_F(InteropTest, test_name_hit) { "Targus BT Laser Notebook Mouse")); EXPECT_TRUE(interop_match_name(INTEROP_REMOVE_HID_DIG_DESCRIPTOR, "Motorola Keyboard KZ500")); EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "BSMBB09DS")); + EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "DXCMog")); + EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "Dexcom 123")); + EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS, "DX01ab")); EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_AAC_CODEC, "abramtek M1")); EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_AAC_VBR_CODEC, "Audi_MMI_2781")); EXPECT_TRUE(interop_match_name(INTEROP_DISABLE_AVDTP_RECONFIGURE, "KMM-BT51*HD")); diff --git a/system/gd/hci/BUILD.gn b/system/gd/hci/BUILD.gn index d7366c33b2..8f65671fd7 100644 --- a/system/gd/hci/BUILD.gn +++ b/system/gd/hci/BUILD.gn @@ -43,6 +43,7 @@ source_set("BluetoothHciSources") { deps = [ "//bt/flags:bluetooth_flags_c_lib", "//bt/system/gd:gd_default_deps", + "//bt/sysprop:libcom.android.sysprop.bluetooth", ] configs += [ diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc index 3a4ab17dfb..9a3f721110 100644 --- a/system/gd/hci/controller.cc +++ b/system/gd/hci/controller.cc @@ -16,6 +16,7 @@ #include "hci/controller.h" +#include <android_bluetooth_sysprop.h> #include <bluetooth/log.h> #include <com_android_bluetooth_flags.h> @@ -39,7 +40,9 @@ namespace bluetooth { namespace hci { -constexpr uint8_t kMinEncryptionKeySize = 7; // #define MIN_ENCRYPTION_KEY_SIZE 7 +constexpr int kMinEncryptionKeySize = 7; +constexpr int kMinEncryptionKeySizeDefault = kMinEncryptionKeySize; +constexpr int kMaxEncryptionKeySize = 16; constexpr bool kDefaultVendorCapabilitiesEnabled = true; static const std::string kPropertyVendorCapabilitiesEnabled = @@ -107,8 +110,13 @@ struct Controller::impl { handler->BindOnceOn(this, &Controller::impl::read_buffer_size_complete_handler)); if (is_supported(OpCode::SET_MIN_ENCRYPTION_KEY_SIZE)) { + uint8_t min_key_size = + (uint8_t)std::min(std::max(android::sysprop::bluetooth::Gap::min_key_size().value_or( + kMinEncryptionKeySizeDefault), + kMinEncryptionKeySize), + kMaxEncryptionKeySize); hci_->EnqueueCommand( - SetMinEncryptionKeySizeBuilder::Create(kMinEncryptionKeySize), + SetMinEncryptionKeySizeBuilder::Create(min_key_size), handler->BindOnceOn(this, &Controller::impl::set_min_encryption_key_size_handler)); } diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc index 6949069a0b..d7928ee749 100644 --- a/system/gd/hci/distance_measurement_manager.cc +++ b/system/gd/hci/distance_measurement_manager.cc @@ -538,7 +538,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { kMaxMainModeSteps, kMainModeRepetition, kMode0Steps, CsRole::INITIATOR, CsConfigRttType::RTT_WITH_128_BIT_RANDOM_SEQUENCE, CsSyncPhy::LE_1M_PHY, channel_map, kChannelMapRepetition, CsChannelSelectionType::TYPE_3B, - CsCh3cShape::HAT_SHAPE, kCh3cJump, Enable::DISABLED), + CsCh3cShape::HAT_SHAPE, kCh3cJump), handler_->BindOnceOn(this, &impl::on_cs_setup_command_status_cb, connection_handle)); } @@ -906,8 +906,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (procedure_data->local_status != CsProcedureDoneStatus::PARTIAL_RESULTS && unsent_data_size <= kMtuForRasData) { procedure_data->segmentation_header_.last_segment_ = 1; - } else if (procedure_data->ras_raw_data_.size() < kMtuForRasData) { - log::verbose("waiting for more data, current size {}", procedure_data->ras_raw_data_.size()); + } else if (unsent_data_size < kMtuForRasData) { + log::verbose("waiting for more data, current unsent data size {}", unsent_data_size); return; } diff --git a/system/gd/rust/linux/dbus_projection/dbus_macros/src/lib.rs b/system/gd/rust/linux/dbus_projection/dbus_macros/src/lib.rs index 430454d216..3cc3c91cf2 100644 --- a/system/gd/rust/linux/dbus_projection/dbus_macros/src/lib.rs +++ b/system/gd/rust/linux/dbus_projection/dbus_macros/src/lib.rs @@ -125,16 +125,21 @@ pub fn generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStre for item in ast.items { if let ImplItem::Method(method) = item { - if method.attrs.len() != 1 { - continue; + // Find the #[dbus_method] attribute + let mut dbus_method_attr = None; + for attr in &method.attrs { + if attr.path.get_ident().unwrap().to_string().eq("dbus_method") { + dbus_method_attr = Some(attr); + break; + } } - let attr = &method.attrs[0]; - if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") { + // Skip the method is not marked with #[dbus_method]. + if dbus_method_attr.is_none() { continue; } - let meta_list = match attr.parse_meta().unwrap() { + let meta_list = match dbus_method_attr.unwrap().parse_meta().unwrap() { Meta::List(meta_list) => meta_list, _ => continue, }; @@ -333,9 +338,17 @@ pub fn generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> T // Iterate on every methods of a trait impl for item in ast.items { if let ImplItem::Method(method) = item { - // If the method is not marked with #[dbus_method], just copy the - // original method body. - if method.attrs.len() != 1 { + // Find the #[dbus_method] attribute + let mut dbus_method_attr = None; + for attr in &method.attrs { + if attr.path.get_ident().unwrap().to_string().eq("dbus_method") { + dbus_method_attr = Some(attr); + break; + } + } + + // If the method is not marked with #[dbus_method], just copy the original method body. + if dbus_method_attr.is_none() { methods = quote! { #methods @@ -344,15 +357,9 @@ pub fn generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> T continue; } - let attr = &method.attrs[0]; - if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") { - continue; - } - - let sig = &method.sig; - // For RPC-friendly method, copy the original signature but add public, async, and wrap // the return with Result. + let sig = &method.sig; let mut rpc_sig = sig.clone(); rpc_sig.asyncness = Some(<syn::Token![async]>::default()); rpc_sig.output = match rpc_sig.output { @@ -367,11 +374,12 @@ pub fn generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> T pub #rpc_sig }; - let dbus_method_name = if let Meta::List(meta_list) = attr.parse_meta().unwrap() { - Some(meta_list.nested[0].clone()) - } else { - None - }; + let dbus_method_name = + if let Meta::List(meta_list) = dbus_method_attr.unwrap().parse_meta().unwrap() { + Some(meta_list.nested[0].clone()) + } else { + None + }; if dbus_method_name.is_none() { continue; @@ -769,9 +777,17 @@ pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream { for item in ast.items { if let ImplItem::Method(method) = item { - // If the method is not marked with #[dbus_method], just copy the - // original method body. - if method.attrs.len() != 1 { + // Find the #[dbus_method] attribute + let mut dbus_method_attr = None; + for attr in &method.attrs { + if attr.path.get_ident().unwrap().to_string().eq("dbus_method") { + dbus_method_attr = Some(attr); + break; + } + } + + // If the method is not marked with #[dbus_method], just copy the original method body. + if dbus_method_attr.is_none() { method_impls = quote! { #method_impls #method @@ -779,12 +795,7 @@ pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream { continue; } - let attr = &method.attrs[0]; - if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") { - continue; - } - - let meta_list = match attr.parse_meta().unwrap() { + let meta_list = match dbus_method_attr.unwrap().parse_meta().unwrap() { Meta::List(meta_list) => meta_list, _ => continue, }; diff --git a/system/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs index 5a6d76b770..e3bd8480c3 100644 --- a/system/gd/rust/topshim/src/btif.rs +++ b/system/gd/rust/topshim/src/btif.rs @@ -1262,9 +1262,7 @@ impl BluetoothInterface { guest_mode, is_common_criteria_mode, config_compare_result, - std::ptr::null_mut(), - is_atv, - std::ptr::null() + is_atv ); self.is_init = init == 0; diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h index 7e891573b9..77ff10a999 100644 --- a/system/include/hardware/bluetooth.h +++ b/system/include/hardware/bluetooth.h @@ -691,8 +691,7 @@ typedef struct { * the local device is an Android TV */ int (*init)(bt_callbacks_t* callbacks, bool guest_mode, bool is_common_criteria_mode, - int config_compare_result, const char** init_flags, bool is_atv, - const char* user_data_directory); + int config_compare_result, bool is_atv); /** Enable Bluetooth. */ int (*enable)(); diff --git a/system/main/Android.bp b/system/main/Android.bp index a0d8ffe633..629735a233 100644 --- a/system/main/Android.bp +++ b/system/main/Android.bp @@ -65,7 +65,6 @@ cc_library { visibility: [ "//cts/hostsidetests:__subpackages__", "//packages/modules/Bluetooth:__subpackages__", - "//vendor:__subpackages__", ], defaults: ["fluoride_defaults"], header_libs: ["libbluetooth_headers"], diff --git a/system/pdl/hci/hci_packets.pdl b/system/pdl/hci/hci_packets.pdl index 9225204045..df7c47db2a 100644 --- a/system/pdl/hci/hci_packets.pdl +++ b/system/pdl/hci/hci_packets.pdl @@ -5012,6 +5012,7 @@ packet LeCsReadLocalSupportedCapabilitiesComplete : CommandComplete (command_op_ optional_t_fcs_times_supported : CsOptionalTFcsTimesSupported, optional_t_pm_times_supported : CsOptionalTPmTimesSupported, t_sw_time_supported : 8, + optional_tx_snr_capability : 8, } packet LeCsReadRemoteSupportedCapabilities : DistanceMeasurementCommand (op_code = LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES) { @@ -5044,6 +5045,7 @@ packet LeCsWriteCachedRemoteSupportedCapabilities : DistanceMeasurementCommand ( optional_t_fcs_times_supported : CsOptionalTFcsTimesSupported, optional_t_pm_times_supported : CsOptionalTPmTimesSupported, t_sw_time_supported : 8, + optional_tx_snr_capability: 8, } packet LeCsWriteCachedRemoteSupportedCapabilitiesComplete : CommandComplete (command_op_code = LE_CS_WRITE_CACHED_REMOTE_SUPPORTED_CAPABILITIES) { @@ -5173,7 +5175,7 @@ packet LeCsCreateConfig : DistanceMeasurementCommand (op_code = LE_CS_CREATE_CON channel_selection_type : CsChannelSelectionType, ch3c_shape : CsCh3cShape, ch3c_jump : 8, - companion_signal_enable : Enable, + _reserved_ : 8, } packet LeCsCreateConfigStatus : CommandStatus (command_op_code = LE_CS_CREATE_CONFIG) { @@ -6801,6 +6803,7 @@ packet LeCsReadRemoteSupportedCapabilitiesComplete : LeMetaEvent (subevent_code optional_t_fcs_times_supported : CsOptionalTFcsTimesSupported, optional_t_pm_times_supported : CsOptionalTPmTimesSupported, t_sw_time_supported : 8, + optional_tx_snr_capability : 8, } packet LeCsReadRemoteFaeTableComplete : LeMetaEvent (subevent_code = LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE) { @@ -6854,7 +6857,7 @@ packet LeCsConfigComplete : LeMetaEvent (subevent_code = LE_CS_CONFIG_COMPLETE) channel_selection_type : CsChannelSelectionType, ch3c_shape : CsCh3cShape, ch3c_jump : 8, - companion_signal_enable : Enable, + _reserved_ : 8, t_ip1_time : 8, t_ip2_time : 8, t_fcs_time : 8, diff --git a/system/stack/btm/btm_sco_hfp_hal_linux.cc b/system/stack/btm/btm_sco_hfp_hal_linux.cc index 1488b6899e..018ba1fd65 100644 --- a/system/stack/btm/btm_sco_hfp_hal_linux.cc +++ b/system/stack/btm/btm_sco_hfp_hal_linux.cc @@ -294,6 +294,13 @@ void init() { bluetooth::log::info("Successfully queried SCO codec capabilities."); } +#ifndef TARGET_FLOSS + // If hfp software path is not enabled, fallback to offload path. + if (!osi_property_get_bool("bluetooth.hfp.software_datapath.enabled", false)) { + enable_offload(true); + } +#endif + close(fd); } diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc index d458bf79a8..e647715e40 100644 --- a/system/stack/btm/btm_sec.cc +++ b/system/stack/btm/btm_sec.cc @@ -26,6 +26,7 @@ #include "stack/btm/btm_sec.h" +#include <android_bluetooth_sysprop.h> #include <base/functional/bind.h> #include <base/strings/stringprintf.h> #include <bluetooth/log.h> @@ -3365,7 +3366,16 @@ void btm_sec_encrypt_change(uint16_t handle, tHCI_STATUS status, uint8_t encr_en } } -constexpr uint8_t MIN_KEY_SIZE = 7; +constexpr int MIN_KEY_SIZE = 7; +constexpr int MIN_KEY_SIZE_DEFAULT = MIN_KEY_SIZE; +constexpr int MAX_KEY_SIZE = 16; +static uint8_t get_min_enc_key_size() { + static uint8_t min_key_size = (uint8_t)std::min( + std::max(android::sysprop::bluetooth::Gap::min_key_size().value_or(MIN_KEY_SIZE_DEFAULT), + MIN_KEY_SIZE), + MAX_KEY_SIZE); + return min_key_size; +} static void read_encryption_key_size_complete_after_encryption_change(uint8_t status, uint16_t handle, @@ -3386,7 +3396,7 @@ static void read_encryption_key_size_complete_after_encryption_change(uint8_t st return; } - if (key_size < MIN_KEY_SIZE) { + if (key_size < get_min_enc_key_size()) { log::error("encryption key too short, disconnecting. handle:0x{:x},key_size:{}", handle, key_size); @@ -3961,7 +3971,7 @@ static void read_encryption_key_size_complete_after_key_refresh(uint8_t status, return; } - if (key_size < MIN_KEY_SIZE) { + if (key_size < get_min_enc_key_size()) { log::error("encryption key too short, disconnecting. handle: 0x{:x} key_size {}", handle, key_size); diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc index dd15c2a557..ebfcb041ec 100644 --- a/system/test/headless/headless.cc +++ b/system/test/headless/headless.cc @@ -258,7 +258,7 @@ void HeadlessStack::SetUp() { const bool is_atv = false; int status = bluetoothInterface.init(&bt_callbacks, start_restricted, is_common_criteria_mode, - config_compare_result, nullptr, is_atv, nullptr); + config_compare_result, is_atv); if (status == BT_STATUS_SUCCESS) { log::info("Initialized bluetooth callbacks"); diff --git a/system/test/mock/mock_stack_btm_interface.cc b/system/test/mock/mock_stack_btm_interface.cc index 7016d4b662..116d2008af 100644 --- a/system/test/mock/mock_stack_btm_interface.cc +++ b/system/test/mock/mock_stack_btm_interface.cc @@ -142,6 +142,10 @@ struct btm_client_interface_t default_btm_client_interface = { }, .BTM_IsLinkKeyKnown = [](const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport */) -> bool { return false; }, + .BTM_SetSecurityLevel = [](bool /* is_originator */, const char* /*p_name */, + uint8_t /* service_id */, uint16_t /* sec_level */, + uint16_t /* psm */, uint32_t /* mx_proto_id */, + uint32_t /* mx_chan_id */) -> bool { return false; }, .BTM_SecClrService = [](uint8_t /* service_id */) -> uint8_t { return 0; }, .BTM_SecClrServiceByPsm = [](uint16_t /* psm */) -> uint8_t { return 0; }, .BTM_SecBond = [](const RawAddress& /* bd_addr */, tBLE_ADDR_TYPE /* addr_type */, @@ -150,7 +154,6 @@ struct btm_client_interface_t default_btm_client_interface = { .BTM_SecBondCancel = [](const RawAddress& /* bd_addr */) -> tBTM_STATUS { return tBTM_STATUS::BTM_SUCCESS; }, - .BTM_RemoteOobDataReply = [](tBTM_STATUS /* res */, const RawAddress& /* bd_addr */, const Octet16& /* c */, const Octet16& /* r */) {}, .BTM_PINCodeReply = [](const RawAddress& /* bd_addr */, tBTM_STATUS /* res */, diff --git a/system/test/suite/adapter/adapter_unittest.cc b/system/test/suite/adapter/adapter_unittest.cc index 65de637a7c..8e2769ea10 100644 --- a/system/test/suite/adapter/adapter_unittest.cc +++ b/system/test/suite/adapter/adapter_unittest.cc @@ -160,7 +160,7 @@ TEST_F(BluetoothTest, AdapterCleanupDuringDiscovery) { ASSERT_TRUE(callbacks != nullptr); for (int i = 0; i < kTestRepeatCount; ++i) { - bt_interface()->init(callbacks, false, false, 0, nullptr, false, nullptr); + bt_interface()->init(callbacks, false, false, 0, false); EXPECT_EQ(bt_interface()->enable(), BT_STATUS_SUCCESS); semaphore_wait(adapter_state_changed_callback_sem_); EXPECT_EQ(GetState(), BT_STATE_ON) << "Adapter did not turn on."; diff --git a/system/test/suite/adapter/bluetooth_test.cc b/system/test/suite/adapter/bluetooth_test.cc index e28a2259fa..4855b7a72d 100644 --- a/system/test/suite/adapter/bluetooth_test.cc +++ b/system/test/suite/adapter/bluetooth_test.cc @@ -86,7 +86,7 @@ void BluetoothTest::SetUp() { remove("/data/misc/bluedroid/bt_config.conf.encrypted-checksum"); instance = this; - int status = bluetoothInterface.init(&callbacks, false, false, 0, nullptr, false, nullptr); + int status = bluetoothInterface.init(&callbacks, false, false, 0, false); ASSERT_EQ(status, BT_STATUS_SUCCESS); } |