blob: 2dd4a710565de6c699af67c4c989c5ed15cf6ce4 [file] [log] [blame]
Jason parks8fd5bc92011-01-12 16:03:31 -06001/*
2 * Copyright (C) 2011 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
17package com.android.settings;
18
Jason parks8fd5bc92011-01-12 16:03:31 -060019import android.app.Activity;
20import android.app.StatusBarManager;
21import android.content.ComponentName;
22import android.content.Context;
Jason parksec5a45e2011-01-18 15:28:36 -060023import android.content.Intent;
Jason parks8fd5bc92011-01-12 16:03:31 -060024import android.content.pm.PackageManager;
Jason parks75c085e2011-02-10 14:03:47 -060025import android.graphics.Rect;
Jason parks8fd5bc92011-01-12 16:03:31 -060026import android.inputmethodservice.KeyboardView;
Jason parks06c5ff42011-03-01 10:17:40 -060027import android.os.AsyncTask;
Jason parks8fd5bc92011-01-12 16:03:31 -060028import android.os.Bundle;
Jason parksec5a45e2011-01-18 15:28:36 -060029import android.os.Handler;
Jason parks8fd5bc92011-01-12 16:03:31 -060030import android.os.IBinder;
Jason parksec5a45e2011-01-18 15:28:36 -060031import android.os.Message;
Jason parks35933812011-01-21 15:48:20 -060032import android.os.PowerManager;
David Brown8373b452011-06-20 12:38:45 -070033import android.os.RemoteException;
Jason parks8fd5bc92011-01-12 16:03:31 -060034import android.os.ServiceManager;
35import android.os.SystemProperties;
36import android.os.storage.IMountService;
David Brown8373b452011-06-20 12:38:45 -070037import android.telephony.TelephonyManager;
Jason parksec5a45e2011-01-18 15:28:36 -060038import android.text.TextUtils;
Jason parks75c085e2011-02-10 14:03:47 -060039import android.util.AttributeSet;
Jason parks8fd5bc92011-01-12 16:03:31 -060040import android.util.Log;
41import android.view.KeyEvent;
Jason parks75c085e2011-02-10 14:03:47 -060042import android.view.MotionEvent;
Andy Stadler13d62042011-01-31 19:21:37 -080043import android.view.View;
44import android.view.View.OnClickListener;
Jason parks8fd5bc92011-01-12 16:03:31 -060045import android.view.inputmethod.EditorInfo;
Jason parks75c085e2011-02-10 14:03:47 -060046import android.view.inputmethod.InputMethodManager;
Andy Stadler13d62042011-01-31 19:21:37 -080047import android.widget.Button;
Jason parksec5a45e2011-01-18 15:28:36 -060048import android.widget.EditText;
49import android.widget.ProgressBar;
Jason parks8fd5bc92011-01-12 16:03:31 -060050import android.widget.TextView;
51
Ben Komalo91a2f052011-08-16 17:48:25 -070052import com.android.internal.telephony.ITelephony;
53import com.android.internal.widget.PasswordEntryKeyboardHelper;
54import com.android.internal.widget.PasswordEntryKeyboardView;
55
56/**
57 * Settings screens to show the UI flows for encrypting/decrypting the device.
58 *
59 * This may be started via adb for debugging the UI layout, without having to go through
60 * encryption flows everytime. It should be noted that starting the activity in this manner
61 * is only useful for verifying UI-correctness - the behavior will not be identical.
62 * <pre>
63 * $ adb shell pm enable com.android.settings/.CryptKeeper
64 * $ adb shell am start \
65 * -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "progress" \
66 * -n com.android.settings/.CryptKeeper
67 * </pre>
68 */
Jason parks8fd5bc92011-01-12 16:03:31 -060069public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
Jason parksec5a45e2011-01-18 15:28:36 -060070 private static final String TAG = "CryptKeeper";
Jason parks35933812011-01-21 15:48:20 -060071
Jason parks8fd5bc92011-01-12 16:03:31 -060072 private static final String DECRYPT_STATE = "trigger_restart_framework";
Jason parksec5a45e2011-01-18 15:28:36 -060073
74 private static final int UPDATE_PROGRESS = 1;
75 private static final int COOLDOWN = 2;
76
77 private static final int MAX_FAILED_ATTEMPTS = 30;
78 private static final int COOL_DOWN_ATTEMPTS = 10;
79 private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
80
David Brown8373b452011-06-20 12:38:45 -070081 // Intent action for launching the Emergency Dialer activity.
82 static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
83
Ben Komalo91a2f052011-08-16 17:48:25 -070084 // Debug Intent extras so that this Activity may be started via adb for debugging UI layouts
85 private static final String EXTRA_FORCE_VIEW =
86 "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW";
87 private static final String FORCE_VIEW_PROGRESS = "progress";
88 private static final String FORCE_VIEW_ENTRY = "entry";
89 private static final String FORCE_VIEW_ERROR = "error";
90
Andy Stadler14997402011-02-01 15:34:59 -080091 private int mCooldown;
92 PowerManager.WakeLock mWakeLock;
Jason parks06c5ff42011-03-01 10:17:40 -060093 private EditText mPasswordEntry;
Andy Stadler14997402011-02-01 15:34:59 -080094
95 /**
96 * Used to propagate state through configuration changes (e.g. screen rotation)
97 */
98 private static class NonConfigurationInstanceState {
99 final PowerManager.WakeLock wakelock;
100
101 NonConfigurationInstanceState(PowerManager.WakeLock _wakelock) {
102 wakelock = _wakelock;
103 }
104 }
105
Jason parksf1dbf552011-01-24 16:19:28 -0600106 // This activity is used to fade the screen to black after the password is entered.
107 public static class Blank extends Activity {
Andy Stadler13d62042011-01-31 19:21:37 -0800108 @Override
109 public void onCreate(Bundle savedInstanceState) {
110 super.onCreate(savedInstanceState);
111 setContentView(R.layout.crypt_keeper_blank);
112 }
Jason parksf1dbf552011-01-24 16:19:28 -0600113 }
Jason parksec5a45e2011-01-18 15:28:36 -0600114
Jason parks75c085e2011-02-10 14:03:47 -0600115 // Use a custom EditText to prevent the input method from showing.
116 public static class CryptEditText extends EditText {
117 InputMethodManager imm;
118
119 public CryptEditText(Context context, AttributeSet attrs) {
120 super(context, attrs);
121 imm = ((InputMethodManager) getContext().
122 getSystemService(Context.INPUT_METHOD_SERVICE));
123 }
124
125 @Override
126 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
127 super.onFocusChanged(focused, direction, previouslyFocusedRect);
128
129 if (focused && imm != null && imm.isActive(this)) {
130 imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
131 }
132 }
133
134 @Override
135 public boolean onTouchEvent(MotionEvent event) {
136 boolean handled = super.onTouchEvent(event);
137
138 if (imm != null && imm.isActive(this)) {
139 imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
140 }
141
142 return handled;
143 }
144 }
145
Jason parks06c5ff42011-03-01 10:17:40 -0600146 private class DecryptTask extends AsyncTask<String, Void, Integer> {
147 @Override
148 protected Integer doInBackground(String... params) {
149 IMountService service = getMountService();
150 try {
151 return service.decryptStorage(params[0]);
152 } catch (Exception e) {
153 Log.e(TAG, "Error while decrypting...", e);
154 return -1;
155 }
156 }
157
158 @Override
159 protected void onPostExecute(Integer failedAttempts) {
160 if (failedAttempts == 0) {
161 // The password was entered successfully. Start the Blank activity
162 // so this activity animates to black before the devices starts. Note
163 // It has 1 second to complete the animation or it will be frozen
164 // until the boot animation comes back up.
165 Intent intent = new Intent(CryptKeeper.this, Blank.class);
166 finish();
167 startActivity(intent);
168 } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
169 // Factory reset the device.
170 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
171 } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
172 mCooldown = COOL_DOWN_INTERVAL;
173 cooldown();
174 } else {
175 TextView tv = (TextView) findViewById(R.id.status);
176 tv.setText(R.string.try_again);
177 tv.setVisibility(View.VISIBLE);
178
179 // Reenable the password entry
180 mPasswordEntry.setEnabled(true);
181 }
182 }
183 }
Jason parks75c085e2011-02-10 14:03:47 -0600184
Ben Komalo91a2f052011-08-16 17:48:25 -0700185 private final Handler mHandler = new Handler() {
Jason parksec5a45e2011-01-18 15:28:36 -0600186 @Override
187 public void handleMessage(Message msg) {
Jason parksec5a45e2011-01-18 15:28:36 -0600188 switch (msg.what) {
Jason parksec5a45e2011-01-18 15:28:36 -0600189 case UPDATE_PROGRESS:
Jason parksf8217302011-01-26 13:11:42 -0600190 updateProgress();
Jason parksec5a45e2011-01-18 15:28:36 -0600191 break;
Jason parks35933812011-01-21 15:48:20 -0600192
Jason parksec5a45e2011-01-18 15:28:36 -0600193 case COOLDOWN:
Jason parksf8217302011-01-26 13:11:42 -0600194 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -0600195 break;
196 }
197 }
198 };
Jason parks35933812011-01-21 15:48:20 -0600199
Ben Komalo91a2f052011-08-16 17:48:25 -0700200 /** @return whether or not this Activity was started for debugging the UI only. */
201 private boolean isDebugView() {
202 return getIntent().hasExtra(EXTRA_FORCE_VIEW);
203 }
204
205 /** @return whether or not this Activity was started for debugging the specific UI view only. */
206 private boolean isDebugView(String viewType /* non-nullable */) {
207 return viewType.equals(getIntent().getStringExtra(EXTRA_FORCE_VIEW));
208 }
209
Jason parks8fd5bc92011-01-12 16:03:31 -0600210 @Override
211 public void onCreate(Bundle savedInstanceState) {
212 super.onCreate(savedInstanceState);
Jason parks35933812011-01-21 15:48:20 -0600213
Andy Stadler95974062011-02-01 17:35:20 -0800214 // If we are not encrypted or encrypting, get out quickly.
Jason parks8fd5bc92011-01-12 16:03:31 -0600215 String state = SystemProperties.get("vold.decrypt");
Ben Komalo91a2f052011-08-16 17:48:25 -0700216 if (!isDebugView() && ("".equals(state) || DECRYPT_STATE.equals(state))) {
Jason parks35933812011-01-21 15:48:20 -0600217 // Disable the crypt keeper.
Jason parks8fd5bc92011-01-12 16:03:31 -0600218 PackageManager pm = getPackageManager();
219 ComponentName name = new ComponentName(this, CryptKeeper.class);
220 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
221 return;
222 }
Jason parks35933812011-01-21 15:48:20 -0600223
Jason parks39f1e042011-01-20 23:29:28 -0600224 // Disable the status bar
225 StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
Andy Stadler13d62042011-01-31 19:21:37 -0800226 sbm.disable(StatusBarManager.DISABLE_EXPAND
227 | StatusBarManager.DISABLE_NOTIFICATION_ICONS
Jason parks39f1e042011-01-20 23:29:28 -0600228 | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
Andy Stadler13d62042011-01-31 19:21:37 -0800229 | StatusBarManager.DISABLE_SYSTEM_INFO
230 | StatusBarManager.DISABLE_NAVIGATION
231 | StatusBarManager.DISABLE_BACK);
Andy Stadler14997402011-02-01 15:34:59 -0800232
233 // Check for (and recover) retained instance data
234 Object lastInstance = getLastNonConfigurationInstance();
235 if (lastInstance instanceof NonConfigurationInstanceState) {
236 NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance;
237 mWakeLock = retained.wakelock;
238 }
Jason parksec5a45e2011-01-18 15:28:36 -0600239 }
Jason parks35933812011-01-21 15:48:20 -0600240
Andy Stadler95974062011-02-01 17:35:20 -0800241 /**
242 * Note, we defer the state check and screen setup to onStart() because this will be
243 * re-run if the user clicks the power button (sleeping/waking the screen), and this is
244 * especially important if we were to lose the wakelock for any reason.
245 */
246 @Override
247 public void onStart() {
248 super.onStart();
249
250 // Check to see why we were started.
251 String progress = SystemProperties.get("vold.encrypt_progress");
Ben Komalo91a2f052011-08-16 17:48:25 -0700252 if (!"".equals(progress)
253 || isDebugView(FORCE_VIEW_PROGRESS)
254 || isDebugView(FORCE_VIEW_ERROR)) {
Andy Stadler95974062011-02-01 17:35:20 -0800255 setContentView(R.layout.crypt_keeper_progress);
256 encryptionProgressInit();
257 } else {
258 setContentView(R.layout.crypt_keeper_password_entry);
259 passwordEntryInit();
260 }
261 }
262
Jason parksf8217302011-01-26 13:11:42 -0600263 @Override
264 public void onStop() {
265 super.onStop();
266
267 mHandler.removeMessages(COOLDOWN);
268 mHandler.removeMessages(UPDATE_PROGRESS);
Andy Stadler14997402011-02-01 15:34:59 -0800269 }
270
271 /**
272 * Reconfiguring, so propagate the wakelock to the next instance. This runs between onStop()
273 * and onDestroy() and only if we are changing configuration (e.g. rotation). Also clears
274 * mWakeLock so the subsequent call to onDestroy does not release it.
275 */
276 @Override
277 public Object onRetainNonConfigurationInstance() {
278 NonConfigurationInstanceState state = new NonConfigurationInstanceState(mWakeLock);
279 mWakeLock = null;
280 return state;
281 }
282
283 @Override
284 public void onDestroy() {
285 super.onDestroy();
Jason parksf8217302011-01-26 13:11:42 -0600286
287 if (mWakeLock != null) {
288 mWakeLock.release();
289 mWakeLock = null;
290 }
291 }
292
Jason parksec5a45e2011-01-18 15:28:36 -0600293 private void encryptionProgressInit() {
Jason parks35933812011-01-21 15:48:20 -0600294 // Accquire a partial wakelock to prevent the device from sleeping. Note
295 // we never release this wakelock as we will be restarted after the device
296 // is encrypted.
297
298 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Jason parksf8217302011-01-26 13:11:42 -0600299 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
Jason parks35933812011-01-21 15:48:20 -0600300
Jason parksf8217302011-01-26 13:11:42 -0600301 mWakeLock.acquire();
Jason parks35933812011-01-21 15:48:20 -0600302
Jason parksf8217302011-01-26 13:11:42 -0600303 ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
304 progressBar.setIndeterminate(true);
305
306 updateProgress();
307 }
308
Andy Stadler13d62042011-01-31 19:21:37 -0800309 private void showFactoryReset() {
310 // Hide the encryption-bot to make room for the "factory reset" button
311 findViewById(R.id.encroid).setVisibility(View.GONE);
312
313 // Show the reset button, failure text, and a divider
314 Button button = (Button) findViewById(R.id.factory_reset);
315 button.setVisibility(View.VISIBLE);
316 button.setOnClickListener(new OnClickListener() {
317 public void onClick(View v) {
318 // Factory reset the device.
319 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
320 }
321 });
322
323 TextView tv = (TextView) findViewById(R.id.title);
324 tv.setText(R.string.crypt_keeper_failed_title);
325
326 tv = (TextView) findViewById(R.id.status);
327 tv.setText(R.string.crypt_keeper_failed_summary);
328
329 View view = findViewById(R.id.bottom_divider);
330 view.setVisibility(View.VISIBLE);
331 }
332
Jason parksf8217302011-01-26 13:11:42 -0600333 private void updateProgress() {
334 String state = SystemProperties.get("vold.encrypt_progress");
335
Ben Komalo91a2f052011-08-16 17:48:25 -0700336 if ("error_partially_encrypted".equals(state) || isDebugView(FORCE_VIEW_ERROR)) {
Andy Stadler13d62042011-01-31 19:21:37 -0800337 showFactoryReset();
338 return;
339 }
340
Jason parksf8217302011-01-26 13:11:42 -0600341 int progress = 0;
342 try {
Ben Komalo91a2f052011-08-16 17:48:25 -0700343 // Force a 50% progress state when debugging the view.
344 progress = isDebugView() ? 50 : Integer.parseInt(state);
Jason parksf8217302011-01-26 13:11:42 -0600345 } catch (Exception e) {
346 Log.w(TAG, "Error parsing progress: " + e.toString());
347 }
348
349 CharSequence status = getText(R.string.crypt_keeper_setup_description);
350 TextView tv = (TextView) findViewById(R.id.status);
351 tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
352
353 // Check the progress every 5 seconds
354 mHandler.removeMessages(UPDATE_PROGRESS);
355 mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
356 }
357
358 private void cooldown() {
359 TextView tv = (TextView) findViewById(R.id.status);
Andy Stadler13d62042011-01-31 19:21:37 -0800360
Jason parksf8217302011-01-26 13:11:42 -0600361 if (mCooldown <= 0) {
362 // Re-enable the password entry
Jason parks06c5ff42011-03-01 10:17:40 -0600363 mPasswordEntry.setEnabled(true);
Jason parksf8217302011-01-26 13:11:42 -0600364
Andy Stadler13d62042011-01-31 19:21:37 -0800365 tv.setVisibility(View.GONE);
Jason parksf8217302011-01-26 13:11:42 -0600366 } else {
Andy Stadler13d62042011-01-31 19:21:37 -0800367 CharSequence template = getText(R.string.crypt_keeper_cooldown);
368 tv.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
369
370 tv.setVisibility(View.VISIBLE);
Jason parksf8217302011-01-26 13:11:42 -0600371
372 mCooldown--;
373 mHandler.removeMessages(COOLDOWN);
374 mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
375 }
Jason parksec5a45e2011-01-18 15:28:36 -0600376 }
Jason parks35933812011-01-21 15:48:20 -0600377
Jason parksec5a45e2011-01-18 15:28:36 -0600378 private void passwordEntryInit() {
Jason parks06c5ff42011-03-01 10:17:40 -0600379 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
380 mPasswordEntry.setOnEditorActionListener(this);
Jason parks35933812011-01-21 15:48:20 -0600381
Jason parks8fd5bc92011-01-12 16:03:31 -0600382 KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
Jason parks35933812011-01-21 15:48:20 -0600383
Jason parks00046d62011-06-13 17:38:45 -0500384 if (keyboardView != null) {
385 PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
386 keyboardView, mPasswordEntry, false);
387 keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
388 }
David Brown8373b452011-06-20 12:38:45 -0700389
390 updateEmergencyCallButtonState();
Jason parks8fd5bc92011-01-12 16:03:31 -0600391 }
392
393 private IMountService getMountService() {
394 IBinder service = ServiceManager.getService("mount");
395 if (service != null) {
396 return IMountService.Stub.asInterface(service);
397 }
398 return null;
399 }
400
401 @Override
402 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Jason parks00046d62011-06-13 17:38:45 -0500403 if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
Jason parks8fd5bc92011-01-12 16:03:31 -0600404 // Get the password
405 String password = v.getText().toString();
406
Jason parksec5a45e2011-01-18 15:28:36 -0600407 if (TextUtils.isEmpty(password)) {
408 return true;
409 }
Jason parks35933812011-01-21 15:48:20 -0600410
Jason parks8fd5bc92011-01-12 16:03:31 -0600411 // Now that we have the password clear the password field.
412 v.setText(null);
413
Jason parks06c5ff42011-03-01 10:17:40 -0600414 // Disable the password entry while checking the password. This
415 // we either be reenabled if the password was wrong or after the
416 // cooldown period.
417 mPasswordEntry.setEnabled(false);
Jason parks8fd5bc92011-01-12 16:03:31 -0600418
Jason parks06c5ff42011-03-01 10:17:40 -0600419 new DecryptTask().execute(password);
Jason parks35933812011-01-21 15:48:20 -0600420
Jason parks8fd5bc92011-01-12 16:03:31 -0600421 return true;
422 }
423 return false;
424 }
David Brown8373b452011-06-20 12:38:45 -0700425
426 //
427 // Code to update the state of, and handle clicks from, the "Emergency call" button.
428 //
429 // This code is mostly duplicated from the corresponding code in
430 // LockPatternUtils and LockPatternKeyguardView under frameworks/base.
431 //
432
433 private void updateEmergencyCallButtonState() {
434 Button button = (Button) findViewById(R.id.emergencyCallButton);
435 // The button isn't present at all in some configurations.
436 if (button == null) return;
437
438 if (isEmergencyCallCapable()) {
439 button.setVisibility(View.VISIBLE);
440 button.setOnClickListener(new View.OnClickListener() {
441 public void onClick(View v) {
442 takeEmergencyCallAction();
443 }
444 });
445 } else {
446 button.setVisibility(View.GONE);
447 return;
448 }
449
450 int newState = TelephonyManager.getDefault().getCallState();
451 int textId;
452 if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
453 // show "return to call" text and show phone icon
454 textId = R.string.cryptkeeper_return_to_call;
455 int phoneCallIcon = R.drawable.stat_sys_phone_call;
456 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
457 } else {
458 textId = R.string.cryptkeeper_emergency_call;
459 int emergencyIcon = R.drawable.ic_emergency;
460 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
461 }
462 button.setText(textId);
463 }
464
465 private boolean isEmergencyCallCapable() {
466 return getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
467 }
468
469 private void takeEmergencyCallAction() {
470 if (TelephonyManager.getDefault().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
471 resumeCall();
472 } else {
473 launchEmergencyDialer();
474 }
475 }
476
477 private void resumeCall() {
478 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
479 if (phone != null) {
480 try {
481 phone.showCallScreen();
482 } catch (RemoteException e) {
483 Log.e(TAG, "Error calling ITelephony service: " + e);
484 }
485 }
486 }
487
488 private void launchEmergencyDialer() {
489 Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
490 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
491 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
492 startActivity(intent);
493 }
494}