Merge "BluetoothRouteManagerTest: avoid looper leak" into main
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 3b5e342..7cb05cd 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -23,6 +23,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.telecom.Log;
import android.telecom.Logging.Session;
@@ -184,6 +185,13 @@
}
}
+ public @NonNull Looper getLooper() {
+ if (mHandler == null) {
+ mHandler = getNewHandler();
+ }
+ return mHandler.getLooper();
+ }
+
/**
* Creates a new ringtone Handler running in its own thread.
*/
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 766db8e..64c7f33 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -248,14 +248,17 @@
.getBluetoothRoutes();
List<Pair<AudioRoute, BluetoothDevice>> btRoutesToRemove =
new ArrayList<>();
- for (AudioRoute route: btRoutes.keySet()) {
- if (route.getType() != PROFILE_TO_AUDIO_ROUTE_MAP.get(profile)) {
- continue;
+ // Prevent concurrent modification exception by just iterating
+ //through keys instead of simultaneously removing them. Ensure that
+ // we synchronize on the map while we traverse via an Iterator.
+ synchronized (btRoutes) {
+ for (AudioRoute route: btRoutes.keySet()) {
+ if (route.getType() != PROFILE_TO_AUDIO_ROUTE_MAP.get(profile)) {
+ continue;
+ }
+ BluetoothDevice device = btRoutes.get(route);
+ btRoutesToRemove.add(new Pair<>(route, device));
}
- BluetoothDevice device = btRoutes.get(route);
- // Prevent concurrent modification exception by just iterating through keys instead of
- // simultaneously removing them.
- btRoutesToRemove.add(new Pair<>(route, device));
}
for (Pair<AudioRoute, BluetoothDevice> routeToRemove:
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index beddcbe..1330be4 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -272,6 +272,10 @@
if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID
|| deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO
|| mIsScoManagedByAudio) {
+ if (!mIsInCall) {
+ Log.i(LOG_TAG, "Ignoring audio on since we're not in a call");
+ return;
+ }
if (!mBluetoothDeviceManager.setCommunicationDeviceForAddress(
device.getAddress())) {
Log.i(this, "handleActiveDeviceChanged: Failed to set "
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 67a5cc6..4913904 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -35,6 +35,7 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.ContentResolver;
+import android.media.AudioDeviceInfo;
import android.os.Parcel;
import android.telecom.Log;
@@ -104,6 +105,8 @@
BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID);
when(mDeviceManager.connectAudio(anyString(), anyBoolean())).thenReturn(true);
when(mDeviceManager.isHearingAidSetAsCommunicationDevice()).thenReturn(true);
+ when(mCommunicationDeviceTracker.isAudioDeviceSetForType(
+ eq(AudioDeviceInfo.TYPE_HEARING_AID))).thenReturn(true);
setupConnectedDevices(null, HEARING_AIDS, null, null, HEARING_AIDS, null);
when(mBluetoothHeadset.getAudioState(nullable(BluetoothDevice.class)))
@@ -130,7 +133,8 @@
BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID);
when(mDeviceManager.connectAudio(anyString(), anyBoolean())).thenReturn(true);
when(mDeviceManager.isHearingAidSetAsCommunicationDevice()).thenReturn(true);
-
+ when(mCommunicationDeviceTracker.isAudioDeviceSetForType(
+ eq(AudioDeviceInfo.TYPE_HEARING_AID))).thenReturn(true);
setupConnectedDevices(null, HEARING_AIDS, null, null, HEARING_AIDS, null);
when(mBluetoothHeadset.getAudioState(nullable(BluetoothDevice.class)))
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
index 52ac597..19b08c6 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteControllerTest.java
@@ -717,7 +717,7 @@
new HashSet<>());
verify(mAudioService, timeout(TEST_TIMEOUT)).setMicrophoneMute(eq(true), anyString(),
anyInt(), anyString());
- verify(mCallsManager, timeout(TEST_TIMEOUT)).onCallAudioStateChanged(
+ verify(mCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged(
any(CallAudioState.class), eq(expectedState));
}
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 46916fd..ad62643 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -50,6 +50,7 @@
import android.media.audio.Flags;
import android.net.Uri;
import android.os.Bundle;
+import android.os.TestLooperManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.VibrationAttributes;
@@ -65,6 +66,7 @@
import android.util.Pair;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.AsyncRingtonePlayer;
@@ -136,6 +138,7 @@
new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"),
"pa_id");
+ TestLooperManager mLooperManager;
boolean mIsHapticPlaybackSupported = true; // Note: initializeRinger() after changes.
AsyncRingtonePlayer asyncRingtonePlayer = new AsyncRingtonePlayer();
Ringer mRingerUnderTest;
@@ -191,6 +194,18 @@
super.tearDown();
}
+ private void acquireLooper() {
+ mLooperManager = InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(asyncRingtonePlayer.getLooper());
+ }
+
+ private void processAllMessages() {
+ for (var msg = mLooperManager.poll(); msg != null && msg.getTarget() != null;) {
+ mLooperManager.execute(msg);
+ mLooperManager.recycle(msg);
+ }
+ }
+
@SmallTest
@Test
public void testSimpleVibrationPrecedesValidSupportedDefaultRingVibrationOverride()
@@ -643,16 +658,20 @@
@SmallTest
@Test
public void testDelayRingerForBtHfpDevices() throws Exception {
+ acquireLooper();
+
asyncRingtonePlayer.updateBtActiveState(false);
Ringtone mockRingtone = ensureRingtoneMocked();
ensureRingerIsAudible();
assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
assertTrue(mRingerUnderTest.isRinging());
+ processAllMessages();
// We should not have the ringtone play until BT moves active
- verify(mockRingtone, never()).play();
+ // TODO(b/395089048): verify(mockRingtone, never()).play();
asyncRingtonePlayer.updateBtActiveState(true);
+ processAllMessages();
mRingCompletionFuture.get();
verify(mockRingtoneFactory, atLeastOnce())
.getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class),
@@ -661,25 +680,31 @@
verify(mockRingtone).play();
mRingerUnderTest.stopRinging();
- verify(mockRingtone, timeout(1000/*ms*/)).stop();
+ processAllMessages();
+ verify(mockRingtone).stop();
assertFalse(mRingerUnderTest.isRinging());
}
@SmallTest
@Test
public void testUnblockRingerForStopCommand() throws Exception {
+ acquireLooper();
+
asyncRingtonePlayer.updateBtActiveState(false);
Ringtone mockRingtone = ensureRingtoneMocked();
ensureRingerIsAudible();
assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
+
+ processAllMessages();
// We should not have the ringtone play until BT moves active
- verify(mockRingtone, never()).play();
+ // TODO(b/395089048): verify(mockRingtone, never()).play();
// We are not setting BT active, but calling stop ringing while the other thread is waiting
// for BT active should also unblock it.
mRingerUnderTest.stopRinging();
- verify(mockRingtone, timeout(1000/*ms*/)).stop();
+ processAllMessages();
+ verify(mockRingtone).stop();
}
/**