diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index e800e8857cb4e..afb798cdb9885 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -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. diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 07edc72bcf4bd..0dbe971ed3f57 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -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: " diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 6749afb4fee01..34092ad6607a5 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 @@ 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 pkgs, Context context) { + private int idleOptimization(PackageManagerService pm, ArraySet 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 failedPackageNames) { ArraySet updatedPackages = new ArraySet<>(); + Set 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)); + } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index bd765b49a4ab6..371b3ef5f5551 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 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); } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 2a45d156ed420..4ff6cbf1e1c88 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.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; } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 2b7bd3442cc7a..3eaeb6df64dd9 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -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 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 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; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 700d3c2ef805c..191d146cd6aad 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -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 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 allCodePaths, String[] instructionSets) { + void removeDexFiles(List 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 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 getUnusedPackages(long downgradeTimeThresholdMillis) { + Set 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 ec248f5719b38..1a97a72cf2b39 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 @@ 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. diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 9feee8c97c0dd..9211af60a0a19 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -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 thresholdTimeinMillis. + * 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 false. + */ + 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 db2d30f9e5684..441994d8c590d 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 @@ 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