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