CryptKeeper.java revision f8217304073b5cd13823a0602568394be93bf310
1/*
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;
26import android.content.Intent;
27import android.content.pm.PackageManager;
28import android.inputmethodservice.KeyboardView;
29import android.os.Bundle;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Message;
33import android.os.PowerManager;
34import android.os.ServiceManager;
35import android.os.SystemProperties;
36import android.os.storage.IMountService;
37import android.text.TextUtils;
38import android.text.format.DateFormat;
39import android.util.Log;
40import android.view.KeyEvent;
41import android.view.inputmethod.EditorInfo;
42import android.widget.EditText;
43import android.widget.ProgressBar;
44import android.widget.TextView;
45
46import java.util.Date;
47
48public class CryptKeeper extends Activity implements TextView.OnEditorActionListener {
49    private static final String TAG = "CryptKeeper";
50
51    private static final String DECRYPT_STATE = "trigger_restart_framework";
52
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
60    // This activity is used to fade the screen to black after the password is entered.
61    public static class Blank extends Activity {
62    }
63
64    private Handler mHandler = new Handler() {
65        @Override
66        public void handleMessage(Message msg) {
67            switch (msg.what) {
68            case UPDATE_PROGRESS:
69                updateProgress();
70                break;
71
72            case COOLDOWN:
73                cooldown();
74                break;
75            }
76        }
77    };
78
79    private int mCooldown;
80    PowerManager.WakeLock mWakeLock;
81
82    @Override
83    public void onCreate(Bundle savedInstanceState) {
84        super.onCreate(savedInstanceState);
85
86        String state = SystemProperties.get("vold.decrypt");
87        if ("".equals(state) || DECRYPT_STATE.equals(state)) {
88            // Disable the crypt keeper.
89            PackageManager pm = getPackageManager();
90            ComponentName name = new ComponentName(this, CryptKeeper.class);
91            pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
92            return;
93        }
94
95        // Check to see why we were started.
96        String progress = SystemProperties.get("vold.encrypt_progress");
97
98        if (!"".equals(progress)) {
99            setContentView(R.layout.crypt_keeper_progress);
100            encryptionProgressInit();
101        } else {
102            setContentView(R.layout.crypt_keeper_password_entry);
103            passwordEntryInit();
104        }
105
106        // Disable the status bar
107        StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
108        sbm.disable(StatusBarManager.DISABLE_EXPAND | StatusBarManager.DISABLE_NOTIFICATION_ICONS
109                | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
110                | StatusBarManager.DISABLE_SYSTEM_INFO | StatusBarManager.DISABLE_NAVIGATION);
111    }
112
113    @Override
114    public void onStop() {
115        super.onStop();
116
117        mHandler.removeMessages(COOLDOWN);
118        mHandler.removeMessages(UPDATE_PROGRESS);
119
120        if (mWakeLock != null) {
121            mWakeLock.release();
122            mWakeLock = null;
123        }
124    }
125
126    private void encryptionProgressInit() {
127        // Accquire a partial wakelock to prevent the device from sleeping. Note
128        // we never release this wakelock as we will be restarted after the device
129        // is encrypted.
130
131        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
132        mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
133
134        mWakeLock.acquire();
135
136        ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
137        progressBar.setIndeterminate(true);
138
139        updateProgress();
140    }
141
142    private void updateProgress() {
143        String state = SystemProperties.get("vold.encrypt_progress");
144
145        int progress = 0;
146        try {
147            progress = Integer.parseInt(state);
148        } catch (Exception e) {
149            Log.w(TAG, "Error parsing progress: " + e.toString());
150        }
151
152        CharSequence status = getText(R.string.crypt_keeper_setup_description);
153        TextView tv = (TextView) findViewById(R.id.status);
154        tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress)));
155
156        // Check the progress every 5 seconds
157        mHandler.removeMessages(UPDATE_PROGRESS);
158        mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000);
159    }
160
161    private void cooldown() {
162        TextView tv = (TextView) findViewById(R.id.status);
163        if (mCooldown <= 0) {
164            // Re-enable the password entry
165            EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
166            passwordEntry.setEnabled(true);
167
168            tv.setText(R.string.try_again);
169
170        } else {
171            CharSequence tempalte = getText(R.string.crypt_keeper_cooldown);
172            tv.setText(TextUtils.expandTemplate(tempalte, Integer.toString(mCooldown)));
173
174            mCooldown--;
175            mHandler.removeMessages(COOLDOWN);
176            mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second
177        }
178    }
179
180    private void passwordEntryInit() {
181        TextView passwordEntry = (TextView) findViewById(R.id.passwordEntry);
182        passwordEntry.setOnEditorActionListener(this);
183
184        KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
185
186        PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
187                keyboardView, passwordEntry, false);
188        keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
189
190
191        passwordEntry.setCompoundDrawablesWithIntrinsicBounds(android.R.drawable.ic_lock_idle_lock,
192                0, 0, 0);
193
194        String dateFormatString = getString(com.android.internal.R.string.full_wday_month_day_no_year);
195        TextView date = (TextView) findViewById(R.id.date);
196        date.setText(DateFormat.format(dateFormatString, new Date()));
197    }
198
199    private IMountService getMountService() {
200        IBinder service = ServiceManager.getService("mount");
201        if (service != null) {
202            return IMountService.Stub.asInterface(service);
203        }
204        return null;
205    }
206
207    @Override
208    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
209        if (actionId == EditorInfo.IME_NULL) {
210            // Get the password
211            String password = v.getText().toString();
212
213            if (TextUtils.isEmpty(password)) {
214                return true;
215            }
216
217            // Now that we have the password clear the password field.
218            v.setText(null);
219
220            IMountService service = getMountService();
221            try {
222                int failedAttempts = service.decryptStorage(password);
223
224                if (failedAttempts == 0) {
225                    // The password was entered successfully. Start the Blank activity
226                    // so this activity animates to black before the devices starts. Note
227                    // It has 1 second to complete the animation or it will be frozen
228                    // until the boot animation comes back up.
229                    Intent intent = new Intent(this, Blank.class);
230                    finish();
231                    startActivity(intent);
232                } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
233                    // Factory reset the device.
234                    sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
235                } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
236                    mCooldown = COOL_DOWN_INTERVAL;
237                    EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
238                    passwordEntry.setEnabled(false);
239                    cooldown();
240                } else {
241                    TextView tv = (TextView) findViewById(R.id.status);
242                    tv.setText(R.string.try_again);
243                }
244            } catch (Exception e) {
245                Log.e(TAG, "Error while decrypting...", e);
246            }
247
248            return true;
249        }
250        return false;
251    }
252}