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