1 /* 2 * Copyright (C) 2008 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 17 package com.android.settings; 18 19 import com.android.internal.widget.LockPatternUtils; 20 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.app.Fragment; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.res.Resources; 30 import android.os.BatteryManager; 31 import android.os.Bundle; 32 import android.preference.Preference; 33 import android.preference.PreferenceActivity; 34 import android.text.TextUtils; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.widget.Button; 39 40 public class CryptKeeperSettings extends Fragment { 41 private static final String TAG = "CryptKeeper"; 42 43 private static final int KEYGUARD_REQUEST = 55; 44 45 // This is the minimum acceptable password quality. If the current password quality is 46 // lower than this, encryption should not be activated. 47 static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 48 49 // Minimum battery charge level (in percent) to launch encryption. If the battery charge is 50 // lower than this, encryption should not be activated. 51 private static final int MIN_BATTERY_LEVEL = 80; 52 53 private View mContentView; 54 private Button mInitiateButton; 55 private View mPowerWarning; 56 private View mBatteryWarning; 57 private IntentFilter mIntentFilter; 58 59 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 60 @Override 61 public void onReceive(Context context, Intent intent) { 62 String action = intent.getAction(); 63 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 64 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 65 int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); 66 int invalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0); 67 68 boolean levelOk = level >= MIN_BATTERY_LEVEL; 69 boolean pluggedOk = 70 (plugged == BatteryManager.BATTERY_PLUGGED_AC || 71 plugged == BatteryManager.BATTERY_PLUGGED_USB) && 72 invalidCharger == 0; 73 74 // Update UI elements based on power/battery status 75 mInitiateButton.setEnabled(levelOk && pluggedOk); 76 mPowerWarning.setVisibility(pluggedOk ? View.GONE : View.VISIBLE ); 77 mBatteryWarning.setVisibility(levelOk ? View.GONE : View.VISIBLE); 78 } 79 } 80 }; 81 82 /** 83 * If the user clicks to begin the reset sequence, we next require a 84 * keyguard confirmation if the user has currently enabled one. If there 85 * is no keyguard available, we prompt the user to set a password. 86 */ 87 private Button.OnClickListener mInitiateListener = new Button.OnClickListener() { 88 89 public void onClick(View v) { 90 if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) { 91 // TODO replace (or follow) this dialog with an explicit launch into password UI 92 new AlertDialog.Builder(getActivity()) 93 .setTitle(R.string.crypt_keeper_dialog_need_password_title) 94 .setIcon(android.R.drawable.ic_dialog_alert) 95 .setMessage(R.string.crypt_keeper_dialog_need_password_message) 96 .setPositiveButton(android.R.string.ok, null) 97 .create() 98 .show(); 99 } 100 } 101 }; 102 103 @Override 104 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { 105 mContentView = inflater.inflate(R.layout.crypt_keeper_settings, null); 106 107 mIntentFilter = new IntentFilter(); 108 mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 109 110 mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_encrypt); 111 mInitiateButton.setOnClickListener(mInitiateListener); 112 mInitiateButton.setEnabled(false); 113 114 mPowerWarning = mContentView.findViewById(R.id.warning_unplugged); 115 mBatteryWarning = mContentView.findViewById(R.id.warning_low_charge); 116 117 return mContentView; 118 } 119 120 @Override 121 public void onResume() { 122 super.onResume(); 123 getActivity().registerReceiver(mIntentReceiver, mIntentFilter); 124 } 125 126 @Override 127 public void onPause() { 128 super.onPause(); 129 getActivity().unregisterReceiver(mIntentReceiver); 130 } 131 132 /** 133 * If encryption is already started, and this launched via a "start encryption" intent, 134 * then exit immediately - it's already up and running, so there's no point in "starting" it. 135 */ 136 @Override 137 public void onActivityCreated(Bundle savedInstanceState) { 138 super.onActivityCreated(savedInstanceState); 139 Activity activity = getActivity(); 140 Intent intent = activity.getIntent(); 141 if (DevicePolicyManager.ACTION_START_ENCRYPTION.equals(intent.getAction())) { 142 DevicePolicyManager dpm = (DevicePolicyManager) 143 activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 144 if (dpm != null) { 145 int status = dpm.getStorageEncryptionStatus(); 146 if (status != DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE) { 147 // There is nothing to do here, so simply finish() (which returns to caller) 148 activity.finish(); 149 } 150 } 151 } 152 } 153 154 /** 155 * Keyguard validation is run using the standard {@link ConfirmLockPattern} 156 * component as a subactivity 157 * @param request the request code to be returned once confirmation finishes 158 * @return true if confirmation launched 159 */ 160 private boolean runKeyguardConfirmation(int request) { 161 // 1. Confirm that we have a sufficient PIN/Password to continue 162 LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity()); 163 int quality = lockPatternUtils.getActivePasswordQuality(); 164 if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK 165 && lockPatternUtils.isLockPasswordEnabled()) { 166 // Use the alternate as the quality. We expect this to be 167 // PASSWORD_QUALITY_SOMETHING(pattern) or PASSWORD_QUALITY_NUMERIC(PIN). 168 quality = lockPatternUtils.getKeyguardStoredPasswordQuality(); 169 } 170 if (quality < MIN_PASSWORD_QUALITY) { 171 return false; 172 } 173 // 2. Ask the user to confirm the current PIN/Password 174 Resources res = getActivity().getResources(); 175 return new ChooseLockSettingsHelper(getActivity(), this) 176 .launchConfirmationActivity(request, 177 res.getText(R.string.master_clear_gesture_prompt), 178 res.getText(R.string.master_clear_gesture_explanation)); 179 } 180 181 @Override 182 public void onActivityResult(int requestCode, int resultCode, Intent data) { 183 super.onActivityResult(requestCode, resultCode, data); 184 185 if (requestCode != KEYGUARD_REQUEST) { 186 return; 187 } 188 189 // If the user entered a valid keyguard trace, present the final 190 // confirmation prompt; otherwise, go back to the initial state. 191 if (resultCode == Activity.RESULT_OK && data != null) { 192 String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 193 if (!TextUtils.isEmpty(password)) { 194 showFinalConfirmation(password); 195 } 196 } 197 } 198 199 private void showFinalConfirmation(String password) { 200 Preference preference = new Preference(getActivity()); 201 preference.setFragment(CryptKeeperConfirm.class.getName()); 202 preference.setTitle(R.string.crypt_keeper_confirm_title); 203 preference.getExtras().putString("password", password); 204 ((PreferenceActivity) getActivity()).onPreferenceStartFragment(null, preference); 205 } 206 } 207 208