Merge "Fix deadlock in CachedBluetoothDeviceManager"

This commit is contained in:
TreeHugger Robot
2019-11-13 05:24:43 +00:00
committed by Android (Google) Code Review
4 changed files with 159 additions and 141 deletions

View File

@@ -212,15 +212,21 @@ public class BluetoothEventManager {
}
private void dispatchAudioModeChanged() {
mDeviceManager.dispatchAudioModeChanged();
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
cachedDevice.onAudioModeChanged();
}
for (BluetoothCallback callback : mCallbacks) {
callback.onAudioModeChanged();
}
}
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
@VisibleForTesting
void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
boolean isActive = Objects.equals(cachedDevice, activeDevice);
cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
}
for (BluetoothCallback callback : mCallbacks) {
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
}

View File

@@ -26,7 +26,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -229,14 +228,6 @@ public class CachedBluetoothDeviceManager {
}
}
public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
boolean isActive = Objects.equals(cachedDevice, activeDevice);
cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
}
}
public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
cachedDevice, int state) {
return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
@@ -257,12 +248,6 @@ public class CachedBluetoothDeviceManager {
}
}
public synchronized void dispatchAudioModeChanged() {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
cachedDevice.onAudioModeChanged();
}
}
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);

View File

@@ -15,6 +15,8 @@
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -41,6 +43,9 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BluetoothEventManagerTest {
@@ -54,10 +59,24 @@ public class BluetoothEventManagerTest {
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
private BluetoothDevice mBluetoothDevice;
@Mock
private HeadsetProfile mHfpProfile;
@Mock
private A2dpProfile mA2dpProfile;
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
private Context mContext;
private Intent mIntent;
private BluetoothEventManager mBluetoothEventManager;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
@Before
public void setUp() {
@@ -67,6 +86,12 @@ public class BluetoothEventManagerTest {
mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
}
@Test
@@ -194,4 +219,129 @@ public class BluetoothEventManagerTest {
verify(mBluetoothCallback, never()).onAclConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED);
}
/**
* Test to verify onActiveDeviceChanged().
*/
@Test
public void dispatchActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedDevice1);
cachedDevices.add(mCachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
// Connect both devices for A2DP and HFP
mCachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The first device is active for A2DP, the second device is active for HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// The first device is active for A2DP and HFP
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The second device is active for A2DP and HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for A2DP
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.A2DP);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
}
/**
* Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
*/
@Test
public void dispatchActiveDeviceChanged_withA2dpAndHearingAid() {
final List<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
cachedDevices.add(mCachedDevice1);
cachedDevices.add(mCachedDevice2);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
// Connect device1 for A2DP and HFP and device2 for Hearing Aid
mCachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The first device is active for A2DP and HFP
mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.A2DP);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice1, BluetoothProfile.HEADSET);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The second device is active for Hearing Aid and the first device is not active
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.A2DP);
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEADSET);
mBluetoothEventManager
.dispatchActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEARING_AID);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
// No active device for Hearing Aid
mBluetoothEventManager.dispatchActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
}
}

View File

@@ -26,7 +26,6 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import org.junit.Before;
@@ -50,8 +49,6 @@ public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
private final static String DEVICE_SUMMARY_1 = "summary 1";
private final static String DEVICE_SUMMARY_2 = "summary 2";
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
private final BluetoothClass DEVICE_CLASS_1 =
@@ -401,124 +398,4 @@ public class CachedBluetoothDeviceManagerTest {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
}
/**
* Test to verify onActiveDeviceChanged().
*/
@Test
public void onActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
// Connect both devices for A2DP and HFP
cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The first device is active for A2DP, the second device is active for HFP
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// The first device is active for A2DP and HFP
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
// The second device is active for A2DP and HFP
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.A2DP);
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for A2DP
mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
// No active device for HFP
mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
}
/**
* Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
*/
@Test
public void onActiveDeviceChanged_withA2dpAndHearingAid() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
// Connect device1 for A2DP and HFP and device2 for Hearing Aid
cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
// Verify that both devices are connected and none is Active
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The first device is active for A2DP and HFP
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
// The second device is active for Hearing Aid and the first device is not active
mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEARING_AID);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
// No active device for Hearing Aid
mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
}
}