From 14e56413217bedbf1ba011ef521316b91fb9fed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Thu, 2 Mar 2023 15:08:18 +0000 Subject: [PATCH 1/4] CachedBluetoothDevice: Add missing LeAudioProfile LeAudioProfile state changes were not handled and so we couldn't detect timeouts on this profile. Bug: 268587046 Test: BluetoothInstrumentationTest Change-Id: I492bfc970103a3235a2ec07ebea88016753507d9 --- .../android/settingslib/bluetooth/CachedBluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index c8187b89b0ed1..445e282c65a26 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -208,7 +208,7 @@ public class CachedBluetoothDevice implements Comparable synchronized (mProfileLock) { if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile - || profile instanceof HearingAidProfile) { + || profile instanceof HearingAidProfile || profile instanceof LeAudioProfile) { setProfileConnectedStatus(profile.getProfileId(), false); switch (newProfileState) { case BluetoothProfile.STATE_CONNECTED: From f9da437a5fcb84346a12bd0fac8659cc8f5dab59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Thu, 2 Mar 2023 15:09:23 +0000 Subject: [PATCH 2/4] CachedBluetoothDevice: Fix detecting connect fail Transition from CONNECTING to DISCONNECTED doesn't have to mean that the connection has failed. Add check to verify that connection policy for profile is ALLOWED and only then trigger connection failed. Setting connection failed results in "Problem connecting." in Bluetooth connected devices UI. Bug: 268587046 Test: BluetoothInstrumentationTest Change-Id: I1eb116e4ae1735fafe3d7f35fc0bf51d21cb87d3 --- .../bluetooth/CachedBluetoothDevice.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 445e282c65a26..8b2eebe8afe51 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -226,7 +226,20 @@ public class CachedBluetoothDevice implements Comparable case BluetoothProfile.STATE_DISCONNECTED: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); - setProfileConnectedStatus(profile.getProfileId(), true); + if (profile.getConnectionPolicy(mDevice) > + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { + /* + * If we received state DISCONNECTED and previous state was + * CONNECTING and connection policy is FORBIDDEN or UNKNOWN + * then it's not really a failure to connect. + * + * Connection profile is considered as failed when connection + * policy indicates that profile should be connected + * but it got disconnected. + */ + Log.w(TAG, "onProfileStateChanged(): Failed to connect profile"); + setProfileConnectedStatus(profile.getProfileId(), true); + } } break; default: @@ -1188,6 +1201,13 @@ public class CachedBluetoothDevice implements Comparable } private boolean isProfileConnectedFail() { + Log.d(TAG, "anonymizedAddress=" + mDevice.getAnonymizedAddress() + + " mIsA2dpProfileConnectedFail=" + mIsA2dpProfileConnectedFail + + " mIsHearingAidProfileConnectedFail=" + mIsHearingAidProfileConnectedFail + + " mIsLeAudioProfileConnectedFail=" + mIsLeAudioProfileConnectedFail + + " mIsHeadsetProfileConnectedFail=" + mIsHeadsetProfileConnectedFail + + " isConnectedSapDevice()=" + isConnectedSapDevice()); + return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail || (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail) || mIsLeAudioProfileConnectedFail; From 0ed31dcff196cb31db22bf67a617f1925f1e64fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Mon, 6 Mar 2023 13:34:43 +0000 Subject: [PATCH 3/4] CachedBluetoothDevice: Fix merge conflict Bug: 268587046 Test: atest CachedBluetoothDeviceTest Merged-In: I9c50a7b759b7fbcd80f1f5706909ce0a6ddc5959 Change-Id: I46df1c8b43781dac076bc9a4a9f2ea32d20f6337 --- .../settingslib/bluetooth/CachedBluetoothDeviceTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 315ab0aac878d..3f19792f342ce 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -74,6 +74,8 @@ public class CachedBluetoothDeviceTest { @Mock private HearingAidProfile mHearingAidProfile; @Mock + private LeAudioProfile mLeAudioProfile; + @Mock private BluetoothDevice mDevice; @Mock private BluetoothDevice mSubDevice; From fb4369ae83de1b1fd873a8444db27bc225a55a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Mon, 6 Mar 2023 11:28:55 +0000 Subject: [PATCH 4/4] CachedBluetoothDevice: Add tests for connect fail Bug: 268587046 Test: atest CachedBluetoothDeviceTest Change-Id: I3af271d0a67fa38fa1225f77fb76a6b5f855760b --- .../bluetooth/CachedBluetoothDeviceTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 3f19792f342ce..df3861a699b07 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -94,15 +94,76 @@ public class CachedBluetoothDeviceTest { mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHfpProfile.isProfileReady()).thenReturn(true); + when(mHfpProfile.getProfileId()).thenReturn(BluetoothProfile.HEADSET); when(mA2dpProfile.isProfileReady()).thenReturn(true); + when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP); when(mPanProfile.isProfileReady()).thenReturn(true); + when(mPanProfile.getProfileId()).thenReturn(BluetoothProfile.PAN); when(mHearingAidProfile.isProfileReady()).thenReturn(true); + when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID); + when(mLeAudioProfile.isProfileReady()).thenReturn(true); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice)); mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice)); doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel(); doAnswer((invocation) -> mBatteryLevel).when(mSubCachedDevice).getBatteryLevel(); } + private void testTransitionFromConnectingToDisconnected( + LocalBluetoothProfile connectingProfile, LocalBluetoothProfile connectedProfile, + int connectionPolicy, String expectedSummary) { + // Arrange: + // At least one profile has to be connected + updateProfileStatus(connectedProfile, BluetoothProfile.STATE_CONNECTED); + // Set profile under test to CONNECTING + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_CONNECTING); + // Set connection policy + when(connectingProfile.getConnectionPolicy(mDevice)).thenReturn(connectionPolicy); + + // Act & Assert: + // Get the expected connection summary. + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_DISCONNECTED); + assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(expectedSummary); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyAllowed_problem() { + String connectTimeoutString = mContext.getString(R.string.profile_connect_timeout_subtext); + + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyForbidden_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyUnknown_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + } + @Test public void getConnectionSummary_testProfilesInactive_returnPairing() { // Arrange: