Merge "Reduce app size by downgrading inactive apps"

This commit is contained in:
Treehugger Robot
2017-11-01 02:19:57 +00:00
committed by Gerrit Code Review
10 changed files with 210 additions and 65 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

@@ -577,7 +577,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;
@@ -261,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");
@@ -280,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());
}
@@ -319,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

@@ -125,7 +125,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;
}
@@ -133,7 +133,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);
}
@@ -148,7 +149,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);
@@ -185,7 +186,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,
@@ -209,8 +211,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;
}
@@ -229,8 +231,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();
@@ -258,12 +264,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);
}
@@ -305,7 +311,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.
@@ -335,7 +341,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;
@@ -436,11 +443,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,
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;

View File

@@ -280,6 +280,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;
@@ -559,8 +560,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(
@@ -9396,7 +9398,8 @@ public class PackageManagerService extends IPackageManager.Stub
false /* checkProfiles */,
compilerFilter,
false /* force */,
bootComplete);
bootComplete,
false /* downgrade */);
boolean secondaryDexOptStatus = true;
if (pkg.isSystemApp()) {
@@ -9405,7 +9408,8 @@ public class PackageManagerService extends IPackageManager.Stub
secondaryDexOptStatus = mDexManager.dexoptSecondaryDex(pkg.packageName,
compilerFilter,
false /* force */,
true /* compileOnlySharedDex */);
true /* compileOnlySharedDex */,
false /* downgrade */);
}
if (secondaryDexOptStatus) {
@@ -9493,14 +9497,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;
}
@@ -9511,9 +9517,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
@@ -9526,17 +9533,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);
}
@@ -9546,7 +9553,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);
@@ -9561,7 +9568,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);
@@ -9582,7 +9589,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.
@@ -9607,12 +9614,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.
@@ -9627,12 +9635,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);
}
/**
@@ -9811,7 +9819,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) {
@@ -16242,7 +16251,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");
@@ -18280,7 +18289,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);
}
@@ -25054,6 +25064,29 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
}
}
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,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 com.android.internal.util.ArrayUtils;
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;
@@ -185,6 +189,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