Merge "Exempt hibernating apps from dex optimization" into sc-v2-dev

This commit is contained in:
Kevin Han
2021-11-19 01:06:06 +00:00
committed by Android (Google) Code Review
6 changed files with 92 additions and 15 deletions

View File

@@ -43,4 +43,9 @@ public abstract class AppHibernationManagerInternal {
* @see AppHibernationService#setHibernatingGlobally * @see AppHibernationService#setHibernatingGlobally
*/ */
public abstract void setHibernatingGlobally(String packageName, boolean isHibernating); public abstract void setHibernatingGlobally(String packageName, boolean isHibernating);
/**
* @see AppHibernationService#isOatArtifactDeletionEnabled
*/
public abstract boolean isOatArtifactDeletionEnabled();
} }

View File

@@ -199,6 +199,14 @@ public final class AppHibernationService extends SystemService {
} }
} }
/**
* Whether global hibernation should delete ART ahead-of-time compilation artifacts and prevent
* package manager from re-optimizing the APK.
*/
private boolean isOatArtifactDeletionEnabled() {
return mOatArtifactDeletionEnabled;
}
/** /**
* Whether a package is hibernating for a given user. * Whether a package is hibernating for a given user.
* *
@@ -742,6 +750,11 @@ public final class AppHibernationService extends SystemService {
public boolean isHibernatingGlobally(String packageName) { public boolean isHibernatingGlobally(String packageName) {
return mService.isHibernatingGlobally(packageName); return mService.isHibernatingGlobally(packageName);
} }
@Override
public boolean isOatArtifactDeletionEnabled() {
return mService.isOatArtifactDeletionEnabled();
}
} }
private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);

View File

@@ -381,7 +381,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
} }
// Does the package have code? If not, there won't be any artifacts. // Does the package have code? If not, there won't be any artifacts.
if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (!mPackageManagerService.mPackageDexOptimizer.canOptimizePackage(pkg)) {
continue; continue;
} }
if (pkg.getPath() == null) { if (pkg.getPath() == null) {

View File

@@ -63,7 +63,10 @@ import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.ArtStatsLogUtils; import com.android.server.pm.dex.ArtStatsLogUtils;
@@ -107,16 +110,24 @@ public class PackageDexOptimizer {
private volatile boolean mSystemReady; private volatile boolean mSystemReady;
private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger(); private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger();
private final Injector mInjector;
private static final Random sRandom = new Random(); private static final Random sRandom = new Random();
PackageDexOptimizer(Installer installer, Object installLock, Context context, PackageDexOptimizer(Installer installer, Object installLock, Context context,
String wakeLockTag) { String wakeLockTag) {
this.mInstaller = installer; this(new Injector() {
this.mInstallLock = installLock; @Override
public AppHibernationManagerInternal getAppHibernationManagerInternal() {
return LocalServices.getService(AppHibernationManagerInternal.class);
}
PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @Override
mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag); public PowerManager getPowerManager(Context context) {
return context.getSystemService(PowerManager.class);
}
}, installer, installLock, context, wakeLockTag);
} }
protected PackageDexOptimizer(PackageDexOptimizer from) { protected PackageDexOptimizer(PackageDexOptimizer from) {
@@ -124,9 +135,21 @@ public class PackageDexOptimizer {
this.mInstallLock = from.mInstallLock; this.mInstallLock = from.mInstallLock;
this.mDexoptWakeLock = from.mDexoptWakeLock; this.mDexoptWakeLock = from.mDexoptWakeLock;
this.mSystemReady = from.mSystemReady; this.mSystemReady = from.mSystemReady;
this.mInjector = from.mInjector;
} }
static boolean canOptimizePackage(AndroidPackage pkg) { @VisibleForTesting
PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
Context context, String wakeLockTag) {
this.mInstaller = installer;
this.mInstallLock = installLock;
PowerManager powerManager = injector.getPowerManager(context);
mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag);
mInjector = injector;
}
boolean canOptimizePackage(AndroidPackage pkg) {
// We do not dexopt a package with no code. // We do not dexopt a package with no code.
// Note that the system package is marked as having no code, however we can // Note that the system package is marked as having no code, however we can
// still optimize it via dexoptSystemServerPath. // still optimize it via dexoptSystemServerPath.
@@ -134,6 +157,17 @@ public class PackageDexOptimizer {
return false; return false;
} }
// We do not dexopt unused packages.
// It's possible for this to be called before app hibernation service is ready due to
// an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
// a hibernating app should have no artifacts to copy in the first place.
AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal();
if (ahm != null
&& ahm.isHibernatingGlobally(pkg.getPackageName())
&& ahm.isOatArtifactDeletionEnabled()) {
return false;
}
return true; return true;
} }
@@ -921,4 +955,13 @@ public class PackageDexOptimizer {
return flags | DEXOPT_FORCE; return flags | DEXOPT_FORCE;
} }
} }
/**
* Injector for {@link PackageDexOptimizer} dependencies
*/
interface Injector {
AppHibernationManagerInternal getAppHibernationManagerInternal();
PowerManager getPowerManager(Context context);
}
} }

View File

@@ -367,7 +367,6 @@ import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog; import com.android.server.Watchdog;
import com.android.server.apphibernation.AppHibernationManagerInternal; import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.apphibernation.AppHibernationService;
import com.android.server.compat.CompatChange; import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat; import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal;
@@ -1458,7 +1457,7 @@ public class PackageManagerService extends IPackageManager.Stub
final ArtManagerService mArtManagerService; final ArtManagerService mArtManagerService;
private final PackageDexOptimizer mPackageDexOptimizer; final PackageDexOptimizer mPackageDexOptimizer;
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps). // is used by other apps).
private final DexManager mDexManager; private final DexManager mDexManager;
@@ -12741,7 +12740,7 @@ public class PackageManagerService extends IPackageManager.Stub
} }
} }
if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (!mPackageDexOptimizer.canOptimizePackage(pkg)) {
if (DEBUG_DEXOPT) { if (DEBUG_DEXOPT) {
Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName()); Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
} }
@@ -12992,16 +12991,11 @@ public class PackageManagerService extends IPackageManager.Stub
ArraySet<String> pkgs = new ArraySet<>(); ArraySet<String> pkgs = new ArraySet<>();
synchronized (mLock) { synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) { for (AndroidPackage p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) { if (mPackageDexOptimizer.canOptimizePackage(p)) {
pkgs.add(p.getPackageName()); pkgs.add(p.getPackageName());
} }
} }
} }
if (AppHibernationService.isAppHibernationEnabled()) {
AppHibernationManagerInternal appHibernationManager =
mInjector.getLocalService(AppHibernationManagerInternal.class);
pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
}
return pkgs; return pkgs;
} }

View File

@@ -16,8 +16,10 @@
package com.android.server.pm package com.android.server.pm
import android.content.Context
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.PowerManager
import android.provider.DeviceConfig import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
@@ -55,6 +57,8 @@ class PackageManagerServiceHibernationTests {
@Mock @Mock
lateinit var appHibernationManager: AppHibernationManagerInternal lateinit var appHibernationManager: AppHibernationManagerInternal
@Mock
lateinit var powerManager: PowerManager
@Before @Before
@Throws(Exception::class) @Throws(Exception::class)
@@ -68,6 +72,24 @@ class PackageManagerServiceHibernationTests {
.thenReturn(appHibernationManager) .thenReturn(appHibernationManager)
whenever(rule.mocks().injector.handler) whenever(rule.mocks().injector.handler)
.thenReturn(Handler(TestableLooper.get(this).looper)) .thenReturn(Handler(TestableLooper.get(this).looper))
val injector = object : PackageDexOptimizer.Injector {
override fun getAppHibernationManagerInternal(): AppHibernationManagerInternal {
return appHibernationManager
}
override fun getPowerManager(context: Context?): PowerManager {
return powerManager
}
}
val packageDexOptimizer = PackageDexOptimizer(
injector,
rule.mocks().installer,
rule.mocks().installLock,
rule.mocks().context,
"*dexopt*")
whenever(rule.mocks().injector.packageDexOptimizer)
.thenReturn(packageDexOptimizer)
whenever(appHibernationManager.isOatArtifactDeletionEnabled).thenReturn(true)
} }
@Test @Test