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