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

Commit ba037792 authored by Tyler Gunn's avatar Tyler Gunn Committed by Android (Google) Code Review
Browse files

Merge "Confirm managed call when there are ongoing self-managed calls." into oc-dev

parents f9e7fbc5 754dc5de
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -212,6 +212,8 @@
                <action android:name="com.android.server.telecom.ACTION_SEND_SMS_FROM_NOTIFICATION" />
                <action android:name="com.android.server.telecom.ACTION_ANSWER_FROM_NOTIFICATION" />
                <action android:name="com.android.server.telecom.ACTION_REJECT_FROM_NOTIFICATION" />
                <action android:name="com.android.server.telecom.PROCEED_WITH_CALL" />
                <action android:name="com.android.server.telecom.CANCEL_CALL" />
            </intent-filter>
        </receiver>

@@ -254,6 +256,14 @@
                android:process=":ui">
        </activity>

        <activity android:name=".ui.ConfirmCallDialogActivity"
                android:configChanges="orientation|screenSize|keyboardHidden"
                android:excludeFromRecents="true"
                android:launchMode="singleInstance"
                android:theme="@style/Theme.Telecomm.Transparent"
                android:process=":ui">
        </activity>

        <activity android:name=".components.ChangeDefaultDialerDialog"
                  android:label="@string/change_default_dialer_dialog_title"
                  android:excludeFromRecents="true"
@@ -265,7 +275,6 @@
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity android:name=".testapps.IncomingSelfManagedCallActivity" />

        <receiver android:name=".components.PrimaryCallReceiver"
                android:exported="true"
+4 −0
Original line number Diff line number Diff line
@@ -252,4 +252,8 @@
    <string name="notification_channel_incoming_call">Incoming calls</string>
    <!-- Notification channel name for a channel containing missed call notifications. -->
    <string name="notification_channel_missed_call">Missed calls</string>

    <!-- Alert dialog content used to inform the user that placing a new outgoing call will end the
         ongoing call in the app "other_app". -->
    <string name="alert_outgoing_call">Placing this call will end your <xliff:g id="other_app">%1$s</xliff:g> call.</string>
</resources>
+15 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -310,6 +311,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {

    private Bundle mIntentExtras = new Bundle();

    /**
     * The {@link Intent} which originally created this call.  Only populated when we are putting a
     * call into a pending state and need to pick up initiation of the call later.
     */
    private Intent mOriginalCallIntent = null;

    /** Set of listeners on this call.
     *
     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
@@ -1706,6 +1713,14 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        mIntentExtras = extras;
    }

    public Intent getOriginalCallIntent() {
        return mOriginalCallIntent;
    }

    public void setOriginalCallIntent(Intent intent) {
        mOriginalCallIntent = intent;
    }

    /**
     * @return the uri of the contact associated with this call.
     */
+23 −18
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@ import android.os.Bundle;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.telecom.Connection;
import android.telecom.DefaultDialerManager;
import android.telecom.Log;
import android.telecom.PhoneAccount;
@@ -128,8 +127,6 @@ public class CallIntentProcessor {
                VideoProfile.STATE_AUDIO_ONLY);
        clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);

        final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);

        boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
        // Show the toast to warn user that it is a personal call though initiated in work profile.
        if (fixedInitiatingUser) {
@@ -140,14 +137,23 @@ public class CallIntentProcessor {

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent);

        if (call != null) {
            sendNewOutgoingCallIntent(context, call, callsManager, intent);
        }
    }

    static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
            Intent intent) {
        // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
        // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
        // killed if memory is scarce. However, this is OK here because the entire Telecom
        // process will be running throughout the duration of the phone call and should never
        // be killed.
        final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);

        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer);
@@ -158,7 +164,6 @@ public class CallIntentProcessor {
            disconnectCallAndShowErrorDialog(context, call, result);
        }
    }
    }

    /**
     * If the call is initiated from managed profile but there is no work dialer installed, treat
+142 −19
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
import com.android.server.telecom.callfiltering.IncomingCallFilter;
import com.android.server.telecom.components.ErrorDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.IncomingCallNotifier;

import java.util.ArrayList;
@@ -208,6 +209,12 @@ public class CallsManager extends Call.ListenerBase
    private final Set<Call> mCalls = Collections.newSetFromMap(
            new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));

    /**
     * A pending call is one which requires user-intervention in order to be placed.
     * Used by {@link #startCallConfirmation(Call)}.
     */
    private Call mPendingCall;

    /**
     * The current telecom call ID.  Used when creating new instances of {@link Call}.  Should
     * only be accessed using the {@link #getNextCallId()} method which synchronizes on the
@@ -949,15 +956,15 @@ public class CallsManager extends Call.ListenerBase
     * For managed connections, this is the first step to launching the Incall UI.
     * For self-managed connections, we don't expect the Incall UI to launch, but this is still a
     * first step in getting the self-managed ConnectionService to create the connection.
     *
     * @param handle Handle to connect the call with.
     * @param phoneAccountHandle The phone account which contains the component name of the
     *        connection service to use for this call.
     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
     * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
     * @param originalIntent
     */
    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
            UserHandle initiatingUser) {
            UserHandle initiatingUser, Intent originalIntent) {
        boolean isReusedCall = true;
        Call call = reuseOutgoingCall(handle);

@@ -997,7 +1004,6 @@ public class CallsManager extends Call.ListenerBase
            }

            call.setInitiatingUser(initiatingUser);

            isReusedCall = false;
        }

@@ -1115,9 +1121,15 @@ public class CallsManager extends Call.ListenerBase
        }
        setIntentExtrasAndStartTime(call, extras);

        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode)
                && !needsAccountSelection) {
            // Do not add the call if it is a potential MMI code.
        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
            call.addListener(this);
        } else if (!isSelfManaged && hasSelfManagedCalls() && !call.isEmergencyCall()) {
            // Adding a managed call and there are ongoing self-managed call(s).
            call.setOriginalCallIntent(originalIntent);
            startCallConfirmation(call);
            return null;
        } else if (!mCalls.contains(call)) {
            // We check if mCalls already contains the call because we could potentially be reusing
            // a call which was previously added (See {@link #reuseOutgoingCall}).
@@ -1190,23 +1202,11 @@ public class CallsManager extends Call.ListenerBase
            if (call.isSelfManaged() && !isOutgoingCallPermitted) {
                notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
            } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {
                Call activeCall = getActiveCall();
                CharSequence errorMessage;
                if (activeCall == null) {
                    // Realistically this shouldn't happen, but best to handle gracefully
                    errorMessage = mContext.getText(R.string.cant_call_due_to_ongoing_unknown_call);
                } else {
                    errorMessage = mContext.getString(R.string.cant_call_due_to_ongoing_call,
                            activeCall.getTargetPhoneAccountLabel());
                }
                // Call is managed and there are ongoing self-managed calls.
                markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR,
                        errorMessage, errorMessage, "Ongoing call in another app."));
                markCallAsRemoved(call);
                markCallDisconnectedDueToSelfManagedCall(call);
            } else {
                if (call.isEmergencyCall()) {
                    // Disconnect all self-managed calls to make priority for emergency call.
                    mCalls.stream().filter(c -> c.isSelfManaged()).forEach(c -> c.disconnect());
                    disconnectSelfManagedCalls();
                }

                call.startCreateConnection(mPhoneAccountRegistrar);
@@ -1702,6 +1702,33 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    /**
     * Given a call, marks the call as disconnected and removes it.  Set the error message to
     * indicate to the user that the call cannot me placed due to an ongoing call in another app.
     *
     * Used when there are ongoing self-managed calls and the user tries to make an outgoing managed
     * call.  Called by {@link #startCallConfirmation(Call)} when the user is already confirming an
     * outgoing call.  Realistically this should almost never be called since in practice the user
     * won't make multiple outgoing calls at the same time.
     *
     * @param call The call to mark as disconnected.
     */
    void markCallDisconnectedDueToSelfManagedCall(Call call) {
        Call activeCall = getActiveCall();
        CharSequence errorMessage;
        if (activeCall == null) {
            // Realistically this shouldn't happen, but best to handle gracefully
            errorMessage = mContext.getText(R.string.cant_call_due_to_ongoing_unknown_call);
        } else {
            errorMessage = mContext.getString(R.string.cant_call_due_to_ongoing_call,
                    activeCall.getTargetPhoneAccountLabel());
        }
        // Call is managed and there are ongoing self-managed calls.
        markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR,
                errorMessage, errorMessage, "Ongoing call in another app."));
        markCallAsRemoved(call);
    }

    /**
     * Cleans up any calls currently associated with the specified connection service when the
     * service binder disconnects unexpectedly.
@@ -2659,6 +2686,97 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    /**
     * Used to confirm creation of an outgoing call which was marked as pending confirmation in
     * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}.
     * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via
     * {@link ConfirmCallDialogActivity}.
     * @param callId The call ID of the call to confirm.
     */
    public void confirmPendingCall(String callId) {
        Log.i(this, "confirmPendingCall: callId=%s", callId);
        if (mPendingCall != null && mPendingCall.getId().equals(callId)) {
            Log.addEvent(mPendingCall, LogUtils.Events.USER_CONFIRMED);
            addCall(mPendingCall);

            // We are going to place the new outgoing call, so disconnect any ongoing self-managed
            // calls which are ongoing at this time.
            disconnectSelfManagedCalls();

            // Kick of the new outgoing call intent from where it left off prior to confirming the
            // call.
            CallIntentProcessor.sendNewOutgoingCallIntent(mContext, mPendingCall, this,
                    mPendingCall.getOriginalCallIntent());
            mPendingCall = null;
        }
    }

    /**
     * Used to cancel an outgoing call which was marked as pending confirmation in
     * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}.
     * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via
     * {@link ConfirmCallDialogActivity}.
     * @param callId The call ID of the call to cancel.
     */
    public void cancelPendingCall(String callId) {
        Log.i(this, "cancelPendingCall: callId=%s", callId);
        if (mPendingCall != null && mPendingCall.getId().equals(callId)) {
            Log.addEvent(mPendingCall, LogUtils.Events.USER_CANCELLED);
            markCallAsDisconnected(mPendingCall, new DisconnectCause(DisconnectCause.CANCELED));
            markCallAsRemoved(mPendingCall);
            mPendingCall = null;
        }
    }

    /**
     * Called from {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)} when
     * a managed call is added while there are ongoing self-managed calls.  Starts
     * {@link ConfirmCallDialogActivity} to prompt the user to see if they wish to place the
     * outgoing call or not.
     * @param call The call to confirm.
     */
    private void startCallConfirmation(Call call) {
        if (mPendingCall != null) {
            Log.i(this, "startCallConfirmation: call %s is already pending; disconnecting %s",
                    mPendingCall.getId(), call.getId());
            markCallDisconnectedDueToSelfManagedCall(call);
            return;
        }
        Log.addEvent(call, LogUtils.Events.USER_CONFIRMATION);
        mPendingCall = call;

        // Figure out the name of the app in charge of the self-managed call(s).
        Call selfManagedCall = mCalls.stream()
                .filter(c -> c.isSelfManaged())
                .findFirst()
                .orElse(null);
        CharSequence ongoingAppName = "";
        if (selfManagedCall != null) {
            ongoingAppName = selfManagedCall.getTargetPhoneAccountLabel();
        }
        Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(),
                ongoingAppName);

        Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class);
        confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId());
        confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName);
        confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
    }

    /**
     * Disconnects all self-managed calls.
     */
    private void disconnectSelfManagedCalls() {
        // Disconnect all self-managed calls to make priority for emergency call.
        // Use Call.disconnect() to command the ConnectionService to disconnect the calls.
        // CallsManager.markCallAsDisconnected doesn't actually tell the ConnectionService to
        // disconnect.
        mCalls.stream()
                .filter(c -> c.isSelfManaged())
                .forEach(c -> c.disconnect());
    }

    /**
     * Dumps the state of the {@link CallsManager}.
     *
@@ -2675,6 +2793,11 @@ public class CallsManager extends Call.ListenerBase
            pw.decreaseIndent();
        }

        if (mPendingCall != null) {
            pw.print("mPendingCall:");
            pw.println(mPendingCall.getId());
        }

        if (mCallAudioManager != null) {
            pw.println("mCallAudioManager:");
            pw.increaseIndent();
Loading