Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea1754a8 authored by Harshit Mahajan's avatar Harshit Mahajan Committed by Android (Google) Code Review
Browse files

Merge changes If47feb07,Idf3d00e9 into main

* changes:
  Utils required for CrashRecovery module
  Cleanup code behind flag calling hidden api
parents e93c4a1e 7fe77d5d
Loading
Loading
Loading
Loading
+9 −28
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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")
+3 −26
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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;
@@ -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);
    }

@@ -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,
@@ -323,9 +319,6 @@ public class PackageWatchdog {
                    this::onSyncRequestNotified);
            setPropertyChangedListenerLocked();
            updateConfigs();
            if (!Flags.refactorCrashrecovery()) {
                registerConnectivityModuleHealthListener();
            }
        }
    }

@@ -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.
     */
+5 −136
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;

/**
@@ -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()) {
@@ -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
@@ -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
@@ -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
+36 −72
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -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();
@@ -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
@@ -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) {
@@ -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;
@@ -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;
            }
        }
    }

    /**
@@ -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
@@ -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());
        }
    }

    /**
+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