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

Commit 4d47bcb4 authored by Hall Liu's avatar Hall Liu
Browse files

Cleanup in preparation for the NOCIB refactor

Move a few classes around and split large methods to prepare for the
NewOutgoingCallIntentBroadcaster refactor to improve the async flow.

Test: unit, manual
Bug: 123904954
Change-Id: I10fcba635be5fb6823e0a423a7d248a014080e40
parent c6fb66d0
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -178,12 +178,15 @@ public class CallIntentProcessor {
        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer);
        final int result = broadcaster.processIntent();
        final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

        if (!success && call != null) {
            disconnectCallAndShowErrorDialog(context, call, result);
        // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
        NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
        if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
            disconnectCallAndShowErrorDialog(context, call, disposition.disconnectCause);
            return;
        }

        broadcaster.processCall(disposition);
    }

    /**
+7 −29
Original line number Diff line number Diff line
@@ -102,7 +102,6 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -150,27 +149,6 @@ public class CallsManager extends Call.ListenerBase
        void performAction();
    }

    /** An executor that starts a log session before executing a runnable */
    private class LoggedHandlerExecutor implements Executor {
        private Handler mHandler;
        private String mSessionName;

        public LoggedHandlerExecutor(Handler handler, String sessionName) {
            mHandler = handler;
            mSessionName = sessionName;
        }

        @Override
        public void execute(java.lang.Runnable command) {
            mHandler.post(new Runnable(mSessionName, mLock) {
                @Override
                public void loggedRun() {
                    command.run();
                }
            }.prepare());
        }
    }

    private static final String TAG = "CallsManager";

    /**
@@ -1319,7 +1297,7 @@ public class CallsManager extends Call.ListenerBase
                CompletableFuture.completedFuture((Void) null).thenComposeAsync((x) ->
                                findOutgoingCallPhoneAccount(requestedAccountHandle, handle,
                                        VideoProfile.isVideo(finalVideoState), initiatingUser),
                        new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP"));
                        new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP", mLock));

        // This is a block of code that executes after the list of potential phone accts has been
        // retrieved.
@@ -1333,7 +1311,7 @@ public class CallsManager extends Call.ListenerBase
                        phoneAccountHandle = null;
                    }
                    finalCall.setTargetPhoneAccount(phoneAccountHandle);
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA"));
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA", mLock));


        // This composes the future containing the potential phone accounts with code that queries
@@ -1352,7 +1330,7 @@ public class CallsManager extends Call.ListenerBase
                    }
                    return PhoneAccountSuggestionHelper.bindAndGetSuggestions(mContext,
                            finalCall.getHandle(), potentialPhoneAccounts);
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS"));
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS", mLock));


        // This future checks the status of existing calls and attempts to make room for the
@@ -1393,7 +1371,7 @@ public class CallsManager extends Call.ListenerBase
                        return CompletableFuture.completedFuture(null);
                    }
                    return CompletableFuture.completedFuture(finalCall);
        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP"));
        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock));

        // The outgoing call can be placed, go forward. This future glues together the results of
        // the account suggestion stage and the make room for call stage.
@@ -1447,7 +1425,7 @@ public class CallsManager extends Call.ListenerBase

                            addCall(callToPlace);
                            return mPendingAccountSelection;
                        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA"));
                        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA", mLock));

        // Potentially perform call identification for dialed TEL scheme numbers.
        if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) {
@@ -1474,7 +1452,7 @@ public class CallsManager extends Call.ListenerBase
                        if (!isInContacts) {
                            performCallIdentification(theCall);
                        }
            }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB"));
            }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB", mLock));
        }

        // Finally, after all user interaction is complete, we execute this code to finish setting
@@ -1534,7 +1512,7 @@ public class CallsManager extends Call.ListenerBase
                        addCall(callToUse);
                    }
                    return CompletableFuture.completedFuture(callToUse);
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP"));
                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP", mLock));
        return mLatestPostSelectionProcessingFuture;
    }

+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.telecom;

import android.os.Handler;
import android.telecom.Logging.Runnable;

import java.util.concurrent.Executor;

/** An executor that starts a log session before executing a runnable */
public class LoggedHandlerExecutor implements Executor {
    private Handler mHandler;
    private String mSessionName;
    private TelecomSystem.SyncRoot mLock;

    public LoggedHandlerExecutor(Handler handler, String sessionName,
            TelecomSystem.SyncRoot lock) {
        mHandler = handler;
        mSessionName = sessionName;
        mLock = lock;
    }

    @Override
    public void execute(java.lang.Runnable command) {
        mHandler.post(new Runnable(mSessionName, mLock) {
            @Override
            public void loggedRun() {
                command.run();
            }
        }.prepare());
    }
}
+108 −83
Original line number Diff line number Diff line
@@ -82,6 +82,19 @@ public class NewOutgoingCallIntentBroadcaster {
     */
    private final boolean mIsDefaultOrSystemPhoneApp;

    public static class CallDisposition {
        // True for certain types of numbers that are not intended to be intercepted or modified
        // by third parties (e.g. emergency numbers).
        public boolean callImmediately = false;
        // True for all managed calls, false for self-managed calls.
        public boolean sendBroadcast = true;
        // True for requesting call redirection, false for not requesting it.
        public boolean requestRedirection = true;
        public int disconnectCause = DisconnectCause.NOT_DISCONNECTED;
        String number;
        Uri callingAddress;
    }

    @VisibleForTesting
    public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call,
            Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
@@ -193,8 +206,8 @@ public class NewOutgoingCallIntentBroadcaster {
     *         {@link DisconnectCause} if the call did not, describing why it failed.
     */
    @VisibleForTesting
    public int processIntent() {
        Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");
    public CallDisposition evaluateCall() {
        CallDisposition result = new CallDisposition();

        Intent intent = mIntent;
        String action = intent.getAction();
@@ -202,7 +215,8 @@ public class NewOutgoingCallIntentBroadcaster {

        if (handle == null) {
            Log.w(this, "Empty handle obtained from the call intent.");
            return DisconnectCause.INVALID_NUMBER;
            result.disconnectCause = DisconnectCause.INVALID_NUMBER;
            return result;
        }

        boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());
@@ -210,16 +224,18 @@ public class NewOutgoingCallIntentBroadcaster {
            if (Intent.ACTION_CALL.equals(action)
                    || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
                // Voicemail calls will be handled directly by the telephony connection manager

                boolean speakerphoneOn = mIntent.getBooleanExtra(
                        TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
                placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn,
                Log.i(this, "Voicemail number dialed. Skipping redirection and broadcast", intent);
                mIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                        VideoProfile.STATE_AUDIO_ONLY);

                return DisconnectCause.NOT_DISCONNECTED;
                result.callImmediately = true;
                result.requestRedirection = false;
                result.sendBroadcast = false;
                result.callingAddress = handle;
                return result;
            } else {
                Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
                return DisconnectCause.OUTGOING_CANCELED;
                result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
                return result;
            }
        }

@@ -235,37 +251,31 @@ public class NewOutgoingCallIntentBroadcaster {
            }
        }

        String number = "";
        // True for certain types of numbers that are not intended to be intercepted or modified
        // by third parties (e.g. emergency numbers).
        boolean callImmediately = false;
        // True for all managed calls, false for self-managed calls.
        boolean sendNewOutgoingCallBroadcast = true;
        // True for requesting call redirection, false for not requesting it.
        boolean requestCallRedirection = true;
        Uri callingAddress = handle;
        result.number = "";
        result.callingAddress = handle;

        if (!isSelfManaged) {
            // Placing a managed call
            number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
            if (TextUtils.isEmpty(number)) {
                Log.w(this, "Empty number obtained from the call intent.");
                return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
        if (isSelfManaged) {
            // Self-managed call.
            result.callImmediately = true;
            result.sendBroadcast = false;
            result.requestRedirection = false;
            Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
            return result;
        }

            // TODO: Cleanup this dialing code; it makes the assumption that we're dialing with a
            // SIP or TEL URI.
            boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
            if (!isUriNumber) {
                number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
                number = mPhoneNumberUtilsAdapter.stripSeparators(number);
        // Placing a managed call
        String number = getNumberFromCallIntent(intent);
        result.number = number;
        if (number == null) {
            result.disconnectCause = DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
            return result;
        }

        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
        Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);

            rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
            action = intent.getAction();
        action = calculateCallIntentAction(intent, isPotentialEmergencyNumber);
        intent.setAction(action);

        if (Intent.ACTION_CALL.equals(action)) {
            if (isPotentialEmergencyNumber) {
@@ -273,41 +283,56 @@ public class NewOutgoingCallIntentBroadcaster {
                    Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
                            + "unless caller is system or default dialer.", number, intent);
                    launchSystemDialer(intent.getData());
                        return DisconnectCause.OUTGOING_CANCELED;
                    result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
                    return result;
                } else {
                        callImmediately = true;
                    result.callImmediately = true;
                }
            }
        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            if (!isPotentialEmergencyNumber) {
                Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
                        + "Intent %s.", number, intent);
                    return DisconnectCause.OUTGOING_CANCELED;
                result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
                return result;
            }
                callImmediately = true;
            result.callImmediately = true;
        } else {
            Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
                return DisconnectCause.INVALID_NUMBER;
            result.disconnectCause = DisconnectCause.INVALID_NUMBER;
            return result;
        }

            // TODO: Support dialing using URIs instead of just assuming SIP or TEL.
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            callingAddress = Uri.fromParts(scheme, number, null);
        } else {
            // Self-managed call.
            callImmediately = true;
            sendNewOutgoingCallBroadcast = false;
            requestCallRedirection = false;
            Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
        String scheme = mPhoneNumberUtilsAdapter.isUriNumber(number)
                ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
        result.callingAddress = Uri.fromParts(scheme, number, null);
        return result;
    }

    private String getNumberFromCallIntent(Intent intent) {
        String number;
        number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
        if (TextUtils.isEmpty(number)) {
            Log.w(this, "Empty number obtained from the call intent.");
            return null;
        }

        if (callImmediately) {
        boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
        if (!isUriNumber) {
            number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
            number = mPhoneNumberUtilsAdapter.stripSeparators(number);
        }
        return number;
    }

    public void processCall(CallDisposition disposition) {
        if (disposition.callImmediately) {
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            placeOutgoingCallImmediately(mCall, callingAddress, null,
            placeOutgoingCallImmediately(mCall, disposition.callingAddress, null,
                    speakerphoneOn, videoState);

            // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
@@ -317,14 +342,14 @@ public class NewOutgoingCallIntentBroadcaster {
        }

        boolean callRedirectionWithService = false;
        if (requestCallRedirection) {
        if (disposition.requestRedirection) {
            CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor(
                    mContext, mCallsManager, mCall, callingAddress,
                    mContext, mCallsManager, mCall, disposition.callingAddress,
                    mCallsManager.getPhoneAccountRegistrar(),
                    getGateWayInfoFromIntent(intent, handle),
                    intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                    getGateWayInfoFromIntent(mIntent, mIntent.getData()),
                    mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                            false),
                    intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                            VideoProfile.STATE_AUDIO_ONLY));
            /**
             * If there is an available {@link android.telecom.CallRedirectionService}, use the
@@ -338,13 +363,12 @@ public class NewOutgoingCallIntentBroadcaster {
            }
        }

        if (sendNewOutgoingCallBroadcast) {
        if (disposition.sendBroadcast) {
            UserHandle targetUser = mCall.getInitiatingUser();
            Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
            broadcastIntent(intent, number,
                    !callImmediately && !callRedirectionWithService, targetUser);
            broadcastIntent(mIntent, disposition.number,
                    !disposition.callImmediately && !callRedirectionWithService, targetUser);
        }
        return DisconnectCause.NOT_DISCONNECTED;
    }

    /**
@@ -494,14 +518,15 @@ public class NewOutgoingCallIntentBroadcaster {
    }

    /**
     * Given a call intent and whether or not the number to dial is an emergency number, rewrite
     * the call intent action to an appropriate one.
     * Given a call intent and whether or not the number to dial is an emergency number, determine
     * the appropriate call intent action.
     *
     * @param intent Intent to rewrite the action for
     * @param intent Intent to evaluate
     * @param isPotentialEmergencyNumber Whether or not the number is potentially an emergency
     * number.
     * @return The appropriate action.
     */
    private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) {
    private String calculateCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) {
        String action = intent.getAction();

        /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
@@ -514,8 +539,8 @@ public class NewOutgoingCallIntentBroadcaster {
                action = Intent.ACTION_CALL;
            }
            Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action);
            intent.setAction(action);
        }
        return action;
    }

    private long getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout) {
+14 −10
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
    @Test
    public void testNullHandle() {
        Intent intent = new Intent(Intent.ACTION_CALL, null);
        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;
        assertEquals(DisconnectCause.INVALID_NUMBER, result);
        verifyNoBroadcastSent();
        verifyNoCallPlaced();
@@ -120,7 +120,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(voicemailNumber));
        intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);

        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
        verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(Uri.parse(voicemailNumber)),
@@ -148,7 +148,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
    private void badCallActionHelper(Uri handle, int expectedCode) {
        Intent intent = new Intent(Intent.ACTION_ALARM_CHANGED, handle);

        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(expectedCode, result);
        verifyNoBroadcastSent();
@@ -176,7 +176,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        Uri handle = Uri.parse("tel:");
        Intent intent = new Intent(Intent.ACTION_CALL, handle);

        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(DisconnectCause.NO_PHONE_NUMBER_SUPPLIED, result);
        verifyNoBroadcastSent();
@@ -197,7 +197,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        mComponentContextFixture.putResource(R.string.dialer_default_class,
                dialer_default_class_string);

        int result = processIntent(intent, false);
        int result = processIntent(intent, false).disconnectCause;

        assertEquals(DisconnectCause.OUTGOING_CANCELED, result);
        verifyNoBroadcastSent();
@@ -257,7 +257,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        doReturn(false).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
                any(Context.class), eq(handle.getSchemeSpecificPart()));
        Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY, handle);
        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(DisconnectCause.OUTGOING_CANCELED, result);
        verifyNoCallPlaced();
@@ -272,7 +272,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
                any(Context.class), eq(handle.getSchemeSpecificPart()));
        intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn);
        intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
        verify(mCallsManager).placeOutgoingCall(eq(mCall), eq(handle), isNull(GatewayInfo.class),
@@ -401,7 +401,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn);
        intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);

        int result = processIntent(intent, true);
        int result = processIntent(intent, true).disconnectCause;

        assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
        Bundle expectedExtras = createNumberExtras(handle.getSchemeSpecificPart());
@@ -419,12 +419,16 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
        return i;
    }

    private int processIntent(Intent intent,
    private NewOutgoingCallIntentBroadcaster.CallDisposition processIntent(Intent intent,
            boolean isDefaultPhoneApp) {
        NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster(
                mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapterSpy,
                isDefaultPhoneApp);
        return b.processIntent();
        NewOutgoingCallIntentBroadcaster.CallDisposition cd = b.evaluateCall();
        if (cd.disconnectCause == DisconnectCause.NOT_DISCONNECTED) {
            b.processCall(cd);
        }
        return cd;
    }

    private ReceiverIntentPair verifyBroadcastSent(String number, Bundle expectedExtras) {