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

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

Fix issue with call redirection activity.

The call redirection activity fails to show on top of the dialer when
the dialer launches just after the confirm dialog activity starts.
Removing the intermediary call redirection activity allows us to use
the SYSTEM_ALERT_WINDOW type for the dialog, ensuring it shows up on top
of the dialer window in all instances.

Test: Manually reproduced bug and verified that the new implementation
does not suffer from race conditions indicated above.
Bug: 137223601
Change-Id: I503ce9f23606ee8fa387fa8924aaeb51173f5571
parent 1e05f4a4
Loading
Loading
Loading
Loading
+0 −8
Original line number Original line Diff line number Diff line
@@ -291,14 +291,6 @@
                android:process=":ui">
                android:process=":ui">
        </activity>
        </activity>


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

        <activity android:name=".ui.CallRedirectionTimeoutDialogActivity"
        <activity android:name=".ui.CallRedirectionTimeoutDialogActivity"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:excludeFromRecents="true"
                  android:excludeFromRecents="true"
+115 −11
Original line number Original line Diff line number Diff line
@@ -31,15 +31,19 @@ import static android.telecom.TelecomManager.VERY_SHORT_CALL_TIME_MS;
import android.Manifest;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.AudioSystem;
import android.media.ToneGenerator;
import android.media.ToneGenerator;
@@ -85,6 +89,11 @@ import android.util.Pair;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import android.telecom.CallerInfo;
import android.telecom.CallerInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;

import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
@@ -99,9 +108,9 @@ import com.android.server.telecom.callfiltering.IncomingCallFilterGraph;
import com.android.server.telecom.callfiltering.NewCallScreeningServiceFilter;
import com.android.server.telecom.callfiltering.NewCallScreeningServiceFilter;
import com.android.server.telecom.callredirection.CallRedirectionProcessor;
import com.android.server.telecom.callredirection.CallRedirectionProcessor;
import com.android.server.telecom.components.ErrorDialogActivity;
import com.android.server.telecom.components.ErrorDialogActivity;
import com.android.server.telecom.components.TelecomBroadcastReceiver;
import com.android.server.telecom.settings.BlockedNumbersUtil;
import com.android.server.telecom.settings.BlockedNumbersUtil;
import com.android.server.telecom.ui.AudioProcessingNotification;
import com.android.server.telecom.ui.AudioProcessingNotification;
import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
@@ -2028,23 +2037,106 @@ public class CallsManager extends Call.ListenerBase
                            + "callId=%s, callRedirectionAppName=%s",
                            + "callId=%s, callRedirectionAppName=%s",
                    call.getId(), callRedirectionApp);
                    call.getId(), callRedirectionApp);


            Intent confirmIntent = new Intent(mContext,
            showRedirectionDialog(call.getId());
                    CallRedirectionConfirmDialogActivity.class);
            confirmIntent.putExtra(
                    CallRedirectionConfirmDialogActivity.EXTRA_REDIRECTION_OUTGOING_CALL_ID,
                    call.getId());
            confirmIntent.putExtra(CallRedirectionConfirmDialogActivity.EXTRA_REDIRECTION_APP_NAME,
                    mRoleManagerAdapter.getApplicationLabelForPackageName(callRedirectionApp));
            confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
        } else {
        } else {
            call.setTargetPhoneAccount(phoneAccountHandle);
            call.setTargetPhoneAccount(phoneAccountHandle);
            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
        }
        }
    }
    }


    /**
     * Shows the call redirection confirmation dialog.  This is explicitly done here instead of in
     * an activity class such as {@link ConfirmCallDialogActivity}.  This was originally done with
     * an activity class, however due to the fact that the InCall UI is being spun up at the same
     * time as the dialog activity, there is a potential race condition where the InCall UI will
     * often be shown instead of the dialog.  Activity manager chooses not to show the redirection
     * dialog in that case since the new top activity from dialer is going to show.
     * By showing the dialog here we're able to set the dialog's window type to
     * {@link WindowManager.LayoutParams#TYPE_SYSTEM_ALERT} which guarantees it shows above other
     * content on the screen.
     * @param callId The ID of the call to show the redirection dialog for.
     */
    private void showRedirectionDialog(@NonNull String callId) {
        AlertDialog confirmDialog = new AlertDialog.Builder(mContext).create();
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        View dialogView = layoutInflater.inflate(R.layout.call_redirection_confirm_dialog, null);

        Button buttonFirstLine = (Button) dialogView.findViewById(R.id.buttonFirstLine);
        buttonFirstLine.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent proceedWithoutRedirectedCall = new Intent(
                        TelecomBroadcastIntentProcessor.ACTION_PLACE_UNREDIRECTED_CALL,
                        null, mContext,
                        TelecomBroadcastReceiver.class);
                proceedWithoutRedirectedCall.putExtra(
                        TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID,
                        callId);
                mContext.sendBroadcast(proceedWithoutRedirectedCall);
                confirmDialog.dismiss();
            }
        });

        Button buttonSecondLine = (Button) dialogView.findViewById(R.id.buttonSecondLine);
        buttonSecondLine.setText(mContext.getText(
                R.string.alert_place_outgoing_call_with_redirection));
        buttonSecondLine.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent proceedWithRedirectedCall = new Intent(
                        TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL, null,
                        mContext,
                        TelecomBroadcastReceiver.class);
                proceedWithRedirectedCall.putExtra(
                        TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID,
                        callId);
                mContext.sendBroadcast(proceedWithRedirectedCall);
                confirmDialog.dismiss();
            }
        });

        Button buttonThirdLine = (Button) dialogView.findViewById(R.id.buttonThirdLine);
        buttonThirdLine.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                cancelRedirection(callId);
                confirmDialog.dismiss();
            }
        });

        confirmDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                cancelRedirection(callId);
                confirmDialog.dismiss();
            }
        });

        confirmDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        confirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

        confirmDialog.setCancelable(false);
        confirmDialog.setCanceledOnTouchOutside(false);
        confirmDialog.setView(dialogView);

        confirmDialog.show();
    }

    /**
     * Signals to Telecom that redirection of the call is to be cancelled.
     */
    private void cancelRedirection(String callId) {
        Intent cancelRedirectedCall = new Intent(
                TelecomBroadcastIntentProcessor.ACTION_CANCEL_REDIRECTED_CALL,
                null, mContext,
                TelecomBroadcastReceiver.class);
        cancelRedirectedCall.putExtra(
                TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID, callId);
        mContext.sendBroadcastAsUser(cancelRedirectedCall, UserHandle.CURRENT);
    }

    public void processRedirectedOutgoingCallAfterUserInteraction(String callId, String action) {
    public void processRedirectedOutgoingCallAfterUserInteraction(String callId, String action) {
        Log.i(this, "processRedirectedOutgoingCallAfterUserInteraction for Call ID %s", callId);
        Log.i(this, "processRedirectedOutgoingCallAfterUserInteraction for Call ID %s, action=%s",
                callId, action);
        if (mPendingRedirectedOutgoingCall != null && mPendingRedirectedOutgoingCall.getId()
        if (mPendingRedirectedOutgoingCall != null && mPendingRedirectedOutgoingCall.getId()
                .equals(callId)) {
                .equals(callId)) {
            if (action.equals(TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL)) {
            if (action.equals(TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL)) {
@@ -4383,6 +4475,18 @@ public class CallsManager extends Call.ListenerBase
            pw.println(mPendingCall.getId());
            pw.println(mPendingCall.getId());
        }
        }


        if (mPendingRedirectedOutgoingCallInfo.size() > 0) {
            pw.print("mPendingRedirectedOutgoingCallInfo:");
            pw.println(mPendingRedirectedOutgoingCallInfo.keySet().stream().collect(
                    Collectors.joining(", ")));
        }

        if (mPendingUnredirectedOutgoingCallInfo.size() > 0) {
            pw.print("mPendingUnredirectedOutgoingCallInfo:");
            pw.println(mPendingUnredirectedOutgoingCallInfo.keySet().stream().collect(
                    Collectors.joining(", ")));
        }

        if (mCallAudioManager != null) {
        if (mCallAudioManager != null) {
            pw.println("mCallAudioManager:");
            pw.println("mCallAudioManager:");
            pw.increaseIndent();
            pw.increaseIndent();
+13 −13
Original line number Original line Diff line number Diff line
@@ -24,7 +24,6 @@ import android.os.UserHandle;
import android.telecom.Log;
import android.telecom.Log;
import android.widget.Toast;
import android.widget.Toast;


import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
import com.android.server.telecom.ui.DisconnectedCallNotifier;


@@ -80,27 +79,31 @@ public final class TelecomBroadcastIntentProcessor {
            "com.android.server.telecom.CANCEL_CALL";
            "com.android.server.telecom.CANCEL_CALL";


    /**
    /**
     * The action used to proceed with a redirected call being confirmed via
     * The action used to proceed with a redirected call being confirmed via the call redirection
     * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
     * confirmation dialog.
     */
     */
    public static final String ACTION_PLACE_REDIRECTED_CALL =
    public static final String ACTION_PLACE_REDIRECTED_CALL =
            "com.android.server.telecom.PROCEED_WITH_REDIRECTED_CALL";
            "com.android.server.telecom.PROCEED_WITH_REDIRECTED_CALL";


    /**
    /**
     * The action used to confirm to proceed the call without redirection via
     * The action used to confirm to proceed the call without redirection via the call redirection
     * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
     * confirmation dialog.
     */
     */
    public static final String ACTION_PLACE_UNREDIRECTED_CALL =
    public static final String ACTION_PLACE_UNREDIRECTED_CALL =
            "com.android.server.telecom.PROCEED_WITH_UNREDIRECTED_CALL";
            "com.android.server.telecom.PROCEED_WITH_UNREDIRECTED_CALL";


    /**
    /**
     * The action used to cancel a redirected call being confirmed via
     * The action used to cancel a redirected call being confirmed via the call redirection
     * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
     * confirmation dialog.
     */
     */
    public static final String ACTION_CANCEL_REDIRECTED_CALL =
    public static final String ACTION_CANCEL_REDIRECTED_CALL =
            "com.android.server.telecom.CANCEL_REDIRECTED_CALL";
            "com.android.server.telecom.CANCEL_REDIRECTED_CALL";


    public static final String EXTRA_USERHANDLE = "userhandle";
    public static final String EXTRA_USERHANDLE = "userhandle";
    public static final String EXTRA_REDIRECTION_OUTGOING_CALL_ID =
            "android.telecom.extra.REDIRECTION_OUTGOING_CALL_ID";
    public static final String EXTRA_REDIRECTION_APP_NAME =
            "android.telecom.extra.REDIRECTION_APP_NAME";


    private final Context mContext;
    private final Context mContext;
    private final CallsManager mCallsManager;
    private final CallsManager mCallsManager;
@@ -214,8 +217,7 @@ public final class TelecomBroadcastIntentProcessor {
            Log.startSession("TBIP.aPRC");
            Log.startSession("TBIP.aPRC");
            try {
            try {
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                        intent.getStringExtra(CallRedirectionConfirmDialogActivity
                        intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                                .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                        ACTION_PLACE_REDIRECTED_CALL);
                        ACTION_PLACE_REDIRECTED_CALL);
            } finally {
            } finally {
                Log.endSession();
                Log.endSession();
@@ -224,8 +226,7 @@ public final class TelecomBroadcastIntentProcessor {
            Log.startSession("TBIP.aPUC");
            Log.startSession("TBIP.aPUC");
            try {
            try {
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                        intent.getStringExtra(CallRedirectionConfirmDialogActivity
                        intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                                .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                        ACTION_PLACE_UNREDIRECTED_CALL);
                        ACTION_PLACE_UNREDIRECTED_CALL);
            } finally {
            } finally {
                Log.endSession();
                Log.endSession();
@@ -234,8 +235,7 @@ public final class TelecomBroadcastIntentProcessor {
            Log.startSession("TBIP.aCRC");
            Log.startSession("TBIP.aCRC");
            try {
            try {
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
                        intent.getStringExtra(CallRedirectionConfirmDialogActivity
                        intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                                .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
                        ACTION_CANCEL_REDIRECTED_CALL);
                        ACTION_CANCEL_REDIRECTED_CALL);
            } finally {
            } finally {
                Log.endSession();
                Log.endSession();
+0 −161
Original line number Original line 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.ui;

import com.android.server.telecom.R;
import com.android.server.telecom.TelecomBroadcastIntentProcessor;
import com.android.server.telecom.components.TelecomBroadcastReceiver;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.telecom.Log;
import android.view.LayoutInflater;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;

/**
 * Dialog activity used when there is an ongoing call redirected by the call redirection service.
 * The dialog prompts the user to see if they want to place the redirected outgoing call.
 */
public class CallRedirectionConfirmDialogActivity extends Activity {
    public static final String EXTRA_REDIRECTION_OUTGOING_CALL_ID =
            "android.telecom.extra.REDIRECTION_OUTGOING_CALL_ID";
    public static final String EXTRA_REDIRECTION_APP_NAME =
            "android.telecom.extra.REDIRECTION_APP_NAME";

    private String mCallId;
    private AlertDialog mConfirmDialog;
    /**
     * Tracks whether the activity has stopped due to a loss of focus (e.g. use hitting the home
     * button) or whether its going to stop because a button in the dialog was pressed.
     */
    private boolean mHasLostFocus = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(this, "CallRedirectionConfirmDialogActivity onCreate.");
        final CharSequence redirectionAppName = getIntent().getStringExtra(
                EXTRA_REDIRECTION_APP_NAME);
        mCallId = getIntent().getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID);
        showDialog(redirectionAppName);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mHasLostFocus) {
            Log.i(this, "onStop: dialog lost focus; canceling redirection for call %s", mCallId);
            mConfirmDialog.dismiss();
            cancelRedirection();
        }
    }

    private void showDialog(final CharSequence redirectionAppName) {
        Log.i(this, "showDialog: confirming redirection with %s", redirectionAppName);

        mConfirmDialog = new AlertDialog.Builder(this).create();
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        View dialogView = layoutInflater.inflate(R.layout.call_redirection_confirm_dialog, null);

        Button buttonFirstLine = (Button) dialogView.findViewById(R.id.buttonFirstLine);
        buttonFirstLine.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent proceedWithoutRedirectedCall = new Intent(
                        TelecomBroadcastIntentProcessor.ACTION_PLACE_UNREDIRECTED_CALL,
                        null, CallRedirectionConfirmDialogActivity.this,
                        TelecomBroadcastReceiver.class);
                proceedWithoutRedirectedCall.putExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID, mCallId);
                sendBroadcast(proceedWithoutRedirectedCall);
                mConfirmDialog.dismiss();
                mHasLostFocus = false;
                finish();
            }
        });

        Button buttonSecondLine = (Button) dialogView.findViewById(R.id.buttonSecondLine);
        buttonSecondLine.setText(getString(R.string.alert_place_outgoing_call_with_redirection,
                redirectionAppName));
        buttonSecondLine.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent proceedWithRedirectedCall = new Intent(
                        TelecomBroadcastIntentProcessor
                                .ACTION_PLACE_REDIRECTED_CALL, null,
                        CallRedirectionConfirmDialogActivity.this,
                        TelecomBroadcastReceiver.class);
                proceedWithRedirectedCall.putExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID, mCallId);
                sendBroadcast(proceedWithRedirectedCall);
                mConfirmDialog.dismiss();
                mHasLostFocus = false;
                finish();
            }
        });

        Button buttonThirdLine = (Button) dialogView.findViewById(R.id.buttonThirdLine);
        buttonThirdLine.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Intent cancelRedirectedCall = new Intent(
                        TelecomBroadcastIntentProcessor.ACTION_CANCEL_REDIRECTED_CALL,
                        null, CallRedirectionConfirmDialogActivity.this,
                        TelecomBroadcastReceiver.class);
                cancelRedirectedCall.putExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID, mCallId);
                sendBroadcast(cancelRedirectedCall);
                mConfirmDialog.dismiss();
                mHasLostFocus = false;
                finish();
            }
        });

        mConfirmDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                cancelRedirection();
                dialog.dismiss();
                mHasLostFocus = false;
                finish();
            }
        });

        mConfirmDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

        mConfirmDialog.setCancelable(false);
        mConfirmDialog.setCanceledOnTouchOutside(false);
        mConfirmDialog.setView(dialogView);

        mConfirmDialog.show();
    }

    /**
     * Signals to Telecom that redirection of the call is to be cancelled.
     */
    private void cancelRedirection() {
        Intent cancelRedirectedCall = new Intent(
                TelecomBroadcastIntentProcessor.ACTION_CANCEL_REDIRECTED_CALL,
                null, CallRedirectionConfirmDialogActivity.this,
                TelecomBroadcastReceiver.class);
        cancelRedirectedCall.putExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID, mCallId);
        sendBroadcast(cancelRedirectedCall);
    }
}