diff options
author | Vikram Aggarwal <viki@google.com> | 2012-05-01 15:37:30 -0700 |
---|---|---|
committer | Vikram Aggarwal <viki@google.com> | 2012-05-01 16:05:44 -0700 |
commit | de3c9cb412688eb841993ad971d06039d8a952c3 (patch) | |
tree | 4a1d780020f6597f30c9f9be9e9a0892e5516e6e | |
parent | 7706a2a1432a67720e3f7906647c3c0d2afbfb8b (diff) | |
download | packages_apps_settings-de3c9cb412688eb841993ad971d06039d8a952c3.zip packages_apps_settings-de3c9cb412688eb841993ad971d06039d8a952c3.tar.gz packages_apps_settings-de3c9cb412688eb841993ad971d06039d8a952c3.tar.bz2 |
Notify user when waiting for decryption password
1. Disable back presses from physical keyboard during encryption: Fix b/6139810
2. Keep screen on when waiting for password. Fix b/6153213 and b/6149606
3. Alert the user with sound when waiting for password. Fix b/6149606
4. Add debugging feature to display the password screen without having to reboot the device.
Change-Id: I588aa7d96e1140f95a6fa91e0281117907f666f7
-rw-r--r-- | src/com/android/settings/CryptKeeper.java | 129 |
1 files changed, 102 insertions, 27 deletions
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java index 3f35fa8..75dd565 100644 --- a/src/com/android/settings/CryptKeeper.java +++ b/src/com/android/settings/CryptKeeper.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.media.AudioManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; @@ -68,10 +69,14 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList private static final String TAG = "CryptKeeper"; private static final String DECRYPT_STATE = "trigger_restart_framework"; - - private static final int UPDATE_PROGRESS = 1; - private static final int COOLDOWN = 2; - + /** Message sent to us to indicate encryption update progress. */ + private static final int MESSAGE_UPDATE_PROGRESS = 1; + /** Message sent to us to cool-down (waste user's time between password attempts) */ + private static final int MESSAGE_COOLDOWN = 2; + /** Message sent to us to indicate alerting the user that we are waiting for password entry */ + private static final int MESSAGE_NOTIFY = 3; + + // Constants used to control policy. private static final int MAX_FAILED_ATTEMPTS = 30; private static final int COOL_DOWN_ATTEMPTS = 10; private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds @@ -84,12 +89,15 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW"; private static final String FORCE_VIEW_PROGRESS = "progress"; private static final String FORCE_VIEW_ERROR = "error"; + private static final String FORCE_VIEW_PASSWORD = "password"; /** When encryption is detected, this flag indicates whether or not we've checked for errors. */ private boolean mValidationComplete; private boolean mValidationRequested; /** A flag to indicate that the volume is in a bad state (e.g. partially encrypted). */ private boolean mEncryptionGoneBad; + /** A flag to indicate when the back event should be ignored */ + private boolean mIgnoreBack = false; private int mCooldown; PowerManager.WakeLock mWakeLock; @@ -115,12 +123,17 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList super.onCreate(savedInstanceState); setContentView(R.layout.crypt_keeper_blank); } + /** Ignore all back events. */ + @Override + public void onBackPressed() { + return; + } } private class DecryptTask extends AsyncTask<String, Void, Integer> { @Override protected Integer doInBackground(String... params) { - IMountService service = getMountService(); + final IMountService service = getMountService(); try { return service.decryptStorage(params[0]); } catch (Exception e) { @@ -159,7 +172,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList private class ValidationTask extends AsyncTask<Void, Void, Boolean> { @Override protected Boolean doInBackground(Void... params) { - IMountService service = getMountService(); + final IMountService service = getMountService(); try { Log.d(TAG, "Validating encryption state."); int state = service.getEncryptionState(); @@ -191,17 +204,23 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList @Override public void handleMessage(Message msg) { switch (msg.what) { - case UPDATE_PROGRESS: + case MESSAGE_UPDATE_PROGRESS: updateProgress(); break; - case COOLDOWN: + case MESSAGE_COOLDOWN: cooldown(); break; + + case MESSAGE_NOTIFY: + notifyUser(); + break; } } }; + private AudioManager mAudioManager; + /** @return whether or not this Activity was started for debugging the UI only. */ private boolean isDebugView() { return getIntent().hasExtra(EXTRA_FORCE_VIEW); @@ -212,12 +231,45 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList return viewType.equals(getIntent().getStringExtra(EXTRA_FORCE_VIEW)); } + /** + * Notify the user that we are awaiting input. Currently this sends an audio alert. + */ + private void notifyUser() { + Log.d(TAG, "Notifying user that we are waiting for input..."); + if (mAudioManager != null) { + try { + // Play the standard keypress sound at full volume. This should be available on + // every device. We cannot play a ringtone here because media services aren't + // available yet. A DTMF-style tone is too soft to be noticed, and might not exist + // on tablet devices. The idea is to alert the user that something is needed: this + // does not have to be pleasing. + mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, 100); + } catch (Exception e) { + Log.w(TAG, "notifyUser: Exception while playing sound: " + e); + } + } + // Notify the user again in 30 seconds. + mHandler.removeMessages(MESSAGE_NOTIFY); + mHandler.sendEmptyMessageDelayed(MESSAGE_NOTIFY, 30 * 1000); + } + + /** + * Ignore back events after the user has entered the decrypt screen and while the device is + * encrypting. + */ + @Override + public void onBackPressed() { + if (mIgnoreBack) + return; + super.onBackPressed(); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // If we are not encrypted or encrypting, get out quickly. - String state = SystemProperties.get("vold.decrypt"); + final String state = SystemProperties.get("vold.decrypt"); if (!isDebugView() && ("".equals(state) || DECRYPT_STATE.equals(state))) { // Disable the crypt keeper. PackageManager pm = getPackageManager(); @@ -245,8 +297,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList | StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT); + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); // Check for (and recover) retained instance data - Object lastInstance = getLastNonConfigurationInstance(); + final Object lastInstance = getLastNonConfigurationInstance(); if (lastInstance instanceof NonConfigurationInstanceState) { NonConfigurationInstanceState retained = (NonConfigurationInstanceState) lastInstance; mWakeLock = retained.wakelock; @@ -262,7 +315,6 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList @Override public void onStart() { super.onStart(); - setupUi(); } @@ -277,11 +329,11 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList return; } - String progress = SystemProperties.get("vold.encrypt_progress"); + final String progress = SystemProperties.get("vold.encrypt_progress"); if (!"".equals(progress) || isDebugView(FORCE_VIEW_PROGRESS)) { setContentView(R.layout.crypt_keeper_progress); encryptionProgressInit(); - } else if (mValidationComplete) { + } else if (mValidationComplete || isDebugView(FORCE_VIEW_PASSWORD)) { setContentView(R.layout.crypt_keeper_password_entry); passwordEntryInit(); } else if (!mValidationRequested) { @@ -294,8 +346,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList @Override public void onStop() { super.onStop(); - mHandler.removeMessages(COOLDOWN); - mHandler.removeMessages(UPDATE_PROGRESS); + mHandler.removeMessages(MESSAGE_COOLDOWN); + mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS); + mHandler.removeMessages(MESSAGE_NOTIFY); } /** @@ -322,11 +375,13 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } } + /** + * Start encrypting the device. + */ private void encryptionProgressInit() { // Accquire a partial wakelock to prevent the device from sleeping. Note // we never release this wakelock as we will be restarted after the device // is encrypted. - Log.d(TAG, "Encryption progress screen initializing."); if (mWakeLock == null) { Log.d(TAG, "Acquiring wakelock."); @@ -336,6 +391,10 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList } ((ProgressBar) findViewById(R.id.progress_bar)).setIndeterminate(true); + // Ignore all back presses from now, both hard and soft keys. + mIgnoreBack = true; + // Start the first run of progress manually. This method sets up messages to occur at + // repeated intervals. updateProgress(); } @@ -384,20 +443,22 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList final CharSequence status = getText(R.string.crypt_keeper_setup_description); Log.v(TAG, "Encryption progress: " + progress); final TextView tv = (TextView) findViewById(R.id.status); - tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress))); - + if (tv != null) { + tv.setText(TextUtils.expandTemplate(status, Integer.toString(progress))); + } // Check the progress every 5 seconds - mHandler.removeMessages(UPDATE_PROGRESS); - mHandler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 5000); + mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS); + mHandler.sendEmptyMessageDelayed(MESSAGE_UPDATE_PROGRESS, 5000); } + /** Disable password input for a while to force the user to waste time between retries */ private void cooldown() { final TextView status = (TextView) findViewById(R.id.status); if (mCooldown <= 0) { - // Re-enable the password entry + // Re-enable the password entry and back presses. mPasswordEntry.setEnabled(true); - + mIgnoreBack = false; status.setVisibility(View.GONE); } else { CharSequence template = getText(R.string.crypt_keeper_cooldown); @@ -406,8 +467,8 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList status.setVisibility(View.VISIBLE); mCooldown--; - mHandler.removeMessages(COOLDOWN); - mHandler.sendEmptyMessageDelayed(COOLDOWN, 1000); // Tick every second + mHandler.removeMessages(MESSAGE_COOLDOWN); + mHandler.sendEmptyMessageDelayed(MESSAGE_COOLDOWN, 1000); // Tick every second } } @@ -429,6 +490,17 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList }); } + // We want to keep the screen on while waiting for input. In minimal boot mode, the device + // is completely non-functional, and we want the user to notice the device and enter a + // password. + if (mWakeLock == null) { + Log.d(TAG, "Acquiring wakelock."); + final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (pm != null) { + mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); + mWakeLock.acquire(); + } + } // Asynchronously throw up the IME, since there are issues with requesting it to be shown // immediately. mHandler.postDelayed(new Runnable() { @@ -438,6 +510,9 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList }, 0); updateEmergencyCallButtonState(); + // Notify the user that we are waiting for him to enter the password to get the device + // out of this completely dead state. + notifyUser(); } /** @@ -509,10 +584,10 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList // Now that we have the password clear the password field. v.setText(null); - // Disable the password entry while checking the password. This - // we either be re-enabled if the password was wrong or after the - // cooldown period. + // Disable the password entry and back keypress while checking the password. These + // we either be re-enabled if the password was wrong or after the cooldown period. mPasswordEntry.setEnabled(false); + mIgnoreBack = true; Log.d(TAG, "Attempting to send command to decrypt"); new DecryptTask().execute(password); |