Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d9ddf06 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Fix issue where Telecom ANRs due to call into AudioManager.

The audiomanager calls in this case are not related to anything Telecom
is doing, so we can move them onto their own single thread handler for
async one-shot tasks.  The functionality looks at the current voice call
stream volume and increases it to default at the start of the call if
needed.

Test: Added new unit tests to cover this functionality.
Fixes: 268579695
Change-Id: Iaa35d3025583cfeb75e56d52847a7d78b8ee518f
Merged-In: Iaa35d3025583cfeb75e56d52847a7d78b8ee518f
(cherry picked from commit 8acedf08)
parent 69c948cc
Loading
Loading
Loading
Loading
+30 −12
Original line number Diff line number Diff line
@@ -154,6 +154,7 @@ import java.util.concurrent.CompletableFuture;
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 @@ public class CallsManager extends Call.ListenerBase

    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 @@ public class CallsManager extends Call.ListenerBase
            RoleManagerAdapter roleManagerAdapter,
            ToastFactory toastFactory,
            CallEndpointControllerFactory callEndpointControllerFactory,
            CallAnomalyWatchdog callAnomalyWatchdog) {
            CallAnomalyWatchdog callAnomalyWatchdog,
            Executor asyncTaskExecutor) {
        mContext = context;
        mLock = lock;
        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -670,6 +675,7 @@ public class CallsManager extends Call.ListenerBase
        mGraphHandlerThreads = new LinkedList<>();

        mCallAnomalyWatchdog = callAnomalyWatchdog;
        mAsyncTaskExecutor = asyncTaskExecutor;
    }

    public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -3327,7 +3333,8 @@ public class CallsManager extends Call.ListenerBase
        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 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    /**
     * 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() {
        // 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.");
                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);
            }
        });
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -363,7 +363,8 @@ public class TelecomSystem {
                    roleManagerAdapter,
                    toastFactory,
                    callEndpointControllerFactory,
                    callAnomalyWatchdog);
                    callAnomalyWatchdog,
                    Executors.newSingleThreadExecutor());

            mIncomingCallNotifier = incomingCallNotifier;
            incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
+49 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.content.ContentResolver;
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.Map;
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 @@ public class CallsManagerTest extends TelecomTestCase {
                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);
@@ -2413,6 +2417,50 @@ public class CallsManagerTest extends TelecomTestCase {
        verify(listener).onRingbackRequested(call, ringback);
    }

    @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() {
+4 −0
Original line number Diff line number Diff line
@@ -783,6 +783,10 @@ public class ComponentContextFixture implements TestFixture<Context> {
        return mTelephonyManager;
    }

    public AudioManager getAudioManager() {
        return mAudioManager;
    }

    public CarrierConfigManager getCarrierConfigManager() {
        return mCarrierConfigManager;
    }