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
17package com.android.settings;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.admin.DevicePolicyManager;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.res.Resources;
27import android.os.BatteryManager;
28import android.os.Bundle;
29import android.os.UserHandle;
30import android.os.storage.StorageManager;
31import android.support.v7.preference.Preference;
32import android.text.TextUtils;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.view.ViewGroup;
36import android.widget.Button;
37
38import com.android.internal.logging.MetricsProto.MetricsEvent;
39
40public 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
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
118    protected int getMetricsCategory() {
119        return MetricsEvent.CRYPT_KEEPER;
120    }
121
122    @Override
123    public void onResume() {
124        super.onResume();
125        getActivity().registerReceiver(mIntentReceiver, mIntentFilter);
126    }
127
128    @Override
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
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     */
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
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
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
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