• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.phone;
18 
19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
20 
21 import android.app.Activity;
22 import android.app.AlertDialog;
23 import android.app.Dialog;
24 import android.app.ProgressDialog;
25 import android.content.BroadcastReceiver;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnCancelListener;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.ServiceConnection;
33 import android.icu.text.MessageFormat;
34 import android.os.AsyncResult;
35 import android.os.Bundle;
36 import android.os.CountDownTimer;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.Looper;
40 import android.os.Message;
41 import android.telephony.TelephonyManager;
42 import android.util.Log;
43 
44 import com.android.internal.telephony.Phone;
45 import com.android.internal.telephony.TelephonyIntents;
46 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
47 import com.android.internal.telephony.emergency.EmergencyStateTracker;
48 
49 import java.util.HashMap;
50 import java.util.Map;
51 
52 /**
53  * Displays dialog that enables users to exit Emergency Callback Mode
54  *
55  * @see EmergencyCallbackModeService
56  */
57 public class EmergencyCallbackModeExitDialog extends Activity implements OnCancelListener {
58 
59     private static final String TAG = "EmergencyCallbackMode";
60 
61     /** 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";
64 
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;
80     private boolean mIsResumed = false;
81 
82     @Override
onCreate(Bundle savedInstanceState)83     public void onCreate(Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85         getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
86         mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
87         // Check if phone is in Emergency Callback Mode. If not, exit.
88         if (mPhone == null || !mPhone.isInEcm()) {
89             Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
90             finish();
91             return;
92         }
93         Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone);
94 
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
104         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
onResume()113     public void onResume() {
114         super.onResume();
115         mIsResumed = true;
116     }
117 
118     @Override
onPause()119     public void onPause() {
120         super.onPause();
121         mIsResumed = false;
122     }
123 
124     @Override
onDestroy()125     public void onDestroy() {
126         super.onDestroy();
127         try {
128             unregisterReceiver(mEcmExitReceiver);
129         } catch (IllegalArgumentException e) {
130             // Receiver was never registered - silently ignore.
131         }
132         // Unregister ECM timer reset notification
133         if (mPhone != null) {
134             mPhone.unregisterForEcmTimerReset(mHandler);
135         }
136     }
137 
138     @Override
onRestoreInstanceState(Bundle savedInstanceState)139     protected void onRestoreInstanceState(Bundle savedInstanceState) {
140         super.onRestoreInstanceState(savedInstanceState);
141         mDialogType = savedInstanceState.getInt("DIALOG_TYPE");
142     }
143 
144     @Override
onSaveInstanceState(Bundle outState)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();
178                 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                 }
186             }
187 
188             // 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      */
showEmergencyCallbackModeExitDialog()200     private void showEmergencyCallbackModeExitDialog() {
201         if (isDestroyed()) {
202             Log.w(TAG, "Tried to show dialog, but activity was already finished");
203             return;
204         }
205         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
onCreateDialog(int id)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);
242             mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
243                     android.R.style.Theme_DeviceDefault_Dialog_Alert)
244                     .setIcon(R.drawable.ic_emergency_callback_mode)
245                     .setTitle(R.string.phone_in_ecm_notification_title)
246                     .setMessage(text)
247                     .setPositiveButton(R.string.alert_dialog_yes,
248                             new DialogInterface.OnClickListener() {
249                                 public void onClick(DialogInterface dialog,
250                                         int whichButton) {
251                                     // User clicked Yes. Exit Emergency Callback Mode.
252                                     if (DomainSelectionResolver.getInstance()
253                                             .isDomainSelectionSupported()) {
254                                         EmergencyStateTracker.getInstance()
255                                                 .exitEmergencyCallbackMode(
256                                                         TelephonyManager.STOP_REASON_USER_ACTION);
257                                     } else {
258                                         mPhone.exitEmergencyCallbackMode();
259                                     }
260 
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
270                                     setResult(RESULT_CANCELED);
271                                     finish();
272                                 }
273                             }).create();
274             mAlertDialog.setOnCancelListener(this);
275             return mAlertDialog;
276 
277         case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG:
278             mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
279                     android.R.style.Theme_DeviceDefault_Dialog_Alert)
280                     .setIcon(R.drawable.ic_emergency_callback_mode)
281                     .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
287                                     setResult(RESULT_CANCELED);
288                                     finish();
289                                 }
290                             }).create();
291             mAlertDialog.setOnCancelListener(this);
292             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      */
getDialogText(long millisUntilFinished)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);
314         Map<String, Object> msgArgs = new HashMap<>();
315         msgArgs.put("count", minutes);
316 
317         switch (mDialogType) {
318         case EXIT_ECM_BLOCK_OTHERS:
319             return MessageFormat.format(getResources().getString(
320                     R.string.alert_dialog_not_avaialble_in_ecm, time), msgArgs);
321         case EXIT_ECM_DIALOG:
322                 boolean shouldRestrictData = mPhone.getImsPhone() != null
323                         && mPhone.getImsPhone().isInImsEcm();
324                 return MessageFormat.format(getResources().getString(
325                         // During IMS ECM, data restriction hint should be removed.
326                         shouldRestrictData
327                         ? R.string.alert_dialog_exit_ecm_without_data_restriction_hint
328                         : R.string.alert_dialog_exit_ecm,
329                         time), msgArgs);
330         }
331         return null;
332     }
333 
334     /**
335      * Closes activity when dialog is canceled
336      */
337     @Override
onCancel(DialogInterface dialog)338     public void onCancel(DialogInterface dialog) {
339         EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
340         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)) {
352                 // 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)) {
354                     if (mAlertDialog != null)
355                         mAlertDialog.dismiss();
356                     if (mProgressDialog != null)
357                         mProgressDialog.dismiss();
358                     EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK);
359                     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()) {
390                         EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
391                         finish();
392                     }
393                     break;
394             }
395         }
396     };
397 }
398