Merge "Fix issue where Telecom ANRs due to call into AudioManager." into udc-dev
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 48aa9e6..1eb65ad 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -154,6 +154,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -455,6 +456,9 @@
private LinkedList<HandlerThread> mGraphHandlerThreads;
+ // An executor that can be used to fire off async tasks that do not block Telecom in any manner.
+ private final Executor mAsyncTaskExecutor;
+
private boolean mHasActiveRttCall = false;
private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
@@ -553,7 +557,8 @@
RoleManagerAdapter roleManagerAdapter,
ToastFactory toastFactory,
CallEndpointControllerFactory callEndpointControllerFactory,
- CallAnomalyWatchdog callAnomalyWatchdog) {
+ CallAnomalyWatchdog callAnomalyWatchdog,
+ Executor asyncTaskExecutor) {
mContext = context;
mLock = lock;
mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -670,6 +675,7 @@
mGraphHandlerThreads = new LinkedList<>();
mCallAnomalyWatchdog = callAnomalyWatchdog;
+ mAsyncTaskExecutor = asyncTaskExecutor;
}
public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -3327,7 +3333,8 @@
setCallState(call, CallState.RINGING, "ringing set explicitly");
}
- void markCallAsDialing(Call call) {
+ @VisibleForTesting
+ public void markCallAsDialing(Call call) {
setCallState(call, CallState.DIALING, "dialing set explicitly");
maybeMoveToSpeakerPhone(call);
maybeTurnOffMute(call);
@@ -4893,17 +4900,28 @@
}
}
+ /**
+ * Ensures that the call will be audible to the user by checking if the voice call stream is
+ * audible, and if not increasing the volume to the default value.
+ */
private void ensureCallAudible() {
- AudioManager am = mContext.getSystemService(AudioManager.class);
- if (am == null) {
- Log.w(this, "ensureCallAudible: audio manager is null");
- return;
- }
- if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
- Log.i(this, "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
- am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
- AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
- }
+ // Audio manager APIs can be somewhat slow. To prevent a potential ANR we will fire off
+ // this opreation on the async task executor. Note that this operation does not have any
+ // dependency on any Telecom state, so we can safely launch this on a different thread
+ // without worrying that it is in the Telecom sync lock.
+ mAsyncTaskExecutor.execute(() -> {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ if (am == null) {
+ Log.w(this, "ensureCallAudible: audio manager is null");
+ return;
+ }
+ if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
+ Log.i(this,
+ "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
+ am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
+ }
+ });
}
/**
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 63852f3..4d1bca9 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -363,7 +363,8 @@
roleManagerAdapter,
toastFactory,
callEndpointControllerFactory,
- callAnomalyWatchdog);
+ callAnomalyWatchdog,
+ Executors.newSingleThreadExecutor());
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 75a9a96..9876138 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -48,6 +48,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -136,6 +137,7 @@
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -294,7 +296,9 @@
mRoleManagerAdapter,
mToastFactory,
mCallEndpointControllerFactory,
- mCallAnomalyWatchdog);
+ mCallAnomalyWatchdog,
+ // Just do async tasks synchronously to support testing.
+ command -> command.run());
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
@@ -2415,6 +2419,50 @@
@MediumTest
@Test
+ public void testSetCallDialingAndDontIncreaseVolume() {
+ // Start with a non zero volume.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 4, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsDialing(call);
+
+ // We set the volume to non-zero above, so expect 1
+ verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+ @MediumTest
+ @Test
+ public void testSetCallDialingAndIncreaseVolume() {
+ // Start with a zero volume stream.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 0, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsDialing(call);
+
+ // We set the volume to zero above, so expect 2
+ verify(mComponentContextFixture.getAudioManager(), times(2)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+
+ @MediumTest
+ @Test
+ public void testSetCallActiveAndDontIncreaseVolume() {
+ // Start with a non-zero volume.
+ mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+ 4, 0 /* flags */);
+
+ Call call = mock(Call.class);
+ mCallsManager.markCallAsActive(call);
+
+ // We set the volume to non-zero above, so expect 1 only.
+ verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+ eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+ }
+
+ @MediumTest
+ @Test
public void testHandoverToIsAccepted() {
Call sourceCall = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
Call call = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 0743805..ea135eb 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -783,6 +783,10 @@
return mTelephonyManager;
}
+ public AudioManager getAudioManager() {
+ return mAudioManager;
+ }
+
public CarrierConfigManager getCarrierConfigManager() {
return mCarrierConfigManager;
}