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 } else { 257 mPhone.exitEmergencyCallbackMode(); 258 } 259 260 // Show progress dialog 261 showDialog(EXIT_ECM_PROGRESS_DIALOG); 262 mTimer.cancel(); 263 } 264 }) 265 .setNegativeButton(R.string.alert_dialog_no, 266 new DialogInterface.OnClickListener() { 267 public void onClick(DialogInterface dialog, int whichButton) { 268 // User clicked No 269 setResult(RESULT_CANCELED); 270 finish(); 271 } 272 }).create(); 273 mAlertDialog.setOnCancelListener(this); 274 return mAlertDialog; 275 276 case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG: 277 mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this, 278 android.R.style.Theme_DeviceDefault_Dialog_Alert) 279 .setIcon(R.drawable.ic_emergency_callback_mode) 280 .setTitle(R.string.phone_in_ecm_notification_title) 281 .setMessage(R.string.alert_dialog_in_ecm_call) 282 .setNeutralButton(R.string.alert_dialog_dismiss, 283 new DialogInterface.OnClickListener() { 284 public void onClick(DialogInterface dialog, int whichButton) { 285 // User clicked Dismiss 286 setResult(RESULT_CANCELED); 287 finish(); 288 } 289 }).create(); 290 mAlertDialog.setOnCancelListener(this); 291 return mAlertDialog; 292 293 case EXIT_ECM_PROGRESS_DIALOG: 294 mProgressDialog = new ProgressDialog(EmergencyCallbackModeExitDialog.this); 295 mProgressDialog.setMessage(getText(R.string.progress_dialog_exiting_ecm)); 296 mProgressDialog.setIndeterminate(true); 297 mProgressDialog.setCancelable(false); 298 return mProgressDialog; 299 300 default: 301 return null; 302 } 303 } 304 305 /** 306 * Returns dialog box text with updated timeout value 307 */ getDialogText(long millisUntilFinished)308 private CharSequence getDialogText(long millisUntilFinished) { 309 // Format time 310 int minutes = (int)(millisUntilFinished / 60000); 311 String time = String.format("%d:%02d", minutes, 312 (millisUntilFinished % 60000) / 1000); 313 Map<String, Object> msgArgs = new HashMap<>(); 314 msgArgs.put("count", minutes); 315 316 switch (mDialogType) { 317 case EXIT_ECM_BLOCK_OTHERS: 318 return MessageFormat.format(getResources().getString( 319 R.string.alert_dialog_not_avaialble_in_ecm, time), msgArgs); 320 case EXIT_ECM_DIALOG: 321 boolean shouldRestrictData = mPhone.getImsPhone() != null 322 && mPhone.getImsPhone().isInImsEcm(); 323 return MessageFormat.format(getResources().getString( 324 // During IMS ECM, data restriction hint should be removed. 325 shouldRestrictData 326 ? R.string.alert_dialog_exit_ecm_without_data_restriction_hint 327 : R.string.alert_dialog_exit_ecm, 328 time), msgArgs); 329 } 330 return null; 331 } 332 333 /** 334 * Closes activity when dialog is canceled 335 */ 336 @Override onCancel(DialogInterface dialog)337 public void onCancel(DialogInterface dialog) { 338 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED); 339 finish(); 340 } 341 342 /** 343 * Listens for Emergency Callback Mode state change intents 344 */ 345 private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() { 346 @Override 347 public void onReceive(Context context, Intent intent) { 348 // Received exit Emergency Callback Mode notification close all dialogs 349 if (intent.getAction().equals( 350 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 351 // Cancel if the sticky broadcast extra for whether or not we are in ECM is false. 352 if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) { 353 if (mAlertDialog != null) 354 mAlertDialog.dismiss(); 355 if (mProgressDialog != null) 356 mProgressDialog.dismiss(); 357 EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK); 358 finish(); 359 } 360 } 361 } 362 }; 363 364 /** 365 * Class for interacting with the interface of the service 366 */ 367 private ServiceConnection mConnection = new ServiceConnection() { 368 public void onServiceConnected(ComponentName className, IBinder service) { 369 mService = ((EmergencyCallbackModeService.LocalBinder)service).getService(); 370 // Notify thread that connection is ready 371 synchronized (EmergencyCallbackModeExitDialog.this) { 372 EmergencyCallbackModeExitDialog.this.notify(); 373 } 374 } 375 376 public void onServiceDisconnected(ComponentName className) { 377 mService = null; 378 } 379 }; 380 381 /** 382 * Class for receiving framework timer reset notifications 383 */ 384 private Handler mTimerResetHandler = new Handler () { 385 public void handleMessage(Message msg) { 386 switch (msg.what) { 387 case ECM_TIMER_RESET: 388 if(!((Boolean)((AsyncResult) msg.obj).result).booleanValue()) { 389 EmergencyCallbackModeExitDialog.this.setResult(RESULT_CANCELED); 390 finish(); 391 } 392 break; 393 } 394 } 395 }; 396 } 397