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(boolean mayPerformFactoryReset)330     private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
331         if (!mayPerformFactoryReset
332                 || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
333             return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
334         }
335         return LEVEL_FACTORY_RESET;
336     }
337 
338     /**
339      * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
340      *
341      * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
342      * @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
343      *                              failure.
344      * @return the rescue level for the n-th mitigation attempt.
345      */
getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset)346     private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
347         if (mitigationCount == 1) {
348             return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
349         } else if (mitigationCount == 2) {
350             return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
351         } else if (mitigationCount == 3) {
352             return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
353         } else if (mitigationCount == 4) {
354             return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
355         } else if (mitigationCount >= 5) {
356             return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
357         } else {
358             Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
359             return LEVEL_NONE;
360         }
361     }
362 
executeRescueLevel(Context context, @Nullable String failedPackage, int level)363     private static void executeRescueLevel(Context context, @Nullable String failedPackage,
364             int level) {
365         Slog.w(TAG, "Attempting rescue level " + levelToString(level));
366         try {
367             executeRescueLevelInternal(context, level, failedPackage);
368             EventLogTags.writeRescueSuccess(level);
369             String successMsg = "Finished rescue level " + levelToString(level);
370             if (!TextUtils.isEmpty(failedPackage)) {
371                 successMsg += " for package " + failedPackage;
372             }
373             logCriticalInfo(Log.DEBUG, successMsg);
374         } catch (Throwable t) {
375             logRescueException(level, failedPackage, t);
376         }
377     }
378 
executeRescueLevelInternal(Context context, int level, @Nullable String failedPackage)379     private static void executeRescueLevelInternal(Context context, int level, @Nullable
380             String failedPackage) throws Exception {
381         FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
382         // Try our best to reset all settings possible, and once finished
383         // rethrow any exception that we encountered
384         Exception res = null;
385         Runnable runnable;
386         Thread thread;
387         switch (level) {
388             case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
389                 try {
390                     resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
391                             level);
392                 } catch (Exception e) {
393                     res = e;
394                 }
395                 try {
396                     resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
397                 } catch (Exception e) {
398                     res = e;
399                 }
400                 break;
401             case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
402                 try {
403                     resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
404                             level);
405                 } catch (Exception e) {
406                     res = e;
407                 }
408                 try {
409                     resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
410                 } catch (Exception e) {
411                     res = e;
412                 }
413                 break;
414             case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
415                 try {
416                     resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
417                             level);
418                 } catch (Exception e) {
419                     res = e;
420                 }
421                 try {
422                     resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
423                 } catch (Exception e) {
424                     res = e;
425                 }
426                 break;
427             case LEVEL_WARM_REBOOT:
428                 // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
429                 // when device shutting down.
430                 SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true");
431                 runnable = () -> {
432                     try {
433                         PowerManager pm = context.getSystemService(PowerManager.class);
434                         if (pm != null) {
435                             pm.reboot(TAG);
436                         }
437                     } catch (Throwable t) {
438                         logRescueException(level, failedPackage, t);
439                     }
440                 };
441                 thread = new Thread(runnable);
442                 thread.start();
443                 break;
444             case LEVEL_FACTORY_RESET:
445                 SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true");
446                 runnable = new Runnable() {
447                     @Override
448                     public void run() {
449                         try {
450                             RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
451                         } catch (Throwable t) {
452                             logRescueException(level, failedPackage, t);
453                         }
454                     }
455                 };
456                 thread = new Thread(runnable);
457                 thread.start();
458                 break;
459         }
460 
461         if (res != null) {
462             throw res;
463         }
464     }
465 
logRescueException(int level, @Nullable String failedPackageName, Throwable t)466     private static void logRescueException(int level, @Nullable String failedPackageName,
467             Throwable t) {
468         final String msg = ExceptionUtils.getCompleteMessage(t);
469         EventLogTags.writeRescueFailure(level, msg);
470         String failureMsg = "Failed rescue level " + levelToString(level);
471         if (!TextUtils.isEmpty(failedPackageName)) {
472             failureMsg += " for package " + failedPackageName;
473         }
474         logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
475     }
476 
mapRescueLevelToUserImpact(int rescueLevel)477     private static int mapRescueLevelToUserImpact(int rescueLevel) {
478         switch(rescueLevel) {
479             case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
480             case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
481                 return PackageHealthObserverImpact.USER_IMPACT_LOW;
482             case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
483             case LEVEL_WARM_REBOOT:
484             case LEVEL_FACTORY_RESET:
485                 return PackageHealthObserverImpact.USER_IMPACT_HIGH;
486             default:
487                 return PackageHealthObserverImpact.USER_IMPACT_NONE;
488         }
489     }
490 
resetAllSettingsIfNecessary(Context context, int mode, int level)491     private static void resetAllSettingsIfNecessary(Context context, int mode,
492             int level) throws Exception {
493         // No need to reset Settings again if they are already reset in the current level once.
494         if (SystemProperties.getInt(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, LEVEL_NONE) >= level) {
495             return;
496         }
497         SystemProperties.set(PROP_MAX_RESCUE_LEVEL_ATTEMPTED, Integer.toString(level));
498         // Try our best to reset all settings possible, and once finished
499         // rethrow any exception that we encountered
500         Exception res = null;
501         final ContentResolver resolver = context.getContentResolver();
502         try {
503             Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
504         } catch (Exception e) {
505             res = new RuntimeException("Failed to reset global settings", e);
506         }
507         for (int userId : getAllUserIds()) {
508             try {
509                 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
510             } catch (Exception e) {
511                 res = new RuntimeException("Failed to reset secure settings for " + userId, e);
512             }
513         }
514         if (res != null) {
515             throw res;
516         }
517     }
518 
resetDeviceConfig(Context context, boolean isScoped, @Nullable String failedPackage)519     private static void resetDeviceConfig(Context context, boolean isScoped,
520             @Nullable String failedPackage) throws Exception {
521         final ContentResolver resolver = context.getContentResolver();
522         try {
523             if (!isScoped || failedPackage == null) {
524                 resetAllAffectedNamespaces(context);
525             } else {
526                 performScopedReset(context, failedPackage);
527             }
528         } catch (Exception e) {
529             throw new RuntimeException("Failed to reset config settings", e);
530         }
531     }
532 
resetAllAffectedNamespaces(Context context)533     private static void resetAllAffectedNamespaces(Context context) {
534         RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
535         Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet();
536 
537         Slog.w(TAG,
538                 "Performing reset for all affected namespaces: "
539                         + Arrays.toString(allAffectedNamespaces.toArray()));
540         Iterator<String> it = allAffectedNamespaces.iterator();
541         while (it.hasNext()) {
542             String namespace = it.next();
543             // Don't let RescueParty reset the namespace for RescueParty switches.
544             if (NAMESPACE_CONFIGURATION.equals(namespace)) {
545                 continue;
546             }
547             DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
548         }
549     }
550 
performScopedReset(Context context, @NonNull String failedPackage)551     private static void performScopedReset(Context context, @NonNull String failedPackage) {
552         RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
553         Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
554                 failedPackage);
555         // If we can't find namespaces affected for current package,
556         // skip this round of reset.
557         if (affectedNamespaces != null) {
558             Slog.w(TAG,
559                     "Performing scoped reset for package: " + failedPackage
560                             + ", affected namespaces: "
561                             + Arrays.toString(affectedNamespaces.toArray()));
562             Iterator<String> it = affectedNamespaces.iterator();
563             while (it.hasNext()) {
564                 String namespace = it.next();
565                 // Don't let RescueParty reset the namespace for RescueParty switches.
566                 if (NAMESPACE_CONFIGURATION.equals(namespace)) {
567                     continue;
568                 }
569                 DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
570             }
571         }
572     }
573 
574     /**
575      * Handle mitigation action for package failures. This observer will be register to Package
576      * Watchdog and will receive calls about package failures. This observer is persistent so it
577      * may choose to mitigate failures for packages it has not explicitly asked to observe.
578      */
579     public static class RescuePartyObserver implements PackageHealthObserver {
580 
581         private final Context mContext;
582         private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>();
583         private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>();
584 
585         @GuardedBy("RescuePartyObserver.class")
586         static RescuePartyObserver sRescuePartyObserver;
587 
RescuePartyObserver(Context context)588         private RescuePartyObserver(Context context) {
589             mContext = context;
590         }
591 
592         /** Creates or gets singleton instance of RescueParty. */
getInstance(Context context)593         public static RescuePartyObserver getInstance(Context context) {
594             synchronized (RescuePartyObserver.class) {
595                 if (sRescuePartyObserver == null) {
596                     sRescuePartyObserver = new RescuePartyObserver(context);
597                 }
598                 return sRescuePartyObserver;
599             }
600         }
601 
602         /** Gets singleton instance. It returns null if the instance is not created yet.*/
603         @Nullable
getInstanceIfCreated()604         public static RescuePartyObserver getInstanceIfCreated() {
605             synchronized (RescuePartyObserver.class) {
606                 return sRescuePartyObserver;
607             }
608         }
609 
610         @VisibleForTesting
reset()611         static void reset() {
612             synchronized (RescuePartyObserver.class) {
613                 sRescuePartyObserver = null;
614             }
615         }
616 
617         @Override
onHealthCheckFailed(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount)618         public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
619                 @FailureReasons int failureReason, int mitigationCount) {
620             if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
621                     || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
622                 return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
623                         mayPerformFactoryReset(failedPackage)));
624             } else {
625                 return PackageHealthObserverImpact.USER_IMPACT_NONE;
626             }
627         }
628 
629         @Override
execute(@ullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount)630         public boolean execute(@Nullable VersionedPackage failedPackage,
631                 @FailureReasons int failureReason, int mitigationCount) {
632             if (isDisabled()) {
633                 return false;
634             }
635             if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
636                     || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
637                 final int level = getRescueLevel(mitigationCount,
638                         mayPerformFactoryReset(failedPackage));
639                 executeRescueLevel(mContext,
640                         failedPackage == null ? null : failedPackage.getPackageName(), level);
641                 return true;
642             } else {
643                 return false;
644             }
645         }
646 
647         @Override
isPersistent()648         public boolean isPersistent() {
649             return true;
650         }
651 
652         @Override
mayObservePackage(String packageName)653         public boolean mayObservePackage(String packageName) {
654             PackageManager pm = mContext.getPackageManager();
655             try {
656                 // A package is a module if this is non-null
657                 if (pm.getModuleInfo(packageName, 0) != null) {
658                     return true;
659                 }
660             } catch (PackageManager.NameNotFoundException ignore) {
661             }
662 
663             return isPersistentSystemApp(packageName);
664         }
665 
666         @Override
onBootLoop(int mitigationCount)667         public int onBootLoop(int mitigationCount) {
668             if (isDisabled()) {
669                 return PackageHealthObserverImpact.USER_IMPACT_NONE;
670             }
671             return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
672         }
673 
674         @Override
executeBootLoopMitigation(int mitigationCount)675         public boolean executeBootLoopMitigation(int mitigationCount) {
676             if (isDisabled()) {
677                 return false;
678             }
679             executeRescueLevel(mContext, /*failedPackage=*/ null,
680                     getRescueLevel(mitigationCount, true));
681             return true;
682         }
683 
684         @Override
getName()685         public String getName() {
686             return NAME;
687         }
688 
689         /**
690          * Returns {@code true} if the failing package is non-null and performing a reboot or
691          * prompting a factory reset is an acceptable mitigation strategy for the package's
692          * failure, {@code false} otherwise.
693          */
mayPerformFactoryReset(@ullable VersionedPackage failingPackage)694         private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
695             if (failingPackage == null) {
696                 return false;
697             }
698 
699             return isPersistentSystemApp(failingPackage.getPackageName());
700         }
701 
isPersistentSystemApp(@onNull String packageName)702         private boolean isPersistentSystemApp(@NonNull String packageName) {
703             PackageManager pm = mContext.getPackageManager();
704             try {
705                 ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
706                 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
707             } catch (PackageManager.NameNotFoundException e) {
708                 return false;
709             }
710         }
711 
recordDeviceConfigAccess(@onNull String callingPackage, @NonNull String namespace)712         private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
713                 @NonNull String namespace) {
714             // Record it in calling packages to namespace map
715             Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
716             if (namespaceSet == null) {
717                 namespaceSet = new ArraySet<>();
718                 mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
719             }
720             namespaceSet.add(namespace);
721             // Record it in namespace to calling packages map
722             Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
723             if (callingPackageSet == null) {
724                 callingPackageSet = new ArraySet<>();
725             }
726             callingPackageSet.add(callingPackage);
727             mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
728         }
729 
getAffectedNamespaceSet(String failedPackage)730         private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
731             return mCallingPackageNamespaceSetMap.get(failedPackage);
732         }
733 
getAllAffectedNamespaceSet()734         private synchronized Set<String> getAllAffectedNamespaceSet() {
735             return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
736         }
737 
getCallingPackagesSet(String namespace)738         private synchronized Set<String> getCallingPackagesSet(String namespace) {
739             return mNamespaceCallingPackageSetMap.get(namespace);
740         }
741     }
742 
getAllUserIds()743     private static int[] getAllUserIds() {
744         int[] userIds = { UserHandle.USER_SYSTEM };
745         try {
746             for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
747                 try {
748                     final int userId = Integer.parseInt(file.getName());
749                     if (userId != UserHandle.USER_SYSTEM) {
750                         userIds = ArrayUtils.appendInt(userIds, userId);
751                     }
752                 } catch (NumberFormatException ignored) {
753                 }
754             }
755         } catch (Throwable t) {
756             Slog.w(TAG, "Trouble discovering users", t);
757         }
758         return userIds;
759     }
760 
761     /**
762      * Hacky test to check if the device has an active USB connection, which is
763      * a good proxy for someone doing local development work.
764      */
isUsbActive()765     private static boolean isUsbActive() {
766         if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) {
767             Slog.v(TAG, "Assuming virtual device is connected over USB");
768             return true;
769         }
770         try {
771             final String state = FileUtils
772                     .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
773             return "CONFIGURED".equals(state.trim());
774         } catch (Throwable t) {
775             Slog.w(TAG, "Failed to determine if device was on USB", t);
776             return false;
777         }
778     }
779 
levelToString(int level)780     private static String levelToString(int level) {
781         switch (level) {
782             case LEVEL_NONE: return "NONE";
783             case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
784             case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES";
785             case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS";
786             case LEVEL_WARM_REBOOT: return "WARM_REBOOT";
787             case LEVEL_FACTORY_RESET: return "FACTORY_RESET";
788             default: return Integer.toString(level);
789         }
790     }
791 }
792