blob: 131a22b07c7baa9bbb66497aebede8f3a48d5369 [file] [log] [blame]
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -07001/*
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
17package com.android.server;
18
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070019import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
20
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000021import android.annotation.NonNull;
Gavin Corkery69395652019-12-12 19:06:47 +000022import android.annotation.Nullable;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070023import android.content.ContentResolver;
24import android.content.Context;
Gavin Corkery69395652019-12-12 19:06:47 +000025import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.VersionedPackage;
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -070028import android.os.Build;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000029import android.os.Bundle;
Jeff Sharkey82311462017-04-02 23:42:17 -060030import android.os.Environment;
Jeff Sharkey1bec4482017-02-23 12:40:54 -070031import android.os.FileUtils;
Gavin Corkeryaa57ef32019-12-17 19:02:54 +000032import android.os.Process;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070033import android.os.RecoverySystem;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000034import android.os.RemoteCallback;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070035import android.os.SystemClock;
36import android.os.SystemProperties;
37import android.os.UserHandle;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000038import android.provider.DeviceConfig;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070039import android.provider.Settings;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000040import android.util.ArraySet;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070041import android.util.ExceptionUtils;
Jeff Sharkeybc9caa12017-03-11 20:38:21 -070042import android.util.Log;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070043import android.util.MathUtils;
44import android.util.Slog;
Christian Brunschenf86039e2018-12-21 12:26:14 +000045import android.util.StatsLog;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070046
Gavin Corkery69395652019-12-12 19:06:47 +000047import com.android.internal.annotations.GuardedBy;
bpetrivsa7101952019-02-07 16:01:24 +000048import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070049import com.android.internal.util.ArrayUtils;
Gavin Corkery69395652019-12-12 19:06:47 +000050import com.android.server.PackageWatchdog.FailureReasons;
51import com.android.server.PackageWatchdog.PackageHealthObserver;
52import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
bpetrivs93075f42019-02-28 12:08:12 +000053import com.android.server.am.SettingsToPropertiesMapper;
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070054
Jeff Sharkey1bec4482017-02-23 12:40:54 -070055import java.io.File;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000056import java.util.ArrayList;
bpetrivs93075f42019-02-28 12:08:12 +000057import java.util.Arrays;
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000058import java.util.HashMap;
59import java.util.Iterator;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63import java.util.concurrent.TimeUnit;
Jeff Sharkeyd9574c72017-02-20 10:45:06 -070064
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070065/**
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 */
74public class RescueParty {
bpetrivsa7101952019-02-07 16:01:24 +000075 @VisibleForTesting
76 static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
77 @VisibleForTesting
bpetrivsa7101952019-02-07 16:01:24 +000078 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";
bpetrivsa7101952019-02-07 16:01:24 +000091 @VisibleForTesting
bpetrivsa7101952019-02-07 16:01:24 +000092 static final String TAG = "RescueParty";
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +000093 @VisibleForTesting
94 static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -070095
Gavin Corkery69395652019-12-12 19:06:47 +000096 private static final String NAME = "rescue-party-observer";
97
98
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -070099 private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
Jeff Sharkey9d640952017-06-26 19:57:16 -0600100 private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700101
Gavin Corkery69395652019-12-12 19:06:47 +0000102 private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
103 | ApplicationInfo.FLAG_SYSTEM;
104
Gavin Corkery69395652019-12-12 19:06:47 +0000105 /** Register the Rescue Party observer as a Package Watchdog health observer */
106 public static void registerHealthObserver(Context context) {
107 PackageWatchdog.getInstance(context).registerHealthObserver(
108 RescuePartyObserver.getInstance(context));
109 }
110
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -0700111 private static boolean isDisabled() {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700112 // Check if we're explicitly enabled for testing
Jeff Sharkey82311462017-04-02 23:42:17 -0600113 if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700114 return false;
115 }
116
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700117 // We're disabled on all engineering devices
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700118 if (Build.IS_ENG) {
119 Slog.v(TAG, "Disabled because of eng build");
120 return true;
121 }
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700122
123 // We're disabled on userdebug devices connected over USB, since that's
124 // a decent signal that someone is actively trying to debug the device,
125 // or that it's in a lab environment.
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700126 if (Build.IS_USERDEBUG && isUsbActive()) {
127 Slog.v(TAG, "Disabled because of active USB connection");
128 return true;
Jeff Sharkeycdee83a2017-01-26 15:29:16 -0700129 }
130
131 // One last-ditch check
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700132 if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
133 Slog.v(TAG, "Disabled because of manual property");
134 return true;
135 }
136
137 return false;
Jeff Sharkey9f1fc2d2017-01-24 11:05:16 -0700138 }
139
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700140 /**
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700141 * Check if we're currently attempting to reboot for a factory reset.
142 */
143 public static boolean isAttemptingFactoryReset() {
144 return SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) == LEVEL_FACTORY_RESET;
145 }
146
147 /**
bpetrivsa7101952019-02-07 16:01:24 +0000148 * Called when {@code SettingsProvider} has been published, which is a good
149 * opportunity to reset any settings depending on our rescue level.
150 */
151 public static void onSettingsProviderPublished(Context context) {
bpetrivs93075f42019-02-28 12:08:12 +0000152 handleNativeRescuePartyResets();
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000153 executeRescueLevel(context, /*failedPackage=*/ null);
154 ContentResolver contentResolver = context.getContentResolver();
155 Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> {
156 handleMonitorCallback(context, result);
157 }));
bpetrivsa7101952019-02-07 16:01:24 +0000158 }
159
160 @VisibleForTesting
bpetrivsa7101952019-02-07 16:01:24 +0000161 static long getElapsedRealtime() {
162 return SystemClock.elapsedRealtime();
163 }
164
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000165 private static void handleMonitorCallback(Context context, Bundle result) {
166 String callbackType = result.getString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, "");
167 switch (callbackType) {
168 case Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK:
169 String updatedNamespace = result.getString(Settings.EXTRA_NAMESPACE);
170 if (updatedNamespace != null) {
171 startObservingPackages(context, updatedNamespace);
172 }
173 break;
174 case Settings.EXTRA_ACCESS_CALLBACK:
175 String callingPackage = result.getString(Settings.EXTRA_CALLING_PACKAGE, null);
176 String namespace = result.getString(Settings.EXTRA_NAMESPACE, null);
177 if (namespace != null && callingPackage != null) {
178 RescuePartyObserver.getInstance(context).recordDeviceConfigAccess(
179 callingPackage,
180 namespace);
181 }
182 break;
183 default:
184 Slog.w(TAG, "Unrecognized DeviceConfig callback");
185 break;
186 }
187 }
188
189 private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
190 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
191 Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace);
192 if (callingPackages == null) {
193 return;
194 }
195 List<String> callingPackageList = new ArrayList<>();
196 callingPackageList.addAll(callingPackages);
197 Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
198 + updatedNamespace);
199 PackageWatchdog.getInstance(context).startObservingHealth(
200 rescuePartyObserver,
201 callingPackageList,
202 DEFAULT_OBSERVING_DURATION_MS);
203 }
204
bpetrivs93075f42019-02-28 12:08:12 +0000205 private static void handleNativeRescuePartyResets() {
206 if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000207 String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories();
208 for (int i = 0; i < resetNativeCategories.length; i++) {
209 DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
210 resetNativeCategories[i]);
211 }
bpetrivs93075f42019-02-28 12:08:12 +0000212 }
213 }
214
bpetrivsa7101952019-02-07 16:01:24 +0000215 /**
Gavin Corkeryaa57ef32019-12-17 19:02:54 +0000216 * Get the current rescue level.
217 */
218 private static int getRescueLevel() {
219 return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
220 LEVEL_NONE, LEVEL_FACTORY_RESET);
221 }
222
223 /**
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700224 * Escalate to the next rescue level. After incrementing the level you'll
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000225 * probably want to call {@link #executeRescueLevel(Context, String)}.
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700226 */
227 private static void incrementRescueLevel(int triggerUid) {
228 final int level = MathUtils.constrain(
229 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
230 LEVEL_NONE, LEVEL_FACTORY_RESET);
231 SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level));
232
233 EventLogTags.writeRescueLevel(level, triggerUid);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700234 logCriticalInfo(Log.WARN, "Incremented rescue level to "
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700235 + levelToString(level) + " triggered by UID " + triggerUid);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700236 }
237
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000238 private static void executeRescueLevel(Context context, @Nullable String failedPackage) {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700239 final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE);
240 if (level == LEVEL_NONE) return;
241
242 Slog.w(TAG, "Attempting rescue level " + levelToString(level));
243 try {
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000244 executeRescueLevelInternal(context, level, failedPackage);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700245 EventLogTags.writeRescueSuccess(level);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700246 logCriticalInfo(Log.DEBUG,
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700247 "Finished rescue level " + levelToString(level));
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700248 } catch (Throwable t) {
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700249 final String msg = ExceptionUtils.getCompleteMessage(t);
250 EventLogTags.writeRescueFailure(level, msg);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700251 logCriticalInfo(Log.ERROR,
Jeff Sharkeybc9caa12017-03-11 20:38:21 -0700252 "Failed rescue level " + levelToString(level) + ": " + msg);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700253 }
254 }
255
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000256 private static void executeRescueLevelInternal(Context context, int level, @Nullable
257 String failedPackage) throws Exception {
Christian Brunschenf86039e2018-12-21 12:26:14 +0000258 StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700259 switch (level) {
260 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000261 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, failedPackage);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700262 break;
263 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000264 resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, failedPackage);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700265 break;
266 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000267 resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700268 break;
269 case LEVEL_FACTORY_RESET:
270 RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
271 break;
272 }
273 }
274
Gavin Corkery69395652019-12-12 19:06:47 +0000275 private static int mapRescueLevelToUserImpact(int rescueLevel) {
276 switch(rescueLevel) {
277 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
278 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
279 return PackageHealthObserverImpact.USER_IMPACT_LOW;
280 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
281 case LEVEL_FACTORY_RESET:
282 return PackageHealthObserverImpact.USER_IMPACT_HIGH;
283 default:
284 return PackageHealthObserverImpact.USER_IMPACT_NONE;
285 }
286 }
287
288 private static int getPackageUid(Context context, String packageName) {
289 try {
290 return context.getPackageManager().getPackageUid(packageName, 0);
291 } catch (PackageManager.NameNotFoundException e) {
292 // Since UIDs are always >= 0, this value means the UID could not be determined.
293 return -1;
294 }
295 }
296
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000297 private static void resetAllSettings(Context context, int mode, @Nullable String failedPackage)
298 throws Exception {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700299 // Try our best to reset all settings possible, and once finished
300 // rethrow any exception that we encountered
301 Exception res = null;
302 final ContentResolver resolver = context.getContentResolver();
303 try {
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000304 resetDeviceConfig(context, mode, failedPackage);
bpetrivs62f15982019-02-13 17:18:16 +0000305 } catch (Exception e) {
306 res = new RuntimeException("Failed to reset config settings", e);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700307 }
bpetrivs0254ff62019-03-01 11:50:45 +0000308 try {
309 Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
310 } catch (Exception e) {
311 res = new RuntimeException("Failed to reset global settings", e);
312 }
Jeff Sharkey82311462017-04-02 23:42:17 -0600313 for (int userId : getAllUserIds()) {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700314 try {
315 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
bpetrivs62f15982019-02-13 17:18:16 +0000316 } catch (Exception e) {
317 res = new RuntimeException("Failed to reset secure settings for " + userId, e);
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700318 }
319 }
320 if (res != null) {
321 throw res;
322 }
323 }
324
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000325 private static void resetDeviceConfig(Context context, int resetMode,
326 @Nullable String failedPackage) {
327 if (!shouldPerformScopedResets() || failedPackage == null) {
328 DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
329 } else {
330 performScopedReset(context, resetMode, failedPackage);
331 }
332 }
333
334 private static boolean shouldPerformScopedResets() {
335 int rescueLevel = MathUtils.constrain(
336 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
337 LEVEL_NONE, LEVEL_FACTORY_RESET);
338 return rescueLevel <= LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
339 }
340
341 private static void performScopedReset(Context context, int resetMode,
342 @NonNull String failedPackage) {
343 RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
344 Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
345 failedPackage);
346 if (affectedNamespaces == null) {
347 DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
348 } else {
349 Slog.w(TAG,
350 "Performing scoped reset for package: " + failedPackage
351 + ", affected namespaces: "
352 + Arrays.toString(affectedNamespaces.toArray()));
353 Iterator<String> it = affectedNamespaces.iterator();
354 while (it.hasNext()) {
355 DeviceConfig.resetToDefaults(resetMode, it.next());
356 }
357 }
358 }
359
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700360 /**
Gavin Corkery69395652019-12-12 19:06:47 +0000361 * Handle mitigation action for package failures. This observer will be register to Package
362 * Watchdog and will receive calls about package failures. This observer is persistent so it
363 * may choose to mitigate failures for packages it has not explicitly asked to observe.
364 */
365 public static class RescuePartyObserver implements PackageHealthObserver {
366
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000367 private final Context mContext;
368 private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>();
369 private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>();
Gavin Corkery69395652019-12-12 19:06:47 +0000370
371 @GuardedBy("RescuePartyObserver.class")
372 static RescuePartyObserver sRescuePartyObserver;
373
374 private RescuePartyObserver(Context context) {
375 mContext = context;
376 }
377
378 /** Creates or gets singleton instance of RescueParty. */
379 public static RescuePartyObserver getInstance(Context context) {
380 synchronized (RescuePartyObserver.class) {
381 if (sRescuePartyObserver == null) {
382 sRescuePartyObserver = new RescuePartyObserver(context);
383 }
384 return sRescuePartyObserver;
385 }
386 }
387
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000388 @VisibleForTesting
389 static void reset() {
390 synchronized (RescuePartyObserver.class) {
391 sRescuePartyObserver = null;
392 }
393 }
394
Gavin Corkery69395652019-12-12 19:06:47 +0000395 @Override
396 public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
397 @FailureReasons int failureReason) {
398 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
399 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
400 int rescueLevel = MathUtils.constrain(
401 SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
402 LEVEL_NONE, LEVEL_FACTORY_RESET);
403 return mapRescueLevelToUserImpact(rescueLevel);
404 } else {
405 return PackageHealthObserverImpact.USER_IMPACT_NONE;
406 }
407 }
408
409 @Override
410 public boolean execute(@Nullable VersionedPackage failedPackage,
411 @FailureReasons int failureReason) {
412 if (isDisabled()) {
413 return false;
414 }
415 if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
416 || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
417 int triggerUid = getPackageUid(mContext, failedPackage.getPackageName());
418 incrementRescueLevel(triggerUid);
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000419 executeRescueLevel(mContext,
420 failedPackage == null ? null : failedPackage.getPackageName());
Gavin Corkery69395652019-12-12 19:06:47 +0000421 return true;
422 } else {
423 return false;
424 }
425 }
426
427 @Override
428 public boolean isPersistent() {
429 return true;
430 }
431
432 @Override
433 public boolean mayObservePackage(String packageName) {
434 PackageManager pm = mContext.getPackageManager();
435 try {
436 // A package is a Mainline module if this is non-null
437 if (pm.getModuleInfo(packageName, 0) != null) {
438 return true;
439 }
440 ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
441 return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
442 } catch (PackageManager.NameNotFoundException e) {
443 return false;
444 }
445 }
446
447 @Override
Gavin Corkeryaa57ef32019-12-17 19:02:54 +0000448 public int onBootLoop() {
449 if (isDisabled()) {
450 return PackageHealthObserverImpact.USER_IMPACT_NONE;
451 }
452 return mapRescueLevelToUserImpact(getRescueLevel());
453 }
454
455 @Override
456 public boolean executeBootLoopMitigation() {
457 if (isDisabled()) {
458 return false;
459 }
460 incrementRescueLevel(Process.ROOT_UID);
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000461 executeRescueLevel(mContext, /*failedPackage=*/ null);
Gavin Corkeryaa57ef32019-12-17 19:02:54 +0000462 return true;
463 }
464
465 @Override
Gavin Corkery69395652019-12-12 19:06:47 +0000466 public String getName() {
467 return NAME;
468 }
Bohdan Petrivskyy095a4d32019-12-18 16:03:47 +0000469
470 private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
471 @NonNull String namespace) {
472 // Record it in calling packages to namespace map
473 Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
474 if (namespaceSet == null) {
475 namespaceSet = new ArraySet<>();
476 mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
477 }
478 namespaceSet.add(namespace);
479 // Record it in namespace to calling packages map
480 Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
481 if (callingPackageSet == null) {
482 callingPackageSet = new ArraySet<>();
483 }
484 callingPackageSet.add(callingPackage);
485 mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
486 }
487
488 private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
489 return mCallingPackageNamespaceSetMap.get(failedPackage);
490 }
491
492 private synchronized Set<String> getCallingPackagesSet(String namespace) {
493 return mNamespaceCallingPackageSetMap.get(namespace);
494 }
Gavin Corkery69395652019-12-12 19:06:47 +0000495 }
496
Jeff Sharkey82311462017-04-02 23:42:17 -0600497 private static int[] getAllUserIds() {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700498 int[] userIds = { UserHandle.USER_SYSTEM };
499 try {
Jeff Sharkey82311462017-04-02 23:42:17 -0600500 for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) {
501 try {
502 final int userId = Integer.parseInt(file.getName());
503 if (userId != UserHandle.USER_SYSTEM) {
504 userIds = ArrayUtils.appendInt(userIds, userId);
505 }
506 } catch (NumberFormatException ignored) {
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700507 }
508 }
509 } catch (Throwable t) {
510 Slog.w(TAG, "Trouble discovering users", t);
511 }
512 return userIds;
513 }
514
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700515 /**
516 * Hacky test to check if the device has an active USB connection, which is
Jeff Sharkey1bec4482017-02-23 12:40:54 -0700517 * a good proxy for someone doing local development work.
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700518 */
519 private static boolean isUsbActive() {
Jeff Sharkey9d640952017-06-26 19:57:16 -0600520 if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) {
521 Slog.v(TAG, "Assuming virtual device is connected over USB");
522 return true;
523 }
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700524 try {
Jeff Sharkey1bec4482017-02-23 12:40:54 -0700525 final String state = FileUtils
526 .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
527 return "CONFIGURED".equals(state.trim());
Jeff Sharkeyd9574c72017-02-20 10:45:06 -0700528 } catch (Throwable t) {
529 Slog.w(TAG, "Failed to determine if device was on USB", t);
530 return false;
531 }
532 }
533
Jeff Sharkeyfe6f85c2017-01-20 10:42:57 -0700534 private static String levelToString(int level) {
535 switch (level) {
536 case LEVEL_NONE: return "NONE";
537 case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
538 case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES";
539 case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS";
540 case LEVEL_FACTORY_RESET: return "FACTORY_RESET";
541 default: return Integer.toString(level);
542 }
543 }
544}