blob: 6918d48aaf6ac9f4d202901fc4bd083a4ba88670 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
Shuo Qian479dd9e2021-02-22 18:32:21 -080019import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
20
Santos Cordon7d4ddf62013-07-10 11:58:08 -070021import android.app.Activity;
Brad Ebinger6d4ef742018-02-07 10:59:33 -080022import android.app.AlertDialog;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070023import android.app.Dialog;
24import android.app.ProgressDialog;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070025import android.content.BroadcastReceiver;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.DialogInterface;
Li Wei65667ea2017-08-02 16:06:59 +080029import android.content.DialogInterface.OnCancelListener;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.ServiceConnection;
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +000033import android.icu.text.MessageFormat;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.os.AsyncResult;
35import android.os.Bundle;
36import android.os.CountDownTimer;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.Looper;
40import android.os.Message;
Brad Ebinger3b3c30a2020-03-18 13:59:44 -070041import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042import android.util.Log;
43
44import com.android.internal.telephony.Phone;
45import com.android.internal.telephony.TelephonyIntents;
Daniel Bantae91feb42022-12-23 06:37:53 +000046import com.android.internal.telephony.domainselection.DomainSelectionResolver;
47import com.android.internal.telephony.emergency.EmergencyStateTracker;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +000049import java.util.HashMap;
50import java.util.Map;
51
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052/**
53 * Displays dialog that enables users to exit Emergency Callback Mode
54 *
55 * @see EmergencyCallbackModeService
56 */
Li Wei65667ea2017-08-02 16:06:59 +080057public class EmergencyCallbackModeExitDialog extends Activity implements OnCancelListener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058
Yorke Lee34a72cb2014-10-12 13:17:04 -070059 private static final String TAG = "EmergencyCallbackMode";
60
Santos Cordon7d4ddf62013-07-10 11:58:08 -070061 /** Intent to trigger the Emergency Callback Mode exit dialog */
62 static final String ACTION_SHOW_ECM_EXIT_DIALOG =
63 "com.android.phone.action.ACTION_SHOW_ECM_EXIT_DIALOG";
Santos Cordon7d4ddf62013-07-10 11:58:08 -070064
65 public static final int EXIT_ECM_BLOCK_OTHERS = 1;
66 public static final int EXIT_ECM_DIALOG = 2;
67 public static final int EXIT_ECM_PROGRESS_DIALOG = 3;
68 public static final int EXIT_ECM_IN_EMERGENCY_CALL_DIALOG = 4;
69
70 AlertDialog mAlertDialog = null;
71 ProgressDialog mProgressDialog = null;
72 CountDownTimer mTimer = null;
73 EmergencyCallbackModeService mService = null;
74 Handler mHandler = null;
75 int mDialogType = 0;
76 long mEcmTimeout = 0;
77 private boolean mInEmergencyCall = false;
78 private static final int ECM_TIMER_RESET = 1;
79 private Phone mPhone = null;
Jordan Liu1cfab0d2019-09-27 11:17:52 -070080 private boolean mIsResumed = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070081
82 @Override
83 public void onCreate(Bundle savedInstanceState) {
84 super.onCreate(savedInstanceState);
Shuo Qian479dd9e2021-02-22 18:32:21 -080085 getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
Sandeep Kuntade73a6a2014-10-15 18:45:56 +053086 mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070087 // Check if phone is in Emergency Callback Mode. If not, exit.
Brad Ebinger6d4ef742018-02-07 10:59:33 -080088 if (mPhone == null || !mPhone.isInEcm()) {
89 Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090 finish();
Yorke Lee34a72cb2014-10-12 13:17:04 -070091 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070092 }
Brad Ebinger6d4ef742018-02-07 10:59:33 -080093 Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone);
Santos Cordon7d4ddf62013-07-10 11:58:08 -070094
95 mHandler = new Handler();
96
97 // Start thread that will wait for the connection completion so that it can get
98 // timeout value from the service
99 Thread waitForConnectionCompleteThread = new Thread(null, mTask,
100 "EcmExitDialogWaitThread");
101 waitForConnectionCompleteThread.start();
102
103 // Register ECM timer reset notfication
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700104 mPhone.registerForEcmTimerReset(mTimerResetHandler, ECM_TIMER_RESET, null);
105
106 // Register receiver for intent closing the dialog
107 IntentFilter filter = new IntentFilter();
108 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
109 registerReceiver(mEcmExitReceiver, filter);
110 }
111
112 @Override
Jordan Liu1cfab0d2019-09-27 11:17:52 -0700113 public void onResume() {
114 super.onResume();
115 mIsResumed = true;
116 }
117
118 @Override
119 public void onPause() {
120 super.onPause();
121 mIsResumed = false;
122 }
123
124 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125 public void onDestroy() {
126 super.onDestroy();
Yorke Lee34a72cb2014-10-12 13:17:04 -0700127 try {
128 unregisterReceiver(mEcmExitReceiver);
129 } catch (IllegalArgumentException e) {
130 // Receiver was never registered - silently ignore.
131 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700132 // Unregister ECM timer reset notification
Yorke Lee34a72cb2014-10-12 13:17:04 -0700133 if (mPhone != null) {
134 mPhone.unregisterForEcmTimerReset(mHandler);
135 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700136 }
137
138 @Override
139 protected void onRestoreInstanceState(Bundle savedInstanceState) {
140 super.onRestoreInstanceState(savedInstanceState);
141 mDialogType = savedInstanceState.getInt("DIALOG_TYPE");
142 }
143
144 @Override
145 protected void onSaveInstanceState(Bundle outState) {
146 super.onSaveInstanceState(outState);
147 outState.putInt("DIALOG_TYPE", mDialogType);
148 }
149
150 /**
151 * Waits until bind to the service completes
152 */
153 private Runnable mTask = new Runnable() {
154 public void run() {
155 Looper.prepare();
156
157 // Bind to the remote service
158 bindService(new Intent(EmergencyCallbackModeExitDialog.this,
159 EmergencyCallbackModeService.class), mConnection, Context.BIND_AUTO_CREATE);
160
161 // Wait for bind to finish
162 synchronized (EmergencyCallbackModeExitDialog.this) {
163 try {
164 if (mService == null) {
165 EmergencyCallbackModeExitDialog.this.wait();
166 }
167 } catch (InterruptedException e) {
168 Log.d("ECM", "EmergencyCallbackModeExitDialog InterruptedException: "
169 + e.getMessage());
170 e.printStackTrace();
171 }
172 }
173
174 // Get timeout value and call state from the service
175 if (mService != null) {
176 mEcmTimeout = mService.getEmergencyCallbackModeTimeout();
177 mInEmergencyCall = mService.getEmergencyCallbackModeCallState();
Yorke Lee34a72cb2014-10-12 13:17:04 -0700178 try {
179 // Unbind from remote service
180 unbindService(mConnection);
181 } catch (IllegalArgumentException e) {
182 // Failed to unbind from service. Don't crash as this brings down the entire
183 // radio.
184 Log.w(TAG, "Failed to unbind from EmergencyCallbackModeService");
185 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700186 }
187
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700188 // Show dialog
189 mHandler.post(new Runnable() {
190 public void run() {
191 showEmergencyCallbackModeExitDialog();
192 }
193 });
194 }
195 };
196
197 /**
198 * Shows Emergency Callback Mode dialog and starts countdown timer
199 */
200 private void showEmergencyCallbackModeExitDialog() {
Josh Hou704c1f92020-05-28 20:54:29 +0800201 if (isDestroyed()) {
Yorke Lee34a72cb2014-10-12 13:17:04 -0700202 Log.w(TAG, "Tried to show dialog, but activity was already finished");
203 return;
204 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700205 if(mInEmergencyCall) {
206 mDialogType = EXIT_ECM_IN_EMERGENCY_CALL_DIALOG;
207 showDialog(EXIT_ECM_IN_EMERGENCY_CALL_DIALOG);
208 } else {
209 if (getIntent().getAction().equals(
210 TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) {
211 mDialogType = EXIT_ECM_BLOCK_OTHERS;
212 showDialog(EXIT_ECM_BLOCK_OTHERS);
213 } else if (getIntent().getAction().equals(ACTION_SHOW_ECM_EXIT_DIALOG)) {
214 mDialogType = EXIT_ECM_DIALOG;
215 showDialog(EXIT_ECM_DIALOG);
216 }
217
218 mTimer = new CountDownTimer(mEcmTimeout, 1000) {
219 @Override
220 public void onTick(long millisUntilFinished) {
221 CharSequence text = getDialogText(millisUntilFinished);
222 mAlertDialog.setMessage(text);
223 }
224
225 @Override
226 public void onFinish() {
227 //Do nothing
228 }
229 }.start();
230 }
231 }
232
233 /**
234 * Creates dialog that enables users to exit Emergency Callback Mode
235 */
236 @Override
237 protected Dialog onCreateDialog(int id) {
238 switch (id) {
239 case EXIT_ECM_BLOCK_OTHERS:
240 case EXIT_ECM_DIALOG:
241 CharSequence text = getDialogText(mEcmTimeout);
Brad Ebinger1a3fa362018-04-19 13:43:31 -0700242 mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
243 android.R.style.Theme_DeviceDefault_Dialog_Alert)
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700244 .setIcon(R.drawable.ic_emergency_callback_mode)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700245 .setTitle(R.string.phone_in_ecm_notification_title)
246 .setMessage(text)
247 .setPositiveButton(R.string.alert_dialog_yes,
248 new DialogInterface.OnClickListener() {
Daniel Bantae91feb42022-12-23 06:37:53 +0000249 public void onClick(DialogInterface dialog,
250 int whichButton) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700251 // User clicked Yes. Exit Emergency Callback Mode.
Daniel Bantae91feb42022-12-23 06:37:53 +0000252 if (DomainSelectionResolver.getInstance()
253 .isDomainSelectionSupported()) {
254 EmergencyStateTracker.getInstance()
yongnamcha2980ac02024-10-11 05:13:52 +0000255 .exitEmergencyCallbackMode(
256 TelephonyManager.STOP_REASON_USER_ACTION);
Daniel Bantae91feb42022-12-23 06:37:53 +0000257 } else {
258 mPhone.exitEmergencyCallbackMode();
259 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700260
261 // Show progress dialog
262 showDialog(EXIT_ECM_PROGRESS_DIALOG);
263 mTimer.cancel();
264 }
265 })
266 .setNegativeButton(R.string.alert_dialog_no,
267 new DialogInterface.OnClickListener() {
268 public void onClick(DialogInterface dialog, int whichButton) {
269 // User clicked No
Pengquan Meng252acb32018-10-11 17:16:25 -0700270 setResult(RESULT_CANCELED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700271 finish();
272 }
273 }).create();
Li Wei65667ea2017-08-02 16:06:59 +0800274 mAlertDialog.setOnCancelListener(this);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700275 return mAlertDialog;
276
277 case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG:
Brad Ebinger1a3fa362018-04-19 13:43:31 -0700278 mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
279 android.R.style.Theme_DeviceDefault_Dialog_Alert)
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700280 .setIcon(R.drawable.ic_emergency_callback_mode)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700281 .setTitle(R.string.phone_in_ecm_notification_title)
282 .setMessage(R.string.alert_dialog_in_ecm_call)
283 .setNeutralButton(R.string.alert_dialog_dismiss,
284 new DialogInterface.OnClickListener() {
285 public void onClick(DialogInterface dialog, int whichButton) {
286 // User clicked Dismiss
Pengquan Meng252acb32018-10-11 17:16:25 -0700287 setResult(RESULT_CANCELED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700288 finish();
289 }
290 }).create();
Li Wei65667ea2017-08-02 16:06:59 +0800291 mAlertDialog.setOnCancelListener(this);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700292 return mAlertDialog;
293
294 case EXIT_ECM_PROGRESS_DIALOG:
295 mProgressDialog = new ProgressDialog(EmergencyCallbackModeExitDialog.this);
296 mProgressDialog.setMessage(getText(R.string.progress_dialog_exiting_ecm));
297 mProgressDialog.setIndeterminate(true);
298 mProgressDialog.setCancelable(false);
299 return mProgressDialog;
300
301 default:
302 return null;
303 }
304 }
305
306 /**
307 * Returns dialog box text with updated timeout value
308 */
309 private CharSequence getDialogText(long millisUntilFinished) {
310 // Format time
311 int minutes = (int)(millisUntilFinished / 60000);
312 String time = String.format("%d:%02d", minutes,
313 (millisUntilFinished % 60000) / 1000);
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +0000314 Map<String, Object> msgArgs = new HashMap<>();
315 msgArgs.put("count", minutes);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700316
317 switch (mDialogType) {
318 case EXIT_ECM_BLOCK_OTHERS:
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +0000319 return MessageFormat.format(getResources().getString(
320 R.string.alert_dialog_not_avaialble_in_ecm, time), msgArgs);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700321 case EXIT_ECM_DIALOG:
Wei Huang821f3d92019-04-11 11:19:13 +0900322 boolean shouldRestrictData = mPhone.getImsPhone() != null
323 && mPhone.getImsPhone().isInImsEcm();
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +0000324 return MessageFormat.format(getResources().getString(
Wei Huang821f3d92019-04-11 11:19:13 +0900325 // During IMS ECM, data restriction hint should be removed.
326 shouldRestrictData
Aishwarya Mallampati6ad54362022-01-22 01:08:21 +0000327 ? R.string.alert_dialog_exit_ecm_without_data_restriction_hint
328 : R.string.alert_dialog_exit_ecm,
329 time), msgArgs);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700330 }
331 return null;
332 }
333
334 /**
Li Wei65667ea2017-08-02 16:06:59 +0800335 * Closes activity when dialog is canceled
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700336 */
Yorke Lee34a72cb2014-10-12 13:17:04 -0700337 @Override
Li Wei65667ea2017-08-02 16:06:59 +0800338 public void onCancel(DialogInterface dialog) {
Pengquan Meng252acb32018-10-11 17:16:25 -0700339 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700340 finish();
341 }
342
343 /**
344 * Listens for Emergency Callback Mode state change intents
345 */
346 private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() {
347 @Override
348 public void onReceive(Context context, Intent intent) {
349 // Received exit Emergency Callback Mode notification close all dialogs
350 if (intent.getAction().equals(
351 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
Brad Ebinger3b3c30a2020-03-18 13:59:44 -0700352 // Cancel if the sticky broadcast extra for whether or not we are in ECM is false.
353 if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700354 if (mAlertDialog != null)
355 mAlertDialog.dismiss();
356 if (mProgressDialog != null)
357 mProgressDialog.dismiss();
Pengquan Meng252acb32018-10-11 17:16:25 -0700358 EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700359 finish();
360 }
361 }
362 }
363 };
364
365 /**
366 * Class for interacting with the interface of the service
367 */
368 private ServiceConnection mConnection = new ServiceConnection() {
369 public void onServiceConnected(ComponentName className, IBinder service) {
370 mService = ((EmergencyCallbackModeService.LocalBinder)service).getService();
371 // Notify thread that connection is ready
372 synchronized (EmergencyCallbackModeExitDialog.this) {
373 EmergencyCallbackModeExitDialog.this.notify();
374 }
375 }
376
377 public void onServiceDisconnected(ComponentName className) {
378 mService = null;
379 }
380 };
381
382 /**
383 * Class for receiving framework timer reset notifications
384 */
385 private Handler mTimerResetHandler = new Handler () {
386 public void handleMessage(Message msg) {
387 switch (msg.what) {
388 case ECM_TIMER_RESET:
389 if(!((Boolean)((AsyncResult) msg.obj).result).booleanValue()) {
Pengquan Meng252acb32018-10-11 17:16:25 -0700390 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700391 finish();
392 }
393 break;
394 }
395 }
396 };
397}