blob: e8e3b39d51123c7824d965e139aae6babbc36aa2 [file] [log] [blame]
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -07001/*
2 * Copyright (C) 2017 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
17package com.android.server;
18
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070019import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
20
Gavin Corkery69395652019-12-12 19:06:47 +000021import android.annotation.Nullable;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070022import android.content.ContentResolver;
23import android.content.Context;
Gavin Corkery69395652019-12-12 19:06:47 +000024import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.VersionedPackage;
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -070027import android.os.Build;
Jeff Sharkey82311462017-04-02 23:42:17 -060028import android.os.Environment;
Jeff Sharkey1bec4482017-02-23 12:40:54 -070029import android.os.FileUtils;
Gavin Corkeryaa57ef32019-12-17 19:02:54 +000030import android.os.Process;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070031import android.os.RecoverySystem;
32import android.os.SystemClock;
33import android.os.SystemProperties;
34import android.os.UserHandle;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070035import android.provider.Settings;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070036import android.util.ExceptionUtils;
Jeff Sharkeybc9caa12017-03-11 20:38:21 -070037import android.util.Log;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070038import android.util.MathUtils;
39import android.util.Slog;
Christian Brunschenf86039e2018-12-21 12:26:14 +000040import android.util.StatsLog;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070041
Gavin Corkery69395652019-12-12 19:06:47 +000042import com.android.internal.annotations.GuardedBy;
bpetrivsa7101952019-02-07 16:01:24 +000043import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070044import com.android.internal.util.ArrayUtils;
Gavin Corkery69395652019-12-12 19:06:47 +000045import com.android.server.PackageWatchdog.FailureReasons;
46import com.android.server.PackageWatchdog.PackageHealthObserver;
47import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
bpetrivs93075f42019-02-28 12:08:12 +000048import com.android.server.am.SettingsToPropertiesMapper;
bpetrivs62f15982019-02-13 17:18:16 +000049import com.android.server.utils.FlagNamespaceUtils;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070050
Jeff Sharkey1bec4482017-02-23 12:40:54 -070051import java.io.File;
bpetrivs93075f42019-02-28 12:08:12 +000052import java.util.Arrays;
Jeff Sharkeyd9574c72017-02-20 10:45:06 -070053
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070054/**
55 * Utilities to help rescue the system from crash loops. Callers are expected to
56 * report boot events and persistent app crashes, and if they happen frequently
57 * enough this class will slowly escalate through several rescue operations
58 * before finally rebooting and prompting the user if they want to wipe data as
59 * a last resort.
60 *
61 * @hide
62 */
63public class RescueParty {
bpetrivsa7101952019-02-07 16:01:24 +000064 @VisibleForTesting
65 static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
66 @VisibleForTesting
67 static final int TRIGGER_COUNT = 5;
68 @VisibleForTesting
69 static final String PROP_RESCUE_LEVEL = "sys.rescue_level";
70 @VisibleForTesting
71 static final int LEVEL_NONE = 0;
72 @VisibleForTesting
73 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
74 @VisibleForTesting
75 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2;
76 @VisibleForTesting
77 static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
78 @VisibleForTesting
79 static final int LEVEL_FACTORY_RESET = 4;
80 @VisibleForTesting
81 static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
bpetrivsa7101952019-02-07 16:01:24 +000082 @VisibleForTesting
bpetrivsa7101952019-02-07 16:01:24 +000083 static final String TAG = "RescueParty";
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070084
Gavin Corkery69395652019-12-12 19:06:47 +000085 private static final String NAME = "rescue-party-observer";
86
87
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -070088 private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
Jeff Sharkey9d640952017-06-26 19:57:16 -060089 private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070090
Gavin Corkery69395652019-12-12 19:06:47 +000091 private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
92 | ApplicationInfo.FLAG_SYSTEM;
93
Gavin Corkery69395652019-12-12 19:06:47 +000094 /** Register the Rescue Party observer as a Package Watchdog health observer */
95 public static void registerHealthObserver(Context context) {
96 PackageWatchdog.getInstance(context).registerHealthObserver(
97 RescuePartyObserver.getInstance(context));
98 }
99
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -0700100 private static boolean isDisabled() {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700101 // Check if we're explicitly enabled for testing
Jeff Sharkey82311462017-04-02 23:42:17 -0600102 if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700103 return false;
104 }
105
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700106 // We're disabled on all engineering devices
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700107 if (Build.IS_ENG) {
108 Slog.v(TAG, "Disabled because of eng build");
109 return true;
110 }
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700111
112 // We're disabled on userdebug devices connected over USB, since that's
113 // a decent signal that someone is actively trying to debug the device,
114 // or that it's in a lab environment.
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700115 if (Build.IS_USERDEBUG && isUsbActive()) {
116 Slog.v(TAG, "Disabled because of active USB connection");
117 return true;
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700118 }
119
120 // One last-ditch check
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700121 if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
122 Slog.v(TAG, "Disabled because of manual property");
123 return true;
124 }
125
126 return false;
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -0700127 }
128
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700129 /**
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700130 * Check if we're currently attempting to reboot for a factory reset.
131 */
132 public static boolean isAttemptingFactoryReset() {
133 return SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) == LEVEL_FACTORY_RESET;
134 }
135
136 /**
bpetrivsa7101952019-02-07 16:01:24 +0000137 * Called when {@code SettingsProvider} has been published, which is a good
138 * opportunity to reset any settings depending on our rescue level.
139 */
140 public static void onSettingsProviderPublished(Context context) {
bpetrivs93075f42019-02-28 12:08:12 +0000141 handleNativeRescuePartyResets();
bpetrivsa7101952019-02-07 16:01:24 +0000142 executeRescueLevel(context);
143 }
144
145 @VisibleForTesting
bpetrivsa7101952019-02-07 16:01:24 +0000146 static long getElapsedRealtime() {
147 return SystemClock.elapsedRealtime();
148 }
149
bpetrivs93075f42019-02-28 12:08:12 +0000150 private static void handleNativeRescuePartyResets() {
151 if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
152 FlagNamespaceUtils.resetDeviceConfig(Settings.RESET_MODE_TRUSTED_DEFAULTS,
153 Arrays.asList(SettingsToPropertiesMapper.getResetNativeCategories()));
154 }
155 }
156
bpetrivsa7101952019-02-07 16:01:24 +0000157 /**
Gavin Corkeryaa57ef32019-12-17 19:02:54 +0000158 * Get the current rescue level.
159 */
160 private static int getRescueLevel() {
161 return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
162 LEVEL_NONE, LEVEL_FACTORY_RESET);
163 }
164
165 /**
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700166 * Escalate to the next rescue level. After incrementing the level you'll
167 * probably want to call {@link #executeRescueLevel(Context)}.
168 */
169 private static void incrementRescueLevel(int triggerUid) {
170 final int level = MathUtils.constrain(
171 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
172 LEVEL_NONE, LEVEL_FACTORY_RESET);
173 SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level));
174
175 EventLogTags.writeRescueLevel(level, triggerUid);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700176 logCriticalInfo(Log.WARN, "Incremented rescue level to "
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700177 + levelToString(level) + " triggered by UID " + triggerUid);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700178 }
179
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700180 private static void executeRescueLevel(Context context) {
181 final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE);
182 if (level == LEVEL_NONE) return;
183
184 Slog.w(TAG, "Attempting rescue level " + levelToString(level));
185 try {
186 executeRescueLevelInternal(context, level);
187 EventLogTags.writeRescueSuccess(level);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700188 logCriticalInfo(Log.DEBUG,
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700189 "Finished rescue level " + levelToString(level));
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700190 } catch (Throwable t) {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700191 final String msg = ExceptionUtils.getCompleteMessage(t);
192 EventLogTags.writeRescueFailure(level, msg);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700193 logCriticalInfo(Log.ERROR,
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700194 "Failed rescue level " + levelToString(level) + ": " + msg);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700195 }
196 }
197
198 private static void executeRescueLevelInternal(Context context, int level) throws Exception {
Christian Brunschenf86039e2018-12-21 12:26:14 +0000199 StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700200 switch (level) {
201 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
202 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
203 break;
204 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
205 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES);
206 break;
207 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
208 resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS);
209 break;
210 case LEVEL_FACTORY_RESET:
211 RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
212 break;
213 }
bpetrivs62f15982019-02-13 17:18:16 +0000214 FlagNamespaceUtils.addToKnownResetNamespaces(
215 FlagNamespaceUtils.NAMESPACE_NO_PACKAGE);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700216 }
217
Gavin Corkery69395652019-12-12 19:06:47 +0000218 private static int mapRescueLevelToUserImpact(int rescueLevel) {
219 switch(rescueLevel) {
220 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
221 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
222 return PackageHealthObserverImpact.USER_IMPACT_LOW;
223 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
224 case LEVEL_FACTORY_RESET:
225 return PackageHealthObserverImpact.USER_IMPACT_HIGH;
226 default:
227 return PackageHealthObserverImpact.USER_IMPACT_NONE;
228 }
229 }
230
231 private static int getPackageUid(Context context, String packageName) {
232 try {
233 return context.getPackageManager().getPackageUid(packageName, 0);
234 } catch (PackageManager.NameNotFoundException e) {
235 // Since UIDs are always >= 0, this value means the UID could not be determined.
236 return -1;
237 }
238 }
239
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700240 private static void resetAllSettings(Context context, int mode) throws Exception {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700241 // Try our best to reset all settings possible, and once finished
242 // rethrow any exception that we encountered
243 Exception res = null;
244 final ContentResolver resolver = context.getContentResolver();
245 try {
bpetrivs62f15982019-02-13 17:18:16 +0000246 FlagNamespaceUtils.resetDeviceConfig(mode);
247 } catch (Exception e) {
248 res = new RuntimeException("Failed to reset config settings", e);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700249 }
bpetrivs0254ff62019-03-01 11:50:45 +0000250 try {
251 Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
252 } catch (Exception e) {
253 res = new RuntimeException("Failed to reset global settings", e);
254 }
Jeff Sharkey82311462017-04-02 23:42:17 -0600255 for (int userId : getAllUserIds()) {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700256 try {
257 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
bpetrivs62f15982019-02-13 17:18:16 +0000258 } catch (Exception e) {
259 res = new RuntimeException("Failed to reset secure settings for " + userId, e);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700260 }
261 }
262 if (res != null) {
263 throw res;
264 }
265 }
266
267 /**
Gavin Corkery69395652019-12-12 19:06:47 +0000268 * Handle mitigation action for package failures. This observer will be register to Package
269 * Watchdog and will receive calls about package failures. This observer is persistent so it
270 * may choose to mitigate failures for packages it has not explicitly asked to observe.
271 */
272 public static class RescuePartyObserver implements PackageHealthObserver {
273
274 private Context mContext;
275
276 @GuardedBy("RescuePartyObserver.class")
277 static RescuePartyObserver sRescuePartyObserver;
278
279 private RescuePartyObserver(Context context) {
280 mContext = context;
281 }
282
283 /** Creates or gets singleton instance of RescueParty. */
284 public static RescuePartyObserver getInstance(Context context) {
285 synchronized (RescuePartyObserver.class) {
286 if (sRescuePartyObserver == null) {
287 sRescuePartyObserver = new RescuePartyObserver(context);
288 }
289 return sRescuePartyObserver;
290 }
291 }
292
293 @Override
294 public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
295 @FailureReasons int failureReason) {
296 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
297 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
298 int rescueLevel = MathUtils.constrain(
299 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
300 LEVEL_NONE, LEVEL_FACTORY_RESET);
301 return mapRescueLevelToUserImpact(rescueLevel);
302 } else {
303 return PackageHealthObserverImpact.USER_IMPACT_NONE;
304 }
305 }
306
307 @Override
308 public boolean execute(@Nullable VersionedPackage failedPackage,
309 @FailureReasons int failureReason) {
310 if (isDisabled()) {
311 return false;
312 }
313 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
314 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
315 int triggerUid = getPackageUid(mContext, failedPackage.getPackageName());
316 incrementRescueLevel(triggerUid);
317 executeRescueLevel(mContext);
318 return true;
319 } else {
320 return false;
321 }
322 }
323
324 @Override
325 public boolean isPersistent() {
326 return true;
327 }
328
329 @Override
330 public boolean mayObservePackage(String packageName) {
331 PackageManager pm = mContext.getPackageManager();
332 try {
333 // A package is a Mainline module if this is non-null
334 if (pm.getModuleInfo(packageName, 0) != null) {
335 return true;
336 }
337 ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
338 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
339 } catch (PackageManager.NameNotFoundException e) {
340 return false;
341 }
342 }
343
344 @Override
Gavin Corkeryaa57ef32019-12-17 19:02:54 +0000345 public int onBootLoop() {
346 if (isDisabled()) {
347 return PackageHealthObserverImpact.USER_IMPACT_NONE;
348 }
349 return mapRescueLevelToUserImpact(getRescueLevel());
350 }
351
352 @Override
353 public boolean executeBootLoopMitigation() {
354 if (isDisabled()) {
355 return false;
356 }
357 incrementRescueLevel(Process.ROOT_UID);
358 executeRescueLevel(mContext);
359 return true;
360 }
361
362 @Override
Gavin Corkery69395652019-12-12 19:06:47 +0000363 public String getName() {
364 return NAME;
365 }
366 }
367
Jeff Sharkey82311462017-04-02 23:42:17 -0600368 private static int[] getAllUserIds() {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700369 int[] userIds = { UserHandle.USER_SYSTEM };
370 try {
Jeff Sharkey82311462017-04-02 23:42:17 -0600371 for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
372 try {
373 final int userId = Integer.parseInt(file.getName());
374 if (userId != UserHandle.USER_SYSTEM) {
375 userIds = ArrayUtils.appendInt(userIds, userId);
376 }
377 } catch (NumberFormatException ignored) {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700378 }
379 }
380 } catch (Throwable t) {
381 Slog.w(TAG, "Trouble discovering users", t);
382 }
383 return userIds;
384 }
385
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700386 /**
387 * Hacky test to check if the device has an active USB connection, which is
Jeff Sharkey1bec4482017-02-23 12:40:54 -0700388 * a good proxy for someone doing local development work.
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700389 */
390 private static boolean isUsbActive() {
Jeff Sharkey9d640952017-06-26 19:57:16 -0600391 if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) {
392 Slog.v(TAG, "Assuming virtual device is connected over USB");
393 return true;
394 }
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700395 try {
Jeff Sharkey1bec4482017-02-23 12:40:54 -0700396 final String state = FileUtils
397 .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
398 return "CONFIGURED".equals(state.trim());
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700399 } catch (Throwable t) {
400 Slog.w(TAG, "Failed to determine if device was on USB", t);
401 return false;
402 }
403 }
404
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700405 private static String levelToString(int level) {
406 switch (level) {
407 case LEVEL_NONE: return "NONE";
408 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
409 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES";
410 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS";
411 case LEVEL_FACTORY_RESET: return "FACTORY_RESET";
412 default: return Integer.toString(level);
413 }
414 }
415}