[automerger skipped] Resolve account image icon profile boundary exploit. am: a604311f86 am: 13980cb7c8 am: 4603102279 am: 9bbf25ed9e am: a4bc335e6b am: 733a8f6b8d am: 4fd0e1e44d am: 3af8461ca5 -s ours am: db7db13aa6 -s ours am: 5d76129c49 -s ours
am skip reason: Merged-In I2b6418f019a373ee9f02ba8683e5b694e7ab80a5 with SHA-1 a604311f86 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/24876043
Change-Id: I5f4401e8dd161c6983c648c3e60e761fc66cbba2
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/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/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/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
index 5986f59..1014001 100644
--- a/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
@@ -18,31 +18,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="2907804426411305091">"Transactional API test Activity"</string>
- <!-- no translation found for in_call_activity_name (7545884666442897585) -->
- <skip />
+ <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional In Call Activity"</string>
<string name="register_phone_account" msgid="1920315963082350332">"Register Phone Account"</string>
- <!-- no translation found for start_foreground_service (8968755699895128574) -->
- <skip />
- <!-- no translation found for start_outgoing (1441644037370361864) -->
- <skip />
- <!-- no translation found for start_incoming (6444983300186361271) -->
- <skip />
- <!-- no translation found for get_call_id (5513943242738347108) -->
- <skip />
- <!-- no translation found for set_call_active (3365404393507589899) -->
- <skip />
- <!-- no translation found for answer (5423590397665409939) -->
- <skip />
- <!-- no translation found for set_call_inactive (7106775211368705195) -->
- <skip />
- <!-- no translation found for disconnect_call (1349412380315371385) -->
- <skip />
- <!-- no translation found for request_earpiece_endpoint (6649571985089296573) -->
- <skip />
- <!-- no translation found for request_speaker_endpoint (1033259535289845405) -->
- <skip />
- <!-- no translation found for request_bluetooth_endpoint (5933254250623451836) -->
- <skip />
- <!-- no translation found for start_stream (3567634786280097431) -->
- <skip />
+ <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+ <string name="start_outgoing" msgid="1441644037370361864">"Start Outgoing Call"</string>
+ <string name="start_incoming" msgid="6444983300186361271">"Start Incoming Call"</string>
+ <string name="get_call_id" msgid="5513943242738347108">"call id not set"</string>
+ <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+ <string name="answer" msgid="5423590397665409939">"answer"</string>
+ <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+ <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+ <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+ <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+ <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+ <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
</resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
index 764ebda..40b0016 100644
--- a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
@@ -18,31 +18,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="2907804426411305091">"Transactional API test Activity"</string>
- <!-- no translation found for in_call_activity_name (7545884666442897585) -->
- <skip />
+ <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional In Call Activity"</string>
<string name="register_phone_account" msgid="1920315963082350332">"Register Phone Account"</string>
- <!-- no translation found for start_foreground_service (8968755699895128574) -->
- <skip />
- <!-- no translation found for start_outgoing (1441644037370361864) -->
- <skip />
- <!-- no translation found for start_incoming (6444983300186361271) -->
- <skip />
- <!-- no translation found for get_call_id (5513943242738347108) -->
- <skip />
- <!-- no translation found for set_call_active (3365404393507589899) -->
- <skip />
- <!-- no translation found for answer (5423590397665409939) -->
- <skip />
- <!-- no translation found for set_call_inactive (7106775211368705195) -->
- <skip />
- <!-- no translation found for disconnect_call (1349412380315371385) -->
- <skip />
- <!-- no translation found for request_earpiece_endpoint (6649571985089296573) -->
- <skip />
- <!-- no translation found for request_speaker_endpoint (1033259535289845405) -->
- <skip />
- <!-- no translation found for request_bluetooth_endpoint (5933254250623451836) -->
- <skip />
- <!-- no translation found for start_stream (3567634786280097431) -->
- <skip />
+ <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+ <string name="start_outgoing" msgid="1441644037370361864">"Start Outgoing Call"</string>
+ <string name="start_incoming" msgid="6444983300186361271">"Start Incoming Call"</string>
+ <string name="get_call_id" msgid="5513943242738347108">"call id not set"</string>
+ <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+ <string name="answer" msgid="5423590397665409939">"answer"</string>
+ <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+ <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+ <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+ <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+ <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+ <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
</resources>
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..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(