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