blob: d5f13d9e06a50a3e5e8359774569fb2f0a23121e [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 parks06c5ff42011-03-01 10:17:40 -060025import android.os.AsyncTask;
Jason parks8fd5bc92011-01-12 16:03:31 -060026import android.os.Bundle;
Jason parksec5a45e2011-01-18 15:28:36 -060027import android.os.Handler;
Jason parks8fd5bc92011-01-12 16:03:31 -060028import android.os.IBinder;
Jason parksec5a45e2011-01-18 15:28:36 -060029import android.os.Message;
Jason parks35933812011-01-21 15:48:20 -060030import android.os.PowerManager;
David Brown8373b452011-06-20 12:38:45 -070031import android.os.RemoteException;
Jason parks8fd5bc92011-01-12 16:03:31 -060032import android.os.ServiceManager;
33import android.os.SystemProperties;
34import android.os.storage.IMountService;
David Brown8373b452011-06-20 12:38:45 -070035import android.telephony.TelephonyManager;
Jason parksec5a45e2011-01-18 15:28:36 -060036import android.text.TextUtils;
Jason parks8fd5bc92011-01-12 16:03:31 -060037import android.util.Log;
38import android.view.KeyEvent;
Andy Stadler13d62042011-01-31 19:21:37 -080039import android.view.View;
40import android.view.View.OnClickListener;
Jason parks8fd5bc92011-01-12 16:03:31 -060041import android.view.inputmethod.EditorInfo;
Ben Komalo9fcb6a72011-08-26 14:40:18 -070042import android.view.inputmethod.InputMethodInfo;
Jason parks75c085e2011-02-10 14:03:47 -060043import android.view.inputmethod.InputMethodManager;
Ben Komalo9fcb6a72011-08-26 14:40:18 -070044import android.view.inputmethod.InputMethodSubtype;
Andy Stadler13d62042011-01-31 19:21:37 -080045import android.widget.Button;
Jason parksec5a45e2011-01-18 15:28:36 -060046import android.widget.EditText;
47import android.widget.ProgressBar;
Jason parks8fd5bc92011-01-12 16:03:31 -060048import android.widget.TextView;
49
Ben Komalo91a2f052011-08-16 17:48:25 -070050import com.android.internal.telephony.ITelephony;
Ben Komalo9fcb6a72011-08-26 14:40:18 -070051
52import java.util.List;
Ben Komalo91a2f052011-08-16 17:48:25 -070053
54/**
55 * Settings screens to show the UI flows for encrypting/decrypting the device.
56 *
57 * This may be started via adb for debugging the UI layout, without having to go through
58 * encryption flows everytime. It should be noted that starting the activity in this manner
59 * is only useful for verifying UI-correctness - the behavior will not be identical.
60 * <pre>
61 * $ adb shell pm enable com.android.settings/.CryptKeeper
62 * $ adb shell am start \
63 * -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "progress" \
64 * -n com.android.settings/.CryptKeeper
65 * </pre>
66 */
Jason parks8fd5bc92011-01-12 16:03:31 -060067public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
Jason parksec5a45e2011-01-18 15:28:36 -060068 private static final String TAG = "CryptKeeper";
Jason parks35933812011-01-21 15:48:20 -060069
Jason parks8fd5bc92011-01-12 16:03:31 -060070 private static final String DECRYPT_STATE = "trigger_restart_framework";
Jason parksec5a45e2011-01-18 15:28:36 -060071
72 private static final int UPDATE_PROGRESS = 1;
73 private static final int COOLDOWN = 2;
74
75 private static final int MAX_FAILED_ATTEMPTS = 30;
76 private static final int COOL_DOWN_ATTEMPTS = 10;
77 private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
78
David Brown8373b452011-06-20 12:38:45 -070079 // Intent action for launching the Emergency Dialer activity.
80 static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
81
Ben Komalo91a2f052011-08-16 17:48:25 -070082 // Debug Intent extras so that this Activity may be started via adb for debugging UI layouts
83 private static final String EXTRA_FORCE_VIEW =
84 "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW";
85 private static final String FORCE_VIEW_PROGRESS = "progress";
Ben Komalo91a2f052011-08-16 17:48:25 -070086 private static final String FORCE_VIEW_ERROR = "error";
87
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -070088 /** When encryption is detected, this flag indicates whether or not we've checked for errors. */
Ben Komalo0e666092011-09-01 15:42:00 -070089 private boolean mValidationComplete;
Ben Komalod4758ef2011-09-08 14:36:08 -070090 private boolean mValidationRequested;
Ben Komalo0e666092011-09-01 15:42:00 -070091 /** A flag to indicate that the volume is in a bad state (e.g. partially encrypted). */
92 private boolean mEncryptionGoneBad;
93
Andy Stadler14997402011-02-01 15:34:59 -080094 private int mCooldown;
95 PowerManager.WakeLock mWakeLock;
Jason parks06c5ff42011-03-01 10:17:40 -060096 private EditText mPasswordEntry;
Andy Stadler14997402011-02-01 15:34:59 -080097
98 /**
99 * Used to propagate state through configuration changes (e.g. screen rotation)
100 */
101 private static class NonConfigurationInstanceState {
102 final PowerManager.WakeLock wakelock;
103
104 NonConfigurationInstanceState(PowerManager.WakeLock _wakelock) {
105 wakelock = _wakelock;
106 }
107 }
108
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700109 /**
110 * Activity used to fade the screen to black after the password is entered.
111 */
112 private static class FadeToBlack extends Activity {
Andy Stadler13d62042011-01-31 19:21:37 -0800113 @Override
114 public void onCreate(Bundle savedInstanceState) {
115 super.onCreate(savedInstanceState);
116 setContentView(R.layout.crypt_keeper_blank);
117 }
Jason parksf1dbf552011-01-24 16:19:28 -0600118 }
Jason parksec5a45e2011-01-18 15:28:36 -0600119
Jason parks06c5ff42011-03-01 10:17:40 -0600120 private class DecryptTask extends AsyncTask<String, Void, Integer> {
121 @Override
122 protected Integer doInBackground(String... params) {
123 IMountService service = getMountService();
124 try {
125 return service.decryptStorage(params[0]);
126 } catch (Exception e) {
127 Log.e(TAG, "Error while decrypting...", e);
128 return -1;
129 }
130 }
131
132 @Override
133 protected void onPostExecute(Integer failedAttempts) {
134 if (failedAttempts == 0) {
135 // The password was entered successfully. Start the Blank activity
136 // so this activity animates to black before the devices starts. Note
137 // It has 1 second to complete the animation or it will be frozen
138 // until the boot animation comes back up.
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700139 Intent intent = new Intent(CryptKeeper.this, FadeToBlack.class);
Jason parks06c5ff42011-03-01 10:17:40 -0600140 finish();
141 startActivity(intent);
142 } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
143 // Factory reset the device.
144 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
145 } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
146 mCooldown = COOL_DOWN_INTERVAL;
147 cooldown();
148 } else {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700149 final TextView status = (TextView) findViewById(R.id.status);
150 status.setText(R.string.try_again);
151 status.setVisibility(View.VISIBLE);
Jason parks06c5ff42011-03-01 10:17:40 -0600152
153 // Reenable the password entry
154 mPasswordEntry.setEnabled(true);
155 }
156 }
157 }
Jason parks75c085e2011-02-10 14:03:47 -0600158
Ben Komalo0e666092011-09-01 15:42:00 -0700159 private class ValidationTask extends AsyncTask<Void, Void, Boolean> {
160 @Override
161 protected Boolean doInBackground(Void... params) {
162 IMountService service = getMountService();
163 try {
Ben Komalod4758ef2011-09-08 14:36:08 -0700164 Log.d(TAG, "Validating encryption state.");
Ben Komalo0e666092011-09-01 15:42:00 -0700165 int state = service.getEncryptionState();
166 if (state == IMountService.ENCRYPTION_STATE_NONE) {
167 Log.w(TAG, "Unexpectedly in CryptKeeper even though there is no encryption.");
168 return true; // Unexpected, but fine, I guess...
169 }
170 return state == IMountService.ENCRYPTION_STATE_OK;
171 } catch (RemoteException e) {
172 Log.w(TAG, "Unable to get encryption state properly");
173 return true;
174 }
175 }
176
177 @Override
178 protected void onPostExecute(Boolean result) {
179 mValidationComplete = true;
180 if (Boolean.FALSE.equals(result)) {
181 Log.w(TAG, "Incomplete, or corrupted encryption detected. Prompting user to wipe.");
182 mEncryptionGoneBad = true;
Ben Komalod4758ef2011-09-08 14:36:08 -0700183 } else {
184 Log.d(TAG, "Encryption state validated. Proceeding to configure UI");
Ben Komalo0e666092011-09-01 15:42:00 -0700185 }
186 setupUi();
187 }
188 }
189
Ben Komalo91a2f052011-08-16 17:48:25 -0700190 private final Handler mHandler = new Handler() {
Jason parksec5a45e2011-01-18 15:28:36 -0600191 @Override
192 public void handleMessage(Message msg) {
Jason parksec5a45e2011-01-18 15:28:36 -0600193 switch (msg.what) {
Jason parksec5a45e2011-01-18 15:28:36 -0600194 case UPDATE_PROGRESS:
Jason parksf8217302011-01-26 13:11:42 -0600195 updateProgress();
Jason parksec5a45e2011-01-18 15:28:36 -0600196 break;
Jason parks35933812011-01-21 15:48:20 -0600197
Jason parksec5a45e2011-01-18 15:28:36 -0600198 case COOLDOWN:
Jason parksf8217302011-01-26 13:11:42 -0600199 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -0600200 break;
201 }
202 }
203 };
Jason parks35933812011-01-21 15:48:20 -0600204
Ben Komalo91a2f052011-08-16 17:48:25 -0700205 /** @return whether or not this Activity was started for debugging the UI only. */
206 private boolean isDebugView() {
207 return getIntent().hasExtra(EXTRA_FORCE_VIEW);
208 }
209
210 /** @return whether or not this Activity was started for debugging the specific UI view only. */
211 private boolean isDebugView(String viewType /* non-nullable */) {
212 return viewType.equals(getIntent().getStringExtra(EXTRA_FORCE_VIEW));
213 }
214
Jason parks8fd5bc92011-01-12 16:03:31 -0600215 @Override
216 public void onCreate(Bundle savedInstanceState) {
217 super.onCreate(savedInstanceState);
Jason parks35933812011-01-21 15:48:20 -0600218
Andy Stadler95974062011-02-01 17:35:20 -0800219 // If we are not encrypted or encrypting, get out quickly.
Jason parks8fd5bc92011-01-12 16:03:31 -0600220 String state = SystemProperties.get("vold.decrypt");
Ben Komalo91a2f052011-08-16 17:48:25 -0700221 if (!isDebugView() && ("".equals(state) || DECRYPT_STATE.equals(state))) {
Jason parks35933812011-01-21 15:48:20 -0600222 // Disable the crypt keeper.
Jason parks8fd5bc92011-01-12 16:03:31 -0600223 PackageManager pm = getPackageManager();
224 ComponentName name = new ComponentName(this, CryptKeeper.class);
Dianne Hackborn140f6c62011-10-16 11:49:00 -0700225 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
226 PackageManager.DONT_KILL_APP);
227 // Typically CryptKeeper is launched as the home app. We didn't
Dianne Hackborn644fa422011-10-18 13:38:03 -0700228 // want to be running, so need to finish this activity. We can count
229 // on the activity manager re-launching the new home app upon finishing
230 // this one, since this will leave the activity stack empty.
Dianne Hackborn140f6c62011-10-16 11:49:00 -0700231 // NOTE: This is really grungy. I think it would be better for the
232 // activity manager to explicitly launch the crypt keeper instead of
233 // home in the situation where we need to decrypt the device
234 finish();
Jason parks8fd5bc92011-01-12 16:03:31 -0600235 return;
236 }
Jason parks35933812011-01-21 15:48:20 -0600237
Jason parks39f1e042011-01-20 23:29:28 -0600238 // Disable the status bar
239 StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
Andy Stadler13d62042011-01-31 19:21:37 -0800240 sbm.disable(StatusBarManager.DISABLE_EXPAND
241 | StatusBarManager.DISABLE_NOTIFICATION_ICONS
Jason parks39f1e042011-01-20 23:29:28 -0600242 | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
Andy Stadler13d62042011-01-31 19:21:37 -0800243 | StatusBarManager.DISABLE_SYSTEM_INFO
Daniel Sandler4d2bfd12011-10-12 15:34:23 -0400244 | StatusBarManager.DISABLE_HOME
245 | StatusBarManager.DISABLE_RECENT
Andy Stadler13d62042011-01-31 19:21:37 -0800246 | StatusBarManager.DISABLE_BACK);
Andy Stadler14997402011-02-01 15:34:59 -0800247
248 // Check for (and recover) retained instance data
249 Object lastInstance = getLastNonConfigurationInstance();
250 if (lastInstance instanceof NonConfigurationInstanceState) {
251 NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance;
252 mWakeLock = retained.wakelock;
Ben Komalo04606752011-08-18 14:50:26 -0700253 Log.d(TAG, "Restoring wakelock from NonConfigurationInstanceState");
Andy Stadler14997402011-02-01 15:34:59 -0800254 }
Jason parksec5a45e2011-01-18 15:28:36 -0600255 }
Jason parks35933812011-01-21 15:48:20 -0600256
Andy Stadler95974062011-02-01 17:35:20 -0800257 /**
258 * Note, we defer the state check and screen setup to onStart() because this will be
259 * re-run if the user clicks the power button (sleeping/waking the screen), and this is
260 * especially important if we were to lose the wakelock for any reason.
261 */
262 @Override
263 public void onStart() {
264 super.onStart();
265
Ben Komalod4758ef2011-09-08 14:36:08 -0700266 setupUi();
Ben Komalo0e666092011-09-01 15:42:00 -0700267 }
268
269 /**
270 * Initializes the UI based on the current state of encryption.
271 * This is idempotent - calling repeatedly will simply re-initialize the UI.
272 */
273 private void setupUi() {
274 if (mEncryptionGoneBad || isDebugView(FORCE_VIEW_ERROR)) {
275 setContentView(R.layout.crypt_keeper_progress);
276 showFactoryReset();
277 return;
278 }
279
Andy Stadler95974062011-02-01 17:35:20 -0800280 String progress = SystemProperties.get("vold.encrypt_progress");
Ben Komalo0e666092011-09-01 15:42:00 -0700281 if (!"".equals(progress) || isDebugView(FORCE_VIEW_PROGRESS)) {
Andy Stadler95974062011-02-01 17:35:20 -0800282 setContentView(R.layout.crypt_keeper_progress);
283 encryptionProgressInit();
Ben Komalod4758ef2011-09-08 14:36:08 -0700284 } else if (mValidationComplete) {
Andy Stadler95974062011-02-01 17:35:20 -0800285 setContentView(R.layout.crypt_keeper_password_entry);
286 passwordEntryInit();
Ben Komalod4758ef2011-09-08 14:36:08 -0700287 } else if (!mValidationRequested) {
288 // We're supposed to be encrypted, but no validation has been done.
289 new ValidationTask().execute((Void[]) null);
290 mValidationRequested = true;
Andy Stadler95974062011-02-01 17:35:20 -0800291 }
292 }
293
Jason parksf8217302011-01-26 13:11:42 -0600294 @Override
295 public void onStop() {
296 super.onStop();
Jason parksf8217302011-01-26 13:11:42 -0600297 mHandler.removeMessages(COOLDOWN);
298 mHandler.removeMessages(UPDATE_PROGRESS);
Andy Stadler14997402011-02-01 15:34:59 -0800299 }
300
301 /**
302 * Reconfiguring, so propagate the wakelock to the next instance. This runs between onStop()
303 * and onDestroy() and only if we are changing configuration (e.g. rotation). Also clears
304 * mWakeLock so the subsequent call to onDestroy does not release it.
305 */
306 @Override
307 public Object onRetainNonConfigurationInstance() {
308 NonConfigurationInstanceState state = new NonConfigurationInstanceState(mWakeLock);
Ben Komalo04606752011-08-18 14:50:26 -0700309 Log.d(TAG, "Handing wakelock off to NonConfigurationInstanceState");
Andy Stadler14997402011-02-01 15:34:59 -0800310 mWakeLock = null;
311 return state;
312 }
313
314 @Override
315 public void onDestroy() {
316 super.onDestroy();
Jason parksf8217302011-01-26 13:11:42 -0600317
318 if (mWakeLock != null) {
Ben Komalo04606752011-08-18 14:50:26 -0700319 Log.d(TAG, "Releasing and destroying wakelock");
Jason parksf8217302011-01-26 13:11:42 -0600320 mWakeLock.release();
321 mWakeLock = null;
322 }
323 }
324
Jason parksec5a45e2011-01-18 15:28:36 -0600325 private void encryptionProgressInit() {
Jason parks35933812011-01-21 15:48:20 -0600326 // Accquire a partial wakelock to prevent the device from sleeping. Note
327 // we never release this wakelock as we will be restarted after the device
328 // is encrypted.
329
Ben Komalo04606752011-08-18 14:50:26 -0700330 Log.d(TAG, "Encryption progress screen initializing.");
Ben Komalo9ee164f2011-09-21 10:40:50 -0700331 if (mWakeLock == null) {
Ben Komalo04606752011-08-18 14:50:26 -0700332 Log.d(TAG, "Acquiring wakelock.");
333 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
334 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
335 mWakeLock.acquire();
336 }
Jason parks35933812011-01-21 15:48:20 -0600337
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700338 ((ProgressBar) findViewById(R.id.progress_bar)).setIndeterminate(true);
Jason parksf8217302011-01-26 13:11:42 -0600339 updateProgress();
340 }
341
Andy Stadler13d62042011-01-31 19:21:37 -0800342 private void showFactoryReset() {
343 // Hide the encryption-bot to make room for the "factory reset" button
344 findViewById(R.id.encroid).setVisibility(View.GONE);
345
346 // Show the reset button, failure text, and a divider
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700347 final Button button = (Button) findViewById(R.id.factory_reset);
Andy Stadler13d62042011-01-31 19:21:37 -0800348 button.setVisibility(View.VISIBLE);
349 button.setOnClickListener(new OnClickListener() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700350 @Override
Andy Stadler13d62042011-01-31 19:21:37 -0800351 public void onClick(View v) {
352 // Factory reset the device.
353 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
354 }
355 });
356
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700357 // Alert the user of the failure.
358 ((TextView) findViewById(R.id.title)).setText(R.string.crypt_keeper_failed_title);
359 ((TextView) findViewById(R.id.status)).setText(R.string.crypt_keeper_failed_summary);
Andy Stadler13d62042011-01-31 19:21:37 -0800360
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700361 final View view = findViewById(R.id.bottom_divider);
362 // TODO(viki): Why would the bottom divider be missing in certain layouts? Investigate.
Ben Komalof0104df2011-08-16 17:48:42 -0700363 if (view != null) {
364 view.setVisibility(View.VISIBLE);
365 }
Andy Stadler13d62042011-01-31 19:21:37 -0800366 }
367
Jason parksf8217302011-01-26 13:11:42 -0600368 private void updateProgress() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700369 final String state = SystemProperties.get("vold.encrypt_progress");
Jason parksf8217302011-01-26 13:11:42 -0600370
Ben Komalo0e666092011-09-01 15:42:00 -0700371 if ("error_partially_encrypted".equals(state)) {
Andy Stadler13d62042011-01-31 19:21:37 -0800372 showFactoryReset();
373 return;
374 }
375
Jason parksf8217302011-01-26 13:11:42 -0600376 int progress = 0;
377 try {
Ben Komalo91a2f052011-08-16 17:48:25 -0700378 // Force a 50% progress state when debugging the view.
379 progress = isDebugView() ? 50 : Integer.parseInt(state);
Jason parksf8217302011-01-26 13:11:42 -0600380 } catch (Exception e) {
381 Log.w(TAG, "Error parsing progress: " + e.toString());
382 }
383
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700384 final CharSequence status = getText(R.string.crypt_keeper_setup_description);
Ben Komalo04606752011-08-18 14:50:26 -0700385 Log.v(TAG, "Encryption progress: " + progress);
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700386 final TextView tv = (TextView) findViewById(R.id.status);
Jason parksf8217302011-01-26 13:11:42 -0600387 tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
388
389 // Check the progress every 5 seconds
390 mHandler.removeMessages(UPDATE_PROGRESS);
391 mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
392 }
393
394 private void cooldown() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700395 final TextView status = (TextView) findViewById(R.id.status);
Andy Stadler13d62042011-01-31 19:21:37 -0800396
Jason parksf8217302011-01-26 13:11:42 -0600397 if (mCooldown <= 0) {
398 // Re-enable the password entry
Jason parks06c5ff42011-03-01 10:17:40 -0600399 mPasswordEntry.setEnabled(true);
Jason parksf8217302011-01-26 13:11:42 -0600400
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700401 status.setVisibility(View.GONE);
Jason parksf8217302011-01-26 13:11:42 -0600402 } else {
Andy Stadler13d62042011-01-31 19:21:37 -0800403 CharSequence template = getText(R.string.crypt_keeper_cooldown);
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700404 status.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
Andy Stadler13d62042011-01-31 19:21:37 -0800405
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700406 status.setVisibility(View.VISIBLE);
Jason parksf8217302011-01-26 13:11:42 -0600407
408 mCooldown--;
409 mHandler.removeMessages(COOLDOWN);
410 mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
411 }
Jason parksec5a45e2011-01-18 15:28:36 -0600412 }
Jason parks35933812011-01-21 15:48:20 -0600413
Jason parksec5a45e2011-01-18 15:28:36 -0600414 private void passwordEntryInit() {
Jason parks06c5ff42011-03-01 10:17:40 -0600415 mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
416 mPasswordEntry.setOnEditorActionListener(this);
Ben Komalo9fcb6a72011-08-26 14:40:18 -0700417 mPasswordEntry.requestFocus();
Jason parks35933812011-01-21 15:48:20 -0600418
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700419 final View imeSwitcher = findViewById(R.id.switch_ime_button);
Ben Komalo9fcb6a72011-08-26 14:40:18 -0700420 final InputMethodManager imm = (InputMethodManager) getSystemService(
421 Context.INPUT_METHOD_SERVICE);
422 if (imeSwitcher != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
423 imeSwitcher.setVisibility(View.VISIBLE);
424 imeSwitcher.setOnClickListener(new OnClickListener() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700425 @Override
Ben Komalo9fcb6a72011-08-26 14:40:18 -0700426 public void onClick(View v) {
427 imm.showInputMethodPicker();
428 }
429 });
Jason parks00046d62011-06-13 17:38:45 -0500430 }
David Brown8373b452011-06-20 12:38:45 -0700431
Ben Komalo9fcb6a72011-08-26 14:40:18 -0700432 // Asynchronously throw up the IME, since there are issues with requesting it to be shown
433 // immediately.
434 mHandler.postDelayed(new Runnable() {
435 @Override public void run() {
436 imm.showSoftInputUnchecked(0, null);
437 }
438 }, 0);
439
David Brown8373b452011-06-20 12:38:45 -0700440 updateEmergencyCallButtonState();
Jason parks8fd5bc92011-01-12 16:03:31 -0600441 }
442
Ben Komalo9fcb6a72011-08-26 14:40:18 -0700443 /**
444 * Method adapted from com.android.inputmethod.latin.Utils
445 *
446 * @param imm The input method manager
447 * @param shouldIncludeAuxiliarySubtypes
448 * @return true if we have multiple IMEs to choose from
449 */
450 private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
451 final boolean shouldIncludeAuxiliarySubtypes) {
452 final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
453
454 // Number of the filtered IMEs
455 int filteredImisCount = 0;
456
457 for (InputMethodInfo imi : enabledImis) {
458 // We can return true immediately after we find two or more filtered IMEs.
459 if (filteredImisCount > 1) return true;
460 final List<InputMethodSubtype> subtypes =
461 imm.getEnabledInputMethodSubtypeList(imi, true);
462 // IMEs that have no subtypes should be counted.
463 if (subtypes.isEmpty()) {
464 ++filteredImisCount;
465 continue;
466 }
467
468 int auxCount = 0;
469 for (InputMethodSubtype subtype : subtypes) {
470 if (subtype.isAuxiliary()) {
471 ++auxCount;
472 }
473 }
474 final int nonAuxCount = subtypes.size() - auxCount;
475
476 // IMEs that have one or more non-auxiliary subtypes should be counted.
477 // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
478 // subtypes should be counted as well.
479 if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
480 ++filteredImisCount;
481 continue;
482 }
483 }
484
485 return filteredImisCount > 1
486 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
487 // input method subtype (The current IME should be LatinIME.)
488 || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
489 }
490
Jason parks8fd5bc92011-01-12 16:03:31 -0600491 private IMountService getMountService() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700492 final IBinder service = ServiceManager.getService("mount");
Jason parks8fd5bc92011-01-12 16:03:31 -0600493 if (service != null) {
494 return IMountService.Stub.asInterface(service);
495 }
496 return null;
497 }
498
499 @Override
500 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Jason parks00046d62011-06-13 17:38:45 -0500501 if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) {
Jason parks8fd5bc92011-01-12 16:03:31 -0600502 // Get the password
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700503 final String password = v.getText().toString();
Jason parks8fd5bc92011-01-12 16:03:31 -0600504
Jason parksec5a45e2011-01-18 15:28:36 -0600505 if (TextUtils.isEmpty(password)) {
506 return true;
507 }
Jason parks35933812011-01-21 15:48:20 -0600508
Jason parks8fd5bc92011-01-12 16:03:31 -0600509 // Now that we have the password clear the password field.
510 v.setText(null);
511
Jason parks06c5ff42011-03-01 10:17:40 -0600512 // Disable the password entry while checking the password. This
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700513 // we either be re-enabled if the password was wrong or after the
Jason parks06c5ff42011-03-01 10:17:40 -0600514 // cooldown period.
515 mPasswordEntry.setEnabled(false);
Jason parks8fd5bc92011-01-12 16:03:31 -0600516
Ben Komalo04606752011-08-18 14:50:26 -0700517 Log.d(TAG, "Attempting to send command to decrypt");
Jason parks06c5ff42011-03-01 10:17:40 -0600518 new DecryptTask().execute(password);
Jason parks35933812011-01-21 15:48:20 -0600519
Jason parks8fd5bc92011-01-12 16:03:31 -0600520 return true;
521 }
522 return false;
523 }
David Brown8373b452011-06-20 12:38:45 -0700524
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700525 /**
526 * Code to update the state of, and handle clicks from, the "Emergency call" button.
527 *
528 * This code is mostly duplicated from the corresponding code in
529 * LockPatternUtils and LockPatternKeyguardView under frameworks/base.
530 */
David Brown8373b452011-06-20 12:38:45 -0700531 private void updateEmergencyCallButtonState() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700532 final Button emergencyCall = (Button) findViewById(R.id.emergencyCallButton);
David Brown8373b452011-06-20 12:38:45 -0700533 // The button isn't present at all in some configurations.
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700534 if (emergencyCall == null)
535 return;
David Brown8373b452011-06-20 12:38:45 -0700536
537 if (isEmergencyCallCapable()) {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700538 emergencyCall.setVisibility(View.VISIBLE);
539 emergencyCall.setOnClickListener(new View.OnClickListener() {
540 @Override
541
David Brown8373b452011-06-20 12:38:45 -0700542 public void onClick(View v) {
543 takeEmergencyCallAction();
544 }
545 });
546 } else {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700547 emergencyCall.setVisibility(View.GONE);
David Brown8373b452011-06-20 12:38:45 -0700548 return;
549 }
550
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700551 final int newState = TelephonyManager.getDefault().getCallState();
David Brown8373b452011-06-20 12:38:45 -0700552 int textId;
553 if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700554 // Show "return to call" text and show phone icon
David Brown8373b452011-06-20 12:38:45 -0700555 textId = R.string.cryptkeeper_return_to_call;
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700556 final int phoneCallIcon = R.drawable.stat_sys_phone_call;
557 emergencyCall.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
David Brown8373b452011-06-20 12:38:45 -0700558 } else {
559 textId = R.string.cryptkeeper_emergency_call;
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700560 final int emergencyIcon = R.drawable.ic_emergency;
561 emergencyCall.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
David Brown8373b452011-06-20 12:38:45 -0700562 }
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700563 emergencyCall.setText(textId);
David Brown8373b452011-06-20 12:38:45 -0700564 }
565
566 private boolean isEmergencyCallCapable() {
567 return getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
568 }
569
570 private void takeEmergencyCallAction() {
571 if (TelephonyManager.getDefault().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
572 resumeCall();
573 } else {
574 launchEmergencyDialer();
575 }
576 }
577
578 private void resumeCall() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700579 final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
David Brown8373b452011-06-20 12:38:45 -0700580 if (phone != null) {
581 try {
582 phone.showCallScreen();
583 } catch (RemoteException e) {
584 Log.e(TAG, "Error calling ITelephony service: " + e);
585 }
586 }
587 }
588
589 private void launchEmergencyDialer() {
Vikram Aggarwalbfa3a642012-03-29 14:07:22 -0700590 final Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
David Brown8373b452011-06-20 12:38:45 -0700591 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
592 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
593 startActivity(intent);
594 }
595}