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

Commit c20713f4 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Send CALL intent directly to Telecom CallIntentProcessor.

TelecomManager#placeCall results in a CALL intent being sent from Telecom
to itself.  If there are delays in the system broadcast queue, the CALL
intent could be held up in the queue causing delays in the outgoing call
flow.

Changing the code so we instead just send the intent directly to the
call intent processor.

Test: Manually reproduced broadcast queue spamming issue and verified that
placeCall API does not block in this case any more.
Bug: 76005593

Change-Id: I578fdd56b8ff9542022dd68a4e3a5490307b9a5c
parent 581b1cb8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2968,7 +2968,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return mOriginalConnectionId;
    }

    ConnectionServiceFocusManager getConnectionServiceFocusManager() {
    public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
        return mCallsManager.getConnectionServiceFocusManager();
    }

+12 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom;

import static android.Manifest.permission.CALL_PHONE;
import static android.Manifest.permission.CALL_PRIVILEGED;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.Manifest.permission.READ_PHONE_STATE;
@@ -1233,12 +1234,20 @@ public class TelecomServiceImpl {

                final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
                        PackageManager.PERMISSION_GRANTED;
                // The Emergency Dialer has call privileged permission and uses this to place
                // emergency calls.  We ensure permission checks in
                // NewOutgoingCallIntentBroadcaster#process pass by sending this to
                // Telecom as an ACTION_CALL_PRIVILEGED intent (which makes sense since the
                // com.android.phone process has that permission).
                final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
                        CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;

                synchronized (mLock) {
                    final UserHandle userHandle = Binder.getCallingUserHandle();
                    long token = Binder.clearCallingIdentity();
                    try {
                        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                        if (extras != null) {
                            extras.setDefusable(true);
                            intent.putExtras(extras);
@@ -1246,7 +1255,8 @@ public class TelecomServiceImpl {
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, isSelfManaged ||
                                                (hasCallAppOp && hasCallPermission));
                                                (hasCallAppOp && hasCallPermission),
                                        true /* isLocalInvocation */);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
+1 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ public class UserCallActivity extends Activity implements TelecomSystem.Componen
            // ActivityThread.ActivityClientRecord#intent directly.
            // Modifying directly may be a potential risk when relaunching this activity.
            new UserCallIntentProcessor(this, userHandle).processIntent(new Intent(intent),
                    getCallingPackage(), true /* hasCallAppOp*/);
                    getCallingPackage(), true /* hasCallAppOp*/, false /* isLocalInvocation */);
        } finally {
            Log.endSession();
            wakelock.release();
+33 −11
Original line number Diff line number Diff line
@@ -18,15 +18,14 @@ package com.android.server.telecom.components;

import com.android.server.telecom.CallIntentProcessor;
import com.android.server.telecom.R;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.TelephonyUtil;
import com.android.server.telecom.UserUtil;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
@@ -37,7 +36,6 @@ import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.widget.Toast;

// TODO: Needed for move to system service: import com.android.internal.R;

@@ -72,9 +70,18 @@ public class UserCallIntentProcessor {
     * Processes intents sent to the activity.
     *
     * @param intent The intent.
     * @param callingPackageName The package name of the calling app.
     * @param canCallNonEmergency {@code true} if the caller is permitted to call non-emergency
     *                            numbers.
     * @param isLocalInvocation {@code true} if the caller is within the system service (i.e. the
     *                            caller is {@link com.android.server.telecom.TelecomServiceImpl})
     *                            and we can skip the re-broadcast of the intent to Telecom.
     *                            When {@code false}, we need to re-broadcast the intent to Telcom
     *                            to trampoline it to the system service where the Telecom
     *                            service resides.
     */
    public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
            boolean canCallNonEmergency, boolean isLocalInvocation) {
        // Ensure call intents are not processed on devices that are not capable of calling.
        if (!isVoiceCapable()) {
            return;
@@ -85,12 +92,13 @@ public class UserCallIntentProcessor {
        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
                    isLocalInvocation);
        }
    }

    private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
            boolean canCallNonEmergency, boolean isLocalInvocation) {
        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();
@@ -144,7 +152,7 @@ public class UserCallIntentProcessor {
        // Save the user handle of current user before forwarding the intent to primary user.
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendBroadcastToReceiver(intent);
        sendIntentToDestination(intent, isLocalInvocation);
    }

    private boolean isDefaultOrSystemDialer(String callingPackageName) {
@@ -174,14 +182,28 @@ public class UserCallIntentProcessor {
    }

    /**
     * Trampolines the intent to the broadcast receiver that runs only as the primary user.
     * Potentially trampolines the intent to the broadcast receiver that runs only as the primary
     * user.  If the caller is local to the Telecom service, we send the intent to Telecom without
     * rebroadcasting it.
     */
    private boolean sendBroadcastToReceiver(Intent intent) {
    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.setClass(mContext, PrimaryCallReceiver.class);
        Log.d(this, "Sending broadcast as user to CallReceiver");
        if (isLocalInvocation) {
            // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
            // bother trampolining the intent, just sent it directly to the call intent processor.
            // TODO: We should not be using an intent here; this whole flows needs cleanup.
            Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
            synchronized (TelecomSystem.getInstance().getLock()) {
                TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
            }
        } else {
            // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
            // process; we need to trampoline to TelecomSystem in the system server process.
            Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
        }
        return true;
    }

+25 −5
Original line number Diff line number Diff line
@@ -442,6 +442,7 @@ public class BasicCallTests extends TelecomSystemTest {

    @LargeTest
    @Test
    @FlakyTest
    public void testIncomingCallFromBlockedNumberIsRejected() throws Exception {
        String phoneNumber = "650-555-1212";
        blockNumber(phoneNumber);
@@ -986,19 +987,37 @@ public class BasicCallTests extends TelecomSystemTest {
    }

    /**
     * Basic test to ensure that when there are other calls, we do not permit outgoing calls by a
     * self managed CS.
     * Ensure if there is a holdable call ongoing we'll be able to place another call.
     * @throws Exception
     */
    @LargeTest
    @Test
    public void testIsOutgoingCallPermittedOngoingHoldable() throws Exception {
        // Start a regular call; the self-managed CS can make a call now since ongoing call can be
        // held
        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());

        assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
                .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
    }

    /**
     * Ensure if there is an unholdable call we can't place another call.
     * @throws Exception
     */
    @LargeTest
    @Test
    public void testIsOutgoingCallPermittedOngoing() throws Exception {
        // Start a regular call; the self-managed CS can't make a call now.
    public void testIsOutgoingCallPermittedOngoingUnHoldable() throws Exception {
        // Start a regular call; the self-managed CS can't make a call now because the ongoing call
        // can't be held.
        mConnectionServiceFixtureA.mConnectionServiceDelegate.mCapabilities = 0;
        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());

        assertFalse(mTelecomSystem.getTelecomServiceImpl().getBinder()
        assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
                .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
    }

@@ -1009,6 +1028,7 @@ public class BasicCallTests extends TelecomSystemTest {
     */
    @LargeTest
    @Test
    @FlakyTest
    public void testDisconnectSelfManaged() throws Exception {
        // Add a self-managed call.
        PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
Loading