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
Merged-In: I2903d09f7dc3fafe59f3ffc4a8bd95d1eec664a7
Change-Id: I2abbf2f94e8058b741a6849974ddc9acc1ae6399
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3003aad..7c57599 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -291,14 +291,6 @@
android:process=":ui">
</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"
android:configChanges="orientation|screenSize|keyboardHidden"
android:excludeFromRecents="true"
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index dbbfd08..ed7e7e7 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -31,15 +31,19 @@
import android.Manifest;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.ToneGenerator;
@@ -85,6 +89,11 @@
import com.android.internal.annotations.VisibleForTesting;
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.server.telecom.bluetooth.BluetoothRouteManager;
import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
@@ -99,9 +108,9 @@
import com.android.server.telecom.callfiltering.NewCallScreeningServiceFilter;
import com.android.server.telecom.callredirection.CallRedirectionProcessor;
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.ui.AudioProcessingNotification;
-import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
@@ -2028,31 +2037,106 @@
+ "callId=%s, callRedirectionAppName=%s",
call.getId(), callRedirectionApp);
- Intent confirmIntent = new Intent(mContext,
- 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);
-
- // A small delay to start the activity after any Dialer's In Call UI starts
- mHandler.postDelayed(new Runnable("CM.oCRC", mLock) {
- @Override
- public void loggedRun() {
- mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
- }
- }.prepare(), 500 /* Milliseconds delay */);
-
+ showRedirectionDialog(call.getId());
} else {
call.setTargetPhoneAccount(phoneAccountHandle);
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) {
- 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()
.equals(callId)) {
if (action.equals(TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL)) {
@@ -4384,6 +4468,18 @@
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) {
pw.println("mCallAudioManager:");
pw.increaseIndent();
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index 06aa174..e1f2d08 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -24,7 +24,6 @@
import android.telecom.Log;
import android.widget.Toast;
-import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.DisconnectedCallNotifier;
@@ -80,27 +79,31 @@
"com.android.server.telecom.CANCEL_CALL";
/**
- * The action used to proceed with a redirected call being confirmed via
- * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
+ * The action used to proceed with a redirected call being confirmed via the call redirection
+ * confirmation dialog.
*/
public static final String ACTION_PLACE_REDIRECTED_CALL =
"com.android.server.telecom.PROCEED_WITH_REDIRECTED_CALL";
/**
- * The action used to confirm to proceed the call without redirection via
- * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
+ * The action used to confirm to proceed the call without redirection via the call redirection
+ * confirmation dialog.
*/
public static final String ACTION_PLACE_UNREDIRECTED_CALL =
"com.android.server.telecom.PROCEED_WITH_UNREDIRECTED_CALL";
/**
- * The action used to cancel a redirected call being confirmed via
- * {@link com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity}.
+ * The action used to cancel a redirected call being confirmed via the call redirection
+ * confirmation dialog.
*/
public static final String ACTION_CANCEL_REDIRECTED_CALL =
"com.android.server.telecom.CANCEL_REDIRECTED_CALL";
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 CallsManager mCallsManager;
@@ -214,8 +217,7 @@
Log.startSession("TBIP.aPRC");
try {
mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
- intent.getStringExtra(CallRedirectionConfirmDialogActivity
- .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
+ intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
ACTION_PLACE_REDIRECTED_CALL);
} finally {
Log.endSession();
@@ -224,8 +226,7 @@
Log.startSession("TBIP.aPUC");
try {
mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
- intent.getStringExtra(CallRedirectionConfirmDialogActivity
- .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
+ intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
ACTION_PLACE_UNREDIRECTED_CALL);
} finally {
Log.endSession();
@@ -234,8 +235,7 @@
Log.startSession("TBIP.aCRC");
try {
mCallsManager.processRedirectedOutgoingCallAfterUserInteraction(
- intent.getStringExtra(CallRedirectionConfirmDialogActivity
- .EXTRA_REDIRECTION_OUTGOING_CALL_ID),
+ intent.getStringExtra(EXTRA_REDIRECTION_OUTGOING_CALL_ID),
ACTION_CANCEL_REDIRECTED_CALL);
} finally {
Log.endSession();
diff --git a/src/com/android/server/telecom/ui/CallRedirectionConfirmDialogActivity.java b/src/com/android/server/telecom/ui/CallRedirectionConfirmDialogActivity.java
deleted file mode 100644
index 0d3599e..0000000
--- a/src/com/android/server/telecom/ui/CallRedirectionConfirmDialogActivity.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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);
- }
-}