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