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

Commit 47b7e38b authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "Merge "Fixed PackageWatchdog health check state" into qt-dev am: f4e878d7 am: a06322a0"

parents b79bd0bd 94b1c3c5
Loading
Loading
Loading
Loading
+13 −6
Original line number Original line Diff line number Diff line
@@ -35,7 +35,9 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.service.watchdog.ExplicitHealthCheckService;
import android.service.watchdog.ExplicitHealthCheckService;
import android.service.watchdog.IExplicitHealthCheckService;
import android.service.watchdog.IExplicitHealthCheckService;
import android.service.watchdog.PackageInfo;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
@@ -69,7 +71,7 @@ class ExplicitHealthCheckController {
    // To prevent deadlocks between the controller and watchdog threads, we have
    // To prevent deadlocks between the controller and watchdog threads, we have
    // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
    // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class.
    // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
    // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer.
    @GuardedBy("mLock") @Nullable private Consumer<List<String>> mSupportedConsumer;
    @GuardedBy("mLock") @Nullable private Consumer<List<PackageInfo>> mSupportedConsumer;
    // Called everytime we need to notify the watchdog to sync requests between itself and the
    // Called everytime we need to notify the watchdog to sync requests between itself and the
    // health check service. In practice, should never be null after it has been #setEnabled.
    // health check service. In practice, should never be null after it has been #setEnabled.
    // To prevent deadlocks between the controller and watchdog threads, we have
    // To prevent deadlocks between the controller and watchdog threads, we have
@@ -104,7 +106,7 @@ class ExplicitHealthCheckController {
     * ensure a happens-before relationship of the set parameters and visibility on other threads.
     * ensure a happens-before relationship of the set parameters and visibility on other threads.
     */
     */
    public void setCallbacks(Consumer<String> passedConsumer,
    public void setCallbacks(Consumer<String> passedConsumer,
            Consumer<List<String>> supportedConsumer, Runnable notifySyncRunnable) {
            Consumer<List<PackageInfo>> supportedConsumer, Runnable notifySyncRunnable) {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mPassedConsumer != null || mSupportedConsumer != null
            if (mPassedConsumer != null || mSupportedConsumer != null
                    || mNotifySyncRunnable != null) {
                    || mNotifySyncRunnable != null) {
@@ -144,14 +146,18 @@ class ExplicitHealthCheckController {
            return;
            return;
        }
        }


        getSupportedPackages(supportedPackages -> {
        getSupportedPackages(supportedPackageInfos -> {
            // Notify the watchdog without lock held
            // Notify the watchdog without lock held
            mSupportedConsumer.accept(supportedPackages);
            mSupportedConsumer.accept(supportedPackageInfos);
            getRequestedPackages(previousRequestedPackages -> {
            getRequestedPackages(previousRequestedPackages -> {
                synchronized (mLock) {
                synchronized (mLock) {
                    // Hold lock so requests and cancellations are sent atomically.
                    // Hold lock so requests and cancellations are sent atomically.
                    // It is important we don't mix requests from multiple threads.
                    // It is important we don't mix requests from multiple threads.


                    Set<String> supportedPackages = new ArraySet<>();
                    for (PackageInfo info : supportedPackageInfos) {
                        supportedPackages.add(info.getPackageName());
                    }
                    // Note, this may modify newRequestedPackages
                    // Note, this may modify newRequestedPackages
                    newRequestedPackages.retainAll(supportedPackages);
                    newRequestedPackages.retainAll(supportedPackages);


@@ -229,7 +235,7 @@ class ExplicitHealthCheckController {
     * Returns the packages that we can request explicit health checks for.
     * Returns the packages that we can request explicit health checks for.
     * The packages will be returned to the {@code consumer}.
     * The packages will be returned to the {@code consumer}.
     */
     */
    private void getSupportedPackages(Consumer<List<String>> consumer) {
    private void getSupportedPackages(Consumer<List<PackageInfo>> consumer) {
        synchronized (mLock) {
        synchronized (mLock) {
            if (!prepareServiceLocked("get health check supported packages")) {
            if (!prepareServiceLocked("get health check supported packages")) {
                return;
                return;
@@ -238,7 +244,8 @@ class ExplicitHealthCheckController {
            Slog.d(TAG, "Getting health check supported packages");
            Slog.d(TAG, "Getting health check supported packages");
            try {
            try {
                mRemoteService.getSupportedPackages(new RemoteCallback(result -> {
                mRemoteService.getSupportedPackages(new RemoteCallback(result -> {
                    List<String> packages = result.getStringArrayList(EXTRA_SUPPORTED_PACKAGES);
                    List<PackageInfo> packages =
                            result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES);
                    Slog.i(TAG, "Explicit health check supported packages " + packages);
                    Slog.i(TAG, "Explicit health check supported packages " + packages);
                    consumer.accept(packages);
                    consumer.accept(packages);
                }));
                }));
+311 −190

File changed.

Preview size limit exceeded, changes collapsed.

+112 −3
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server;
package com.android.server;


import static com.android.server.PackageWatchdog.MonitoredPackage;
import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT;
import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -27,6 +28,7 @@ import android.content.Context;
import android.content.pm.VersionedPackage;
import android.content.pm.VersionedPackage;
import android.os.Handler;
import android.os.Handler;
import android.os.test.TestLooper;
import android.os.test.TestLooper;
import android.service.watchdog.PackageInfo;
import android.util.AtomicFile;
import android.util.AtomicFile;


import androidx.test.InstrumentationRegistry;
import androidx.test.InstrumentationRegistry;
@@ -143,6 +145,31 @@ public class PackageWatchdogTest {
        assertNull(watchdog.getPackages(observer3));
        assertNull(watchdog.getPackages(observer3));
    }
    }


    /** Observing already observed package extends the observation time. */
    @Test
    public void testObserveAlreadyObservedPackage() throws Exception {
        PackageWatchdog watchdog = createWatchdog();
        TestObserver observer = new TestObserver(OBSERVER_NAME_1);

        // Start observing APP_A
        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);

        // Then advance time half-way
        Thread.sleep(SHORT_DURATION / 2);
        mTestLooper.dispatchAll();

        // Start observing APP_A again
        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);

        // Then advance time such that it should have expired were it not for the second observation
        Thread.sleep((SHORT_DURATION / 2) + 1);
        mTestLooper.dispatchAll();

        // Verify that APP_A not expired since second observation extended the time
        assertEquals(1, watchdog.getPackages(observer).size());
        assertTrue(watchdog.getPackages(observer).contains(APP_A));
    }

    /**
    /**
     * Test package observers are persisted and loaded on startup
     * Test package observers are persisted and loaded on startup
     */
     */
@@ -577,6 +604,84 @@ public class PackageWatchdogTest {
        assertEquals(APP_C, observer.mFailedPackages.get(0));
        assertEquals(APP_C, observer.mFailedPackages.get(0));
    }
    }


    /**
     * Tests failure when health check duration is different from package observation duration
     * Failure is also notified only once.
     */
    @Test
    public void testExplicitHealthCheckFailureBeforeExpiry() throws Exception {
        TestController controller = new TestController();
        PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
        TestObserver observer = new TestObserver(OBSERVER_NAME_1,
                PackageHealthObserverImpact.USER_IMPACT_MEDIUM);

        // Start observing with explicit health checks for APP_A and
        // package observation duration == LONG_DURATION
        // health check duration == SHORT_DURATION (set by default in the TestController)
        controller.setSupportedPackages(Arrays.asList(APP_A));
        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION);

        // Then APP_A has exceeded health check duration
        Thread.sleep(SHORT_DURATION);
        mTestLooper.dispatchAll();

        // Verify that health check is failed
        assertEquals(1, observer.mFailedPackages.size());
        assertEquals(APP_A, observer.mFailedPackages.get(0));

        // Then clear failed packages and start observing a random package so requests are synced
        // and PackageWatchdog#onSupportedPackages is called and APP_A has a chance to fail again
        // this time due to package expiry.
        observer.mFailedPackages.clear();
        watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);

        // Verify that health check failure is not notified again
        assertTrue(observer.mFailedPackages.isEmpty());
    }

    /** Tests {@link MonitoredPackage} health check state transitions. */
    @Test
    public void testPackageHealthCheckStateTransitions() {
        MonitoredPackage m1 = new MonitoredPackage(APP_A, LONG_DURATION,
                false /* hasPassedHealthCheck */);
        MonitoredPackage m2 = new MonitoredPackage(APP_B, LONG_DURATION, false);
        MonitoredPackage m3 = new MonitoredPackage(APP_C, LONG_DURATION, false);
        MonitoredPackage m4 = new MonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true);

        // Verify transition: inactive -> active -> passed
        // Verify initially inactive
        assertEquals(MonitoredPackage.STATE_INACTIVE, m1.getHealthCheckStateLocked());
        // Verify still inactive, until we #setHealthCheckActiveLocked
        assertEquals(MonitoredPackage.STATE_INACTIVE, m1.handleElapsedTimeLocked(SHORT_DURATION));
        // Verify now active
        assertEquals(MonitoredPackage.STATE_ACTIVE, m1.setHealthCheckActiveLocked(SHORT_DURATION));
        // Verify now passed
        assertEquals(MonitoredPackage.STATE_PASSED, m1.tryPassHealthCheckLocked());

        // Verify transition: inactive -> active -> failed
        // Verify initially inactive
        assertEquals(MonitoredPackage.STATE_INACTIVE, m2.getHealthCheckStateLocked());
        // Verify now active
        assertEquals(MonitoredPackage.STATE_ACTIVE, m2.setHealthCheckActiveLocked(SHORT_DURATION));
        // Verify now failed
        assertEquals(MonitoredPackage.STATE_FAILED, m2.handleElapsedTimeLocked(SHORT_DURATION));

        // Verify transition: inactive -> failed
        // Verify initially inactive
        assertEquals(MonitoredPackage.STATE_INACTIVE, m3.getHealthCheckStateLocked());
        // Verify now failed because package expired
        assertEquals(MonitoredPackage.STATE_FAILED, m3.handleElapsedTimeLocked(LONG_DURATION));
        // Verify remains failed even when asked to pass
        assertEquals(MonitoredPackage.STATE_FAILED, m3.tryPassHealthCheckLocked());

        // Verify transition: passed
        assertEquals(MonitoredPackage.STATE_PASSED, m4.getHealthCheckStateLocked());
        // Verify remains passed even if health check fails
        assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(SHORT_DURATION));
        // Verify remains passed even if package expires
        assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(LONG_DURATION));
    }

    private PackageWatchdog createWatchdog() {
    private PackageWatchdog createWatchdog() {
        return createWatchdog(new TestController(), true /* withPackagesReady */);
        return createWatchdog(new TestController(), true /* withPackagesReady */);
    }
    }
@@ -636,7 +741,7 @@ public class PackageWatchdogTest {
        private List<String> mSupportedPackages = new ArrayList<>();
        private List<String> mSupportedPackages = new ArrayList<>();
        private List<String> mRequestedPackages = new ArrayList<>();
        private List<String> mRequestedPackages = new ArrayList<>();
        private Consumer<String> mPassedConsumer;
        private Consumer<String> mPassedConsumer;
        private Consumer<List<String>> mSupportedConsumer;
        private Consumer<List<PackageInfo>> mSupportedConsumer;
        private Runnable mNotifySyncRunnable;
        private Runnable mNotifySyncRunnable;


        @Override
        @Override
@@ -649,7 +754,7 @@ public class PackageWatchdogTest {


        @Override
        @Override
        public void setCallbacks(Consumer<String> passedConsumer,
        public void setCallbacks(Consumer<String> passedConsumer,
                Consumer<List<String>> supportedConsumer, Runnable notifySyncRunnable) {
                Consumer<List<PackageInfo>> supportedConsumer, Runnable notifySyncRunnable) {
            mPassedConsumer = passedConsumer;
            mPassedConsumer = passedConsumer;
            mSupportedConsumer = supportedConsumer;
            mSupportedConsumer = supportedConsumer;
            mNotifySyncRunnable = notifySyncRunnable;
            mNotifySyncRunnable = notifySyncRunnable;
@@ -661,7 +766,11 @@ public class PackageWatchdogTest {
            if (mIsEnabled) {
            if (mIsEnabled) {
                packages.retainAll(mSupportedPackages);
                packages.retainAll(mSupportedPackages);
                mRequestedPackages.addAll(packages);
                mRequestedPackages.addAll(packages);
                mSupportedConsumer.accept(mSupportedPackages);
                List<PackageInfo> packageInfos = new ArrayList<>();
                for (String packageName: packages) {
                    packageInfos.add(new PackageInfo(packageName, SHORT_DURATION));
                }
                mSupportedConsumer.accept(packageInfos);
            } else {
            } else {
                mSupportedConsumer.accept(Collections.emptyList());
                mSupportedConsumer.accept(Collections.emptyList());
            }
            }