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