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

Commit 6efe700b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Restrict Rescue Party factory reset packages"

parents 44ecb68a 6d72bede
Loading
Loading
Loading
Loading
+42 −16
Original line number Diff line number Diff line
@@ -327,18 +327,23 @@ public class RescueParty {
        }
    }

    private static int getMaxRescueLevel() {
        return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)
                ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET;
    private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
        if (!mayPerformFactoryReset
                || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
        }
        return LEVEL_FACTORY_RESET;
    }

    /**
     * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
     *
     * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
     * @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
     *                              failure.
     * @return the rescue level for the n-th mitigation attempt.
     */
    private static int getRescueLevel(int mitigationCount) {
    private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
        if (mitigationCount == 1) {
            return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
        } else if (mitigationCount == 2) {
@@ -346,9 +351,9 @@ public class RescueParty {
        } else if (mitigationCount == 3) {
            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
        } else if (mitigationCount == 4) {
            return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT);
            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
        } else if (mitigationCount >= 5) {
            return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET);
            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
        } else {
            Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
            return LEVEL_NONE;
@@ -614,7 +619,8 @@ public class RescueParty {
                @FailureReasons int failureReason, int mitigationCount) {
            if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
                        mayPerformFactoryReset(failedPackage)));
            } else {
                return PackageHealthObserverImpact.USER_IMPACT_NONE;
            }
@@ -628,7 +634,8 @@ public class RescueParty {
            }
            if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                    || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
                final int level = getRescueLevel(mitigationCount);
                final int level = getRescueLevel(mitigationCount,
                        mayPerformFactoryReset(failedPackage));
                executeRescueLevel(mContext,
                        failedPackage == null ? null : failedPackage.getPackageName(), level);
                return true;
@@ -653,12 +660,7 @@ public class RescueParty {
            } catch (PackageManager.NameNotFoundException ignore) {
            }

            try {
                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
            } catch (PackageManager.NameNotFoundException e) {
                return false;
            }
            return isPersistentSystemApp(packageName);
        }

        @Override
@@ -666,7 +668,7 @@ public class RescueParty {
            if (isDisabled()) {
                return PackageHealthObserverImpact.USER_IMPACT_NONE;
            }
            return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
            return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
        }

        @Override
@@ -674,7 +676,8 @@ public class RescueParty {
            if (isDisabled()) {
                return false;
            }
            executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount));
            executeRescueLevel(mContext, /*failedPackage=*/ null,
                    getRescueLevel(mitigationCount, true));
            return true;
        }

@@ -683,6 +686,29 @@ public class RescueParty {
            return NAME;
        }

        /**
         * Returns {@code true} if the failing package is non-null and performing a reboot or
         * prompting a factory reset is an acceptable mitigation strategy for the package's
         * failure, {@code false} otherwise.
         */
        private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
            if (failingPackage == null) {
                return false;
            }

            return isPersistentSystemApp(failingPackage.getPackageName());
        }

        private boolean isPersistentSystemApp(@NonNull String packageName) {
            PackageManager pm = mContext.getPackageManager();
            try {
                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
            } catch (PackageManager.NameNotFoundException e) {
                return false;
            }
        }

        private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
                @NonNull String namespace) {
            // Record it in calling packages to namespace map
+53 −11
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ import static org.mockito.Mockito.times;

import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Bundle;
import android.os.RecoverySystem;
@@ -83,6 +85,8 @@ public class RescuePartyTest {
    private static final String CALLING_PACKAGE1 = "com.package.name1";
    private static final String CALLING_PACKAGE2 = "com.package.name2";
    private static final String CALLING_PACKAGE3 = "com.package.name3";
    private static final String PERSISTENT_PACKAGE = "com.persistent.package";
    private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
    private static final String NAMESPACE1 = "namespace1";
    private static final String NAMESPACE2 = "namespace2";
    private static final String NAMESPACE3 = "namespace3";
@@ -103,6 +107,8 @@ public class RescuePartyTest {
    private PackageWatchdog mMockPackageWatchdog;
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private ContentResolver mMockContentResolver;
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private PackageManager mPackageManager;

    @Captor
    private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
@@ -129,6 +135,17 @@ public class RescuePartyTest {
        mNamespacesWiped = new HashSet<>();

        when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
        ApplicationInfo persistentApplicationInfo = new ApplicationInfo();
        persistentApplicationInfo.flags |=
                ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT;

        // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and
        // system. Don't set any flags otherwise.
        when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE),
                anyInt())).thenReturn(persistentApplicationInfo);
        when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE),
                anyInt())).thenReturn(new ApplicationInfo());
        // Reset observer instance to get new mock context on every run
        RescuePartyObserver.reset();

@@ -241,28 +258,52 @@ public class RescuePartyTest {

    @Test
    public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
        notePersistentAppCrash(1);
        noteAppCrash(1, true);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(2);
        noteAppCrash(2, true);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(3);
        noteAppCrash(3, true);

        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        notePersistentAppCrash(4);
        noteAppCrash(4, true);
        assertTrue(RescueParty.isRebootPropertySet());

        notePersistentAppCrash(5);
        noteAppCrash(5, true);
        assertTrue(RescueParty.isFactoryResetPropertySet());
    }

    @Test
    public void testNonPersistentAppOnlyPerformsFlagResets() {
        noteAppCrash(1, false);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        noteAppCrash(2, false);

        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        noteAppCrash(3, false);

        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                /*configResetVerifiedTimesMap=*/ null);

        noteAppCrash(4, false);
        assertFalse(RescueParty.isRebootPropertySet());

        noteAppCrash(5, false);
        assertFalse(RescueParty.isFactoryResetPropertySet());
    }

    @Test
    public void testNonPersistentAppCrashDetectionWithScopedResets() {
        RescueParty.onSettingsProviderPublished(mMockContext);
@@ -311,11 +352,11 @@ public class RescuePartyTest {

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
        assertTrue(RescueParty.isRebootPropertySet());
        assertFalse(RescueParty.isRebootPropertySet());

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
        assertFalse(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -376,11 +417,11 @@ public class RescuePartyTest {

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
        assertTrue(RescueParty.isRebootPropertySet());
        assertFalse(RescueParty.isRebootPropertySet());

        observer.execute(new VersionedPackage(
                CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
        assertTrue(RescueParty.isFactoryResetPropertySet());
        assertFalse(RescueParty.isFactoryResetPropertySet());
    }

    @Test
@@ -627,9 +668,10 @@ public class RescuePartyTest {
        RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount);
    }

    private void notePersistentAppCrash(int mitigationCount) {
    private void noteAppCrash(int mitigationCount, boolean isPersistent) {
        String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
        RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
                "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
                packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
    }

    private Bundle getConfigAccessBundle(String callingPackage, String namespace) {