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

Commit 5b3dd0e0 authored by Kevin Han's avatar Kevin Han
Browse files

Do not hold PM or AM locks when calling hibernation API

App hibernation calls into activity manager and package manager API. If
package manager calls into AppHibernationService while holding either
the activity manager lock or package manager locks, it can cause a
deadlock. To fix this, we ensure that app hibernation API is never
called with either lock, either by separating out the logic or putting
the app hibernation state mutation on a background thread.

Bug: 184661338
Bug: 182811830
Test: manual, hibernation app, open, see hibernation state is left
Test: atest PackageManagerServiceHibernationTests
Change-Id: I1d7dfb2232a49a63958628dd94f6b6445ece4d55
parent c9f96b6e
Loading
Loading
Loading
Loading
+12 −11
Original line number Diff line number Diff line
@@ -12272,19 +12272,18 @@ public class PackageManagerService extends IPackageManager.Stub
    public ArraySet<String> getOptimizablePackages() {
        ArraySet<String> pkgs = new ArraySet<>();
        final boolean hibernationEnabled = AppHibernationService.isAppHibernationEnabled();
        AppHibernationManagerInternal appHibernationManager =
                mInjector.getLocalService(AppHibernationManagerInternal.class);
        synchronized (mLock) {
            for (AndroidPackage p : mPackages.values()) {
                // Checking hibernation state is an inexpensive call.
                boolean isHibernating = hibernationEnabled
                        && appHibernationManager.isHibernatingGlobally(p.getPackageName());
                if (PackageDexOptimizer.canOptimizePackage(p) && !isHibernating) {
                if (PackageDexOptimizer.canOptimizePackage(p)) {
                    pkgs.add(p.getPackageName());
                }
            }
        }
        if (AppHibernationService.isAppHibernationEnabled()) {
            AppHibernationManagerInternal appHibernationManager =
                    mInjector.getLocalService(AppHibernationManagerInternal.class);
            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
        }
        return pkgs;
    }
@@ -23465,10 +23464,12 @@ public class PackageManagerService extends IPackageManager.Stub
            }
        }
        if (shouldUnhibernate) {
            mHandler.post(() -> {
                AppHibernationManagerInternal ah =
                        mInjector.getLocalService(AppHibernationManagerInternal.class);
                ah.setHibernatingForUser(packageName, userId, false);
                ah.setHibernatingGlobally(packageName, false);
            });
        }
    }
+11 −2
Original line number Diff line number Diff line
@@ -17,8 +17,12 @@
package com.android.server.pm

import android.os.Build
import android.os.Handler
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import com.android.server.apphibernation.AppHibernationManagerInternal
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.testutils.whenever
@@ -28,12 +32,12 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@RunWith(JUnit4::class)
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class PackageManagerServiceHibernationTests {

    companion object {
@@ -60,6 +64,8 @@ class PackageManagerServiceHibernationTests {
        rule.system().stageNominalSystemState()
        whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
            .thenReturn(appHibernationManager)
        whenever(rule.mocks().injector.handler)
            .thenReturn(Handler(TestableLooper.get(this).looper))
    }

    @Test
@@ -74,6 +80,9 @@ class PackageManagerServiceHibernationTests {
        ps!!.setStopped(true, TEST_USER_ID)

        pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)

        TestableLooper.get(this).processAllMessages()

        verify(appHibernationManager).setHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID, false)
        verify(appHibernationManager).setHibernatingGlobally(TEST_PACKAGE_NAME, false)
    }