Merge "Reduce app size by downgrading inactive apps"
am: 8bcd66d35f
Change-Id: I4bfba3f7eb16442a7a69466cf72b22198acde6c4
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4b44a17..e5e7b77 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -513,7 +513,7 @@
* 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.
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2fd2521..429a1d9 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -577,7 +577,7 @@
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: "
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 6749afb..34092ad 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,7 +18,6 @@
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.PinnerService;
import java.io.File;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -73,6 +73,9 @@
// 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 @@
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 @@
/* 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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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));
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index bd765b4..371b3ef 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -279,13 +279,13 @@
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);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 60d7c95..4ff6cbf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -30,7 +30,6 @@
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 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;
@@ -261,11 +259,12 @@
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");
@@ -280,6 +279,7 @@
encodeParameter(builder, volumeUuid);
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
+ encodeParameter(builder, downgrade);
commands.add(builder.toString());
}
@@ -319,12 +319,14 @@
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;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7e9596a..644bab1 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -125,7 +125,7 @@
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;
}
@@ -133,7 +133,8 @@
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);
}
@@ -148,7 +149,7 @@
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);
@@ -185,7 +186,8 @@
}
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,
@@ -209,8 +211,8 @@
@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;
}
@@ -229,8 +231,12 @@
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();
@@ -258,12 +264,12 @@
* 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);
}
@@ -305,7 +311,7 @@
@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.
@@ -335,7 +341,8 @@
// 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;
@@ -436,11 +443,11 @@
* 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,
- false /* downgrade */);
+ dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
+ downgrade);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1934b79..6d501bb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -280,6 +280,7 @@
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;
@@ -559,8 +560,9 @@
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(
@@ -9396,7 +9398,8 @@
false /* checkProfiles */,
compilerFilter,
false /* force */,
- bootComplete);
+ bootComplete,
+ false /* downgrade */);
boolean secondaryDexOptStatus = true;
if (pkg.isSystemApp()) {
@@ -9405,7 +9408,8 @@
secondaryDexOptStatus = mDexManager.dexoptSecondaryDex(pkg.packageName,
compilerFilter,
false /* force */,
- true /* compileOnlySharedDex */);
+ true /* compileOnlySharedDex */,
+ false /* downgrade */);
}
if (secondaryDexOptStatus) {
@@ -9493,14 +9497,16 @@
@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;
}
@@ -9511,9 +9517,10 @@
* {@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
@@ -9526,17 +9533,17 @@
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);
}
@@ -9546,7 +9553,7 @@
// 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);
@@ -9561,7 +9568,7 @@
try {
synchronized (mInstallLock) {
return performDexOptInternalWithDependenciesLI(p, checkProfiles,
- targetCompilerFilter, force, bootComplete);
+ targetCompilerFilter, force, bootComplete, downgrade);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -9582,7 +9589,7 @@
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.
@@ -9607,12 +9614,13 @@
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.
@@ -9627,12 +9635,12 @@
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);
}
/**
@@ -9811,7 +9819,8 @@
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) {
@@ -16242,7 +16251,7 @@
}
}
- 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");
@@ -18280,7 +18289,8 @@
getCompilerFilterForReason(REASON_INSTALL),
getOrCreateCompilerPackageStats(pkg),
mDexManager.isUsedByOtherApps(pkg.packageName),
- true /* bootComplete */);
+ true /* bootComplete */,
+ false /* downgrade */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -25054,6 +25064,29 @@
}
}
}
+
+ 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 {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index ec248f5..1a97a72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,7 +26,7 @@
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.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index eb1e7bd..820b2cb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,6 +16,9 @@
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;
@@ -24,6 +27,7 @@
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;
@@ -186,6 +190,41 @@
}
/**
+ * 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.
*/
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index db2d30f..441994d 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -304,10 +304,11 @@
* @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 @@
* 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 @@
}
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 @@
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