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