• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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