1 /* 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 17 package com.android.server; 18 19 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.VersionedPackage; 28 import android.os.Build; 29 import android.os.Bundle; 30 import android.os.Environment; 31 import android.os.FileUtils; 32 import android.os.Process; 33 import android.os.RecoverySystem; 34 import android.os.RemoteCallback; 35 import android.os.SystemClock; 36 import android.os.SystemProperties; 37 import android.os.UserHandle; 38 import android.provider.DeviceConfig; 39 import android.provider.Settings; 40 import android.util.ArraySet; 41 import android.util.ExceptionUtils; 42 import android.util.Log; 43 import android.util.MathUtils; 44 import android.util.Slog; 45 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.ArrayUtils; 49 import com.android.internal.util.FrameworkStatsLog; 50 import com.android.server.PackageWatchdog.FailureReasons; 51 import com.android.server.PackageWatchdog.PackageHealthObserver; 52 import com.android.server.PackageWatchdog.PackageHealthObserverImpact; 53 import com.android.server.am.SettingsToPropertiesMapper; 54 55 import java.io.File; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.HashMap; 59 import java.util.Iterator; 60 import java.util.List; 61 import java.util.Map; 62 import java.util.Set; 63 import java.util.concurrent.TimeUnit; 64 65 /** 66 * Utilities to help rescue the system from crash loops. Callers are expected to 67 * report boot events and persistent app crashes, and if they happen frequently 68 * enough this class will slowly escalate through several rescue operations 69 * before finally rebooting and prompting the user if they want to wipe data as 70 * a last resort. 71 * 72 * @hide 73 */ 74 public class RescueParty { 75 @VisibleForTesting 76 static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; 77 @VisibleForTesting 78 static final String PROP_RESCUE_LEVEL = "sys.rescue_level"; 79 @VisibleForTesting 80 static final int LEVEL_NONE = 0; 81 @VisibleForTesting 82 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1; 83 @VisibleForTesting 84 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2; 85 @VisibleForTesting 86 static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3; 87 @VisibleForTesting 88 static final int LEVEL_FACTORY_RESET = 4; 89 @VisibleForTesting 90 static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; 91 @VisibleForTesting 92 static final String TAG = "RescueParty"; 93 @VisibleForTesting 94 static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); 95 96 private static final String NAME = "rescue-party-observer"; 97 98 99 private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; 100 private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; 101 private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG = 102 "persist.device_config.configuration.disable_rescue_party"; 103 104 private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT 105 | ApplicationInfo.FLAG_SYSTEM; 106 107 /** Register the Rescue Party observer as a Package Watchdog health observer */ registerHealthObserver(Context context)108 public static void registerHealthObserver(Context context) { 109 PackageWatchdog.getInstance(context).registerHealthObserver( 110 RescuePartyObserver.getInstance(context)); 111 } 112 isDisabled()113 private static boolean isDisabled() { 114 // Check if we're explicitly enabled for testing 115 if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { 116 return false; 117 } 118 119 // We're disabled if the DeviceConfig disable flag is set to true. 120 // This is in case that an emergency rollback of the feature is needed. 121 if (SystemProperties.getBoolean(PROP_DEVICE_CONFIG_DISABLE_FLAG, false)) { 122 Slog.v(TAG, "Disabled because of DeviceConfig flag"); 123 return true; 124 } 125 126 // We're disabled on all engineering devices 127 if (Build.IS_ENG) { 128 Slog.v(TAG, "Disabled because of eng build"); 129 return true; 130 } 131 132 // We're disabled on userdebug devices connected over USB, since that's 133 // a decent signal that someone is actively trying to debug the device, 134 // or that it's in a lab environment. 135 if (Build.IS_USERDEBUG && isUsbActive()) { 136 Slog.v(TAG, "Disabled because of active USB connection"); 137 return true; 138 } 139 140 // One last-ditch check 141 if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) { 142 Slog.v(TAG, "Disabled because of manual property"); 143 return true; 144 } 145 146 return false; 147 } 148 149 /** 150 * Check if we're currently attempting to reboot for a factory reset. 151 */ isAttemptingFactoryReset()152 public static boolean isAttemptingFactoryReset() { 153 return SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) == LEVEL_FACTORY_RESET; 154 } 155 156 /** 157 * Called when {@code SettingsProvider} has been published, which is a good 158 * opportunity to reset any settings depending on our rescue level. 159 */ onSettingsProviderPublished(Context context)160 public static void onSettingsProviderPublished(Context context) { 161 handleNativeRescuePartyResets(); 162 executeRescueLevel(context, /*failedPackage=*/ null); 163 ContentResolver contentResolver = context.getContentResolver(); 164 Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> { 165 handleMonitorCallback(context, result); 166 })); 167 } 168 169 @VisibleForTesting getElapsedRealtime()170 static long getElapsedRealtime() { 171 return SystemClock.elapsedRealtime(); 172 } 173 handleMonitorCallback(Context context, Bundle result)174 private static void handleMonitorCallback(Context context, Bundle result) { 175 String callbackType = result.getString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, ""); 176 switch (callbackType) { 177 case Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK: 178 String updatedNamespace = result.getString(Settings.EXTRA_NAMESPACE); 179 if (updatedNamespace != null) { 180 startObservingPackages(context, updatedNamespace); 181 } 182 break; 183 case Settings.EXTRA_ACCESS_CALLBACK: 184 String callingPackage = result.getString(Settings.EXTRA_CALLING_PACKAGE, null); 185 String namespace = result.getString(Settings.EXTRA_NAMESPACE, null); 186 if (namespace != null && callingPackage != null) { 187 RescuePartyObserver.getInstance(context).recordDeviceConfigAccess( 188 callingPackage, 189 namespace); 190 } 191 break; 192 default: 193 Slog.w(TAG, "Unrecognized DeviceConfig callback"); 194 break; 195 } 196 } 197 startObservingPackages(Context context, @NonNull String updatedNamespace)198 private static void startObservingPackages(Context context, @NonNull String updatedNamespace) { 199 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); 200 Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace); 201 if (callingPackages == null) { 202 return; 203 } 204 List<String> callingPackageList = new ArrayList<>(); 205 callingPackageList.addAll(callingPackages); 206 Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " 207 + updatedNamespace); 208 PackageWatchdog.getInstance(context).startObservingHealth( 209 rescuePartyObserver, 210 callingPackageList, 211 DEFAULT_OBSERVING_DURATION_MS); 212 } 213 handleNativeRescuePartyResets()214 private static void handleNativeRescuePartyResets() { 215 if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { 216 String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); 217 for (int i = 0; i < resetNativeCategories.length; i++) { 218 DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS, 219 resetNativeCategories[i]); 220 } 221 } 222 } 223 224 /** 225 * Get the next rescue level. This indicates the next level of mitigation that may be taken. 226 */ getNextRescueLevel()227 private static int getNextRescueLevel() { 228 return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1, 229 LEVEL_NONE, LEVEL_FACTORY_RESET); 230 } 231 232 /** 233 * Escalate to the next rescue level. After incrementing the level you'll 234 * probably want to call {@link #executeRescueLevel(Context, String)}. 235 */ incrementRescueLevel(int triggerUid)236 private static void incrementRescueLevel(int triggerUid) { 237 final int level = getNextRescueLevel(); 238 SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); 239 240 EventLogTags.writeRescueLevel(level, triggerUid); 241 logCriticalInfo(Log.WARN, "Incremented rescue level to " 242 + levelToString(level) + " triggered by UID " + triggerUid); 243 } 244 executeRescueLevel(Context context, @Nullable String failedPackage)245 private static void executeRescueLevel(Context context, @Nullable String failedPackage) { 246 final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE); 247 if (level == LEVEL_NONE) return; 248 249 Slog.w(TAG, "Attempting rescue level " + levelToString(level)); 250 try { 251 executeRescueLevelInternal(context, level, failedPackage); 252 EventLogTags.writeRescueSuccess(level); 253 logCriticalInfo(Log.DEBUG, 254 "Finished rescue level " + levelToString(level)); 255 } catch (Throwable t) { 256 logRescueException(level, t); 257 } 258 } 259 executeRescueLevelInternal(Context context, int level, @Nullable String failedPackage)260 private static void executeRescueLevelInternal(Context context, int level, @Nullable 261 String failedPackage) throws Exception { 262 FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level); 263 switch (level) { 264 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: 265 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, failedPackage); 266 break; 267 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: 268 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, failedPackage); 269 break; 270 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: 271 resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage); 272 break; 273 case LEVEL_FACTORY_RESET: 274 // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog 275 // when device shutting down. 276 Runnable runnable = new Runnable() { 277 @Override 278 public void run() { 279 try { 280 RecoverySystem.rebootPromptAndWipeUserData(context, TAG); 281 } catch (Throwable t) { 282 logRescueException(level, t); 283 } 284 } 285 }; 286 Thread thread = new Thread(runnable); 287 thread.start(); 288 break; 289 } 290 } 291 logRescueException(int level, Throwable t)292 private static void logRescueException(int level, Throwable t) { 293 final String msg = ExceptionUtils.getCompleteMessage(t); 294 EventLogTags.writeRescueFailure(level, msg); 295 logCriticalInfo(Log.ERROR, 296 "Failed rescue level " + levelToString(level) + ": " + msg); 297 } 298 mapRescueLevelToUserImpact(int rescueLevel)299 private static int mapRescueLevelToUserImpact(int rescueLevel) { 300 switch(rescueLevel) { 301 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: 302 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: 303 return PackageHealthObserverImpact.USER_IMPACT_LOW; 304 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: 305 case LEVEL_FACTORY_RESET: 306 return PackageHealthObserverImpact.USER_IMPACT_HIGH; 307 default: 308 return PackageHealthObserverImpact.USER_IMPACT_NONE; 309 } 310 } 311 getPackageUid(Context context, String packageName)312 private static int getPackageUid(Context context, String packageName) { 313 try { 314 return context.getPackageManager().getPackageUid(packageName, 0); 315 } catch (PackageManager.NameNotFoundException e) { 316 // Since UIDs are always >= 0, this value means the UID could not be determined. 317 return -1; 318 } 319 } 320 resetAllSettings(Context context, int mode, @Nullable String failedPackage)321 private static void resetAllSettings(Context context, int mode, @Nullable String failedPackage) 322 throws Exception { 323 // Try our best to reset all settings possible, and once finished 324 // rethrow any exception that we encountered 325 Exception res = null; 326 final ContentResolver resolver = context.getContentResolver(); 327 try { 328 resetDeviceConfig(context, mode, failedPackage); 329 } catch (Exception e) { 330 res = new RuntimeException("Failed to reset config settings", e); 331 } 332 try { 333 Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM); 334 } catch (Exception e) { 335 res = new RuntimeException("Failed to reset global settings", e); 336 } 337 for (int userId : getAllUserIds()) { 338 try { 339 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); 340 } catch (Exception e) { 341 res = new RuntimeException("Failed to reset secure settings for " + userId, e); 342 } 343 } 344 if (res != null) { 345 throw res; 346 } 347 } 348 resetDeviceConfig(Context context, int resetMode, @Nullable String failedPackage)349 private static void resetDeviceConfig(Context context, int resetMode, 350 @Nullable String failedPackage) { 351 if (!shouldPerformScopedResets() || failedPackage == null) { 352 DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null); 353 } else { 354 performScopedReset(context, resetMode, failedPackage); 355 } 356 } 357 shouldPerformScopedResets()358 private static boolean shouldPerformScopedResets() { 359 int rescueLevel = MathUtils.constrain( 360 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE), 361 LEVEL_NONE, LEVEL_FACTORY_RESET); 362 return rescueLevel <= LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; 363 } 364 performScopedReset(Context context, int resetMode, @NonNull String failedPackage)365 private static void performScopedReset(Context context, int resetMode, 366 @NonNull String failedPackage) { 367 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); 368 Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet( 369 failedPackage); 370 if (affectedNamespaces == null) { 371 DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null); 372 } else { 373 Slog.w(TAG, 374 "Performing scoped reset for package: " + failedPackage 375 + ", affected namespaces: " 376 + Arrays.toString(affectedNamespaces.toArray())); 377 Iterator<String> it = affectedNamespaces.iterator(); 378 while (it.hasNext()) { 379 DeviceConfig.resetToDefaults(resetMode, it.next()); 380 } 381 } 382 } 383 384 /** 385 * Handle mitigation action for package failures. This observer will be register to Package 386 * Watchdog and will receive calls about package failures. This observer is persistent so it 387 * may choose to mitigate failures for packages it has not explicitly asked to observe. 388 */ 389 public static class RescuePartyObserver implements PackageHealthObserver { 390 391 private final Context mContext; 392 private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>(); 393 private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>(); 394 395 @GuardedBy("RescuePartyObserver.class") 396 static RescuePartyObserver sRescuePartyObserver; 397 RescuePartyObserver(Context context)398 private RescuePartyObserver(Context context) { 399 mContext = context; 400 } 401 402 /** Creates or gets singleton instance of RescueParty. */ getInstance(Context context)403 public static RescuePartyObserver getInstance(Context context) { 404 synchronized (RescuePartyObserver.class) { 405 if (sRescuePartyObserver == null) { 406 sRescuePartyObserver = new RescuePartyObserver(context); 407 } 408 return sRescuePartyObserver; 409 } 410 } 411 412 @VisibleForTesting reset()413 static void reset() { 414 synchronized (RescuePartyObserver.class) { 415 sRescuePartyObserver = null; 416 } 417 } 418 419 @Override onHealthCheckFailed(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason)420 public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, 421 @FailureReasons int failureReason) { 422 if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH 423 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { 424 return mapRescueLevelToUserImpact(getNextRescueLevel()); 425 } else { 426 return PackageHealthObserverImpact.USER_IMPACT_NONE; 427 } 428 } 429 430 @Override execute(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason)431 public boolean execute(@Nullable VersionedPackage failedPackage, 432 @FailureReasons int failureReason) { 433 if (isDisabled()) { 434 return false; 435 } 436 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH 437 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { 438 int triggerUid = getPackageUid(mContext, failedPackage.getPackageName()); 439 incrementRescueLevel(triggerUid); 440 executeRescueLevel(mContext, 441 failedPackage == null ? null : failedPackage.getPackageName()); 442 return true; 443 } else { 444 return false; 445 } 446 } 447 448 @Override isPersistent()449 public boolean isPersistent() { 450 return true; 451 } 452 453 @Override mayObservePackage(String packageName)454 public boolean mayObservePackage(String packageName) { 455 PackageManager pm = mContext.getPackageManager(); 456 try { 457 // A package is a Mainline module if this is non-null 458 if (pm.getModuleInfo(packageName, 0) != null) { 459 return true; 460 } 461 ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 462 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; 463 } catch (PackageManager.NameNotFoundException e) { 464 return false; 465 } 466 } 467 468 @Override onBootLoop()469 public int onBootLoop() { 470 if (isDisabled()) { 471 return PackageHealthObserverImpact.USER_IMPACT_NONE; 472 } 473 return mapRescueLevelToUserImpact(getNextRescueLevel()); 474 } 475 476 @Override executeBootLoopMitigation()477 public boolean executeBootLoopMitigation() { 478 if (isDisabled()) { 479 return false; 480 } 481 incrementRescueLevel(Process.ROOT_UID); 482 executeRescueLevel(mContext, /*failedPackage=*/ null); 483 return true; 484 } 485 486 @Override getName()487 public String getName() { 488 return NAME; 489 } 490 recordDeviceConfigAccess(@onNull String callingPackage, @NonNull String namespace)491 private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, 492 @NonNull String namespace) { 493 // Record it in calling packages to namespace map 494 Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); 495 if (namespaceSet == null) { 496 namespaceSet = new ArraySet<>(); 497 mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); 498 } 499 namespaceSet.add(namespace); 500 // Record it in namespace to calling packages map 501 Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); 502 if (callingPackageSet == null) { 503 callingPackageSet = new ArraySet<>(); 504 } 505 callingPackageSet.add(callingPackage); 506 mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); 507 } 508 getAffectedNamespaceSet(String failedPackage)509 private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) { 510 return mCallingPackageNamespaceSetMap.get(failedPackage); 511 } 512 getCallingPackagesSet(String namespace)513 private synchronized Set<String> getCallingPackagesSet(String namespace) { 514 return mNamespaceCallingPackageSetMap.get(namespace); 515 } 516 } 517 getAllUserIds()518 private static int[] getAllUserIds() { 519 int[] userIds = { UserHandle.USER_SYSTEM }; 520 try { 521 for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) { 522 try { 523 final int userId = Integer.parseInt(file.getName()); 524 if (userId != UserHandle.USER_SYSTEM) { 525 userIds = ArrayUtils.appendInt(userIds, userId); 526 } 527 } catch (NumberFormatException ignored) { 528 } 529 } 530 } catch (Throwable t) { 531 Slog.w(TAG, "Trouble discovering users", t); 532 } 533 return userIds; 534 } 535 536 /** 537 * Hacky test to check if the device has an active USB connection, which is 538 * a good proxy for someone doing local development work. 539 */ isUsbActive()540 private static boolean isUsbActive() { 541 if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) { 542 Slog.v(TAG, "Assuming virtual device is connected over USB"); 543 return true; 544 } 545 try { 546 final String state = FileUtils 547 .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, ""); 548 return "CONFIGURED".equals(state.trim()); 549 } catch (Throwable t) { 550 Slog.w(TAG, "Failed to determine if device was on USB", t); 551 return false; 552 } 553 } 554 levelToString(int level)555 private static String levelToString(int level) { 556 switch (level) { 557 case LEVEL_NONE: return "NONE"; 558 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS"; 559 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES"; 560 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS"; 561 case LEVEL_FACTORY_RESET: return "FACTORY_RESET"; 562 default: return Integer.toString(level); 563 } 564 } 565 } 566