blob: 0552b682f2e7af2082d2632efb203660710820c2 [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;
Ben Komalo04606752011-08-18 14:50:26 -0700238 Log.d(TAG, "Restoring wakelock from NonConfigurationInstanceState");
Andy Stadler14997402011-02-01 15:34:59 -0800239 }
Jason parksec5a45e2011-01-18 15:28:36 -0600240 }
Jason parks35933812011-01-21 15:48:20 -0600241
Andy Stadler95974062011-02-01 17:35:20 -0800242 /**
243 * Note, we defer the state check and screen setup to onStart() because this will be
244 * re-run if the user clicks the power button (sleeping/waking the screen), and this is
245 * especially important if we were to lose the wakelock for any reason.
246 */
247 @Override
248 public void onStart() {
249 super.onStart();
250
251 // Check to see why we were started.
252 String progress = SystemProperties.get("vold.encrypt_progress");
Ben Komalo91a2f052011-08-16 17:48:25 -0700253 if (!"".equals(progress)
254 || isDebugView(FORCE_VIEW_PROGRESS)
255 || isDebugView(FORCE_VIEW_ERROR)) {
Andy Stadler95974062011-02-01 17:35:20 -0800256 setContentView(R.layout.crypt_keeper_progress);
257 encryptionProgressInit();
258 } else {
259 setContentView(R.layout.crypt_keeper_password_entry);
260 passwordEntryInit();
261 }
262 }
263
Jason parksf8217302011-01-26 13:11:42 -0600264 @Override
265 public void onStop() {
266 super.onStop();
267
268 mHandler.removeMessages(COOLDOWN);
269 mHandler.removeMessages(UPDATE_PROGRESS);
Andy Stadler14997402011-02-01 15:34:59 -0800270 }
271
272 /**
273 * Reconfiguring, so propagate the wakelock to the next instance. This runs between onStop()
274 * and onDestroy() and only if we are changing configuration (e.g. rotation). Also clears
275 * mWakeLock so the subsequent call to onDestroy does not release it.
276 */
277 @Override
278 public Object onRetainNonConfigurationInstance() {
279 NonConfigurationInstanceState state = new NonConfigurationInstanceState(mWakeLock);
Ben Komalo04606752011-08-18 14:50:26 -0700280 Log.d(TAG, "Handing wakelock off to NonConfigurationInstanceState");
Andy Stadler14997402011-02-01 15:34:59 -0800281 mWakeLock = null;
282 return state;
283 }
284
285 @Override
286 public void onDestroy() {
287 super.onDestroy();
Jason parksf8217302011-01-26 13:11:42 -0600288
289 if (mWakeLock != null) {
Ben Komalo04606752011-08-18 14:50:26 -0700290 Log.d(TAG, "Releasing and destroying wakelock");
Jason parksf8217302011-01-26 13:11:42 -0600291 mWakeLock.release();
292 mWakeLock = null;
293 }
294 }
295
Jason parksec5a45e2011-01-18 15:28:36 -0600296 private void encryptionProgressInit() {
Jason parks35933812011-01-21 15:48:20 -0600297 // Accquire a partial wakelock to prevent the device from sleeping. Note
298 // we never release this wakelock as we will be restarted after the device
299 // is encrypted.
300
Ben Komalo04606752011-08-18 14:50:26 -0700301 Log.d(TAG, "Encryption progress screen initializing.");
302 if (mWakeLock != null) {
303 Log.d(TAG, "Acquiring wakelock.");
304 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
305 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
306 mWakeLock.acquire();
307 }
Jason parks35933812011-01-21 15:48:20 -0600308
Jason parksf8217302011-01-26 13:11:42 -0600309 ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
310 progressBar.setIndeterminate(true);
311
312 updateProgress();
313 }
314
Andy Stadler13d62042011-01-31 19:21:37 -0800315 private void showFactoryReset() {
316 // Hide the encryption-bot to make room for the "factory reset" button
317 findViewById(R.id.encroid).setVisibility(View.GONE);
318
319 // Show the reset button, failure text, and a divider
320 Button button = (Button) findViewById(R.id.factory_reset);
321 button.setVisibility(View.VISIBLE);
322 button.setOnClickListener(new OnClickListener() {
323 public void onClick(View v) {
324 // Factory reset the device.
325 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
326 }
327 });
328
329 TextView tv = (TextView) findViewById(R.id.title);
330 tv.setText(R.string.crypt_keeper_failed_title);
331
332 tv = (TextView) findViewById(R.id.status);
333 tv.setText(R.string.crypt_keeper_failed_summary);
334
335 View view = findViewById(R.id.bottom_divider);
Ben Komalof0104df2011-08-16 17:48:42 -0700336 if (view != null) {
337 view.setVisibility(View.VISIBLE);
338 }
Andy Stadler13d62042011-01-31 19:21:37 -0800339 }
340
Jason parksf8217302011-01-26 13:11:42 -0600341 private void updateProgress() {
342 String state = SystemProperties.get("vold.encrypt_progress");
343
Ben Komalo91a2f052011-08-16 17:48:25 -0700344 if ("error_partially_encrypted".equals(state) || isDebugView(FORCE_VIEW_ERROR)) {
Andy Stadler13d62042011-01-31 19:21:37 -0800345 showFactoryReset();
346 return;
347 }
348
Jason parksf8217302011-01-26 13:11:42 -0600349 int progress = 0;
350 try {
Ben Komalo91a2f052011-08-16 17:48:25 -0700351 // Force a 50% progress state when debugging the view.
352 progress = isDebugView() ? 50 : Integer.parseInt(state);
Jason parksf8217302011-01-26 13:11:42 -0600353 } catch (Exception e) {
354 Log.w(TAG, "Error parsing progress: " + e.toString());
355 }
356
357 CharSequence status = getText(R.string.crypt_keeper_setup_description);
Ben Komalo04606752011-08-18 14:50:26 -0700358 Log.v(TAG, "Encryption progress: " + progress);
Jason parksf8217302011-01-26 13:11:42 -0600359 TextView tv = (TextView) findViewById(R.id.status);
360 tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
361
362 // Check the progress every 5 seconds
363 mHandler.removeMessages(UPDATE_PROGRESS);
364 mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
365 }
366
367 private void cooldown() {
368 TextView tv = (TextView) findViewById(R.id.status);
Andy Stadler13d62042011-01-31 19:21:37 -0800369
Jason parksf8217302011-01-26 13:11:42 -0600370 if (mCooldown <= 0) {
371 // Re-enable the password entry
Jason parks06c5ff42011-03-01 10:17:40 -0600372 mPasswordEntry.setEnabled(true);
Jason parksf8217302011-01-26 13:11:42 -0600373
Andy Stadler13d62042011-01-31 19:21:37 -0800374 tv.setVisibility(View.GONE);
Jason parksf8217302011-01-26 13:11:42 -0600375 } else {
Andy Stadler13d62042011-01-31 19:21:37 -0800376 CharSequence template = getText(R.string.crypt_keeper_cooldown);
377 tv.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
378
379 tv.setVisibility(View.VISIBLE);
Jason parksf8217302011-01-26 13:11:42 -0600380
381 mCooldown--;
382 mHandler.removeMessages(COOLDOWN);
383 mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
384 }
Jason parksec5a45e2011-01-18 15:28:36 -0600385 }
Jason parks35933812011-01-21 15:48:20 -0600386
Jason parksec5a45e2011-01-18 15:28:36 -0600387 private void passwordEntryInit() {
Jason parks06c5ff42011-03-01 10:17:40 -0600388 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
389 mPasswordEntry.setOnEditorActionListener(this);
Jason parks35933812011-01-21 15:48:20 -0600390
Jason parks8fd5bc92011-01-12 16:03:31 -0600391 KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
Jason parks35933812011-01-21 15:48:20 -0600392
Jason parks00046d62011-06-13 17:38:45 -0500393 if (keyboardView != null) {
394 PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
395 keyboardView, mPasswordEntry, false);
396 keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
397 }
David Brown8373b452011-06-20 12:38:45 -0700398
399 updateEmergencyCallButtonState();
Jason parks8fd5bc92011-01-12 16:03:31 -0600400 }
401
402 private IMountService getMountService() {
403 IBinder service = ServiceManager.getService("mount");
404 if (service != null) {
405 return IMountService.Stub.asInterface(service);
406 }
407 return null;
408 }
409
410 @Override
411 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Jason parks00046d62011-06-13 17:38:45 -0500412 if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
Jason parks8fd5bc92011-01-12 16:03:31 -0600413 // Get the password
414 String password = v.getText().toString();
415
Jason parksec5a45e2011-01-18 15:28:36 -0600416 if (TextUtils.isEmpty(password)) {
417 return true;
418 }
Jason parks35933812011-01-21 15:48:20 -0600419
Jason parks8fd5bc92011-01-12 16:03:31 -0600420 // Now that we have the password clear the password field.
421 v.setText(null);
422
Jason parks06c5ff42011-03-01 10:17:40 -0600423 // Disable the password entry while checking the password. This
424 // we either be reenabled if the password was wrong or after the
425 // cooldown period.
426 mPasswordEntry.setEnabled(false);
Jason parks8fd5bc92011-01-12 16:03:31 -0600427
Ben Komalo04606752011-08-18 14:50:26 -0700428 Log.d(TAG, "Attempting to send command to decrypt");
Jason parks06c5ff42011-03-01 10:17:40 -0600429 new DecryptTask().execute(password);
Jason parks35933812011-01-21 15:48:20 -0600430
Jason parks8fd5bc92011-01-12 16:03:31 -0600431 return true;
432 }
433 return false;
434 }
David Brown8373b452011-06-20 12:38:45 -0700435
436 //
437 // Code to update the state of, and handle clicks from, the "Emergency call" button.
438 //
439 // This code is mostly duplicated from the corresponding code in
440 // LockPatternUtils and LockPatternKeyguardView under frameworks/base.
441 //
442
443 private void updateEmergencyCallButtonState() {
444 Button button = (Button) findViewById(R.id.emergencyCallButton);
445 // The button isn't present at all in some configurations.
446 if (button == null) return;
447
448 if (isEmergencyCallCapable()) {
449 button.setVisibility(View.VISIBLE);
450 button.setOnClickListener(new View.OnClickListener() {
451 public void onClick(View v) {
452 takeEmergencyCallAction();
453 }
454 });
455 } else {
456 button.setVisibility(View.GONE);
457 return;
458 }
459
460 int newState = TelephonyManager.getDefault().getCallState();
461 int textId;
462 if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
463 // show "return to call" text and show phone icon
464 textId = R.string.cryptkeeper_return_to_call;
465 int phoneCallIcon = R.drawable.stat_sys_phone_call;
466 button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
467 } else {
468 textId = R.string.cryptkeeper_emergency_call;
469 int emergencyIcon = R.drawable.ic_emergency;
470 button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
471 }
472 button.setText(textId);
473 }
474
475 private boolean isEmergencyCallCapable() {
476 return getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
477 }
478
479 private void takeEmergencyCallAction() {
480 if (TelephonyManager.getDefault().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
481 resumeCall();
482 } else {
483 launchEmergencyDialer();
484 }
485 }
486
487 private void resumeCall() {
488 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
489 if (phone != null) {
490 try {
491 phone.showCallScreen();
492 } catch (RemoteException e) {
493 Log.e(TAG, "Error calling ITelephony service: " + e);
494 }
495 }
496 }
497
498 private void launchEmergencyDialer() {
499 Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
500 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
501 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
502 startActivity(intent);
503 }
504}