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