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 android.provider.DeviceConfig.Properties; 20 21 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.VersionedPackage; 30 import android.os.Build; 31 import android.os.Bundle; 32 import android.os.Environment; 33 import android.os.FileUtils; 34 import android.os.PowerManager; 35 import android.os.RecoverySystem; 36 import android.os.RemoteCallback; 37 import android.os.SystemClock; 38 import android.os.SystemProperties; 39 import android.os.UserHandle; 40 import android.provider.DeviceConfig; 41 import android.provider.Settings; 42 import android.text.TextUtils; 43 import android.util.ArraySet; 44 import android.util.ExceptionUtils; 45 import android.util.Log; 46 import android.util.Slog; 47 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.util.ArrayUtils; 51 import com.android.internal.util.FrameworkStatsLog; 52 import com.android.server.PackageWatchdog.FailureReasons; 53 import com.android.server.PackageWatchdog.PackageHealthObserver; 54 import com.android.server.PackageWatchdog.PackageHealthObserverImpact; 55 import com.android.server.am.SettingsToPropertiesMapper; 56 57 import java.io.File; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.HashMap; 61 import java.util.HashSet; 62 import java.util.Iterator; 63 import java.util.List; 64 import java.util.Map; 65 import java.util.Set; 66 import java.util.concurrent.TimeUnit; 67 68 /** 69 * Utilities to help rescue the system from crash loops. Callers are expected to 70 * report boot events and persistent app crashes, and if they happen frequently 71 * enough this class will slowly escalate through several rescue operations 72 * before finally rebooting and prompting the user if they want to wipe data as 73 * a last resort. 74 * 75 * @hide 76 */ 77 public class RescueParty { 78 @VisibleForTesting 79 static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; 80 static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset"; 81 static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot"; 82 static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted"; 83 @VisibleForTesting 84 static final int LEVEL_NONE = 0; 85 @VisibleForTesting 86 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1; 87 @VisibleForTesting 88 static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2; 89 @VisibleForTesting 90 static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3; 91 @VisibleForTesting 92 static final int LEVEL_WARM_REBOOT = 4; 93 @VisibleForTesting 94 static final int LEVEL_FACTORY_RESET = 5; 95 @VisibleForTesting 96 static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; 97 @VisibleForTesting 98 static final String TAG = "RescueParty"; 99 @VisibleForTesting 100 static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); 101 @VisibleForTesting 102 static final int DEVICE_CONFIG_RESET_MODE = Settings.RESET_MODE_TRUSTED_DEFAULTS; 103 // The DeviceConfig namespace containing all RescueParty switches. 104 @VisibleForTesting 105 static final String NAMESPACE_CONFIGURATION = "configuration"; 106 @VisibleForTesting 107 static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG = 108 "namespace_to_package_mapping"; 109 110 private static final String NAME = "rescue-party-observer"; 111 112 113 private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; 114 private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; 115 private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG = 116 "persist.device_config.configuration.disable_rescue_party"; 117 private static final String PROP_DISABLE_FACTORY_RESET_FLAG = 118 "persist.device_config.configuration.disable_rescue_party_factory_reset"; 119 120 private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT 121 | ApplicationInfo.FLAG_SYSTEM; 122 123 /** Register the Rescue Party observer as a Package Watchdog health observer */ registerHealthObserver(Context context)124 public static void registerHealthObserver(Context context) { 125 PackageWatchdog.getInstance(context).registerHealthObserver( 126 RescuePartyObserver.getInstance(context)); 127 } 128 isDisabled()129 private static boolean isDisabled() { 130 // Check if we're explicitly enabled for testing 131 if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { 132 return false; 133 } 134 135 // We're disabled if the DeviceConfig disable flag is set to true. 136 // This is in case that an emergency rollback of the feature is needed. 137 if (SystemProperties.getBoolean(PROP_DEVICE_CONFIG_DISABLE_FLAG, false)) { 138 Slog.v(TAG, "Disabled because of DeviceConfig flag"); 139 return true; 140 } 141 142 // We're disabled on all engineering devices 143 if (Build.IS_ENG) { 144 Slog.v(TAG, "Disabled because of eng build"); 145 return true; 146 } 147 148 // We're disabled on userdebug devices connected over USB, since that's 149 // a decent signal that someone is actively trying to debug the device, 150 // or that it's in a lab environment. 151 if (Build.IS_USERDEBUG && isUsbActive()) { 152 Slog.v(TAG, "Disabled because of active USB connection"); 153 return true; 154 } 155 156 // One last-ditch check 157 if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) { 158 Slog.v(TAG, "Disabled because of manual property"); 159 return true; 160 } 161 162 return false; 163 } 164 165 /** 166 * Check if we're currently attempting to reboot for a factory reset. This method must 167 * return true if RescueParty tries to reboot early during a boot loop, since the device 168 * will not be fully booted at this time. 169 * 170 * TODO(gavincorkery): Rename method since its scope has expanded. 171 */ isAttemptingFactoryReset()172 public static boolean isAttemptingFactoryReset() { 173 return isFactoryResetPropertySet() || isRebootPropertySet(); 174 } 175 isFactoryResetPropertySet()176 static boolean isFactoryResetPropertySet() { 177 return SystemProperties.getBoolean(PROP_ATTEMPTING_FACTORY_RESET, false); 178 } 179 isRebootPropertySet()180 static boolean isRebootPropertySet() { 181 return SystemProperties.getBoolean(PROP_ATTEMPTING_REBOOT, false); 182 } 183 184 /** 185 * Called when {@code SettingsProvider} has been published, which is a good 186 * opportunity to reset any settings depending on our rescue level. 187 */ onSettingsProviderPublished(Context context)188 public static void onSettingsProviderPublished(Context context) { 189 handleNativeRescuePartyResets(); 190 ContentResolver contentResolver = context.getContentResolver(); 191 Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> { 192 handleMonitorCallback(context, result); 193 })); 194 } 195 196 197 /** 198 * Called when {@code RollbackManager} performs Mainline module rollbacks, 199 * to avoid rolled back modules consuming flag values only expected to work 200 * on modules of newer versions. 201 */ resetDeviceConfigForPackages(List<String> packageNames)202 public static void resetDeviceConfigForPackages(List<String> packageNames) { 203 if (packageNames == null) { 204 return; 205 } 206 Set<String> namespacesToReset = new ArraySet<String>(); 207 Iterator<String> it = packageNames.iterator(); 208 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); 209 // Get runtime package to namespace mapping if created. 210 if (rescuePartyObserver != null) { 211 while (it.hasNext()) { 212 String packageName = it.next(); 213 Set<String> runtimeAffectedNamespaces = 214 rescuePartyObserver.getAffectedNamespaceSet(packageName); 215 if (runtimeAffectedNamespaces != null) { 216 namespacesToReset.addAll(runtimeAffectedNamespaces); 217 } 218 } 219 } 220 // Get preset package to namespace mapping if created. 221 Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( 222 packageNames); 223 if (presetAffectedNamespaces != null) { 224 namespacesToReset.addAll(presetAffectedNamespaces); 225 } 226 227 // Clear flags under the namespaces mapped to these packages. 228 // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. 229 Iterator<String> namespaceIt = namespacesToReset.iterator(); 230 while (namespaceIt.hasNext()) { 231 String namespaceToReset = namespaceIt.next(); 232 Properties properties = new Properties.Builder(namespaceToReset).build(); 233 try { 234 DeviceConfig.setProperties(properties); 235 } catch (DeviceConfig.BadConfigException exception) { 236 logCriticalInfo(Log.WARN, "namespace " + namespaceToReset 237 + " is already banned, skip reset."); 238 } 239 } 240 } 241 getPresetNamespacesForPackages(List<String> packageNames)242 private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { 243 Set<String> resultSet = new ArraySet<String>(); 244 try { 245 String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, 246 NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); 247 String[] mappingEntries = flagVal.split(","); 248 for (int i = 0; i < mappingEntries.length; i++) { 249 if (TextUtils.isEmpty(mappingEntries[i])) { 250 continue; 251 } 252 String[] splittedEntry = mappingEntries[i].split(":"); 253 if (splittedEntry.length != 2) { 254 throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); 255 } 256 String namespace = splittedEntry[0]; 257 String packageName = splittedEntry[1]; 258 259 if (packageNames.contains(packageName)) { 260 resultSet.add(namespace); 261 } 262 } 263 } catch (Exception e) { 264 resultSet.clear(); 265 Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); 266 } finally { 267 return resultSet; 268 } 269 } 270 271 @VisibleForTesting getElapsedRealtime()272 static long getElapsedRealtime() { 273 return SystemClock.elapsedRealtime(); 274 } 275 handleMonitorCallback(Context context, Bundle result)276 private static void handleMonitorCallback(Context context, Bundle result) { 277 String callbackType = result.getString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, ""); 278 switch (callbackType) { 279 case Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK: 280 String updatedNamespace = result.getString(Settings.EXTRA_NAMESPACE); 281 if (updatedNamespace != null) { 282 startObservingPackages(context, updatedNamespace); 283 } 284 break; 285 case Settings.EXTRA_ACCESS_CALLBACK: 286 String callingPackage = result.getString(Settings.EXTRA_CALLING_PACKAGE, null); 287 String namespace = result.getString(Settings.EXTRA_NAMESPACE, null); 288 if (namespace != null && callingPackage != null) { 289 RescuePartyObserver.getInstance(context).recordDeviceConfigAccess( 290 callingPackage, 291 namespace); 292 } 293 break; 294 default: 295 Slog.w(TAG, "Unrecognized DeviceConfig callback"); 296 break; 297 } 298 } 299 startObservingPackages(Context context, @NonNull String updatedNamespace)300 private static void startObservingPackages(Context context, @NonNull String updatedNamespace) { 301 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); 302 Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace); 303 if (callingPackages == null) { 304 return; 305 } 306 List<String> callingPackageList = new ArrayList<>(); 307 callingPackageList.addAll(callingPackages); 308 Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " 309 + updatedNamespace); 310 PackageWatchdog.getInstance(context).startObservingHealth( 311 rescuePartyObserver, 312 callingPackageList, 313 DEFAULT_OBSERVING_DURATION_MS); 314 } 315 handleNativeRescuePartyResets()316 private static void handleNativeRescuePartyResets() { 317 if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { 318 String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); 319 for (int i = 0; i < resetNativeCategories.length; i++) { 320 // Don't let RescueParty reset the namespace for RescueParty switches. 321 if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { 322 continue; 323 } 324 DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, 325 resetNativeCategories[i]); 326 } 327 } 328 } 329 getMaxRescueLevel()330 private static int getMaxRescueLevel() { 331 return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false) 332 ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET; 333 } 334 335 /** 336 * Get the rescue level to perform if this is the n-th attempt at mitigating failure. 337 * 338 * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.) 339 * @return the rescue level for the n-th mitigation attempt. 340 */ getRescueLevel(int mitigationCount)341 private static int getRescueLevel(int mitigationCount) { 342 if (mitigationCount == 1) { 343 return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; 344 } else if (mitigationCount == 2) { 345 return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; 346 } else if (mitigationCount == 3) { 347 return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; 348 } else if (mitigationCount == 4) { 349 return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT); 350 } else if (mitigationCount >= 5) { 351 return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET); 352 } else { 353 Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); 354 return LEVEL_NONE; 355 } 356 } 357 executeRescueLevel(Context context, @Nullable String failedPackage, int level)358 private static void executeRescueLevel(Context context, @Nullable String failedPackage, 359 int level) { 360 Slog.w(TAG, "Attempting rescue level " + levelToString(level)); 361 try { 362 executeRescueLevelInternal(context, level, failedPackage); 363 EventLogTags.writeRescueSuccess(level); 364 String successMsg = "Finished rescue level " + levelToString(level); 365 if (!TextUtils.isEmpty(failedPackage)) { 366 successMsg += " for package " + failedPackage; 367 } 368 logCriticalInfo(Log.DEBUG, successMsg); 369 } catch (Throwable t) { 370 logRescueException(level, failedPackage, t); 371 } 372 } 373 executeRescueLevelInternal(Context context, int level, @Nullable String failedPackage)374 private static void executeRescueLevelInternal(Context context, int level, @Nullable 375 String failedPackage) throws Exception { 376 FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level); 377 // Try our best to reset all settings possible, and once finished 378 // rethrow any exception that we encountered 379 Exception res = null; 380 Runnable runnable; 381 Thread thread; 382 switch (level) { 383 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: 384 try { 385 resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, 386 level); 387 } catch (Exception e) { 388 res = e; 389 } 390 try { 391 resetDeviceConfig(context, /*isScoped=*/true, failedPackage); 392 } catch (Exception e) { 393 res = e; 394 } 395 break; 396 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: 397 try { 398 resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, 399 level); 400 } catch (Exception e) { 401 res = e; 402 } 403 try { 404 resetDeviceConfig(context, /*isScoped=*/true, failedPackage); 405 } catch (Exception e) { 406 res = e; 407 } 408 break; 409 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: 410 try { 411 resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, 412 level); 413 } catch (Exception e) { 414 res = e; 415 } 416 try { 417 resetDeviceConfig(context, /*isScoped=*/false, failedPackage); 418 } catch (Exception e) { 419 res = e; 420 } 421 break; 422 case LEVEL_WARM_REBOOT: 423 // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog 424 // when device shutting down. 425 SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true"); 426 runnable = () -> { 427 try { 428 PowerManager pm = context.getSystemService(PowerManager.class); 429 if (pm != null) { 430 pm.reboot(TAG); 431 } 432 } catch (Throwable t) { 433 logRescueException(level, failedPackage, t); 434 } 435 }; 436 thread = new Thread(runnable); 437 thread.start(); 438 break; 439 case LEVEL_FACTORY_RESET: 440 SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true"); 441 runnable = new Runnable() { 442 @Override 443 public void run() { 444 try { 445 RecoverySystem.rebootPromptAndWipeUserData(context, TAG); 446 } catch (Throwable t) { 447 logRescueException(level, failedPackage, t); 448 } 449 } 450 }; 451 thread = new Thread(runnable); 452 thread.start(); 453 break; 454 } 455 456 if (res != null) { 457 throw res; 458 } 459 } 460 logRescueException(int level, @Nullable String failedPackageName, Throwable t)461 private static void logRescueException(int level, @Nullable String failedPackageName, 462 Throwable t) { 463 final String msg = ExceptionUtils.getCompleteMessage(t); 464 EventLogTags.writeRescueFailure(level, msg); 465 String failureMsg = "Failed rescue level " + levelToString(level); 466 if (!TextUtils.isEmpty(failedPackageName)) { 467 failureMsg += " for package " + failedPackageName; 468 } 469 logCriticalInfo(Log.ERROR, failureMsg + ": " + msg); 470 } 471 mapRescueLevelToUserImpact(int rescueLevel)472 private static int mapRescueLevelToUserImpact(int rescueLevel) { 473 switch(rescueLevel) { 474 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: 475 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: 476 return PackageHealthObserverImpact.USER_IMPACT_LOW; 477 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: 478 case LEVEL_WARM_REBOOT: 479 case LEVEL_FACTORY_RESET: 480 return PackageHealthObserverImpact.USER_IMPACT_HIGH; 481 default: 482 return PackageHealthObserverImpact.USER_IMPACT_NONE; 483 } 484 } 485 resetAllSettingsIfNecessary(Context context, int mode, int level)486 private static void resetAllSettingsIfNecessary(Context context, int mode, 487 int level) throws Exception { 488 // No need to reset Settings again if they are already reset in the current level once. 489 if (SystemProperties.getInt(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, LEVEL_NONE) >= level) { 490 return; 491 } 492 SystemProperties.set(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, Integer.toString(level)); 493 // Try our best to reset all settings possible, and once finished 494 // rethrow any exception that we encountered 495 Exception res = null; 496 final ContentResolver resolver = context.getContentResolver(); 497 try { 498 Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM); 499 } catch (Exception e) { 500 res = new RuntimeException("Failed to reset global settings", e); 501 } 502 for (int userId : getAllUserIds()) { 503 try { 504 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); 505 } catch (Exception e) { 506 res = new RuntimeException("Failed to reset secure settings for " + userId, e); 507 } 508 } 509 if (res != null) { 510 throw res; 511 } 512 } 513 resetDeviceConfig(Context context, boolean isScoped, @Nullable String failedPackage)514 private static void resetDeviceConfig(Context context, boolean isScoped, 515 @Nullable String failedPackage) throws Exception { 516 final ContentResolver resolver = context.getContentResolver(); 517 try { 518 if (!isScoped || failedPackage == null) { 519 resetAllAffectedNamespaces(context); 520 } else { 521 performScopedReset(context, failedPackage); 522 } 523 } catch (Exception e) { 524 throw new RuntimeException("Failed to reset config settings", e); 525 } 526 } 527 resetAllAffectedNamespaces(Context context)528 private static void resetAllAffectedNamespaces(Context context) { 529 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); 530 Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet(); 531 532 Slog.w(TAG, 533 "Performing reset for all affected namespaces: " 534 + Arrays.toString(allAffectedNamespaces.toArray())); 535 Iterator<String> it = allAffectedNamespaces.iterator(); 536 while (it.hasNext()) { 537 String namespace = it.next(); 538 // Don't let RescueParty reset the namespace for RescueParty switches. 539 if (NAMESPACE_CONFIGURATION.equals(namespace)) { 540 continue; 541 } 542 DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace); 543 } 544 } 545 performScopedReset(Context context, @NonNull String failedPackage)546 private static void performScopedReset(Context context, @NonNull String failedPackage) { 547 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); 548 Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet( 549 failedPackage); 550 // If we can't find namespaces affected for current package, 551 // skip this round of reset. 552 if (affectedNamespaces != null) { 553 Slog.w(TAG, 554 "Performing scoped reset for package: " + failedPackage 555 + ", affected namespaces: " 556 + Arrays.toString(affectedNamespaces.toArray())); 557 Iterator<String> it = affectedNamespaces.iterator(); 558 while (it.hasNext()) { 559 String namespace = it.next(); 560 // Don't let RescueParty reset the namespace for RescueParty switches. 561 if (NAMESPACE_CONFIGURATION.equals(namespace)) { 562 continue; 563 } 564 DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace); 565 } 566 } 567 } 568 569 /** 570 * Handle mitigation action for package failures. This observer will be register to Package 571 * Watchdog and will receive calls about package failures. This observer is persistent so it 572 * may choose to mitigate failures for packages it has not explicitly asked to observe. 573 */ 574 public static class RescuePartyObserver implements PackageHealthObserver { 575 576 private final Context mContext; 577 private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>(); 578 private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>(); 579 580 @GuardedBy("RescuePartyObserver.class") 581 static RescuePartyObserver sRescuePartyObserver; 582 RescuePartyObserver(Context context)583 private RescuePartyObserver(Context context) { 584 mContext = context; 585 } 586 587 /** Creates or gets singleton instance of RescueParty. */ getInstance(Context context)588 public static RescuePartyObserver getInstance(Context context) { 589 synchronized (RescuePartyObserver.class) { 590 if (sRescuePartyObserver == null) { 591 sRescuePartyObserver = new RescuePartyObserver(context); 592 } 593 return sRescuePartyObserver; 594 } 595 } 596 597 /** Gets singleton instance. It returns null if the instance is not created yet.*/ 598 @Nullable getInstanceIfCreated()599 public static RescuePartyObserver getInstanceIfCreated() { 600 synchronized (RescuePartyObserver.class) { 601 return sRescuePartyObserver; 602 } 603 } 604 605 @VisibleForTesting reset()606 static void reset() { 607 synchronized (RescuePartyObserver.class) { 608 sRescuePartyObserver = null; 609 } 610 } 611 612 @Override onHealthCheckFailed(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount)613 public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, 614 @FailureReasons int failureReason, int mitigationCount) { 615 if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH 616 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { 617 return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); 618 } else { 619 return PackageHealthObserverImpact.USER_IMPACT_NONE; 620 } 621 } 622 623 @Override execute(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount)624 public boolean execute(@Nullable VersionedPackage failedPackage, 625 @FailureReasons int failureReason, int mitigationCount) { 626 if (isDisabled()) { 627 return false; 628 } 629 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH 630 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { 631 final int level = getRescueLevel(mitigationCount); 632 executeRescueLevel(mContext, 633 failedPackage == null ? null : failedPackage.getPackageName(), level); 634 return true; 635 } else { 636 return false; 637 } 638 } 639 640 @Override isPersistent()641 public boolean isPersistent() { 642 return true; 643 } 644 645 @Override mayObservePackage(String packageName)646 public boolean mayObservePackage(String packageName) { 647 PackageManager pm = mContext.getPackageManager(); 648 try { 649 // A package is a module if this is non-null 650 if (pm.getModuleInfo(packageName, 0) != null) { 651 return true; 652 } 653 } catch (PackageManager.NameNotFoundException ignore) { 654 } 655 656 try { 657 ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 658 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; 659 } catch (PackageManager.NameNotFoundException e) { 660 return false; 661 } 662 } 663 664 @Override onBootLoop(int mitigationCount)665 public int onBootLoop(int mitigationCount) { 666 if (isDisabled()) { 667 return PackageHealthObserverImpact.USER_IMPACT_NONE; 668 } 669 return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); 670 } 671 672 @Override executeBootLoopMitigation(int mitigationCount)673 public boolean executeBootLoopMitigation(int mitigationCount) { 674 if (isDisabled()) { 675 return false; 676 } 677 executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); 678 return true; 679 } 680 681 @Override getName()682 public String getName() { 683 return NAME; 684 } 685 recordDeviceConfigAccess(@onNull String callingPackage, @NonNull String namespace)686 private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, 687 @NonNull String namespace) { 688 // Record it in calling packages to namespace map 689 Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); 690 if (namespaceSet == null) { 691 namespaceSet = new ArraySet<>(); 692 mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); 693 } 694 namespaceSet.add(namespace); 695 // Record it in namespace to calling packages map 696 Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); 697 if (callingPackageSet == null) { 698 callingPackageSet = new ArraySet<>(); 699 } 700 callingPackageSet.add(callingPackage); 701 mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); 702 } 703 getAffectedNamespaceSet(String failedPackage)704 private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) { 705 return mCallingPackageNamespaceSetMap.get(failedPackage); 706 } 707 getAllAffectedNamespaceSet()708 private synchronized Set<String> getAllAffectedNamespaceSet() { 709 return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet()); 710 } 711 getCallingPackagesSet(String namespace)712 private synchronized Set<String> getCallingPackagesSet(String namespace) { 713 return mNamespaceCallingPackageSetMap.get(namespace); 714 } 715 } 716 getAllUserIds()717 private static int[] getAllUserIds() { 718 int[] userIds = { UserHandle.USER_SYSTEM }; 719 try { 720 for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) { 721 try { 722 final int userId = Integer.parseInt(file.getName()); 723 if (userId != UserHandle.USER_SYSTEM) { 724 userIds = ArrayUtils.appendInt(userIds, userId); 725 } 726 } catch (NumberFormatException ignored) { 727 } 728 } 729 } catch (Throwable t) { 730 Slog.w(TAG, "Trouble discovering users", t); 731 } 732 return userIds; 733 } 734 735 /** 736 * Hacky test to check if the device has an active USB connection, which is 737 * a good proxy for someone doing local development work. 738 */ isUsbActive()739 private static boolean isUsbActive() { 740 if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) { 741 Slog.v(TAG, "Assuming virtual device is connected over USB"); 742 return true; 743 } 744 try { 745 final String state = FileUtils 746 .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, ""); 747 return "CONFIGURED".equals(state.trim()); 748 } catch (Throwable t) { 749 Slog.w(TAG, "Failed to determine if device was on USB", t); 750 return false; 751 } 752 } 753 levelToString(int level)754 private static String levelToString(int level) { 755 switch (level) { 756 case LEVEL_NONE: return "NONE"; 757 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS"; 758 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES"; 759 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS"; 760 case LEVEL_WARM_REBOOT: return "WARM_REBOOT"; 761 case LEVEL_FACTORY_RESET: return "FACTORY_RESET"; 762 default: return Integer.toString(level); 763 } 764 } 765 } 766