Loading packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java +9 −28 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ */ package com.android.server; import static android.crashrecovery.flags.Flags.refactorCrashrecovery; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES; Loading Loading @@ -363,7 +363,6 @@ class ExplicitHealthCheckController { @GuardedBy("mLock") @Nullable private ServiceInfo getServiceInfoLocked() { if (refactorCrashrecovery()) { final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA Loading @@ -373,24 +372,6 @@ class ExplicitHealthCheckController { return null; } return resolveInfo.serviceInfo; } else { final String packageName = mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); if (packageName == null) { Slog.w(TAG, "no external services package!"); return null; } final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); intent.setPackage(packageName); final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); if (resolveInfo == null || resolveInfo.serviceInfo == null) { Slog.w(TAG, "No valid components found."); return null; } return resolveInfo.serviceInfo; } } @GuardedBy("mLock") Loading packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +3 −26 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.crashrecovery.flags.Flags; import android.net.ConnectivityModuleConnector; import android.os.Environment; import android.os.Handler; import android.os.Looper; Loading @@ -52,11 +51,11 @@ import android.util.IndentingPrintWriter; import android.util.LongArrayQueue; import android.util.Slog; import android.util.Xml; import android.util.XmlUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.XmlUtils; import com.android.modules.utils.BackgroundThread; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; Loading Loading @@ -227,7 +226,6 @@ public class PackageWatchdog { // File containing the XML data of monitored packages /data/system/package-watchdog.xml private final AtomicFile mPolicyFile; private final ExplicitHealthCheckController mHealthCheckController; private final ConnectivityModuleConnector mConnectivityModuleConnector; private final Runnable mSyncRequests = this::syncRequests; private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason; private final Runnable mSaveToFile = this::saveToFile; Loading Loading @@ -274,7 +272,6 @@ public class PackageWatchdog { "package-watchdog.xml")), new Handler(Looper.myLooper()), BackgroundThread.getHandler(), new ExplicitHealthCheckController(context), ConnectivityModuleConnector.getInstance(), android.os.SystemClock::uptimeMillis); } Loading @@ -284,13 +281,12 @@ public class PackageWatchdog { @VisibleForTesting PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, Handler longTaskHandler, ExplicitHealthCheckController controller, ConnectivityModuleConnector connectivityModuleConnector, SystemClock clock) { SystemClock clock) { mContext = context; mPolicyFile = policyFile; mShortTaskHandler = shortTaskHandler; mLongTaskHandler = longTaskHandler; mHealthCheckController = controller; mConnectivityModuleConnector = connectivityModuleConnector; mSystemClock = clock; mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT, Loading Loading @@ -323,9 +319,6 @@ public class PackageWatchdog { this::onSyncRequestNotified); setPropertyChangedListenerLocked(); updateConfigs(); if (!Flags.refactorCrashrecovery()) { registerConnectivityModuleHealthListener(); } } } Loading Loading @@ -1213,22 +1206,6 @@ public class PackageWatchdog { } } private void registerConnectivityModuleHealthListener() { // TODO: have an internal method to trigger a rollback by reporting high severity errors, // and rely on ActivityManager to inform the watchdog of severe network stack crashes // instead of having this listener in parallel. mConnectivityModuleConnector.registerHealthListener( packageName -> { final VersionedPackage pkg = getVersionedPackage(packageName); if (pkg == null) { Slog.wtf(TAG, "NetworkStack failed but could not find its package"); return; } final List<VersionedPackage> pkgList = Collections.singletonList(pkg); onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); }); } /** * Persists mAllObservers to file. Threshold information is ignored. */ Loading packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java +5 −136 Original line number Diff line number Diff line Loading @@ -16,14 +16,11 @@ package com.android.server; import static android.provider.DeviceConfig.Properties; import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; Loading @@ -31,7 +28,6 @@ import android.content.pm.VersionedPackage; import android.crashrecovery.flags.Flags; import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.SystemClock; Loading @@ -42,17 +38,17 @@ import android.provider.Settings; import android.sysprop.CrashRecoveryProperties; import android.text.TextUtils; import android.util.ArraySet; import android.util.ArrayUtils; import android.util.EventLog; import android.util.FileUtils; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import com.android.server.am.SettingsToPropertiesMapper; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import java.io.File; Loading @@ -61,11 +57,9 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** Loading Loading @@ -245,74 +239,6 @@ public class RescueParty { CrashRecoveryProperties.maxRescueLevelAttempted(level); } /** * Called when {@code SettingsProvider} has been published, which is a good * opportunity to reset any settings depending on our rescue level. */ public static void onSettingsProviderPublished(Context context) { if (!Flags.deprecateFlagsAndSettingsResets()) { handleNativeRescuePartyResets(); ContentResolver contentResolver = context.getContentResolver(); DeviceConfig.setMonitorCallback( contentResolver, Executors.newSingleThreadExecutor(), new RescuePartyMonitorCallback(context)); } } /** * Called when {@code RollbackManager} performs Mainline module rollbacks, * to avoid rolled back modules consuming flag values only expected to work * on modules of newer versions. */ public static void resetDeviceConfigForPackages(List<String> packageNames) { if (!Flags.deprecateFlagsAndSettingsResets()) { if (packageNames == null) { return; } Set<String> namespacesToReset = new ArraySet<String>(); Iterator<String> it = packageNames.iterator(); RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); // Get runtime package to namespace mapping if created. if (rescuePartyObserver != null) { while (it.hasNext()) { String packageName = it.next(); Set<String> runtimeAffectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(packageName); if (runtimeAffectedNamespaces != null) { namespacesToReset.addAll(runtimeAffectedNamespaces); } } } // Get preset package to namespace mapping if created. Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( packageNames); if (presetAffectedNamespaces != null) { namespacesToReset.addAll(presetAffectedNamespaces); } // Clear flags under the namespaces mapped to these packages. // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. Iterator<String> namespaceIt = namespacesToReset.iterator(); while (namespaceIt.hasNext()) { String namespaceToReset = namespaceIt.next(); Properties properties = new Properties.Builder(namespaceToReset).build(); try { if (!DeviceConfig.setProperties(properties)) { logCrashRecoveryEvent(Log.ERROR, "Failed to clear properties under " + namespaceToReset + ". Running `device_config get_sync_disabled_for_tests` will confirm" + " if config-bulk-update is enabled."); } } catch (DeviceConfig.BadConfigException exception) { logCrashRecoveryEvent(Log.WARN, "namespace " + namespaceToReset + " is already banned, skip reset."); } } } } private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { Set<String> resultSet = new ArraySet<String>(); if (!Flags.deprecateFlagsAndSettingsResets()) { Loading Loading @@ -394,23 +320,6 @@ public class RescueParty { } } private static void handleNativeRescuePartyResets() { if (!Flags.deprecateFlagsAndSettingsResets()) { if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); for (int i = 0; i < resetNativeCategories.length; i++) { // Don't let RescueParty reset the namespace for RescueParty switches. if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { continue; } DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, resetNativeCategories[i]); } } } } private static int getMaxRescueLevel(boolean mayPerformReboot) { if (Flags.recoverabilityDetection()) { if (!mayPerformReboot Loading Loading @@ -599,22 +508,13 @@ public class RescueParty { executeWarmReboot(context, level, failedPackage); break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, level); } // do nothing break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, level); } // do nothing break; case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, level); } // do nothing break; case RESCUE_LEVEL_FACTORY_RESET: // Before the completion of Reboot, if any crash happens then PackageWatchdog Loading Loading @@ -757,37 +657,6 @@ public class RescueParty { } } private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { if (!Flags.deprecateFlagsAndSettingsResets()) { // No need to reset Settings again if they are already reset in the current level once. if (getMaxRescueLevelAttempted() >= level) { return; } setMaxRescueLevelAttempted(level); // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; final ContentResolver resolver = context.getContentResolver(); try { Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.SYSTEM.getIdentifier()); } catch (Exception e) { res = new RuntimeException("Failed to reset global settings", e); } for (int userId : getAllUserIds()) { try { Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); } catch (Exception e) { res = new RuntimeException("Failed to reset secure settings for " + userId, e); } } if (res != null) { throw res; } } } /** * Handle mitigation action for package failures. This observer will be register to Package * Watchdog and will receive calls about package failures. This observer is persistent so it Loading packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java +36 −72 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.rollback; import static android.content.pm.Flags.provideInfoOfApkInApex; import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.AnyThread; Loading @@ -38,13 +36,13 @@ import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.crashrecovery.flags.Flags; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.os.SystemProperties; import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import android.util.FileUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; Loading @@ -56,7 +54,6 @@ import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import com.android.server.pm.ApexManager; import java.io.BufferedReader; import java.io.File; Loading Loading @@ -91,7 +88,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private final Context mContext; private final Handler mHandler; private final ApexManager mApexManager; private final File mLastStagedRollbackIdsFile; private final File mTwoPhaseRollbackEnabledFile; // Staged rollback ids that have been committed but their session is not yet ready Loading @@ -99,9 +95,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve // True if needing to roll back only rebootless apexes when native crash happens private boolean mTwoPhaseRollbackEnabled; /** @hide */ @VisibleForTesting public RollbackPackageHealthObserver(Context context, ApexManager apexManager) { public RollbackPackageHealthObserver(@NonNull Context context) { mContext = context; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); Loading @@ -111,7 +106,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled"); PackageWatchdog.getInstance(mContext).registerHealthObserver(this); mApexManager = apexManager; if (SystemProperties.getBoolean("sys.boot_completed", false)) { // Load the value from the file if system server has crashed and restarted Loading @@ -124,10 +118,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } public RollbackPackageHealthObserver(@NonNull Context context) { this(context, ApexManager.getInstance()); } @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { Loading Loading @@ -500,13 +490,11 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve */ @AnyThread private boolean isModule(String packageName) { PackageManager pm = mContext.getPackageManager(); if (Flags.refactorCrashrecovery() && provideInfoOfApkInApex()) { // Check if the package is listed among the system modules or is an // APK inside an updatable APEX. try { final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */); final PackageInfo pkg = mContext.getPackageManager() .getPackageInfo(packageName, 0 /* flags */); String apexPackageName = pkg.getApexPackageName(); if (apexPackageName != null) { packageName = apexPackageName; Loading @@ -516,21 +504,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } catch (PackageManager.NameNotFoundException e) { return false; } } else { // Check if the package is an APK inside an APEX. If it is, use the parent APEX package // when querying PackageManager. String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage( packageName); if (apexPackageName != null) { packageName = apexPackageName; } try { return pm.getModuleInfo(packageName, 0) != null; } catch (PackageManager.NameNotFoundException ignore) { return false; } } } /** Loading Loading @@ -604,7 +577,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } }; if (Flags.refactorCrashrecovery()) { // Define a BroadcastReceiver to handle the result BroadcastReceiver rollbackReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -632,14 +604,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackPendingIntent.getIntentSender()); } else { final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> { mHandler.post(() -> onResult.accept(result)); }); rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender()); } } /** Loading packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import java.io.File; import java.util.List; import java.util.Objects; /** * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java * * @hide */ public class ArrayUtils { private ArrayUtils() { /* cannot be instantiated */ } public static final File[] EMPTY_FILE = new File[0]; /** * Return first index of {@code value} in {@code array}, or {@code -1} if * not found. */ public static <T> int indexOf(@Nullable T[] array, T value) { if (array == null) return -1; for (int i = 0; i < array.length; i++) { if (Objects.equals(array[i], value)) return i; } return -1; } /** @hide */ public static @NonNull File[] defeatNullable(@Nullable File[] val) { return (val != null) ? val : EMPTY_FILE; } /** * Checks if given array is null or has zero elements. */ public static boolean isEmpty(@Nullable int[] array) { return array == null || array.length == 0; } /** * True if the byte array is null or has length 0. */ public static boolean isEmpty(@Nullable byte[] array) { return array == null || array.length == 0; } /** * Converts from List of bytes to byte array * @param list * @return byte[] */ public static byte[] toPrimitive(List<byte[]> list) { if (list.size() == 0) { return new byte[0]; } int byteLen = list.get(0).length; byte[] array = new byte[list.size() * byteLen]; for (int i = 0; i < list.size(); i++) { for (int j = 0; j < list.get(i).length; j++) { array[i * byteLen + j] = list.get(i)[j]; } } return array; } /** * Adds value to given array if not already present, providing set-like * behavior. */ public static @NonNull int[] appendInt(@Nullable int[] cur, int val) { return appendInt(cur, val, false); } /** * Adds value to given array. */ public static @NonNull int[] appendInt(@Nullable int[] cur, int val, boolean allowDuplicates) { if (cur == null) { return new int[] { val }; } final int n = cur.length; if (!allowDuplicates) { for (int i = 0; i < n; i++) { if (cur[i] == val) { return cur; } } } int[] ret = new int[n + 1]; System.arraycopy(cur, 0, ret, 0, n); ret[n] = val; return ret; } } Loading
packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java +9 −28 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ */ package com.android.server; import static android.crashrecovery.flags.Flags.refactorCrashrecovery; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES; import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES; Loading Loading @@ -363,7 +363,6 @@ class ExplicitHealthCheckController { @GuardedBy("mLock") @Nullable private ServiceInfo getServiceInfoLocked() { if (refactorCrashrecovery()) { final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA Loading @@ -373,24 +372,6 @@ class ExplicitHealthCheckController { return null; } return resolveInfo.serviceInfo; } else { final String packageName = mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); if (packageName == null) { Slog.w(TAG, "no external services package!"); return null; } final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); intent.setPackage(packageName); final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); if (resolveInfo == null || resolveInfo.serviceInfo == null) { Slog.w(TAG, "No valid components found."); return null; } return resolveInfo.serviceInfo; } } @GuardedBy("mLock") Loading
packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +3 −26 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.crashrecovery.flags.Flags; import android.net.ConnectivityModuleConnector; import android.os.Environment; import android.os.Handler; import android.os.Looper; Loading @@ -52,11 +51,11 @@ import android.util.IndentingPrintWriter; import android.util.LongArrayQueue; import android.util.Slog; import android.util.Xml; import android.util.XmlUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.XmlUtils; import com.android.modules.utils.BackgroundThread; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; Loading Loading @@ -227,7 +226,6 @@ public class PackageWatchdog { // File containing the XML data of monitored packages /data/system/package-watchdog.xml private final AtomicFile mPolicyFile; private final ExplicitHealthCheckController mHealthCheckController; private final ConnectivityModuleConnector mConnectivityModuleConnector; private final Runnable mSyncRequests = this::syncRequests; private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason; private final Runnable mSaveToFile = this::saveToFile; Loading Loading @@ -274,7 +272,6 @@ public class PackageWatchdog { "package-watchdog.xml")), new Handler(Looper.myLooper()), BackgroundThread.getHandler(), new ExplicitHealthCheckController(context), ConnectivityModuleConnector.getInstance(), android.os.SystemClock::uptimeMillis); } Loading @@ -284,13 +281,12 @@ public class PackageWatchdog { @VisibleForTesting PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, Handler longTaskHandler, ExplicitHealthCheckController controller, ConnectivityModuleConnector connectivityModuleConnector, SystemClock clock) { SystemClock clock) { mContext = context; mPolicyFile = policyFile; mShortTaskHandler = shortTaskHandler; mLongTaskHandler = longTaskHandler; mHealthCheckController = controller; mConnectivityModuleConnector = connectivityModuleConnector; mSystemClock = clock; mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT, Loading Loading @@ -323,9 +319,6 @@ public class PackageWatchdog { this::onSyncRequestNotified); setPropertyChangedListenerLocked(); updateConfigs(); if (!Flags.refactorCrashrecovery()) { registerConnectivityModuleHealthListener(); } } } Loading Loading @@ -1213,22 +1206,6 @@ public class PackageWatchdog { } } private void registerConnectivityModuleHealthListener() { // TODO: have an internal method to trigger a rollback by reporting high severity errors, // and rely on ActivityManager to inform the watchdog of severe network stack crashes // instead of having this listener in parallel. mConnectivityModuleConnector.registerHealthListener( packageName -> { final VersionedPackage pkg = getVersionedPackage(packageName); if (pkg == null) { Slog.wtf(TAG, "NetworkStack failed but could not find its package"); return; } final List<VersionedPackage> pkgList = Collections.singletonList(pkg); onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); }); } /** * Persists mAllObservers to file. Threshold information is ignored. */ Loading
packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java +5 −136 Original line number Diff line number Diff line Loading @@ -16,14 +16,11 @@ package com.android.server; import static android.provider.DeviceConfig.Properties; import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; Loading @@ -31,7 +28,6 @@ import android.content.pm.VersionedPackage; import android.crashrecovery.flags.Flags; import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.SystemClock; Loading @@ -42,17 +38,17 @@ import android.provider.Settings; import android.sysprop.CrashRecoveryProperties; import android.text.TextUtils; import android.util.ArraySet; import android.util.ArrayUtils; import android.util.EventLog; import android.util.FileUtils; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import com.android.server.am.SettingsToPropertiesMapper; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import java.io.File; Loading @@ -61,11 +57,9 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** Loading Loading @@ -245,74 +239,6 @@ public class RescueParty { CrashRecoveryProperties.maxRescueLevelAttempted(level); } /** * Called when {@code SettingsProvider} has been published, which is a good * opportunity to reset any settings depending on our rescue level. */ public static void onSettingsProviderPublished(Context context) { if (!Flags.deprecateFlagsAndSettingsResets()) { handleNativeRescuePartyResets(); ContentResolver contentResolver = context.getContentResolver(); DeviceConfig.setMonitorCallback( contentResolver, Executors.newSingleThreadExecutor(), new RescuePartyMonitorCallback(context)); } } /** * Called when {@code RollbackManager} performs Mainline module rollbacks, * to avoid rolled back modules consuming flag values only expected to work * on modules of newer versions. */ public static void resetDeviceConfigForPackages(List<String> packageNames) { if (!Flags.deprecateFlagsAndSettingsResets()) { if (packageNames == null) { return; } Set<String> namespacesToReset = new ArraySet<String>(); Iterator<String> it = packageNames.iterator(); RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); // Get runtime package to namespace mapping if created. if (rescuePartyObserver != null) { while (it.hasNext()) { String packageName = it.next(); Set<String> runtimeAffectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(packageName); if (runtimeAffectedNamespaces != null) { namespacesToReset.addAll(runtimeAffectedNamespaces); } } } // Get preset package to namespace mapping if created. Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( packageNames); if (presetAffectedNamespaces != null) { namespacesToReset.addAll(presetAffectedNamespaces); } // Clear flags under the namespaces mapped to these packages. // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. Iterator<String> namespaceIt = namespacesToReset.iterator(); while (namespaceIt.hasNext()) { String namespaceToReset = namespaceIt.next(); Properties properties = new Properties.Builder(namespaceToReset).build(); try { if (!DeviceConfig.setProperties(properties)) { logCrashRecoveryEvent(Log.ERROR, "Failed to clear properties under " + namespaceToReset + ". Running `device_config get_sync_disabled_for_tests` will confirm" + " if config-bulk-update is enabled."); } } catch (DeviceConfig.BadConfigException exception) { logCrashRecoveryEvent(Log.WARN, "namespace " + namespaceToReset + " is already banned, skip reset."); } } } } private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { Set<String> resultSet = new ArraySet<String>(); if (!Flags.deprecateFlagsAndSettingsResets()) { Loading Loading @@ -394,23 +320,6 @@ public class RescueParty { } } private static void handleNativeRescuePartyResets() { if (!Flags.deprecateFlagsAndSettingsResets()) { if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) { String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories(); for (int i = 0; i < resetNativeCategories.length; i++) { // Don't let RescueParty reset the namespace for RescueParty switches. if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) { continue; } DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, resetNativeCategories[i]); } } } } private static int getMaxRescueLevel(boolean mayPerformReboot) { if (Flags.recoverabilityDetection()) { if (!mayPerformReboot Loading Loading @@ -599,22 +508,13 @@ public class RescueParty { executeWarmReboot(context, level, failedPackage); break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, level); } // do nothing break; case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, level); } // do nothing break; case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: if (!Flags.deprecateFlagsAndSettingsResets()) { resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, level); } // do nothing break; case RESCUE_LEVEL_FACTORY_RESET: // Before the completion of Reboot, if any crash happens then PackageWatchdog Loading Loading @@ -757,37 +657,6 @@ public class RescueParty { } } private static void resetAllSettingsIfNecessary(Context context, int mode, int level) throws Exception { if (!Flags.deprecateFlagsAndSettingsResets()) { // No need to reset Settings again if they are already reset in the current level once. if (getMaxRescueLevelAttempted() >= level) { return; } setMaxRescueLevelAttempted(level); // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; final ContentResolver resolver = context.getContentResolver(); try { Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.SYSTEM.getIdentifier()); } catch (Exception e) { res = new RuntimeException("Failed to reset global settings", e); } for (int userId : getAllUserIds()) { try { Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); } catch (Exception e) { res = new RuntimeException("Failed to reset secure settings for " + userId, e); } } if (res != null) { throw res; } } } /** * Handle mitigation action for package failures. This observer will be register to Package * Watchdog and will receive calls about package failures. This observer is persistent so it Loading
packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java +36 −72 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.rollback; import static android.content.pm.Flags.provideInfoOfApkInApex; import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.AnyThread; Loading @@ -38,13 +36,13 @@ import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.crashrecovery.flags.Flags; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.os.SystemProperties; import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; import android.util.FileUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; Loading @@ -56,7 +54,6 @@ import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import com.android.server.pm.ApexManager; import java.io.BufferedReader; import java.io.File; Loading Loading @@ -91,7 +88,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private final Context mContext; private final Handler mHandler; private final ApexManager mApexManager; private final File mLastStagedRollbackIdsFile; private final File mTwoPhaseRollbackEnabledFile; // Staged rollback ids that have been committed but their session is not yet ready Loading @@ -99,9 +95,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve // True if needing to roll back only rebootless apexes when native crash happens private boolean mTwoPhaseRollbackEnabled; /** @hide */ @VisibleForTesting public RollbackPackageHealthObserver(Context context, ApexManager apexManager) { public RollbackPackageHealthObserver(@NonNull Context context) { mContext = context; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); Loading @@ -111,7 +106,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled"); PackageWatchdog.getInstance(mContext).registerHealthObserver(this); mApexManager = apexManager; if (SystemProperties.getBoolean("sys.boot_completed", false)) { // Load the value from the file if system server has crashed and restarted Loading @@ -124,10 +118,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } public RollbackPackageHealthObserver(@NonNull Context context) { this(context, ApexManager.getInstance()); } @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { Loading Loading @@ -500,13 +490,11 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve */ @AnyThread private boolean isModule(String packageName) { PackageManager pm = mContext.getPackageManager(); if (Flags.refactorCrashrecovery() && provideInfoOfApkInApex()) { // Check if the package is listed among the system modules or is an // APK inside an updatable APEX. try { final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */); final PackageInfo pkg = mContext.getPackageManager() .getPackageInfo(packageName, 0 /* flags */); String apexPackageName = pkg.getApexPackageName(); if (apexPackageName != null) { packageName = apexPackageName; Loading @@ -516,21 +504,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } catch (PackageManager.NameNotFoundException e) { return false; } } else { // Check if the package is an APK inside an APEX. If it is, use the parent APEX package // when querying PackageManager. String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage( packageName); if (apexPackageName != null) { packageName = apexPackageName; } try { return pm.getModuleInfo(packageName, 0) != null; } catch (PackageManager.NameNotFoundException ignore) { return false; } } } /** Loading Loading @@ -604,7 +577,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } }; if (Flags.refactorCrashrecovery()) { // Define a BroadcastReceiver to handle the result BroadcastReceiver rollbackReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -632,14 +604,6 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackPendingIntent.getIntentSender()); } else { final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver(result -> { mHandler.post(() -> onResult.accept(result)); }); rollbackManager.commitRollback(rollback.getRollbackId(), Collections.singletonList(failedPackage), rollbackReceiver.getIntentSender()); } } /** Loading
packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import java.io.File; import java.util.List; import java.util.Objects; /** * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java * * @hide */ public class ArrayUtils { private ArrayUtils() { /* cannot be instantiated */ } public static final File[] EMPTY_FILE = new File[0]; /** * Return first index of {@code value} in {@code array}, or {@code -1} if * not found. */ public static <T> int indexOf(@Nullable T[] array, T value) { if (array == null) return -1; for (int i = 0; i < array.length; i++) { if (Objects.equals(array[i], value)) return i; } return -1; } /** @hide */ public static @NonNull File[] defeatNullable(@Nullable File[] val) { return (val != null) ? val : EMPTY_FILE; } /** * Checks if given array is null or has zero elements. */ public static boolean isEmpty(@Nullable int[] array) { return array == null || array.length == 0; } /** * True if the byte array is null or has length 0. */ public static boolean isEmpty(@Nullable byte[] array) { return array == null || array.length == 0; } /** * Converts from List of bytes to byte array * @param list * @return byte[] */ public static byte[] toPrimitive(List<byte[]> list) { if (list.size() == 0) { return new byte[0]; } int byteLen = list.get(0).length; byte[] array = new byte[list.size() * byteLen]; for (int i = 0; i < list.size(); i++) { for (int j = 0; j < list.get(i).length; j++) { array[i * byteLen + j] = list.get(i)[j]; } } return array; } /** * Adds value to given array if not already present, providing set-like * behavior. */ public static @NonNull int[] appendInt(@Nullable int[] cur, int val) { return appendInt(cur, val, false); } /** * Adds value to given array. */ public static @NonNull int[] appendInt(@Nullable int[] cur, int val, boolean allowDuplicates) { if (cur == null) { return new int[] { val }; } final int n = cur.length; if (!allowDuplicates) { for (int i = 0; i < n; i++) { if (cur[i] == val) { return cur; } } } int[] ret = new int[n + 1]; System.arraycopy(cur, 0, ret, 0, n); ret[n] = val; return ret; } }