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