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.os.AsyncResult;
34 import android.os.Bundle;
35 import android.os.CountDownTimer;
36 import android.os.Handler;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.telephony.TelephonyManager;
41 import android.util.Log;
42 
43 import com.android.internal.telephony.Phone;
44 import com.android.internal.telephony.TelephonyIntents;
45 
46 /**
47  * Displays dialog that enables users to exit Emergency Callback Mode
48  *
49  * @see EmergencyCallbackModeService
50  */
51 public class EmergencyCallbackModeExitDialog extends Activity implements OnCancelListener {
52 
53     private static final String TAG = "EmergencyCallbackMode";
54 
55     /** Intent to trigger the Emergency Callback Mode exit dialog */
56     static final String ACTION_SHOW_ECM_EXIT_DIALOG =
57             "com.android.phone.action.ACTION_SHOW_ECM_EXIT_DIALOG";
58 
59     public static final int EXIT_ECM_BLOCK_OTHERS = 1;
60     public static final int EXIT_ECM_DIALOG = 2;
61     public static final int EXIT_ECM_PROGRESS_DIALOG = 3;
62     public static final int EXIT_ECM_IN_EMERGENCY_CALL_DIALOG = 4;
63 
64     AlertDialog mAlertDialog = null;
65     ProgressDialog mProgressDialog = null;
66     CountDownTimer mTimer = null;
67     EmergencyCallbackModeService mService = null;
68     Handler mHandler = null;
69     int mDialogType = 0;
70     long mEcmTimeout = 0;
71     private boolean mInEmergencyCall = false;
72     private static final int ECM_TIMER_RESET = 1;
73     private Phone mPhone = null;
74     private boolean mIsResumed = false;
75 
76     @Override
onCreate(Bundle savedInstanceState)77     public void onCreate(Bundle savedInstanceState) {
78         super.onCreate(savedInstanceState);
79         getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
80         mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
81         // Check if phone is in Emergency Callback Mode. If not, exit.
82         if (mPhone == null || !mPhone.isInEcm()) {
83             Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
84             finish();
85             return;
86         }
87         Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone);
88 
89         mHandler = new Handler();
90 
91         // Start thread that will wait for the connection completion so that it can get
92         // timeout value from the service
93         Thread waitForConnectionCompleteThread = new Thread(null, mTask,
94                 "EcmExitDialogWaitThread");
95         waitForConnectionCompleteThread.start();
96 
97         // Register ECM timer reset notfication
98         mPhone.registerForEcmTimerReset(mTimerResetHandler, ECM_TIMER_RESET, null);
99 
100         // Register receiver for intent closing the dialog
101         IntentFilter filter = new IntentFilter();
102         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
103         registerReceiver(mEcmExitReceiver, filter);
104     }
105 
106     @Override
onResume()107     public void onResume() {
108         super.onResume();
109         mIsResumed = true;
110     }
111 
112     @Override
onPause()113     public void onPause() {
114         super.onPause();
115         mIsResumed = false;
116     }
117 
118     @Override
onDestroy()119     public void onDestroy() {
120         super.onDestroy();
121         try {
122             unregisterReceiver(mEcmExitReceiver);
123         } catch (IllegalArgumentException e) {
124             // Receiver was never registered - silently ignore.
125         }
126         // Unregister ECM timer reset notification
127         if (mPhone != null) {
128             mPhone.unregisterForEcmTimerReset(mHandler);
129         }
130     }
131 
132     @Override
onRestoreInstanceState(Bundle savedInstanceState)133     protected void onRestoreInstanceState(Bundle savedInstanceState) {
134         super.onRestoreInstanceState(savedInstanceState);
135         mDialogType = savedInstanceState.getInt("DIALOG_TYPE");
136     }
137 
138     @Override
onSaveInstanceState(Bundle outState)139     protected void onSaveInstanceState(Bundle outState) {
140         super.onSaveInstanceState(outState);
141         outState.putInt("DIALOG_TYPE", mDialogType);
142     }
143 
144     /**
145      * Waits until bind to the service completes
146      */
147     private Runnable mTask = new Runnable() {
148         public void run() {
149             Looper.prepare();
150 
151             // Bind to the remote service
152             bindService(new Intent(EmergencyCallbackModeExitDialog.this,
153                     EmergencyCallbackModeService.class), mConnection, Context.BIND_AUTO_CREATE);
154 
155             // Wait for bind to finish
156             synchronized (EmergencyCallbackModeExitDialog.this) {
157                 try {
158                     if (mService == null) {
159                         EmergencyCallbackModeExitDialog.this.wait();
160                     }
161                 } catch (InterruptedException e) {
162                     Log.d("ECM", "EmergencyCallbackModeExitDialog InterruptedException: "
163                             + e.getMessage());
164                     e.printStackTrace();
165                 }
166             }
167 
168             // Get timeout value and call state from the service
169             if (mService != null) {
170                 mEcmTimeout = mService.getEmergencyCallbackModeTimeout();
171                 mInEmergencyCall = mService.getEmergencyCallbackModeCallState();
172                 try {
173                     // Unbind from remote service
174                     unbindService(mConnection);
175                 } catch (IllegalArgumentException e) {
176                     // Failed to unbind from service. Don't crash as this brings down the entire
177                     // radio.
178                     Log.w(TAG, "Failed to unbind from EmergencyCallbackModeService");
179                 }
180             }
181 
182             // Show dialog
183             mHandler.post(new Runnable() {
184                 public void run() {
185                     showEmergencyCallbackModeExitDialog();
186                 }
187             });
188         }
189     };
190 
191     /**
192      * Shows Emergency Callback Mode dialog and starts countdown timer
193      */
showEmergencyCallbackModeExitDialog()194     private void showEmergencyCallbackModeExitDialog() {
195         if (isDestroyed()) {
196             Log.w(TAG, "Tried to show dialog, but activity was already finished");
197             return;
198         }
199         if(mInEmergencyCall) {
200             mDialogType = EXIT_ECM_IN_EMERGENCY_CALL_DIALOG;
201             showDialog(EXIT_ECM_IN_EMERGENCY_CALL_DIALOG);
202         } else {
203             if (getIntent().getAction().equals(
204                     TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) {
205                 mDialogType = EXIT_ECM_BLOCK_OTHERS;
206                 showDialog(EXIT_ECM_BLOCK_OTHERS);
207             } else if (getIntent().getAction().equals(ACTION_SHOW_ECM_EXIT_DIALOG)) {
208                 mDialogType = EXIT_ECM_DIALOG;
209                 showDialog(EXIT_ECM_DIALOG);
210             }
211 
212             mTimer = new CountDownTimer(mEcmTimeout, 1000) {
213                 @Override
214                 public void onTick(long millisUntilFinished) {
215                     CharSequence text = getDialogText(millisUntilFinished);
216                     mAlertDialog.setMessage(text);
217                 }
218 
219                 @Override
220                 public void onFinish() {
221                     //Do nothing
222                 }
223             }.start();
224         }
225     }
226 
227     /**
228      * Creates dialog that enables users to exit Emergency Callback Mode
229      */
230     @Override
onCreateDialog(int id)231     protected Dialog onCreateDialog(int id) {
232         switch (id) {
233         case EXIT_ECM_BLOCK_OTHERS:
234         case EXIT_ECM_DIALOG:
235             CharSequence text = getDialogText(mEcmTimeout);
236             mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
237                     android.R.style.Theme_DeviceDefault_Dialog_Alert)
238                     .setIcon(R.drawable.ic_emergency_callback_mode)
239                     .setTitle(R.string.phone_in_ecm_notification_title)
240                     .setMessage(text)
241                     .setPositiveButton(R.string.alert_dialog_yes,
242                             new DialogInterface.OnClickListener() {
243                                 public void onClick(DialogInterface dialog,int whichButton) {
244                                     // User clicked Yes. Exit Emergency Callback Mode.
245                                     mPhone.exitEmergencyCallbackMode();
246 
247                                     // Show progress dialog
248                                     showDialog(EXIT_ECM_PROGRESS_DIALOG);
249                                     mTimer.cancel();
250                                 }
251                             })
252                     .setNegativeButton(R.string.alert_dialog_no,
253                             new DialogInterface.OnClickListener() {
254                                 public void onClick(DialogInterface dialog, int whichButton) {
255                                     // User clicked No
256                                     setResult(RESULT_CANCELED);
257                                     finish();
258                                 }
259                             }).create();
260             mAlertDialog.setOnCancelListener(this);
261             return mAlertDialog;
262 
263         case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG:
264             mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
265                     android.R.style.Theme_DeviceDefault_Dialog_Alert)
266                     .setIcon(R.drawable.ic_emergency_callback_mode)
267                     .setTitle(R.string.phone_in_ecm_notification_title)
268                     .setMessage(R.string.alert_dialog_in_ecm_call)
269                     .setNeutralButton(R.string.alert_dialog_dismiss,
270                             new DialogInterface.OnClickListener() {
271                                 public void onClick(DialogInterface dialog, int whichButton) {
272                                     // User clicked Dismiss
273                                     setResult(RESULT_CANCELED);
274                                     finish();
275                                 }
276                             }).create();
277             mAlertDialog.setOnCancelListener(this);
278             return mAlertDialog;
279 
280         case EXIT_ECM_PROGRESS_DIALOG:
281             mProgressDialog = new ProgressDialog(EmergencyCallbackModeExitDialog.this);
282             mProgressDialog.setMessage(getText(R.string.progress_dialog_exiting_ecm));
283             mProgressDialog.setIndeterminate(true);
284             mProgressDialog.setCancelable(false);
285             return mProgressDialog;
286 
287         default:
288             return null;
289         }
290     }
291 
292     /**
293      * Returns dialog box text with updated timeout value
294      */
getDialogText(long millisUntilFinished)295     private CharSequence getDialogText(long millisUntilFinished) {
296         // Format time
297         int minutes = (int)(millisUntilFinished / 60000);
298         String time = String.format("%d:%02d", minutes,
299                 (millisUntilFinished % 60000) / 1000);
300 
301         switch (mDialogType) {
302         case EXIT_ECM_BLOCK_OTHERS:
303             return String.format(getResources().getQuantityText(
304                     R.plurals.alert_dialog_not_avaialble_in_ecm, minutes).toString(), time);
305         case EXIT_ECM_DIALOG:
306                 boolean shouldRestrictData = mPhone.getImsPhone() != null
307                         && mPhone.getImsPhone().isInImsEcm();
308                 return String.format(getResources().getQuantityText(
309                         // During IMS ECM, data restriction hint should be removed.
310                         shouldRestrictData
311                         ? R.plurals.alert_dialog_exit_ecm_without_data_restriction_hint
312                         : R.plurals.alert_dialog_exit_ecm,
313                         minutes).toString(), time);
314         }
315         return null;
316     }
317 
318     /**
319      * Closes activity when dialog is canceled
320      */
321     @Override
onCancel(DialogInterface dialog)322     public void onCancel(DialogInterface dialog) {
323         EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
324         finish();
325     }
326 
327     /**
328      * Listens for Emergency Callback Mode state change intents
329      */
330     private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() {
331         @Override
332         public void onReceive(Context context, Intent intent) {
333             // Received exit Emergency Callback Mode notification close all dialogs
334             if (intent.getAction().equals(
335                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
336                 // Cancel if the sticky broadcast extra for whether or not we are in ECM is false.
337                 if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
338                     if (mAlertDialog != null)
339                         mAlertDialog.dismiss();
340                     if (mProgressDialog != null)
341                         mProgressDialog.dismiss();
342                     EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK);
343                     finish();
344                 }
345             }
346         }
347     };
348 
349     /**
350      * Class for interacting with the interface of the service
351      */
352     private ServiceConnection mConnection = new ServiceConnection() {
353         public void onServiceConnected(ComponentName className, IBinder service) {
354             mService = ((EmergencyCallbackModeService.LocalBinder)service).getService();
355             // Notify thread that connection is ready
356             synchronized (EmergencyCallbackModeExitDialog.this) {
357                 EmergencyCallbackModeExitDialog.this.notify();
358             }
359         }
360 
361         public void onServiceDisconnected(ComponentName className) {
362             mService = null;
363         }
364     };
365 
366     /**
367      * Class for receiving framework timer reset notifications
368      */
369     private Handler mTimerResetHandler = new Handler () {
370         public void handleMessage(Message msg) {
371             switch (msg.what) {
372                 case ECM_TIMER_RESET:
373                     if(!((Boolean)((AsyncResult) msg.obj).result).booleanValue()) {
374                         EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
375                         finish();
376                     }
377                     break;
378             }
379         }
380     };
381 }
382