Loading packages/SettingsLib/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1186,4 +1186,7 @@ <!-- Name of the this device. [CHAR LIMIT=30] --> <string name="media_transfer_this_device_name">This device</string> <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] --> <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string> </resources> packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +86 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.SystemClock; import android.text.TextUtils; Loading Loading @@ -55,6 +58,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Some Hearing Aids (especially the 2nd device) needs more time to do service discovery private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000; private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; private static final long MAX_MEDIA_PROFILE_CONNECT_DELAY = 60000; private final Context mContext; private final BluetoothAdapter mLocalAdapter; Loading Loading @@ -90,9 +94,35 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsActiveDeviceA2dp = false; private boolean mIsActiveDeviceHeadset = false; private boolean mIsActiveDeviceHearingAid = false; // Media profile connect state private boolean mIsA2dpProfileConnectedFail = false; private boolean mIsHeadsetProfileConnectedFail = false; private boolean mIsHearingAidProfileConnectedFail = false; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case BluetoothProfile.A2DP: mIsA2dpProfileConnectedFail = true; break; case BluetoothProfile.HEADSET: mIsHeadsetProfileConnectedFail = true; break; case BluetoothProfile.HEARING_AID: mIsHearingAidProfileConnectedFail = true; break; default: Log.w(TAG, "handleMessage(): unknown message : " + msg.what); break; } Log.w(TAG, "Connect to profile : " + msg.what + " timeout, show error message !"); refresh(); } }; CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager, BluetoothDevice device) { mContext = context; Loading Loading @@ -133,6 +163,35 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } synchronized (mProfileLock) { if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile || profile instanceof HearingAidProfile) { setProfileConnectedStatus(profile.getProfileId(), false); switch (newProfileState) { case BluetoothProfile.STATE_CONNECTED: mHandler.removeMessages(profile.getProfileId()); break; case BluetoothProfile.STATE_CONNECTING: mHandler.sendEmptyMessageDelayed(profile.getProfileId(), MAX_MEDIA_PROFILE_CONNECT_DELAY); break; case BluetoothProfile.STATE_DISCONNECTING: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); } break; case BluetoothProfile.STATE_DISCONNECTED: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); setProfileConnectedStatus(profile.getProfileId(), true); } break; default: Log.w(TAG, "onProfileStateChanged(): unknown profile state : " + newProfileState); break; } } if (newProfileState == BluetoothProfile.STATE_CONNECTED) { if (profile instanceof MapProfile) { profile.setPreferred(mDevice, true); Loading Loading @@ -162,6 +221,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> fetchActiveDevices(); } @VisibleForTesting void setProfileConnectedStatus(int profileId, boolean isFailed) { switch (profileId) { case BluetoothProfile.A2DP: mIsA2dpProfileConnectedFail = isFailed; break; case BluetoothProfile.HEADSET: mIsHeadsetProfileConnectedFail = isFailed; break; case BluetoothProfile.HEARING_AID: mIsHearingAidProfileConnectedFail = isFailed; break; default: Log.w(TAG, "setProfileConnectedStatus(): unknown profile id : " + profileId); break; } } public void disconnect() { synchronized (mProfileLock) { for (LocalBluetoothProfile profile : mProfiles) { Loading Loading @@ -844,6 +921,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> int leftBattery = -1; int rightBattery = -1; if (isProfileConnectedFail() && isConnected()) { return mContext.getString(R.string.profile_connect_timeout_subtext); } synchronized (mProfileLock) { for (LocalBluetoothProfile profile : getProfiles()) { int connectionStatus = getProfileConnectionState(profile); Loading Loading @@ -943,6 +1024,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return leftBattery >= 0 && rightBattery >= 0; } private boolean isProfileConnectedFail() { return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail || mIsHeadsetProfileConnectedFail; } /** * @return resource for android auto string that describes the connection state of this device. */ Loading packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +15 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; Loading @@ -31,6 +32,8 @@ import android.bluetooth.BluetoothProfile; import android.content.Context; import android.media.AudioManager; import com.android.settingslib.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -922,4 +925,16 @@ public class CachedBluetoothDeviceTest { assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1); assertThat(subCachedDevice.mDevice).isEqualTo(mDevice); } @Test public void getConnectionSummary_profileConnectedFail_showErrorMessage() { final A2dpProfile profle = mock(A2dpProfile.class); mCachedDevice.onProfileStateChanged(profle, BluetoothProfile.STATE_CONNECTED); mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true); when(profle.getConnectionStatus(mDevice)).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( mContext.getString(R.string.profile_connect_timeout_subtext)); } } Loading
packages/SettingsLib/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1186,4 +1186,7 @@ <!-- Name of the this device. [CHAR LIMIT=30] --> <string name="media_transfer_this_device_name">This device</string> <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] --> <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string> </resources>
packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +86 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.SystemClock; import android.text.TextUtils; Loading Loading @@ -55,6 +58,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Some Hearing Aids (especially the 2nd device) needs more time to do service discovery private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000; private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; private static final long MAX_MEDIA_PROFILE_CONNECT_DELAY = 60000; private final Context mContext; private final BluetoothAdapter mLocalAdapter; Loading Loading @@ -90,9 +94,35 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsActiveDeviceA2dp = false; private boolean mIsActiveDeviceHeadset = false; private boolean mIsActiveDeviceHearingAid = false; // Media profile connect state private boolean mIsA2dpProfileConnectedFail = false; private boolean mIsHeadsetProfileConnectedFail = false; private boolean mIsHearingAidProfileConnectedFail = false; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case BluetoothProfile.A2DP: mIsA2dpProfileConnectedFail = true; break; case BluetoothProfile.HEADSET: mIsHeadsetProfileConnectedFail = true; break; case BluetoothProfile.HEARING_AID: mIsHearingAidProfileConnectedFail = true; break; default: Log.w(TAG, "handleMessage(): unknown message : " + msg.what); break; } Log.w(TAG, "Connect to profile : " + msg.what + " timeout, show error message !"); refresh(); } }; CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager, BluetoothDevice device) { mContext = context; Loading Loading @@ -133,6 +163,35 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } synchronized (mProfileLock) { if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile || profile instanceof HearingAidProfile) { setProfileConnectedStatus(profile.getProfileId(), false); switch (newProfileState) { case BluetoothProfile.STATE_CONNECTED: mHandler.removeMessages(profile.getProfileId()); break; case BluetoothProfile.STATE_CONNECTING: mHandler.sendEmptyMessageDelayed(profile.getProfileId(), MAX_MEDIA_PROFILE_CONNECT_DELAY); break; case BluetoothProfile.STATE_DISCONNECTING: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); } break; case BluetoothProfile.STATE_DISCONNECTED: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); setProfileConnectedStatus(profile.getProfileId(), true); } break; default: Log.w(TAG, "onProfileStateChanged(): unknown profile state : " + newProfileState); break; } } if (newProfileState == BluetoothProfile.STATE_CONNECTED) { if (profile instanceof MapProfile) { profile.setPreferred(mDevice, true); Loading Loading @@ -162,6 +221,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> fetchActiveDevices(); } @VisibleForTesting void setProfileConnectedStatus(int profileId, boolean isFailed) { switch (profileId) { case BluetoothProfile.A2DP: mIsA2dpProfileConnectedFail = isFailed; break; case BluetoothProfile.HEADSET: mIsHeadsetProfileConnectedFail = isFailed; break; case BluetoothProfile.HEARING_AID: mIsHearingAidProfileConnectedFail = isFailed; break; default: Log.w(TAG, "setProfileConnectedStatus(): unknown profile id : " + profileId); break; } } public void disconnect() { synchronized (mProfileLock) { for (LocalBluetoothProfile profile : mProfiles) { Loading Loading @@ -844,6 +921,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> int leftBattery = -1; int rightBattery = -1; if (isProfileConnectedFail() && isConnected()) { return mContext.getString(R.string.profile_connect_timeout_subtext); } synchronized (mProfileLock) { for (LocalBluetoothProfile profile : getProfiles()) { int connectionStatus = getProfileConnectionState(profile); Loading Loading @@ -943,6 +1024,11 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return leftBattery >= 0 && rightBattery >= 0; } private boolean isProfileConnectedFail() { return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail || mIsHeadsetProfileConnectedFail; } /** * @return resource for android auto string that describes the connection state of this device. */ Loading
packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +15 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; Loading @@ -31,6 +32,8 @@ import android.bluetooth.BluetoothProfile; import android.content.Context; import android.media.AudioManager; import com.android.settingslib.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -922,4 +925,16 @@ public class CachedBluetoothDeviceTest { assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1); assertThat(subCachedDevice.mDevice).isEqualTo(mDevice); } @Test public void getConnectionSummary_profileConnectedFail_showErrorMessage() { final A2dpProfile profle = mock(A2dpProfile.class); mCachedDevice.onProfileStateChanged(profle, BluetoothProfile.STATE_CONNECTED); mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true); when(profle.getConnectionStatus(mDevice)).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( mContext.getString(R.string.profile_connect_timeout_subtext)); } }