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.server; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE; 20 21 import android.annotation.UserIdInt; 22 import android.app.ActivityManager; 23 import android.app.Notification; 24 import android.app.NotificationManager; 25 import android.app.ProgressDialog; 26 import android.app.admin.DevicePolicyManager; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.os.AsyncTask; 31 import android.os.Binder; 32 import android.os.RecoverySystem; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.os.storage.StorageManager; 37 import android.text.TextUtils; 38 import android.util.Slog; 39 import android.view.WindowManager; 40 41 import com.android.internal.R; 42 import com.android.internal.messages.nano.SystemMessageProto; 43 import com.android.internal.notification.SystemNotificationChannels; 44 import com.android.server.utils.Slogf; 45 46 import java.io.IOException; 47 48 public class MasterClearReceiver extends BroadcastReceiver { 49 private static final String TAG = "MasterClear"; 50 private boolean mWipeExternalStorage; 51 private boolean mWipeEsims; 52 53 @Override onReceive(final Context context, final Intent intent)54 public void onReceive(final Context context, final Intent intent) { 55 if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) { 56 if (!"google.com".equals(intent.getStringExtra("from"))) { 57 Slog.w(TAG, "Ignoring master clear request -- not from trusted server."); 58 return; 59 } 60 } 61 if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) { 62 Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, " 63 + "Intent#ACTION_FACTORY_RESET should be used instead."); 64 } 65 if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) { 66 Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, " 67 + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead."); 68 } 69 70 final String factoryResetPackage = context 71 .getString(com.android.internal.R.string.config_factoryResetPackage); 72 if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction()) 73 && !TextUtils.isEmpty(factoryResetPackage)) { 74 Slog.i(TAG, "Re-directing intent to " + factoryResetPackage); 75 intent.setPackage(factoryResetPackage).setComponent(null); 76 context.sendBroadcastAsUser(intent, UserHandle.SYSTEM); 77 return; 78 } 79 80 final boolean shutdown = intent.getBooleanExtra("shutdown", false); 81 final String reason = intent.getStringExtra(Intent.EXTRA_REASON); 82 mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false); 83 mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false); 84 final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false) 85 || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false); 86 87 // TODO(b/189938391): properly handle factory reset on headless system user mode. 88 final int sendingUserId = getSendingUserId(); 89 if (sendingUserId != UserHandle.USER_SYSTEM && !UserManager.isHeadlessSystemUserMode()) { 90 Slogf.w( 91 TAG, 92 "ACTION_FACTORY_RESET received on a non-system user %d, WIPING THE USER!!", 93 sendingUserId); 94 if (!Binder.withCleanCallingIdentity(() -> wipeUser(context, sendingUserId, reason))) { 95 Slogf.e(TAG, "Failed to wipe user %d", sendingUserId); 96 } 97 return; 98 } 99 100 Slog.w(TAG, "!!! FACTORY RESET !!!"); 101 // The reboot call is blocking, so we need to do it on another thread. 102 Thread thr = new Thread("Reboot") { 103 @Override 104 public void run() { 105 try { 106 Slog.i(TAG, "Calling RecoverySystem.rebootWipeUserData(context, " 107 + "shutdown=" + shutdown + ", reason=" + reason 108 + ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims + ")"); 109 RecoverySystem 110 .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims); 111 Slog.wtf(TAG, "Still running after master clear?!"); 112 } catch (IOException e) { 113 Slog.e(TAG, "Can't perform master clear/factory reset", e); 114 } catch (SecurityException e) { 115 Slog.e(TAG, "Can't perform master clear/factory reset", e); 116 } 117 } 118 }; 119 120 if (mWipeExternalStorage) { 121 // thr will be started at the end of this task. 122 Slog.i(TAG, "Wiping external storage on async task"); 123 new WipeDataTask(context, thr).execute(); 124 } else { 125 Slog.i(TAG, "NOT wiping external storage; starting thread " + thr.getName()); 126 thr.start(); 127 } 128 } 129 wipeUser(Context context, @UserIdInt int userId, String wipeReason)130 private boolean wipeUser(Context context, @UserIdInt int userId, String wipeReason) { 131 final UserManager userManager = context.getSystemService(UserManager.class); 132 final int result = userManager.removeUserWhenPossible( 133 UserHandle.of(userId), /* overrideDevicePolicy= */ false); 134 if (!UserManager.isRemoveResultSuccessful(result)) { 135 Slogf.e(TAG, "Can't remove user %d", userId); 136 return false; 137 } 138 if (getCurrentForegroundUserId() == userId) { 139 try { 140 if (!ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM)) { 141 Slogf.w(TAG, "Can't switch from current user %d, user will get removed when " 142 + "it is stopped.", userId); 143 144 } 145 } catch (RemoteException e) { 146 Slogf.w(TAG, "Can't switch from current user %d, user will get removed when " 147 + "it is stopped.", userId); 148 } 149 } 150 if (userManager.isManagedProfile(userId)) { 151 sendWipeProfileNotification(context, wipeReason); 152 } 153 return true; 154 } 155 156 // This method is copied from DevicePolicyManagedService. sendWipeProfileNotification(Context context, String wipeReason)157 private void sendWipeProfileNotification(Context context, String wipeReason) { 158 final Notification notification = 159 new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN) 160 .setSmallIcon(android.R.drawable.stat_sys_warning) 161 .setContentTitle(getWorkProfileDeletedTitle(context)) 162 .setContentText(wipeReason) 163 .setColor(context.getColor(R.color.system_notification_accent_color)) 164 .setStyle(new Notification.BigTextStyle().bigText(wipeReason)) 165 .build(); 166 context.getSystemService(NotificationManager.class).notify( 167 SystemMessageProto.SystemMessage.NOTE_PROFILE_WIPED, notification); 168 } 169 getWorkProfileDeletedTitle(Context context)170 private String getWorkProfileDeletedTitle(Context context) { 171 final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 172 return dpm.getResources().getString(WORK_PROFILE_DELETED_TITLE, 173 () -> context.getString(R.string.work_profile_deleted)); 174 } 175 getCurrentForegroundUserId()176 private @UserIdInt int getCurrentForegroundUserId() { 177 try { 178 return ActivityManager.getCurrentUser(); 179 } catch (Exception e) { 180 Slogf.e(TAG, "Can't get current user", e); 181 } 182 return UserHandle.USER_NULL; 183 } 184 185 private class WipeDataTask extends AsyncTask<Void, Void, Void> { 186 private final Thread mChainedTask; 187 private final Context mContext; 188 private final ProgressDialog mProgressDialog; 189 WipeDataTask(Context context, Thread chainedTask)190 public WipeDataTask(Context context, Thread chainedTask) { 191 mContext = context; 192 mChainedTask = chainedTask; 193 mProgressDialog = new ProgressDialog(context, R.style.Theme_DeviceDefault_System); 194 } 195 196 @Override onPreExecute()197 protected void onPreExecute() { 198 mProgressDialog.setIndeterminate(true); 199 mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 200 mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing)); 201 mProgressDialog.show(); 202 } 203 204 @Override doInBackground(Void... params)205 protected Void doInBackground(Void... params) { 206 Slog.w(TAG, "Wiping adoptable disks"); 207 if (mWipeExternalStorage) { 208 StorageManager sm = (StorageManager) mContext.getSystemService( 209 Context.STORAGE_SERVICE); 210 sm.wipeAdoptableDisks(); 211 } 212 return null; 213 } 214 215 @Override onPostExecute(Void result)216 protected void onPostExecute(Void result) { 217 mProgressDialog.dismiss(); 218 mChainedTask.start(); 219 } 220 221 } 222 } 223