Convert encryption status to preference controller

- Move Crypt/Encryption setting into security package.
- Reformat crypt_keeper_settings.xml
- Add controller and test for encryption status

Bug: 32953042
Test: robotests
Change-Id: I1f4b2f97133435c70a49522a59886ac4da6759af
This commit is contained in:
Fan Zhang
2017-11-01 17:59:53 -07:00
parent b1047d1753
commit c58a88368f
15 changed files with 268 additions and 228 deletions

View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.security;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.CryptKeeperConfirm;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPattern;
public class CryptKeeperSettings extends InstrumentedPreferenceFragment {
private static final String TAG = "CryptKeeper";
private static final String TYPE = "type";
private static final String PASSWORD = "password";
private static final int KEYGUARD_REQUEST = 55;
// Minimum battery charge level (in percent) to launch encryption. If the battery charge is
// lower than this, encryption should not be activated.
private static final int MIN_BATTERY_LEVEL = 80;
private View mContentView;
private Button mInitiateButton;
private View mPowerWarning;
private View mBatteryWarning;
private IntentFilter mIntentFilter;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
final int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
final int invalidCharger = intent.getIntExtra(
BatteryManager.EXTRA_INVALID_CHARGER, 0);
final boolean levelOk = level >= MIN_BATTERY_LEVEL;
final boolean pluggedOk =
((plugged & BatteryManager.BATTERY_PLUGGED_ANY) != 0) &&
invalidCharger == 0;
// Update UI elements based on power/battery status
mInitiateButton.setEnabled(levelOk && pluggedOk);
mPowerWarning.setVisibility(pluggedOk ? View.GONE : View.VISIBLE );
mBatteryWarning.setVisibility(levelOk ? View.GONE : View.VISIBLE);
}
}
};
/**
* If the user clicks to begin the reset sequence, we next require a
* keyguard confirmation if the user has currently enabled one. If there
* is no keyguard available, we prompt the user to set a password.
*/
private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
@Override
public void onClick(View v) {
if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
// TODO replace (or follow) this dialog with an explicit launch into password UI
new AlertDialog.Builder(getActivity())
.setTitle(R.string.crypt_keeper_dialog_need_password_title)
.setMessage(R.string.crypt_keeper_dialog_need_password_message)
.setPositiveButton(android.R.string.ok, null)
.create()
.show();
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
mContentView = inflater.inflate(R.layout.crypt_keeper_settings, null);
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_encrypt);
mInitiateButton.setOnClickListener(mInitiateListener);
mInitiateButton.setEnabled(false);
mPowerWarning = mContentView.findViewById(R.id.warning_unplugged);
mBatteryWarning = mContentView.findViewById(R.id.warning_low_charge);
return mContentView;
}
@Override
public int getMetricsCategory() {
return MetricsEvent.CRYPT_KEEPER;
}
@Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(mIntentReceiver, mIntentFilter);
}
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mIntentReceiver);
}
/**
* If encryption is already started, and this launched via a "start encryption" intent,
* then exit immediately - it's already up and running, so there's no point in "starting" it.
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Activity activity = getActivity();
Intent intent = activity.getIntent();
if (DevicePolicyManager.ACTION_START_ENCRYPTION.equals(intent.getAction())) {
DevicePolicyManager dpm = (DevicePolicyManager)
activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (dpm != null) {
int status = dpm.getStorageEncryptionStatus();
if (status != DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE) {
// There is nothing to do here, so simply finish() (which returns to caller)
activity.finish();
}
}
}
}
/**
* Keyguard validation is run using the standard {@link ConfirmLockPattern}
* component as a subactivity
* @param request the request code to be returned once confirmation finishes
* @return true if confirmation launched
*/
private boolean runKeyguardConfirmation(int request) {
Resources res = getActivity().getResources();
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);
if (helper.utils().getKeyguardStoredPasswordQuality(UserHandle.myUserId())
== DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
showFinalConfirmation(StorageManager.CRYPT_TYPE_DEFAULT, "");
return true;
}
return helper.launchConfirmationActivity(request,
res.getText(R.string.crypt_keeper_encrypt_title), true);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != KEYGUARD_REQUEST) {
return;
}
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK && data != null) {
int type = data.getIntExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE, -1);
String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
if (!TextUtils.isEmpty(password)) {
showFinalConfirmation(type, password);
}
}
}
private void showFinalConfirmation(int type, String password) {
Preference preference = new Preference(getPreferenceManager().getContext());
preference.setFragment(CryptKeeperConfirm.class.getName());
preference.setTitle(R.string.crypt_keeper_confirm_title);
addEncryptionInfoToPreference(preference, type, password);
((SettingsActivity) getActivity()).onPreferenceStartFragment(null, preference);
}
private void addEncryptionInfoToPreference(Preference preference, int type, String password) {
Activity activity = getActivity();
DevicePolicyManager dpm = (DevicePolicyManager)
activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (dpm.getDoNotAskCredentialsOnBoot()) {
preference.getExtras().putInt(TYPE, StorageManager.CRYPT_TYPE_DEFAULT);
preference.getExtras().putString(PASSWORD, "");
} else {
preference.getExtras().putInt(TYPE, type);
preference.getExtras().putString(PASSWORD, password);
}
}
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.security;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
@@ -27,15 +26,16 @@ import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -55,13 +55,9 @@ public class EncryptionAndCredential extends DashboardFragment {
private static final int MY_USER_ID = UserHandle.myUserId();
private UserManager mUm;
private KeyStore mKeyStore;
private RestrictedPreference mResetCredentials;
private boolean mIsAdmin;
@Override
public int getMetricsCategory() {
return MetricsEvent.ENCRYPTION_AND_CREDENTIAL;
@@ -74,13 +70,23 @@ public class EncryptionAndCredential extends DashboardFragment {
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
return null;
return buildPreferenceControllers(context);
}
@Override
protected int getPreferenceScreenResId() {
return 0;
return R.xml.encryption_and_credential;
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final EncryptionStatusPreferenceController encryptStatusController =
new EncryptionStatusPreferenceController(context);
controllers.add(encryptStatusController);
controllers.add(new PreferenceCategoryController(context,
"encryption_and_credentials_status_category",
Arrays.asList(encryptStatusController)));
return controllers;
}
/**
@@ -90,26 +96,7 @@ public class EncryptionAndCredential extends DashboardFragment {
* logic or adding/removing preferences here.
*/
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceScreen();
if (root != null) {
root.removeAll();
}
addPreferencesFromResource(R.xml.encryption_and_credential);
root = getPreferenceScreen();
// Add options for device encryption
mIsAdmin = mUm.isAdminUser();
if (mIsAdmin) {
if (LockPatternUtils.isDeviceEncryptionEnabled()) {
// The device is currently encrypted.
addPreferencesFromResource(R.xml.security_settings_encrypted);
} else {
// This device supports encryption but isn't encrypted.
addPreferencesFromResource(R.xml.security_settings_unencrypted);
}
}
final PreferenceScreen root = getPreferenceScreen();
// Credential storage
mKeyStore = KeyStore.getInstance(); // needs to be initialized for onResume()
@@ -185,14 +172,14 @@ public class EncryptionAndCredential extends DashboardFragment {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final List<SearchIndexableResource> index = new ArrayList<>();
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.encryption_and_credential;
return Arrays.asList(sir);
}
// Add everything. We will suppress some of them in getNonIndexableKeys()
index.add(getSearchResource(context, R.xml.encryption_and_credential));
index.add(getSearchResource(context, R.xml.security_settings_encrypted));
index.add(getSearchResource(context, R.xml.security_settings_unencrypted));
return index;
@Override
public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
return buildPreferenceControllers(context);
}
@Override
@@ -201,12 +188,6 @@ public class EncryptionAndCredential extends DashboardFragment {
return um.isAdminUser();
}
private SearchIndexableResource getSearchResource(Context context, int xmlResId) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = xmlResId;
return sir;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
@@ -223,21 +204,6 @@ public class EncryptionAndCredential extends DashboardFragment {
keys.add(KEY_USER_CREDENTIALS);
}
final DevicePolicyManager dpm = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
switch (dpm.getStorageEncryptionStatus()) {
case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
// The device is currently encrypted. Disable security_settings_unencrypted
keys.addAll(getNonIndexableKeysFromXml(
context, R.xml.security_settings_unencrypted));
break;
default:
// This device supports encryption but isn't encrypted.
keys.addAll(getNonIndexableKeysFromXml(
context, R.xml.security_settings_encrypted));
break;
}
return keys;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.security;
import android.content.Context;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
public class EncryptionStatusPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
private static final String PREF_KEY = "encryption_and_credentials_encryption_status";
private final UserManager mUserManager;
public EncryptionStatusPreferenceController(Context context) {
super(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
@Override
public boolean isAvailable() {
return mUserManager.isAdminUser();
}
@Override
public String getPreferenceKey() {
return PREF_KEY;
}
@Override
public void updateState(Preference preference) {
final boolean encryptionEnabled = LockPatternUtils.isDeviceEncryptionEnabled();
if (encryptionEnabled) {
preference.setFragment(null);
preference.setSummary(R.string.crypt_keeper_encrypted_summary);
} else {
preference.setFragment(CryptKeeperSettings.class.getName());
preference.setSummary(R.string.summary_placeholder);
}
}
}