blob: 3d752a32bca336f2f13a270ed2dd375238216684 [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;
28import android.inputmethodservice.KeyboardView;
29import android.os.Bundle;
Jason parksec5a45e2011-01-18 15:28:36 -060030import android.os.Handler;
Jason parks8fd5bc92011-01-12 16:03:31 -060031import android.os.IBinder;
Jason parksec5a45e2011-01-18 15:28:36 -060032import android.os.Message;
Jason parks35933812011-01-21 15:48:20 -060033import android.os.PowerManager;
Jason parks8fd5bc92011-01-12 16:03:31 -060034import android.os.ServiceManager;
35import android.os.SystemProperties;
36import android.os.storage.IMountService;
Jason parksec5a45e2011-01-18 15:28:36 -060037import android.text.TextUtils;
Jason parks8fd5bc92011-01-12 16:03:31 -060038import android.util.Log;
39import android.view.KeyEvent;
Andy Stadler13d62042011-01-31 19:21:37 -080040import android.view.View;
41import android.view.View.OnClickListener;
Jason parks8fd5bc92011-01-12 16:03:31 -060042import android.view.inputmethod.EditorInfo;
Andy Stadler13d62042011-01-31 19:21:37 -080043import android.widget.Button;
Jason parksec5a45e2011-01-18 15:28:36 -060044import android.widget.EditText;
45import android.widget.ProgressBar;
Jason parks8fd5bc92011-01-12 16:03:31 -060046import android.widget.TextView;
47
Jason parks8fd5bc92011-01-12 16:03:31 -060048public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
Jason parksec5a45e2011-01-18 15:28:36 -060049 private static final String TAG = "CryptKeeper";
Jason parks35933812011-01-21 15:48:20 -060050
Jason parks8fd5bc92011-01-12 16:03:31 -060051 private static final String DECRYPT_STATE = "trigger_restart_framework";
Jason parksec5a45e2011-01-18 15:28:36 -060052
53 private static final int UPDATE_PROGRESS = 1;
54 private static final int COOLDOWN = 2;
55
56 private static final int MAX_FAILED_ATTEMPTS = 30;
57 private static final int COOL_DOWN_ATTEMPTS = 10;
58 private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
59
Andy Stadler14997402011-02-01 15:34:59 -080060 private int mCooldown;
61 PowerManager.WakeLock mWakeLock;
62
63 /**
64 * Used to propagate state through configuration changes (e.g. screen rotation)
65 */
66 private static class NonConfigurationInstanceState {
67 final PowerManager.WakeLock wakelock;
68
69 NonConfigurationInstanceState(PowerManager.WakeLock _wakelock) {
70 wakelock = _wakelock;
71 }
72 }
73
Jason parksf1dbf552011-01-24 16:19:28 -060074 // This activity is used to fade the screen to black after the password is entered.
75 public static class Blank extends Activity {
Andy Stadler13d62042011-01-31 19:21:37 -080076 @Override
77 public void onCreate(Bundle savedInstanceState) {
78 super.onCreate(savedInstanceState);
79 setContentView(R.layout.crypt_keeper_blank);
80 }
Jason parksf1dbf552011-01-24 16:19:28 -060081 }
Jason parksec5a45e2011-01-18 15:28:36 -060082
83 private Handler mHandler = new Handler() {
84 @Override
85 public void handleMessage(Message msg) {
Jason parksec5a45e2011-01-18 15:28:36 -060086 switch (msg.what) {
Jason parksec5a45e2011-01-18 15:28:36 -060087 case UPDATE_PROGRESS:
Jason parksf8217302011-01-26 13:11:42 -060088 updateProgress();
Jason parksec5a45e2011-01-18 15:28:36 -060089 break;
Jason parks35933812011-01-21 15:48:20 -060090
Jason parksec5a45e2011-01-18 15:28:36 -060091 case COOLDOWN:
Jason parksf8217302011-01-26 13:11:42 -060092 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -060093 break;
94 }
95 }
96 };
Jason parks35933812011-01-21 15:48:20 -060097
Jason parks8fd5bc92011-01-12 16:03:31 -060098 @Override
99 public void onCreate(Bundle savedInstanceState) {
100 super.onCreate(savedInstanceState);
Jason parks35933812011-01-21 15:48:20 -0600101
Andy Stadler95974062011-02-01 17:35:20 -0800102 // If we are not encrypted or encrypting, get out quickly.
Jason parks8fd5bc92011-01-12 16:03:31 -0600103 String state = SystemProperties.get("vold.decrypt");
104 if ("".equals(state) || DECRYPT_STATE.equals(state)) {
Jason parks35933812011-01-21 15:48:20 -0600105 // Disable the crypt keeper.
Jason parks8fd5bc92011-01-12 16:03:31 -0600106 PackageManager pm = getPackageManager();
107 ComponentName name = new ComponentName(this, CryptKeeper.class);
108 pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
109 return;
110 }
Jason parks35933812011-01-21 15:48:20 -0600111
Jason parks39f1e042011-01-20 23:29:28 -0600112 // Disable the status bar
113 StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
Andy Stadler13d62042011-01-31 19:21:37 -0800114 sbm.disable(StatusBarManager.DISABLE_EXPAND
115 | StatusBarManager.DISABLE_NOTIFICATION_ICONS
Jason parks39f1e042011-01-20 23:29:28 -0600116 | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
Andy Stadler13d62042011-01-31 19:21:37 -0800117 | StatusBarManager.DISABLE_SYSTEM_INFO
118 | StatusBarManager.DISABLE_NAVIGATION
119 | StatusBarManager.DISABLE_BACK);
Andy Stadler14997402011-02-01 15:34:59 -0800120
121 // Check for (and recover) retained instance data
122 Object lastInstance = getLastNonConfigurationInstance();
123 if (lastInstance instanceof NonConfigurationInstanceState) {
124 NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance;
125 mWakeLock = retained.wakelock;
126 }
Jason parksec5a45e2011-01-18 15:28:36 -0600127 }
Jason parks35933812011-01-21 15:48:20 -0600128
Andy Stadler95974062011-02-01 17:35:20 -0800129 /**
130 * Note, we defer the state check and screen setup to onStart() because this will be
131 * re-run if the user clicks the power button (sleeping/waking the screen), and this is
132 * especially important if we were to lose the wakelock for any reason.
133 */
134 @Override
135 public void onStart() {
136 super.onStart();
137
138 // Check to see why we were started.
139 String progress = SystemProperties.get("vold.encrypt_progress");
140 if (!"".equals(progress)) {
141 setContentView(R.layout.crypt_keeper_progress);
142 encryptionProgressInit();
143 } else {
144 setContentView(R.layout.crypt_keeper_password_entry);
145 passwordEntryInit();
146 }
147 }
148
Jason parksf8217302011-01-26 13:11:42 -0600149 @Override
150 public void onStop() {
151 super.onStop();
152
153 mHandler.removeMessages(COOLDOWN);
154 mHandler.removeMessages(UPDATE_PROGRESS);
Andy Stadler14997402011-02-01 15:34:59 -0800155 }
156
157 /**
158 * Reconfiguring, so propagate the wakelock to the next instance. This runs between onStop()
159 * and onDestroy() and only if we are changing configuration (e.g. rotation). Also clears
160 * mWakeLock so the subsequent call to onDestroy does not release it.
161 */
162 @Override
163 public Object onRetainNonConfigurationInstance() {
164 NonConfigurationInstanceState state = new NonConfigurationInstanceState(mWakeLock);
165 mWakeLock = null;
166 return state;
167 }
168
169 @Override
170 public void onDestroy() {
171 super.onDestroy();
Jason parksf8217302011-01-26 13:11:42 -0600172
173 if (mWakeLock != null) {
174 mWakeLock.release();
175 mWakeLock = null;
176 }
177 }
178
Jason parksec5a45e2011-01-18 15:28:36 -0600179 private void encryptionProgressInit() {
Jason parks35933812011-01-21 15:48:20 -0600180 // Accquire a partial wakelock to prevent the device from sleeping. Note
181 // we never release this wakelock as we will be restarted after the device
182 // is encrypted.
183
184 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Jason parksf8217302011-01-26 13:11:42 -0600185 mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
Jason parks35933812011-01-21 15:48:20 -0600186
Jason parksf8217302011-01-26 13:11:42 -0600187 mWakeLock.acquire();
Jason parks35933812011-01-21 15:48:20 -0600188
Jason parksf8217302011-01-26 13:11:42 -0600189 ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
190 progressBar.setIndeterminate(true);
191
192 updateProgress();
193 }
194
Andy Stadler13d62042011-01-31 19:21:37 -0800195 private void showFactoryReset() {
196 // Hide the encryption-bot to make room for the "factory reset" button
197 findViewById(R.id.encroid).setVisibility(View.GONE);
198
199 // Show the reset button, failure text, and a divider
200 Button button = (Button) findViewById(R.id.factory_reset);
201 button.setVisibility(View.VISIBLE);
202 button.setOnClickListener(new OnClickListener() {
203 public void onClick(View v) {
204 // Factory reset the device.
205 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
206 }
207 });
208
209 TextView tv = (TextView) findViewById(R.id.title);
210 tv.setText(R.string.crypt_keeper_failed_title);
211
212 tv = (TextView) findViewById(R.id.status);
213 tv.setText(R.string.crypt_keeper_failed_summary);
214
215 View view = findViewById(R.id.bottom_divider);
216 view.setVisibility(View.VISIBLE);
217 }
218
Jason parksf8217302011-01-26 13:11:42 -0600219 private void updateProgress() {
220 String state = SystemProperties.get("vold.encrypt_progress");
221
Andy Stadler13d62042011-01-31 19:21:37 -0800222 if ("error_partially_encrypted".equals(state)) {
223 showFactoryReset();
224 return;
225 }
226
Jason parksf8217302011-01-26 13:11:42 -0600227 int progress = 0;
228 try {
229 progress = Integer.parseInt(state);
230 } catch (Exception e) {
231 Log.w(TAG, "Error parsing progress: " + e.toString());
232 }
233
234 CharSequence status = getText(R.string.crypt_keeper_setup_description);
235 TextView tv = (TextView) findViewById(R.id.status);
236 tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
237
238 // Check the progress every 5 seconds
239 mHandler.removeMessages(UPDATE_PROGRESS);
240 mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
241 }
242
243 private void cooldown() {
244 TextView tv = (TextView) findViewById(R.id.status);
Andy Stadler13d62042011-01-31 19:21:37 -0800245
Jason parksf8217302011-01-26 13:11:42 -0600246 if (mCooldown <= 0) {
247 // Re-enable the password entry
248 EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
249 passwordEntry.setEnabled(true);
250
Andy Stadler13d62042011-01-31 19:21:37 -0800251 tv.setVisibility(View.GONE);
Jason parksf8217302011-01-26 13:11:42 -0600252 } else {
Andy Stadler13d62042011-01-31 19:21:37 -0800253 CharSequence template = getText(R.string.crypt_keeper_cooldown);
254 tv.setText(TextUtils.expandTemplate(template, Integer.toString(mCooldown)));
255
256 tv.setVisibility(View.VISIBLE);
Jason parksf8217302011-01-26 13:11:42 -0600257
258 mCooldown--;
259 mHandler.removeMessages(COOLDOWN);
260 mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
261 }
Jason parksec5a45e2011-01-18 15:28:36 -0600262 }
Jason parks35933812011-01-21 15:48:20 -0600263
Jason parksec5a45e2011-01-18 15:28:36 -0600264 private void passwordEntryInit() {
Jason parks8fd5bc92011-01-12 16:03:31 -0600265 TextView passwordEntry = (TextView) findViewById(R.id.passwordEntry);
266 passwordEntry.setOnEditorActionListener(this);
Jason parks35933812011-01-21 15:48:20 -0600267
Jason parks8fd5bc92011-01-12 16:03:31 -0600268 KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
Jason parks35933812011-01-21 15:48:20 -0600269
Jason parks8fd5bc92011-01-12 16:03:31 -0600270 PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
271 keyboardView, passwordEntry, false);
272 keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
Jason parks8fd5bc92011-01-12 16:03:31 -0600273 }
274
275 private IMountService getMountService() {
276 IBinder service = ServiceManager.getService("mount");
277 if (service != null) {
278 return IMountService.Stub.asInterface(service);
279 }
280 return null;
281 }
282
283 @Override
284 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
285 if (actionId == EditorInfo.IME_NULL) {
286 // Get the password
287 String password = v.getText().toString();
288
Jason parksec5a45e2011-01-18 15:28:36 -0600289 if (TextUtils.isEmpty(password)) {
290 return true;
291 }
Jason parks35933812011-01-21 15:48:20 -0600292
Jason parks8fd5bc92011-01-12 16:03:31 -0600293 // Now that we have the password clear the password field.
294 v.setText(null);
295
296 IMountService service = getMountService();
297 try {
Jason parksf1dbf552011-01-24 16:19:28 -0600298 int failedAttempts = service.decryptStorage(password);
Jason parks8fd5bc92011-01-12 16:03:31 -0600299
Jason parksf1dbf552011-01-24 16:19:28 -0600300 if (failedAttempts == 0) {
301 // The password was entered successfully. Start the Blank activity
302 // so this activity animates to black before the devices starts. Note
303 // It has 1 second to complete the animation or it will be frozen
304 // until the boot animation comes back up.
305 Intent intent = new Intent(this, Blank.class);
306 finish();
307 startActivity(intent);
308 } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
Jason parksec5a45e2011-01-18 15:28:36 -0600309 // Factory reset the device.
310 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
Jason parksf1dbf552011-01-24 16:19:28 -0600311 } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
Jason parksec5a45e2011-01-18 15:28:36 -0600312 mCooldown = COOL_DOWN_INTERVAL;
313 EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
314 passwordEntry.setEnabled(false);
Jason parksf8217302011-01-26 13:11:42 -0600315 cooldown();
Jason parksec5a45e2011-01-18 15:28:36 -0600316 } else {
317 TextView tv = (TextView) findViewById(R.id.status);
318 tv.setText(R.string.try_again);
Andy Stadler13d62042011-01-31 19:21:37 -0800319 tv.setVisibility(View.VISIBLE);
Jason parksec5a45e2011-01-18 15:28:36 -0600320 }
Jason parks8fd5bc92011-01-12 16:03:31 -0600321 } catch (Exception e) {
Jason parksec5a45e2011-01-18 15:28:36 -0600322 Log.e(TAG, "Error while decrypting...", e);
Jason parks8fd5bc92011-01-12 16:03:31 -0600323 }
Jason parks35933812011-01-21 15:48:20 -0600324
Jason parks8fd5bc92011-01-12 16:03:31 -0600325 return true;
326 }
327 return false;
328 }
329}