blob: 0374ba646fd1d990d198d57e0937e907b5e10d12 [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
19import com.android.internal.widget.PasswordEntryKeyboardHelper;
20import com.android.internal.widget.PasswordEntryKeyboardView;
21
22import android.app.Activity;
23import android.app.StatusBarManager;
24import android.content.ComponentName;
25import android.content.Context;
Jason parksec5a45e2011-01-18 15:28:36 -060026import android.content.Intent;
Jason parks8fd5bc92011-01-12 16:03:31 -060027import android.content.pm.PackageManager;
Jason parks75c085e2011-02-10 14:03:47 -060028import android.graphics.Rect;
Jason parks8fd5bc92011-01-12 16:03:31 -060029import android.inputmethodservice.KeyboardView;
30import android.os.Bundle;
Jason parksec5a45e2011-01-18 15:28:36 -060031import android.os.Handler;
Jason parks8fd5bc92011-01-12 16:03:31 -060032import android.os.IBinder;
Jason parksec5a45e2011-01-18 15:28:36 -060033import android.os.Message;
Jason parks35933812011-01-21 15:48:20 -060034import android.os.PowerManager;
Jason parks8fd5bc92011-01-12 16:03:31 -060035import android.os.ServiceManager;
36import android.os.SystemProperties;
37import android.os.storage.IMountService;
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
Jason parks8fd5bc92011-01-12 16:03:31 -060052public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
Jason parksec5a45e2011-01-18 15:28:36 -060053 private static final String TAG = "CryptKeeper";
Jason parks35933812011-01-21 15:48:20 -060054
Jason parks8fd5bc92011-01-12 16:03:31 -060055 private static final String DECRYPT_STATE = "trigger_restart_framework";
Jason parksec5a45e2011-01-18 15:28:36 -060056
57 private static final int UPDATE_PROGRESS = 1;
58 private static final int COOLDOWN = 2;
59
60 private static final int MAX_FAILED_ATTEMPTS = 30;
61 private static final int COOL_DOWN_ATTEMPTS = 10;
62 private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
63
Andy Stadler14997402011-02-01 15:34:59 -080064 private int mCooldown;
65 PowerManager.WakeLock mWakeLock;
66
67 /**
68 * Used to propagate state through configuration changes (e.g. screen rotation)
69 */
70 private static class NonConfigurationInstanceState {
71 final PowerManager.WakeLock wakelock;
72
73 NonConfigurationInstanceState(PowerManager.WakeLock _wakelock) {
74 wakelock = _wakelock;
75 }
76 }
77
Jason parksf1dbf552011-01-24 16:19:28 -060078 // This activity is used to fade the screen to black after the password is entered.
79 public static class Blank extends Activity {
Andy Stadler13d62042011-01-31 19:21:37 -080080 @Override
81 public void onCreate(Bundle savedInstanceState) {
82 super.onCreate(savedInstanceState);
83 setContentView(R.layout.crypt_keeper_blank);
84 }
Jason parksf1dbf552011-01-24 16:19:28 -060085 }
Jason parksec5a45e2011-01-18 15:28:36 -060086
Jason parks75c085e2011-02-10 14:03:47 -060087 // Use a custom EditText to prevent the input method from showing.
88 public static class CryptEditText extends EditText {
89 InputMethodManager imm;
90
91 public CryptEditText(Context context, AttributeSet attrs) {
92 super(context, attrs);
93 imm = ((InputMethodManager) getContext().
94 getSystemService(Context.INPUT_METHOD_SERVICE));
95 }
96
97 @Override
98 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
99 super.onFocusChanged(focused, direction, previouslyFocusedRect);
100
101 if (focused && imm != null && imm.isActive(this)) {
102 imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
103 }
104 }
105
106 @Override
107 public boolean onTouchEvent(MotionEvent event) {
108 boolean handled = super.onTouchEvent(event);
109
110 if (imm != null && imm.isActive(this)) {
111 imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
112 }
113
114 return handled;
115 }
116 }
117
118
Jason parksec5a45e2011-01-18 15:28:36 -0600119 private Handler mHandler = new Handler() {
120 @Override
121 public void handleMessage(Message msg) {
Jason parksec5a45e2011-01-18 15:28:36 -0600122 switch (msg.what) {
Jason parksec5a45e2011-01-18 15:28:36 -0600123 case UPDATE_PROGRESS:
Jason parksf8217302011-01-26 13:11:42 -0600124 updateProgress();
Jason parksec5a45e2011-01-18 15:28:36 -0600125 break;
Jason parks35933812011-01-21 15:48:20 -0600126
Jason parksec5a45e2011-01-18 15:28:36 -0600127 case COOLDOWN:
Jason parksf8217302011-01-26 13:11:42 -0600128 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -0600129 break;
130 }
131 }
132 };
Jason parks35933812011-01-21 15:48:20 -0600133
Jason parks8fd5bc92011-01-12 16:03:31 -0600134 @Override
135 public void onCreate(Bundle savedInstanceState) {
136 super.onCreate(savedInstanceState);
Jason parks35933812011-01-21 15:48:20 -0600137
Andy Stadler95974062011-02-01 17:35:20 -0800138 // If we are not encrypted or encrypting, get out quickly.
Jason parks8fd5bc92011-01-12 16:03:31 -0600139 String state = SystemProperties.get("vold.decrypt");
140 if ("".equals(state) || DECRYPT_STATE.equals(state)) {
Jason parks35933812011-01-21 15:48:20 -0600141 // Disable the crypt keeper.
Jason parks8fd5bc92011-01-12 16:03:31 -0600142 PackageManager pm = getPackageManager();
143 ComponentName name = new ComponentName(this, CryptKeeper.class);
144 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
145 return;
146 }
Jason parks35933812011-01-21 15:48:20 -0600147
Jason parks39f1e042011-01-20 23:29:28 -0600148 // Disable the status bar
149 StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
Andy Stadler13d62042011-01-31 19:21:37 -0800150 sbm.disable(StatusBarManager.DISABLE_EXPAND
151 | StatusBarManager.DISABLE_NOTIFICATION_ICONS
Jason parks39f1e042011-01-20 23:29:28 -0600152 | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
Andy Stadler13d62042011-01-31 19:21:37 -0800153 | StatusBarManager.DISABLE_SYSTEM_INFO
154 | StatusBarManager.DISABLE_NAVIGATION
155 | StatusBarManager.DISABLE_BACK);
Andy Stadler14997402011-02-01 15:34:59 -0800156
157 // Check for (and recover) retained instance data
158 Object lastInstance = getLastNonConfigurationInstance();
159 if (lastInstance instanceof NonConfigurationInstanceState) {
160 NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance;
161 mWakeLock = retained.wakelock;
162 }
Jason parksec5a45e2011-01-18 15:28:36 -0600163 }
Jason parks35933812011-01-21 15:48:20 -0600164
Andy Stadler95974062011-02-01 17:35:20 -0800165 /**
166 * Note, we defer the state check and screen setup to onStart() because this will be
167 * re-run if the user clicks the power button (sleeping/waking the screen), and this is
168 * especially important if we were to lose the wakelock for any reason.
169 */
170 @Override
171 public void onStart() {
172 super.onStart();
173
174 // Check to see why we were started.
175 String progress = SystemProperties.get("vold.encrypt_progress");
176 if (!"".equals(progress)) {
177 setContentView(R.layout.crypt_keeper_progress);
178 encryptionProgressInit();
179 } else {
180 setContentView(R.layout.crypt_keeper_password_entry);
181 passwordEntryInit();
182 }
183 }
184
Jason parksf8217302011-01-26 13:11:42 -0600185 @Override
186 public void onStop() {
187 super.onStop();
188
189 mHandler.removeMessages(COOLDOWN);
190 mHandler.removeMessages(UPDATE_PROGRESS);
Andy Stadler14997402011-02-01 15:34:59 -0800191 }
192
193 /**
194 * Reconfiguring, so propagate the wakelock to the next instance. This runs between onStop()
195 * and onDestroy() and only if we are changing configuration (e.g. rotation). Also clears
196 * mWakeLock so the subsequent call to onDestroy does not release it.
197 */
198 @Override
199 public Object onRetainNonConfigurationInstance() {
200 NonConfigurationInstanceState state = new NonConfigurationInstanceState(mWakeLock);
201 mWakeLock = null;
202 return state;
203 }
204
205 @Override
206 public void onDestroy() {
207 super.onDestroy();
Jason parksf8217302011-01-26 13:11:42 -0600208
209 if (mWakeLock != null) {
210 mWakeLock.release();
211 mWakeLock = null;
212 }
213 }
214
Jason parksec5a45e2011-01-18 15:28:36 -0600215 private void encryptionProgressInit() {
Jason parks35933812011-01-21 15:48:20 -0600216 // Accquire a partial wakelock to prevent the device from sleeping. Note
217 // we never release this wakelock as we will be restarted after the device
218 // is encrypted.
219
220 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Jason parksf8217302011-01-26 13:11:42 -0600221 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
Jason parks35933812011-01-21 15:48:20 -0600222
Jason parksf8217302011-01-26 13:11:42 -0600223 mWakeLock.acquire();
Jason parks35933812011-01-21 15:48:20 -0600224
Jason parksf8217302011-01-26 13:11:42 -0600225 ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
226 progressBar.setIndeterminate(true);
227
228 updateProgress();
229 }
230
Andy Stadler13d62042011-01-31 19:21:37 -0800231 private void showFactoryReset() {
232 // Hide the encryption-bot to make room for the "factory reset" button
233 findViewById(R.id.encroid).setVisibility(View.GONE);
234
235 // Show the reset button, failure text, and a divider
236 Button button = (Button) findViewById(R.id.factory_reset);
237 button.setVisibility(View.VISIBLE);
238 button.setOnClickListener(new OnClickListener() {
239 public void onClick(View v) {
240 // Factory reset the device.
241 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
242 }
243 });
244
245 TextView tv = (TextView) findViewById(R.id.title);
246 tv.setText(R.string.crypt_keeper_failed_title);
247
248 tv = (TextView) findViewById(R.id.status);
249 tv.setText(R.string.crypt_keeper_failed_summary);
250
251 View view = findViewById(R.id.bottom_divider);
252 view.setVisibility(View.VISIBLE);
253 }
254
Jason parksf8217302011-01-26 13:11:42 -0600255 private void updateProgress() {
256 String state = SystemProperties.get("vold.encrypt_progress");
257
Andy Stadler13d62042011-01-31 19:21:37 -0800258 if ("error_partially_encrypted".equals(state)) {
259 showFactoryReset();
260 return;
261 }
262
Jason parksf8217302011-01-26 13:11:42 -0600263 int progress = 0;
264 try {
265 progress = Integer.parseInt(state);
266 } catch (Exception e) {
267 Log.w(TAG, "Error parsing progress: " + e.toString());
268 }
269
270 CharSequence status = getText(R.string.crypt_keeper_setup_description);
271 TextView tv = (TextView) findViewById(R.id.status);
272 tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
273
274 // Check the progress every 5 seconds
275 mHandler.removeMessages(UPDATE_PROGRESS);
276 mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
277 }
278
279 private void cooldown() {
280 TextView tv = (TextView) findViewById(R.id.status);
Andy Stadler13d62042011-01-31 19:21:37 -0800281
Jason parksf8217302011-01-26 13:11:42 -0600282 if (mCooldown <= 0) {
283 // Re-enable the password entry
284 EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
285 passwordEntry.setEnabled(true);
286
Andy Stadler13d62042011-01-31 19:21:37 -0800287 tv.setVisibility(View.GONE);
Jason parksf8217302011-01-26 13:11:42 -0600288 } else {
Andy Stadler13d62042011-01-31 19:21:37 -0800289 CharSequence template = getText(R.string.crypt_keeper_cooldown);
290 tv.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
291
292 tv.setVisibility(View.VISIBLE);
Jason parksf8217302011-01-26 13:11:42 -0600293
294 mCooldown--;
295 mHandler.removeMessages(COOLDOWN);
296 mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
297 }
Jason parksec5a45e2011-01-18 15:28:36 -0600298 }
Jason parks35933812011-01-21 15:48:20 -0600299
Jason parksec5a45e2011-01-18 15:28:36 -0600300 private void passwordEntryInit() {
Jason parks8fd5bc92011-01-12 16:03:31 -0600301 TextView passwordEntry = (TextView) findViewById(R.id.passwordEntry);
302 passwordEntry.setOnEditorActionListener(this);
Jason parks35933812011-01-21 15:48:20 -0600303
Jason parks8fd5bc92011-01-12 16:03:31 -0600304 KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
Jason parks35933812011-01-21 15:48:20 -0600305
Jason parks8fd5bc92011-01-12 16:03:31 -0600306 PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
307 keyboardView, passwordEntry, false);
308 keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
Jason parks8fd5bc92011-01-12 16:03:31 -0600309 }
310
311 private IMountService getMountService() {
312 IBinder service = ServiceManager.getService("mount");
313 if (service != null) {
314 return IMountService.Stub.asInterface(service);
315 }
316 return null;
317 }
318
319 @Override
320 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
321 if (actionId == EditorInfo.IME_NULL) {
322 // Get the password
323 String password = v.getText().toString();
324
Jason parksec5a45e2011-01-18 15:28:36 -0600325 if (TextUtils.isEmpty(password)) {
326 return true;
327 }
Jason parks35933812011-01-21 15:48:20 -0600328
Jason parks8fd5bc92011-01-12 16:03:31 -0600329 // Now that we have the password clear the password field.
330 v.setText(null);
331
332 IMountService service = getMountService();
333 try {
Jason parksf1dbf552011-01-24 16:19:28 -0600334 int failedAttempts = service.decryptStorage(password);
Jason parks8fd5bc92011-01-12 16:03:31 -0600335
Jason parksf1dbf552011-01-24 16:19:28 -0600336 if (failedAttempts == 0) {
337 // The password was entered successfully. Start the Blank activity
338 // so this activity animates to black before the devices starts. Note
339 // It has 1 second to complete the animation or it will be frozen
340 // until the boot animation comes back up.
341 Intent intent = new Intent(this, Blank.class);
342 finish();
343 startActivity(intent);
344 } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
Jason parksec5a45e2011-01-18 15:28:36 -0600345 // Factory reset the device.
346 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
Jason parksf1dbf552011-01-24 16:19:28 -0600347 } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
Jason parksec5a45e2011-01-18 15:28:36 -0600348 mCooldown = COOL_DOWN_INTERVAL;
349 EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
350 passwordEntry.setEnabled(false);
Jason parksf8217302011-01-26 13:11:42 -0600351 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -0600352 } else {
353 TextView tv = (TextView) findViewById(R.id.status);
354 tv.setText(R.string.try_again);
Andy Stadler13d62042011-01-31 19:21:37 -0800355 tv.setVisibility(View.VISIBLE);
Jason parksec5a45e2011-01-18 15:28:36 -0600356 }
Jason parks8fd5bc92011-01-12 16:03:31 -0600357 } catch (Exception e) {
Jason parksec5a45e2011-01-18 15:28:36 -0600358 Log.e(TAG, "Error while decrypting...", e);
Jason parks8fd5bc92011-01-12 16:03:31 -0600359 }
Jason parks35933812011-01-21 15:48:20 -0600360
Jason parks8fd5bc92011-01-12 16:03:31 -0600361 return true;
362 }
363 return false;
364 }
365}