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