Reduce app size by downgrading inactive apps

This will trigger when the device will have low space.
Active apps here refer to the apps which were either active
in foregrond or in background and also used by other packages.
Apps which are inactive for X days downgraded to verify. X is
determined by sysprop pm.dexopt.unopt_after_inactive_days

If the system properties are not set, no effect will take place.

The above operations will take place in background dexopt service.
If user uses the app again, it will again be speed-compiled when
background dexopt service starts next time.

Bug: 36598475
Test: manual
* Remove the check in the code that allows downgrade only when
  the space is low on the device.
* adb root
* Set pm.dexopt_unopt_after_inactive_days to 600
* Make sure the current time of the device is correctly set
* Install 2 non system apps - B, C
* Downgrade B to extract
* Upgrade a system apps to speed-profile - E
* Downgrade a system app to quicken - G
* adb shell cmd package bg-dexopt-job

Expected Results:
* Extract - B
* Verify - C
* There should not be any entries for apps E an G
  in dalvik_cache

Change-Id: I68f9f617d6722a7ba8b00aa2181cb38a165cfc51
This commit is contained in:
Shubham Ajmera
2017-05-24 17:46:36 -07:00
parent 39c9e18721
commit 246dccf932
10 changed files with 257 additions and 90 deletions

View File

@@ -513,7 +513,7 @@ interface IPackageManager {
* configuration.
*/
boolean performDexOpt(String packageName, boolean checkProfiles,
int compileReason, boolean force, boolean bootComplete);
int compileReason, boolean force, boolean bootComplete, boolean downgrade);
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.

View File

@@ -548,7 +548,8 @@ public class ZygoteInit {
int dexoptNeeded;
try {
dexoptNeeded = DexFile.getDexOptNeeded(
classPathElement, instructionSet, systemServerFilter, false /* newProfile */);
classPathElement, instructionSet, systemServerFilter,
false /* newProfile */, false /* downgrade */);
} catch (FileNotFoundException ignored) {
// Do not add to the classpath.
Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -572,7 +573,7 @@ public class ZygoteInit {
try {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, sharedLibraries, seInfo);
uuid, sharedLibraries, seInfo, false /* downgrade */);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "

View File

@@ -18,7 +18,6 @@ package com.android.server.pm;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -40,6 +39,7 @@ import com.android.server.LocalServices;
import com.android.server.PinnerService;
import java.io.File;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -73,6 +73,9 @@ public class BackgroundDexOptService extends JobService {
// Optimizations should be aborted. No space left on device.
private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
// Used for calculating space threshold for downgrading unused apps.
private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
/**
* Set of failed packages remembered across job runs.
*/
@@ -92,6 +95,9 @@ public class BackgroundDexOptService extends JobService {
private final File mDataDir = Environment.getDataDirectory();
private static final long mDowngradeUnusedAppsThresholdInMillis =
getDowngradeUnusedAppsThresholdInMillis();
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -215,7 +221,8 @@ public class BackgroundDexOptService extends JobService {
/* checkProfiles */ false,
PackageManagerService.REASON_BOOT,
/* force */ false,
/* bootComplete */ true);
/* bootComplete */ true,
/* downgrade */ false);
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
}
@@ -243,7 +250,8 @@ public class BackgroundDexOptService extends JobService {
}
// Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
Context context) {
Log.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
@@ -274,9 +282,16 @@ public class BackgroundDexOptService extends JobService {
long lowStorageThreshold, boolean is_for_primary_dex,
ArraySet<String> failedPackageNames) {
ArraySet<String> updatedPackages = new ArraySet<>();
Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
// Only downgrade apps when space is low on device.
// Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
// up disk before user hits the actual lowStorageThreshold.
final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
lowStorageThreshold;
boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
for (String pkg : pkgs) {
int abort_code = abortIdleOptimizations(lowStorageThreshold);
if (abort_code != OPTIMIZE_CONTINUE) {
if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
return abort_code;
}
@@ -284,30 +299,57 @@ public class BackgroundDexOptService extends JobService {
if (failedPackageNames.contains(pkg)) {
// Skip previously failing package
continue;
} else {
// Conservatively add package to the list of failing ones in case performDexOpt
// never returns.
failedPackageNames.add(pkg);
}
}
int reason;
boolean downgrade;
// Downgrade unused packages.
if (unusedPackages.contains(pkg) && shouldDowngrade) {
// This applies for system apps or if packages location is not a directory, i.e.
// monolithic install.
if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
// For apps that don't have the oat directory, instead of downgrading,
// remove their compiler artifacts from dalvik cache.
pm.deleteOatArtifactsOfPackage(pkg);
continue;
} else {
reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
downgrade = true;
}
} else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
downgrade = false;
} else {
// can't dexopt because of low space.
continue;
}
synchronized (failedPackageNames) {
// Conservatively add package to the list of failing ones in case
// performDexOpt never returns.
failedPackageNames.add(pkg);
}
// Optimize package if needed. Note that there can be no race between
// concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
boolean success;
if (is_for_primary_dex) {
int result = pm.performDexOptWithStatus(pkg,
/* checkProfiles */ true,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
/* force */ false,
/* bootComplete */ true);
reason,
false /* forceCompile*/,
true /* bootComplete */,
downgrade);
success = result != PackageDexOptimizer.DEX_OPT_FAILED;
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
}
} else {
success = pm.performDexOptSecondary(pkg,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
/* force */ false);
reason,
false /* force */,
downgrade);
}
if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
@@ -347,6 +389,16 @@ public class BackgroundDexOptService extends JobService {
return OPTIMIZE_CONTINUE;
}
// Evaluate whether apps should be downgraded.
private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowStorageThresholdForDowngrade) {
return true;
}
return false;
}
/**
* Execute the idle optimizations immediately.
*/
@@ -415,4 +467,14 @@ public class BackgroundDexOptService extends JobService {
pinnerService.update(updatedPackages);
}
}
private static long getDowngradeUnusedAppsThresholdInMillis() {
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
if (sysPropValue == null || sysPropValue.isEmpty()) {
Log.w(TAG, "SysProp " + sysPropKey + " not set");
return Long.MAX_VALUE;
}
return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
}
}

View File

@@ -279,13 +279,13 @@ public class Installer extends SystemService {
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
@Nullable String seInfo)
@Nullable String seInfo, boolean downgrade)
throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo);
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
} catch (Exception e) {
throw InstallerException.from(e);
}

View File

@@ -30,7 +30,6 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -40,7 +39,6 @@ import com.android.server.pm.Installer.InstallerException;
import java.io.File;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -152,7 +150,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
+ PackageManagerServiceUtils.packagesToString(others));
for (PackageParser.Package pkg : others) {
deleteOatArtifactsOfPackage(pkg);
mPackageManagerService.deleteOatArtifactsOfPackage(pkg.packageName);
}
}
long spaceAvailableNow = getAvailableSpace();
@@ -242,30 +240,6 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
return usableSpace - lowThreshold;
}
private static String getOatDir(PackageParser.Package pkg) {
if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
if (codePath.isDirectory()) {
return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
}
return null;
}
private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
for (String codePath : pkg.getAllCodePaths()) {
for (String isa : instructionSets) {
try {
mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
}
}
}
}
/**
* Generate all dexopt commands for the given package.
*/
@@ -285,11 +259,12 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
@Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
throws InstallerException {
final StringBuilder builder = new StringBuilder();
// The version. Right now it's 2.
builder.append("2 ");
// The version. Right now it's 3.
builder.append("3 ");
builder.append("dexopt");
@@ -304,6 +279,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
encodeParameter(builder, volumeUuid);
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
encodeParameter(builder, downgrade);
commands.add(builder.toString());
}
@@ -343,12 +319,14 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
getCompilerFilterForReason(compilationReason),
null /* CompilerStats.PackageStats */,
mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
true /* bootComplete */);
true /* bootComplete */,
false /* downgrade */);
mPackageManagerService.getDexManager().dexoptSecondaryDex(pkg.packageName,
getCompilerFilterForReason(compilationReason),
false /* force */,
false /* compileOnlySharedDex */);
false /* compileOnlySharedDex */,
false /* downgrade */);
return commands;
}

View File

@@ -114,7 +114,7 @@ public class PackageDexOptimizer {
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps,
boolean bootComplete) {
boolean bootComplete, boolean downgrade) {
if (!canOptimizePackage(pkg)) {
return DEX_OPT_SKIPPED;
}
@@ -122,7 +122,8 @@ public class PackageDexOptimizer {
final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete);
targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete,
downgrade);
} finally {
releaseWakeLockLI(acquireTime);
}
@@ -137,7 +138,7 @@ public class PackageDexOptimizer {
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
String[] targetInstructionSets, boolean checkForProfileUpdates,
String targetCompilerFilter, CompilerStats.PackageStats packageStats,
boolean isUsedByOtherApps, boolean bootComplete) {
boolean isUsedByOtherApps, boolean bootComplete, boolean downgrade) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -174,7 +175,8 @@ public class PackageDexOptimizer {
}
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats,
downgrade);
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -198,8 +200,8 @@ public class PackageDexOptimizer {
@GuardedBy("mInstallLock")
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated);
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
return DEX_OPT_SKIPPED;
}
@@ -218,8 +220,12 @@ public class PackageDexOptimizer {
try {
long startTime = System.currentTimeMillis();
// TODO: Consider adding 2 different APIs for primary and secondary dexopt.
// installd only uses downgrade flag for secondary dex files and ignores it for
// primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
false /* downgrade*/);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -247,12 +253,12 @@ public class PackageDexOptimizer {
* that seems wasteful.
*/
public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
synchronized (mInstallLock) {
final long acquireTime = acquireWakeLockLI(info.uid);
try {
return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
isUsedByOtherApps);
isUsedByOtherApps, downgrade);
} finally {
releaseWakeLockLI(acquireTime);
}
@@ -294,7 +300,7 @@ public class PackageDexOptimizer {
@GuardedBy("mInstallLock")
private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
// Secondary dex files are currently not compiled at boot.
@@ -324,7 +330,8 @@ public class PackageDexOptimizer {
// TODO(calin): maybe add a separate call.
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser);
compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser,
downgrade);
}
return DEX_OPT_PERFORMED;
@@ -425,10 +432,11 @@ public class PackageDexOptimizer {
* configuration (isa, compiler filter, profile).
*/
private int getDexoptNeeded(String path, String isa, String compilerFilter,
boolean newProfile) {
boolean newProfile, boolean downgrade) {
int dexoptNeeded;
try {
dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile);
dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
downgrade);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;

View File

@@ -283,6 +283,7 @@ import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
@@ -561,8 +562,9 @@ public class PackageManagerService extends IPackageManager.Stub
public static final int REASON_INSTALL = 2;
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
public static final int REASON_LAST = REASON_AB_OTA;
public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
/** All dangerous permission names in the same order as the events in MetricsEvent */
private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -9344,7 +9346,8 @@ public class PackageManagerService extends IPackageManager.Stub
false /* checkProfiles */,
compilerFilter,
false /* force */,
bootComplete);
bootComplete,
false /* downgrade */);
if (pkg.isSystemApp()) {
// Only dexopt shared secondary dex files belonging to system apps to not slow down
@@ -9352,7 +9355,8 @@ public class PackageManagerService extends IPackageManager.Stub
mDexManager.dexoptSecondaryDex(pkg.packageName,
compilerFilter,
false /* force */,
true /* compileOnlySharedDex */);
true /* compileOnlySharedDex */,
false /* downgrade */);
}
// TODO(shubhamajmera): Record secondary dexopt stats.
@@ -9437,14 +9441,16 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean performDexOpt(String packageName,
boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
boolean downgrade) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
} else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
return false;
}
int dexoptStatus = performDexOptWithStatus(
packageName, checkProfiles, compileReason, force, bootComplete);
packageName, checkProfiles, compileReason, force, bootComplete,
downgrade);
return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
@@ -9455,9 +9461,10 @@ public class PackageManagerService extends IPackageManager.Stub
* {@link PackageDexOptimizer#DEX_OPT_FAILED}
*/
/* package */ int performDexOptWithStatus(String packageName,
boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
boolean downgrade) {
return performDexOptTraced(packageName, checkProfiles,
getCompilerFilterForReason(compileReason), force, bootComplete);
getCompilerFilterForReason(compileReason), force, bootComplete, downgrade);
}
@Override
@@ -9470,17 +9477,17 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
targetCompilerFilter, force, bootComplete);
targetCompilerFilter, force, bootComplete, false /* downgrade */);
return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
private int performDexOptTraced(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force,
boolean bootComplete) {
boolean bootComplete, boolean downgrade) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
return performDexOptInternal(packageName, checkProfiles,
targetCompilerFilter, force, bootComplete);
targetCompilerFilter, force, bootComplete, downgrade);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -9490,7 +9497,7 @@ public class PackageManagerService extends IPackageManager.Stub
// if the package can now be considered up to date for the given filter.
private int performDexOptInternal(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force,
boolean bootComplete) {
boolean bootComplete, boolean downgrade) {
PackageParser.Package p;
synchronized (mPackages) {
p = mPackages.get(packageName);
@@ -9505,7 +9512,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
synchronized (mInstallLock) {
return performDexOptInternalWithDependenciesLI(p, checkProfiles,
targetCompilerFilter, force, bootComplete);
targetCompilerFilter, force, bootComplete, downgrade);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -9526,7 +9533,7 @@ public class PackageManagerService extends IPackageManager.Stub
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
boolean checkProfiles, String targetCompilerFilter,
boolean force, boolean bootComplete) {
boolean force, boolean bootComplete, boolean downgrade) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
@@ -9551,12 +9558,13 @@ public class PackageManagerService extends IPackageManager.Stub
targetCompilerFilter,
getOrCreateCompilerPackageStats(depPackage),
true /* isUsedByOtherApps */,
bootComplete);
bootComplete,
downgrade);
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
targetCompilerFilter, getOrCreateCompilerPackageStats(p),
mDexManager.isUsedByOtherApps(p.packageName), bootComplete);
mDexManager.isUsedByOtherApps(p.packageName), bootComplete, downgrade);
}
// Performs dexopt on the used secondary dex files belonging to the given package.
@@ -9571,12 +9579,12 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force,
/* compileOnlySharedDex*/ false);
false /* compileOnlySharedDex */, false /* downgrade */);
}
public boolean performDexOptSecondary(String packageName, int compileReason,
boolean force) {
return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
boolean force, boolean downgrade) {
return mDexManager.dexoptSecondaryDex(packageName, compileReason, force, downgrade);
}
/**
@@ -9755,7 +9763,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int res = performDexOptInternalWithDependenciesLI(pkg,
false /* checkProfiles */, getDefaultCompilerFilter(),
true /* force */,
true /* bootComplete */);
true /* bootComplete */,
false /* downgrade */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -16176,7 +16185,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
if (!allCodePaths.isEmpty()) {
if (instructionSets == null) {
throw new IllegalStateException("instructionSet == null");
@@ -18222,7 +18231,8 @@ public class PackageManagerService extends IPackageManager.Stub
getCompilerFilterForReason(REASON_INSTALL),
getOrCreateCompilerPackageStats(pkg),
mDexManager.isUsedByOtherApps(pkg.packageName),
true /* bootComplete */);
true /* bootComplete */,
false /* downgrade */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -24984,6 +24994,73 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
}
}
boolean canHaveOatDir(String packageName) {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
return false;
}
return p.canHaveOatDir();
}
}
private String getOatDir(PackageParser.Package pkg) {
if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
if (codePath.isDirectory()) {
return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
}
return null;
}
void deleteOatArtifactsOfPackage(String packageName) {
final String[] instructionSets;
final List<String> codePaths;
final String oatDir;
final PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
}
instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
codePaths = pkg.getAllCodePaths();
oatDir = getOatDir(pkg);
for (String codePath : codePaths) {
for (String isa : instructionSets) {
try {
mInstaller.deleteOdex(codePath, isa, oatDir);
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
}
}
}
}
Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
Set<String> unusedPackages = new HashSet<>();
long currentTimeInMillis = System.currentTimeMillis();
synchronized (mPackages) {
for (PackageParser.Package pkg : mPackages.values()) {
PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
if (ps == null) {
continue;
}
PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
pkg.packageName);
if (PackageManagerServiceUtils
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
pkg.getLatestPackageUseTimeInMills(),
pkg.getLatestForegroundPackageUseTimeInMills())) {
unusedPackages.add(pkg.packageName);
}
}
}
return unusedPackages;
}
}
interface PackageSender {

View File

@@ -26,7 +26,7 @@ import dalvik.system.DexFile;
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
"first-boot", "boot", "install", "bg-dexopt", "ab-ota"
"first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
};
// Static block to ensure the strings array is of the right length.

View File

@@ -16,12 +16,16 @@
package com.android.server.pm;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.os.Build;
@@ -178,6 +182,41 @@ public class PackageManagerServiceUtils {
return result;
}
/**
* Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
* Package is considered active, if:
* 1) It was active in foreground.
* 2) It was active in background and also used by other apps.
*
* If it doesn't have sufficient information about the package, it return <code>false</code>.
*/
static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
return false;
}
// If the app was active in foreground during the threshold period.
boolean isActiveInForeground = (currentTimeInMillis
- latestForegroundPackageUseTimeInMillis)
< thresholdTimeinMillis;
if (isActiveInForeground) {
return false;
}
// If the app was active in background during the threshold period and was used
// by other packages.
boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
- latestPackageUseTimeInMillis)
< thresholdTimeinMillis)
&& packageUseInfo.isUsedByOtherApps();
return !isActiveInBackgroundAndUsedByOtherPackages;
}
/**
* Returns the canonicalized path of {@code path} as per {@code realpath(3)}
* semantics.

View File

@@ -304,10 +304,11 @@ public class DexManager {
* @return true if all secondary dex files were processed successfully (compiled or skipped
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force,
boolean downgrade) {
return dexoptSecondaryDex(packageName,
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
force, /* compileOnlySharedDex */ false);
force, /* compileOnlySharedDex */ false, downgrade);
}
/**
@@ -316,7 +317,7 @@ public class DexManager {
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force,
boolean compileOnlySharedDex) {
boolean compileOnlySharedDex, boolean downgrade) {
// Select the dex optimizer based on the force parameter.
// Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
// the necessary dexopt flags to make sure that compilation is not skipped. This avoid
@@ -360,7 +361,8 @@ public class DexManager {
}
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps(),
downgrade);
success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
}
return success;
@@ -476,7 +478,7 @@ public class DexManager {
String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
PackageManagerService.REASON_INSTALL);
int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, isas,
compilerFilter, isUsedByOtherApps);
compilerFilter, isUsedByOtherApps, /* downgrade */ false);
// If we fail to optimize the package log an error but don't propagate the error
// back to the app. The app cannot do much about it and the background job