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