1 /*
2  * Copyright (C) 2010 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 
20 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
21 
22 import android.app.ActionBar;
23 import android.app.Activity;
24 import android.app.ProgressDialog;
25 import android.app.admin.DevicePolicyManager;
26 import android.app.admin.FactoryResetProtectionPolicy;
27 import android.app.settings.SettingsEnums;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.ActivityInfo;
31 import android.graphics.Color;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.service.oemlock.OemLockManager;
37 import android.service.persistentdata.PersistentDataBlockManager;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.widget.Button;
43 import android.widget.TextView;
44 
45 import androidx.annotation.VisibleForTesting;
46 
47 import com.android.settings.core.InstrumentedFragment;
48 import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
49 import com.android.settingslib.RestrictedLockUtilsInternal;
50 
51 import com.google.android.setupcompat.template.FooterBarMixin;
52 import com.google.android.setupcompat.template.FooterButton;
53 import com.google.android.setupcompat.template.FooterButton.ButtonType;
54 import com.google.android.setupcompat.util.WizardManagerHelper;
55 import com.google.android.setupdesign.GlifLayout;
56 
57 /**
58  * Confirm and execute a reset of the device to a clean "just out of the box"
59  * state.  Multiple confirmations are required: first, a general "are you sure
60  * you want to do this?" prompt, followed by a keyguard pattern trace if the user
61  * has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
62  * ON THE PHONE" prompt.  If at any time the phone is allowed to go to sleep, is
63  * locked, et cetera, then the confirmation sequence is abandoned.
64  *
65  * This is the confirmation screen.
66  */
67 public class MasterClearConfirm extends InstrumentedFragment {
68     private final static String TAG = "MasterClearConfirm";
69 
70     @VisibleForTesting View mContentView;
71     private boolean mEraseSdCard;
72     @VisibleForTesting boolean mEraseEsims;
73 
74     /**
75      * The user has gone through the multiple confirmation, so now we go ahead
76      * and invoke the Checkin Service to reset the device to its factory-default
77      * state (rebooting in the process).
78      */
79     private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
80 
81         public void onClick(View v) {
82             if (Utils.isMonkeyRunning()) {
83                 return;
84             }
85 
86             final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
87                     getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
88 
89             if (shouldWipePersistentDataBlock(pdbManager)) {
90 
91                 new AsyncTask<Void, Void, Void>() {
92                     int mOldOrientation;
93                     ProgressDialog mProgressDialog;
94 
95                     @Override
96                     protected Void doInBackground(Void... params) {
97                         pdbManager.wipe();
98                         return null;
99                     }
100 
101                     @Override
102                     protected void onPostExecute(Void aVoid) {
103                         mProgressDialog.hide();
104                         if (getActivity() != null) {
105                             getActivity().setRequestedOrientation(mOldOrientation);
106                             doMasterClear();
107                         }
108                     }
109 
110                     @Override
111                     protected void onPreExecute() {
112                         mProgressDialog = getProgressDialog();
113                         mProgressDialog.show();
114 
115                         // need to prevent orientation changes as we're about to go into
116                         // a long IO request, so we won't be able to access inflate resources on
117                         // flash
118                         mOldOrientation = getActivity().getRequestedOrientation();
119                         getActivity().setRequestedOrientation(
120                                 ActivityInfo.SCREEN_ORIENTATION_LOCKED);
121                     }
122                 }.execute();
123             } else {
124                 doMasterClear();
125             }
126         }
127 
128         private ProgressDialog getProgressDialog() {
129             final ProgressDialog progressDialog = new ProgressDialog(getActivity());
130             progressDialog.setIndeterminate(true);
131             progressDialog.setCancelable(false);
132             progressDialog.setTitle(
133                     getActivity().getString(R.string.master_clear_progress_title));
134             progressDialog.setMessage(
135                     getActivity().getString(R.string.master_clear_progress_text));
136             return progressDialog;
137         }
138     };
139 
140     @VisibleForTesting
shouldWipePersistentDataBlock(PersistentDataBlockManager pdbManager)141     boolean shouldWipePersistentDataBlock(PersistentDataBlockManager pdbManager) {
142         if (pdbManager == null) {
143             return false;
144         }
145         // The persistent data block will persist if the device is still being provisioned.
146         if (isDeviceStillBeingProvisioned()) {
147             return false;
148         }
149         // If OEM unlock is allowed, the persistent data block will be wiped during FR
150         // process. If disabled, it will be wiped here instead.
151         if (isOemUnlockedAllowed()) {
152             return false;
153         }
154         final DevicePolicyManager dpm = (DevicePolicyManager) getActivity()
155                 .getSystemService(Context.DEVICE_POLICY_SERVICE);
156         // Do not erase the factory reset protection data (from Settings) if factory reset
157         // protection policy is not supported on the device.
158         if (!dpm.isFactoryResetProtectionPolicySupported()) {
159             return false;
160         }
161         // Do not erase the factory reset protection data (from Settings) if the
162         // device is an organization-owned managed profile device and a factory
163         // reset protection policy has been set.
164         FactoryResetProtectionPolicy frpPolicy = dpm.getFactoryResetProtectionPolicy(null);
165         if (dpm.isOrganizationOwnedDeviceWithManagedProfile() && frpPolicy != null
166                 && frpPolicy.isNotEmpty()) {
167             return false;
168         }
169         return true;
170     }
171 
172     @VisibleForTesting
isOemUnlockedAllowed()173     boolean isOemUnlockedAllowed() {
174         return ((OemLockManager) getActivity().getSystemService(
175                 Context.OEM_LOCK_SERVICE)).isOemUnlockAllowed();
176     }
177 
178     @VisibleForTesting
isDeviceStillBeingProvisioned()179     boolean isDeviceStillBeingProvisioned() {
180         return !WizardManagerHelper.isDeviceProvisioned(getActivity());
181     }
182 
doMasterClear()183     private void doMasterClear() {
184         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
185         intent.setPackage("android");
186         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
187         intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
188         intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
189         intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);
190         getActivity().sendBroadcast(intent);
191         // Intent handling is asynchronous -- assume it will happen soon.
192     }
193 
194     /**
195      * Configure the UI for the final confirmation interaction
196      */
establishFinalConfirmationState()197     private void establishFinalConfirmationState() {
198         final GlifLayout layout = mContentView.findViewById(R.id.setup_wizard_layout);
199 
200         final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
201         mixin.setPrimaryButton(
202                 new FooterButton.Builder(getActivity())
203                         .setText(R.string.master_clear_button_text)
204                         .setListener(mFinalClickListener)
205                         .setButtonType(ButtonType.OTHER)
206                         .setTheme(R.style.SudGlifButton_Primary)
207                         .build()
208         );
209     }
210 
setUpActionBarAndTitle()211     private void setUpActionBarAndTitle() {
212         final Activity activity = getActivity();
213         if (activity == null) {
214             Log.e(TAG, "No activity attached, skipping setUpActionBarAndTitle");
215             return;
216         }
217         final ActionBar actionBar = activity.getActionBar();
218         if (actionBar == null) {
219             Log.e(TAG, "No actionbar, skipping setUpActionBarAndTitle");
220             return;
221         }
222         actionBar.hide();
223         activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
224     }
225 
226     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)227     public View onCreateView(LayoutInflater inflater, ViewGroup container,
228             Bundle savedInstanceState) {
229         final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
230                 getActivity(), UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId());
231         if (RestrictedLockUtilsInternal.hasBaseUserRestriction(getActivity(),
232                 UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId())) {
233             return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
234         } else if (admin != null) {
235             new ActionDisabledByAdminDialogHelper(getActivity())
236                     .prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
237                     .setOnDismissListener(__ -> getActivity().finish())
238                     .show();
239             return new View(getActivity());
240         }
241         mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
242         setUpActionBarAndTitle();
243         establishFinalConfirmationState();
244         setAccessibilityTitle();
245         setSubtitle();
246         return mContentView;
247     }
248 
setAccessibilityTitle()249     private void setAccessibilityTitle() {
250         CharSequence currentTitle = getActivity().getTitle();
251         TextView confirmationMessage = mContentView.findViewById(R.id.sud_layout_description);
252         if (confirmationMessage != null) {
253             String accessibleText = new StringBuilder(currentTitle).append(",").append(
254                     confirmationMessage.getText()).toString();
255             getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibleText));
256         }
257     }
258 
259     @VisibleForTesting
setSubtitle()260     void setSubtitle() {
261         if (mEraseEsims) {
262             ((TextView) mContentView.findViewById(R.id.sud_layout_description))
263                 .setText(R.string.master_clear_final_desc_esim);
264         }
265     }
266 
267     @Override
onCreate(Bundle savedInstanceState)268     public void onCreate(Bundle savedInstanceState) {
269         super.onCreate(savedInstanceState);
270 
271         Bundle args = getArguments();
272         mEraseSdCard = args != null
273                 && args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA);
274         mEraseEsims = args != null
275                 && args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
276     }
277 
278     @Override
getMetricsCategory()279     public int getMetricsCategory() {
280         return SettingsEnums.MASTER_CLEAR_CONFIRM;
281     }
282 }
283