[automerger skipped] Revert "Unbind CS if connection is not created within 15 seconds." am: ccf524181b -s ours am: fb8e325fda -s ours am: 06a967c99e -s ours am: 00e8f94d1d -s ours am: af9ceaa0c8 -s ours am: f1a54cee46 -s ours am: eaa93c1157 -s ours
am skip reason: Merged-In I30caed1481dff5af2223a8ff589846597cee8229 with SHA-1 59052739d8 is already in history. Merged-In was found from reverted change.
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/28728671
Change-Id: I3149d2d3c5e2a3111096bc137bb6b4f3b77ef820
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/Call.java b/src/com/android/server/telecom/Call.java
index dd8e7e8..cf52ce9 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -1121,6 +1121,7 @@
return (!mIsTransactionalCall ? mConnectionService : mTransactionalService);
}
+ @VisibleForTesting
public int getState() {
return mState;
}
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/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 4a03726..dbc6d6a 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -1215,7 +1215,13 @@
// Expected, since we just transitioned here
return HANDLED;
case SPEAKER_OFF:
- sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
+ // Check if we already requested to connect to other devices and just waiting
+ // for their response. In some cases, this SPEAKER_OFF message may come in
+ // before the response, we can just ignore the message here to not re-evaluate
+ // the baseline route incorrectly
+ if (!mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) {
+ sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
+ }
return HANDLED;
case SWITCH_FOCUS:
if (msg.arg1 == NO_FOCUS) {
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/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 42696ca..bcef305 100755
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -45,6 +45,7 @@
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.Log;
+import android.telecom.Logging.Runnable;
import android.telecom.Logging.Session;
import android.telecom.ParcelableConference;
import android.telecom.ParcelableConnection;
@@ -72,12 +73,14 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-import java.util.Objects;
/**
* Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
@@ -94,6 +97,11 @@
private @Nullable CancellationSignal mOngoingQueryLocationRequest = null;
private final ExecutorService mQueryLocationExecutor = Executors.newSingleThreadExecutor();
+ private static final long SERVICE_BINDING_TIMEOUT = 15000L;
+ private ScheduledExecutorService mScheduledExecutor =
+ Executors.newSingleThreadScheduledExecutor();
+ // Pre-allocate space for 2 calls; realistically thats all we should ever need (tm)
+ private final Map<Call, ScheduledFuture<?>> mScheduledFutureMap = new ConcurrentHashMap<>(2);
private final class Adapter extends IConnectionServiceAdapter.Stub {
@Override
@@ -106,6 +114,12 @@
try {
synchronized (mLock) {
logIncoming("handleCreateConnectionComplete %s", callId);
+ Call call = mCallIdMapper.getCall(callId);
+ if (mScheduledFutureMap.containsKey(call)) {
+ ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
+ existingTimeout.cancel(false /* cancelIfRunning */);
+ mScheduledFutureMap.remove(call);
+ }
// Check status hints image for cross user access
if (connection.getStatusHints() != null) {
Icon icon = connection.getStatusHints().getIcon();
@@ -150,6 +164,12 @@
conference.getStatusHints().setIcon(StatusHints.
validateAccountIconUserBoundary(icon, callingUserHandle));
}
+ Call call = mCallIdMapper.getCall(callId);
+ if (mScheduledFutureMap.containsKey(call)) {
+ ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
+ existingTimeout.cancel(false /* cancelIfRunning */);
+ mScheduledFutureMap.remove(call);
+ }
ConnectionServiceWrapper.this
.handleCreateConferenceComplete(callId, request, conference);
@@ -1603,6 +1623,26 @@
.setIsAdhocConferenceCall(call.isAdhocConferenceCall())
.build();
+ Runnable r = new Runnable("CSW.cC", mLock) {
+ @Override
+ public void loggedRun() {
+ if (!call.isCreateConnectionComplete()) {
+ Log.e(this, new Exception(),
+ "Conference %s creation timeout",
+ getComponentName());
+ Log.addEvent(call, LogUtils.Events.CREATE_CONFERENCE_TIMEOUT,
+ Log.piiHandle(call.getHandle()) + " via:" +
+ getComponentName().getPackageName());
+ response.handleCreateConferenceFailure(
+ new DisconnectCause(DisconnectCause.ERROR));
+ }
+ }
+ };
+ // Post cleanup to the executor service and cache the future, so we can cancel it if
+ // needed.
+ ScheduledFuture<?> future = mScheduledExecutor.schedule(r.getRunnableToCancel(),
+ SERVICE_BINDING_TIMEOUT, TimeUnit.MILLISECONDS);
+ mScheduledFutureMap.put(call, future);
try {
mServiceInterface.createConference(
call.getConnectionManagerPhoneAccount(),
@@ -1705,6 +1745,26 @@
.setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
.build();
+ Runnable r = new Runnable("CSW.cC", mLock) {
+ @Override
+ public void loggedRun() {
+ if (!call.isCreateConnectionComplete()) {
+ Log.e(this, new Exception(),
+ "Connection %s creation timeout",
+ getComponentName());
+ Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_TIMEOUT,
+ Log.piiHandle(call.getHandle()) + " via:" +
+ getComponentName().getPackageName());
+ response.handleCreateConnectionFailure(
+ new DisconnectCause(DisconnectCause.ERROR));
+ }
+ }
+ };
+ // Post cleanup to the executor service and cache the future, so we can cancel it if
+ // needed.
+ ScheduledFuture<?> future = mScheduledExecutor.schedule(r.getRunnableToCancel(),
+ SERVICE_BINDING_TIMEOUT, TimeUnit.MILLISECONDS);
+ mScheduledFutureMap.put(call, future);
try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
@@ -2162,7 +2222,8 @@
}
}
- void addCall(Call call) {
+ @VisibleForTesting
+ public void addCall(Call call) {
if (mCallIdMapper.getCallId(call) == null) {
mCallIdMapper.addCall(call);
}
@@ -2629,4 +2690,9 @@
sb.append("]");
return sb.toString();
}
+
+ @VisibleForTesting
+ public void setScheduledExecutorService(ScheduledExecutorService service) {
+ mScheduledExecutor = service;
+ }
}
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 0d6acd5..d98ebfe 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -139,8 +139,10 @@
public static final String STOP_CALL_WAITING_TONE = "STOP_CALL_WAITING_TONE";
public static final String START_CONNECTION = "START_CONNECTION";
public static final String CREATE_CONNECTION_FAILED = "CREATE_CONNECTION_FAILED";
+ public static final String CREATE_CONNECTION_TIMEOUT = "CREATE_CONNECTION_TIMEOUT";
public static final String START_CONFERENCE = "START_CONFERENCE";
public static final String CREATE_CONFERENCE_FAILED = "CREATE_CONFERENCE_FAILED";
+ public static final String CREATE_CONFERENCE_TIMEOUT = "CREATE_CONFERENCE_TIMEOUT";
public static final String BIND_CS = "BIND_CS";
public static final String CS_BOUND = "CS_BOUND";
public static final String CONFERENCE_WITH = "CONF_WITH";
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/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index bd81a2f..51c3b33 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -995,6 +995,7 @@
call.setTargetPhoneAccount(mPhoneAccountA1.getAccountHandle());
assert(call.isVideoCallingSupportedByPhoneAccount());
assertEquals(VideoProfile.STATE_BIDIRECTIONAL, call.getVideoState());
+ call.setIsCreateConnectionComplete(true);
}
/**
@@ -1018,6 +1019,7 @@
call.setTargetPhoneAccount(mPhoneAccountA2.getAccountHandle());
assert(!call.isVideoCallingSupportedByPhoneAccount());
assertEquals(VideoProfile.STATE_AUDIO_ONLY, call.getVideoState());
+ call.setIsCreateConnectionComplete(true);
}
/**
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/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 569c487..8571f1d 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -60,6 +60,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
@@ -173,7 +174,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
@@ -219,7 +219,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
@@ -263,7 +262,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -309,7 +307,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
@@ -354,7 +351,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1);
@@ -433,7 +429,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
@@ -470,7 +465,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
setInBandRing(false);
@@ -526,7 +520,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
List<BluetoothDevice> availableDevices =
@@ -577,7 +570,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
@@ -609,7 +601,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
@@ -644,7 +635,6 @@
mockStatusBarNotifier,
mAudioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
- mThreadHandler.getLooper(),
Runnable::run /** do async stuff sync for test purposes */);
stateMachine.setCallAudioManager(mockCallAudioManager);
List<BluetoothDevice> availableDevices =
@@ -798,6 +788,47 @@
assertEquals(initState, stateMachine.getCurrentCallAudioState());
}
+ @SmallTest
+ @Test
+ public void testIgnoreSpeakerOffMessage() {
+ when(mockBluetoothRouteManager.isInbandRingingEnabled()).thenReturn(true);
+ when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+ .thenReturn(bluetoothDevice1);
+ when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(true);
+ CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+ mContext,
+ mockCallsManager,
+ mockBluetoothRouteManager,
+ mockWiredHeadsetManager,
+ mockStatusBarNotifier,
+ mAudioServiceFactory,
+ CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+ mThreadHandler.getLooper(),
+ Runnable::run /** do async stuff sync for test purposes */);
+ stateMachine.setCallAudioManager(mockCallAudioManager);
+
+ CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+ CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
+ | CallAudioState.ROUTE_BLUETOOTH);
+ stateMachine.initialize(initState);
+
+ doAnswer(
+ (address) -> {
+ stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SPEAKER_OFF);
+ stateMachine.sendMessageDelayed(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED,
+ 5000L);
+ return null;
+ }).when(mockBluetoothRouteManager).connectBluetoothAudio(anyString());
+ stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+ CallAudioRouteStateMachine.ACTIVE_FOCUS);
+ stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH);
+
+ CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+ CallAudioState.ROUTE_SPEAKER | CallAudioState.ROUTE_BLUETOOTH
+ | CallAudioState.ROUTE_EARPIECE);
+ assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+ }
+
private void initializationTestHelper(CallAudioState expectedState,
int earpieceControl) {
when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 9f46336..5134f7c 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -43,6 +43,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.lang.Thread.sleep;
import android.Manifest;
import android.content.ComponentName;
@@ -54,6 +55,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.Process;
@@ -80,6 +82,7 @@
import android.util.Pair;
import android.widget.Toast;
+import com.android.internal.telecom.IConnectionService;
import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.Call;
@@ -97,6 +100,7 @@
import com.android.server.telecom.ConnectionServiceFocusManager;
import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory;
import com.android.server.telecom.ConnectionServiceWrapper;
+import com.android.server.telecom.CreateConnectionResponse;
import com.android.server.telecom.DefaultDialerCache;
import com.android.server.telecom.EmergencyCallDiagnosticLogger;
import com.android.server.telecom.EmergencyCallHelper;
@@ -277,6 +281,7 @@
@Mock private BlockedNumbersAdapter mBlockedNumbersAdapter;
@Mock private PhoneCapability mPhoneCapability;
@Mock private CallStreamingNotification mCallStreamingNotification;
+ @Mock private IConnectionService mIConnectionService;
private CallsManager mCallsManager;
@@ -345,6 +350,8 @@
mAccessibilityManagerAdapter,
// Just do async tasks synchronously to support testing.
command -> command.run(),
+ // For call audio tasks
+ command -> command.run(),
mBlockedNumbersAdapter,
TransactionManager.getTestInstance(),
mEmergencyCallDiagnosticLogger,
@@ -360,11 +367,19 @@
eq(WORK_HANDLE), any())).thenReturn(WORK_ACCOUNT);
when(mToastFactory.makeText(any(), anyInt(), anyInt())).thenReturn(mToast);
when(mToastFactory.makeText(any(), any(), anyInt())).thenReturn(mToast);
+ when(mIConnectionService.asBinder()).thenReturn(mock(IBinder.class));
+
+ mComponentContextFixture.addConnectionService(new ComponentName(mContext.getPackageName(),
+ mContext.getPackageName().getClass().getName()), mIConnectionService);
}
@Override
@After
public void tearDown() throws Exception {
+ mComponentContextFixture.removeConnectionService(
+ new ComponentName(mContext.getPackageName(),
+ mContext.getPackageName().getClass().getName()),
+ mock(IConnectionService.class));
super.tearDown();
}
@@ -3250,6 +3265,31 @@
assertTrue(mCallsManager.isInSelfManagedCall(TEST_PACKAGE_NAME, TEST_USER_HANDLE));
}
+ @Test
+ public void testConnectionServiceCreateConnectionTimeout() throws Exception {
+ ConnectionServiceWrapper service = new ConnectionServiceWrapper(new ComponentName(
+ mContext.getPackageName(), mContext.getPackageName().getClass().getName()), null,
+ mPhoneAccountRegistrar, mCallsManager, mContext, mLock, null);
+ TestScheduledExecutorService scheduledExecutorService = new TestScheduledExecutorService();
+ service.setScheduledExecutorService(scheduledExecutorService);
+ Call call = addSpyCall();
+ service.addCall(call);
+ when(call.isCreateConnectionComplete()).thenReturn(false);
+ CreateConnectionResponse response = mock(CreateConnectionResponse.class);
+
+ service.createConnection(call, response);
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return scheduledExecutorService.isRunnableScheduledAtTime(15000L);
+ }
+ }, 5000L, "Expected job failed to schedule");
+ }
private Call addSpyCall() {
return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
@@ -3353,4 +3393,19 @@
when(mockTelephonyManager.getPhoneCapability()).thenReturn(mPhoneCapability);
when(mPhoneCapability.getMaxActiveVoiceSubscriptions()).thenReturn(num);
}
+
+ private void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
+ String description) throws InterruptedException {
+ final long start = System.currentTimeMillis();
+ while (!condition.expected().equals(condition.actual())
+ && System.currentTimeMillis() - start < timeout) {
+ sleep(50);
+ }
+ assertEquals(description, condition.expected(), condition.actual());
+ }
+
+ protected interface Condition {
+ Object expected();
+ Object actual();
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index cc22de2..df855e9 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -735,6 +735,14 @@
mServiceInfoByComponentName.put(componentName, serviceInfo);
}
+ public void removeConnectionService(
+ ComponentName componentName,
+ IConnectionService service)
+ throws Exception {
+ removeService(ConnectionService.SERVICE_INTERFACE, componentName, service);
+ mServiceInfoByComponentName.remove(componentName);
+ }
+
public void addInCallService(
ComponentName componentName,
IInCallService service,
@@ -828,6 +836,12 @@
mComponentNameByService.put(service, name);
}
+ private void removeService(String action, ComponentName name, IInterface service) {
+ mComponentNamesByAction.remove(action, name);
+ mServiceByComponentName.remove(name);
+ mComponentNameByService.remove(service);
+ }
+
private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<>();
for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
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(