Merge "Move set speakerphone out of async task executor." into udc-dev am: 623b05cc8f
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/24027039
Change-Id: Iedb57c8ff97932b10f7eed9cc1ef928326fd5dc5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 3fbac1f..912305b 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -30,6 +30,9 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
@@ -39,6 +42,9 @@
*/
@VisibleForTesting
public class AsyncRingtonePlayer {
+ // Maximum amount of time we will delay playing a ringtone while waiting for audio routing to
+ // be ready.
+ private static final int PLAY_DELAY_TIMEOUT_MS = 1000;
// Message codes used with the ringtone thread.
private static final int EVENT_PLAY = 1;
private static final int EVENT_STOP = 2;
@@ -49,6 +55,23 @@
/** The current ringtone. Only used by the ringtone thread. */
private Ringtone mRingtone;
+ /**
+ * Set to true if we are setting up to play or are currently playing. False if we are stopping
+ * or have stopped playing.
+ */
+ private boolean mIsPlaying = false;
+
+ /**
+ * Set to true if BT HFP is active and audio is connected.
+ */
+ private boolean mIsBtActive = false;
+
+ /**
+ * A list of pending ringing ready latches, which are used to delay the ringing command until
+ * audio paths are set and ringing is ready.
+ */
+ private final ArrayList<CountDownLatch> mPendingRingingLatches = new ArrayList<>();
+
public AsyncRingtonePlayer() {
// Empty
}
@@ -60,21 +83,81 @@
*
* @param ringtoneSupplier The {@link Ringtone} factory.
* @param ringtoneConsumer The {@link Ringtone} post-creation callback (to start the vibration).
+ * @param isHfpDeviceConnected True if there is a HFP BT device connected, false otherwise.
*/
public void play(@NonNull Supplier<Ringtone> ringtoneSupplier,
- BiConsumer<Ringtone, Boolean> ringtoneConsumer) {
+ BiConsumer<Ringtone, Boolean> ringtoneConsumer, boolean isHfpDeviceConnected) {
Log.d(this, "Posting play.");
+ mIsPlaying = true;
SomeArgs args = SomeArgs.obtain();
args.arg1 = ringtoneSupplier;
args.arg2 = ringtoneConsumer;
args.arg3 = Log.createSubsession();
+ args.arg4 = prepareRingingReadyLatch(isHfpDeviceConnected);
postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
}
/** Stops playing the ringtone. */
public void stop() {
Log.d(this, "Posting stop.");
+ mIsPlaying = false;
postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
+ // Clear any pending ringing latches so that we do not have to wait for its timeout to pass
+ // before calling stop.
+ clearPendingRingingLatches();
+ }
+
+ /**
+ * Called when the BT HFP profile active state changes.
+ * @param isBtActive A BT device is connected and audio is active.
+ */
+ public void updateBtActiveState(boolean isBtActive) {
+ Log.i(this, "updateBtActiveState: " + isBtActive);
+ synchronized (mPendingRingingLatches) {
+ mIsBtActive = isBtActive;
+ if (isBtActive) mPendingRingingLatches.forEach(CountDownLatch::countDown);
+ }
+ }
+
+ /**
+ * Prepares a new ringing ready latch and tracks it in a list. Once the ready latch has been
+ * used, {@link #removePendingRingingReadyLatch(CountDownLatch)} must be called on this latch.
+ * @param isHfpDeviceConnected true if there is a HFP device connected.
+ * @return the newly prepared CountDownLatch
+ */
+ private CountDownLatch prepareRingingReadyLatch(boolean isHfpDeviceConnected) {
+ CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mPendingRingingLatches) {
+ // We only want to delay ringing if BT is connected but not active yet.
+ boolean isDelayRequired = isHfpDeviceConnected && !mIsBtActive;
+ Log.i(this, "prepareRingingReadyLatch:"
+ + " connected=" + isHfpDeviceConnected
+ + ", BT active=" + mIsBtActive
+ + ", isDelayRequired=" + isDelayRequired);
+ if (!isDelayRequired) latch.countDown();
+ mPendingRingingLatches.add(latch);
+ }
+ return latch;
+ }
+
+ /**
+ * Remove a ringing ready latch that has been used and is no longer pending.
+ * @param l The latch to remove.
+ */
+ private void removePendingRingingReadyLatch(CountDownLatch l) {
+ synchronized (mPendingRingingLatches) {
+ mPendingRingingLatches.remove(l);
+ }
+ }
+
+ /**
+ * Count down all pending ringing ready latches and then clear the list.
+ */
+ private void clearPendingRingingLatches() {
+ synchronized (mPendingRingingLatches) {
+ mPendingRingingLatches.forEach(CountDownLatch::countDown);
+ mPendingRingingLatches.clear();
+ }
}
/**
@@ -129,6 +212,7 @@
Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1;
BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2;
Session session = (Session) args.arg3;
+ CountDownLatch ringingReadyLatch = (CountDownLatch) args.arg4;
args.recycle();
Log.continueSession(session, "ARP.hP");
@@ -136,17 +220,29 @@
// Don't bother with any of this if there is an EVENT_STOP waiting, but give the
// consumer a chance to do anything no matter what.
if (mHandler.hasMessages(EVENT_STOP)) {
+ Log.i(this, "handlePlay: skipping play early due to pending STOP");
+ removePendingRingingReadyLatch(ringingReadyLatch);
ringtoneConsumer.accept(null, /* stopped= */ true);
return;
}
Ringtone ringtone = null;
boolean hasStopped = false;
try {
+ try {
+ Log.i(this, "handlePlay: delay ring for ready signal...");
+ boolean reachedZero = ringingReadyLatch.await(PLAY_DELAY_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+ Log.i(this, "handlePlay: ringing ready, timeout=" + !reachedZero);
+ } catch (InterruptedException e) {
+ Log.w(this, "handlePlay: latch exception: " + e);
+ }
ringtone = ringtoneSupplier.get();
- // Ringtone supply can be slow. Re-check for stop event.
+ // Ringtone supply can be slow or stop command could have been issued while waiting
+ // for BT to move to CONNECTED state. Re-check for stop event.
if (mHandler.hasMessages(EVENT_STOP)) {
+ Log.i(this, "handlePlay: skipping play due to pending STOP");
hasStopped = true;
- ringtone.stop(); // proactively release the ringtone.
+ if (ringtone != null) ringtone.stop(); // proactively release the ringtone.
return;
}
// setRingtone even if null - it also stops any current ringtone to be consistent
@@ -168,6 +264,7 @@
mRingtone.play();
Log.i(this, "Play ringtone, looping.");
} finally {
+ removePendingRingingReadyLatch(ringingReadyLatch);
ringtoneConsumer.accept(ringtone, hasStopped);
}
} finally {
@@ -196,11 +293,15 @@
}
}
+ /**
+ * @return true if we are currently preparing or playing a ringtone, false if we are not.
+ */
public boolean isPlaying() {
- return mRingtone != null;
+ return mIsPlaying;
}
private void setRingtone(@Nullable Ringtone ringtone) {
+ Log.i(this, "setRingtone: ringtone null=" + (ringtone == null));
// Make sure that any previously created instance of Ringtone is stopped so the MediaPlayer
// can be released, before replacing mRingtone with a new instance. This is always created
// as a looping Ringtone, so if not stopped it will keep playing on the background.
diff --git a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
index d96f953..af0757c 100644
--- a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
+++ b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
@@ -27,14 +27,17 @@
private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
private final BluetoothRouteManager mBluetoothRouteManager;
+ private final AsyncRingtonePlayer mRingtonePlayer;
public CallAudioRoutePeripheralAdapter(
CallAudioRouteStateMachine callAudioRouteStateMachine,
BluetoothRouteManager bluetoothManager,
WiredHeadsetManager wiredHeadsetManager,
- DockManager dockManager) {
+ DockManager dockManager,
+ AsyncRingtonePlayer ringtonePlayer) {
mCallAudioRouteStateMachine = callAudioRouteStateMachine;
mBluetoothRouteManager = bluetoothManager;
+ mRingtonePlayer = ringtonePlayer;
mBluetoothRouteManager.setListener(this);
wiredHeadsetManager.addListener(this);
@@ -75,12 +78,22 @@
@Override
public void onBluetoothAudioConnected() {
+ mRingtonePlayer.updateBtActiveState(true);
+ mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+ CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+ }
+
+ @Override
+ public void onBluetoothAudioConnecting() {
+ mRingtonePlayer.updateBtActiveState(false);
+ // Pretend like audio is connected when communicating w/ CARSM.
mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
}
@Override
public void onBluetoothAudioDisconnected() {
+ mRingtonePlayer.updateBtActiveState(false);
mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 77570c3..a57449d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -572,6 +572,7 @@
CallAnomalyWatchdog callAnomalyWatchdog,
Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
Executor asyncTaskExecutor,
+ Executor asyncCallAudioTaskExecutor,
BlockedNumbersAdapter blockedNumbersAdapter,
TransactionManager transactionManager,
EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger,
@@ -606,7 +607,7 @@
statusBarNotifier,
audioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
- asyncTaskExecutor
+ asyncCallAudioTaskExecutor
);
callAudioRouteStateMachine.initialize();
@@ -615,7 +616,8 @@
callAudioRouteStateMachine,
bluetoothManager,
wiredHeadsetManager,
- mDockManager);
+ mDockManager,
+ asyncRingtonePlayer);
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
InCallTonePlayer.MediaPlayerFactory mediaPlayerFactory =
(resourceId, attributes) ->
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 1710604..16dc5c4 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -456,7 +456,7 @@
};
deferBlockOnRingingFuture = true; // Run in vibrationLogic.
if (ringtoneSupplier != null) {
- mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic);
+ mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached);
} else {
afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false);
}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 67bb81f..da325f7 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -223,6 +223,7 @@
DeviceIdleControllerAdapter deviceIdleControllerAdapter,
Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
Executor asyncTaskExecutor,
+ Executor asyncCallAudioTaskExecutor,
BlockedNumbersAdapter blockedNumbersAdapter) {
mContext = context.getApplicationContext();
LogUtils.initLogging(mContext);
@@ -396,6 +397,7 @@
callAnomalyWatchdog,
accessibilityManagerAdapter,
asyncTaskExecutor,
+ asyncCallAudioTaskExecutor,
blockedNumbersAdapter,
transactionManager,
emergencyCallDiagnosticLogger,
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 7966f73..bce6e99 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -78,6 +78,7 @@
void onBluetoothActiveDevicePresent();
void onBluetoothActiveDeviceGone();
void onBluetoothAudioConnected();
+ void onBluetoothAudioConnecting();
void onBluetoothAudioDisconnected();
/**
* This gets called when we get an unexpected state change from Bluetooth. Their stack does
@@ -231,8 +232,7 @@
sendMessageDelayed(CONNECTION_TIMEOUT, args,
mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
mContext.getContentResolver()));
- // Pretend like audio is connected when communicating w/ CARSM.
- mListener.onBluetoothAudioConnected();
+ mListener.onBluetoothAudioConnecting();
}
@Override
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index ef85fc7..90a683f 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -215,6 +215,7 @@
}
},
Executors.newCachedThreadPool(),
+ Executors.newSingleThreadExecutor(),
new BlockedNumbersAdapter() {
@Override
public boolean shouldShowEmergencyCallNotification(Context
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index 5eecccc..0f9ffc1 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -66,7 +66,7 @@
public class BluetoothRouteTransitionTests extends TelecomTestCase {
private enum ListenerUpdate {
DEVICE_LIST_CHANGED, ACTIVE_DEVICE_PRESENT, ACTIVE_DEVICE_GONE,
- AUDIO_CONNECTED, AUDIO_DISCONNECTED, UNEXPECTED_STATE_CHANGE
+ AUDIO_CONNECTING, AUDIO_CONNECTED, AUDIO_DISCONNECTED, UNEXPECTED_STATE_CHANGE
}
private static class BluetoothRouteTestParametersBuilder {
@@ -348,6 +348,9 @@
case ACTIVE_DEVICE_GONE:
verify(mListener).onBluetoothActiveDeviceGone();
break;
+ case AUDIO_CONNECTING:
+ verify(mListener).onBluetoothAudioConnecting();
+ break;
case AUDIO_CONNECTED:
verify(mListener).onBluetoothAudioConnected();
break;
@@ -449,7 +452,7 @@
.setConnectedDevices(DEVICE2, DEVICE1)
.setActiveDevice(DEVICE1)
.setMessageType(BluetoothRouteManager.CONNECT_BT)
- .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+ .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
.setExpectedBluetoothInteraction(CONNECT)
.setExpectedConnectionDevice(DEVICE1)
.setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
@@ -505,7 +508,7 @@
.setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
.setMessageType(BluetoothRouteManager.CONNECT_BT)
.setMessageDevice(DEVICE3)
- .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+ .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
.setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
.setExpectedConnectionDevice(DEVICE3)
.setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
@@ -519,7 +522,7 @@
.setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
.setMessageType(BluetoothRouteManager.CONNECT_BT)
.setMessageDevice(DEVICE3)
- .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+ .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTING)
.setExpectedBluetoothInteraction(CONNECT_SWITCH_DEVICE)
.setExpectedConnectionDevice(DEVICE3)
.setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
index dfe1483..2fc6ec6 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRoutePeripheralAdapterTest.java
@@ -26,6 +26,7 @@
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.CallAudioRoutePeripheralAdapter;
import com.android.server.telecom.CallAudioRouteStateMachine;
import com.android.server.telecom.DockManager;
@@ -47,6 +48,7 @@
@Mock private BluetoothRouteManager mBluetoothRouteManager;
@Mock private WiredHeadsetManager mWiredHeadsetManager;
@Mock private DockManager mDockManager;
+ @Mock private AsyncRingtonePlayer mRingtonePlayer;
@Override
@Before
@@ -57,7 +59,8 @@
mCallAudioRouteStateMachine,
mBluetoothRouteManager,
mWiredHeadsetManager,
- mDockManager);
+ mDockManager,
+ mRingtonePlayer);
}
@Override
@@ -126,6 +129,16 @@
mAdapter.onBluetoothAudioConnected();
verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+ verify(mRingtonePlayer).updateBtActiveState(true);
+ }
+
+ @SmallTest
+ @Test
+ public void testOnBluetoothAudioConnecting() {
+ mAdapter.onBluetoothAudioConnecting();
+ verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
+ CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+ verify(mRingtonePlayer).updateBtActiveState(false);
}
@SmallTest
@@ -134,6 +147,7 @@
mAdapter.onBluetoothAudioDisconnected();
verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
+ verify(mRingtonePlayer).updateBtActiveState(false);
}
@SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 9f46336..56cf22f 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -345,6 +345,8 @@
mAccessibilityManagerAdapter,
// Just do async tasks synchronously to support testing.
command -> command.run(),
+ // For call audio tasks
+ command -> command.run(),
mBlockedNumbersAdapter,
TransactionManager.getTestInstance(),
mEmergencyCallDiagnosticLogger,
diff --git a/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
index f11afc1..1f1b939 100644
--- a/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
+++ b/tests/src/com/android/server/telecom/tests/InCallTonePlayerTest.java
@@ -35,6 +35,7 @@
import android.media.ToneGenerator;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallAudioRoutePeripheralAdapter;
import com.android.server.telecom.CallAudioRouteStateMachine;
@@ -69,6 +70,7 @@
@Mock private InCallTonePlayer.ToneGeneratorFactory mToneGeneratorFactory;
@Mock private WiredHeadsetManager mWiredHeadsetManager;
@Mock private DockManager mDockManager;
+ @Mock private AsyncRingtonePlayer mRingtonePlayer;
@Mock private BluetoothDevice mDevice;
@Mock private BluetoothAdapter mBluetoothAdapter;
@@ -124,7 +126,7 @@
mCallAudioRoutePeripheralAdapter = new CallAudioRoutePeripheralAdapter(
mCallAudioRouteStateMachine, mBluetoothRouteManager, mWiredHeadsetManager,
- mDockManager);
+ mDockManager, mRingtonePlayer);
mFactory = new InCallTonePlayer.Factory(mCallAudioRoutePeripheralAdapter, mLock,
mToneGeneratorFactory, mMediaPlayerFactory, mAudioManagerAdapter);
mFactory.setCallAudioManager(mCallAudioManager);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index a4adf77..34360ca 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -125,6 +126,9 @@
when(mockCall2.getState()).thenReturn(CallState.RINGING);
when(mockCall1.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle());
when(mockCall2.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle());
+ // Set BT active state in tests to ensure that we do not end up blocking tests for 1 sec
+ // waiting for BT to connect in unit tests by default.
+ asyncRingtonePlayer.updateBtActiveState(true);
createRingerUnderTest();
}
@@ -434,6 +438,48 @@
verify(mockRingtone).play();
}
+ @SmallTest
+ @Test
+ public void testDelayRingerForBtHfpDevices() throws Exception {
+ asyncRingtonePlayer.updateBtActiveState(false);
+ Ringtone mockRingtone = ensureRingtoneMocked();
+
+ ensureRingerIsAudible();
+ assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
+ assertTrue(mRingerUnderTest.isRinging());
+ // We should not have the ringtone play until BT moves active
+ verify(mockRingtone, never()).play();
+
+ asyncRingtonePlayer.updateBtActiveState(true);
+ mRingCompletionFuture.get();
+ verify(mockRingtoneFactory, times(1))
+ .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class),
+ anyBoolean());
+ verifyNoMoreInteractions(mockRingtoneFactory);
+ verify(mockRingtone).play();
+
+ mRingerUnderTest.stopRinging();
+ verify(mockRingtone, timeout(1000/*ms*/)).stop();
+ assertFalse(mRingerUnderTest.isRinging());
+ }
+
+ @SmallTest
+ @Test
+ public void testUnblockRingerForStopCommand() throws Exception {
+ asyncRingtonePlayer.updateBtActiveState(false);
+ Ringtone mockRingtone = ensureRingtoneMocked();
+
+ ensureRingerIsAudible();
+ assertTrue(mRingerUnderTest.startRinging(mockCall1, true));
+ // We should not have the ringtone play until BT moves active
+ 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();
+ }
+
/**
* test shouldRingForContact will suppress the incoming call if matchesCallFilter returns
* false (meaning DND is ON and the caller cannot bypass the settings)
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index b962b2a..fb35125 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -549,6 +549,7 @@
}
}, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter,
Runnable::run,
+ Runnable::run,
mBlockedNumbersAdapter);
mComponentContextFixture.setTelecomManager(new TelecomManager(