Loading cmds/statsd/src/atoms.proto +44 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,9 @@ message Atom { BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125; BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126; BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127; AppDowngraded app_downgraded = 128; AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129; LowStorageStateChanged low_storage_state_changed = 130; NfcErrorOccurred nfc_error_occurred = 134; NfcStateChanged nfc_state_changed = 135; NfcBeamOccurred nfc_beam_occurred = 136; Loading Loading @@ -2006,6 +2009,47 @@ message ActivityForegroundStateChanged { optional State state = 4; } /** * Logs when a volume entered low Storage state. * Logged from: * frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java */ message LowStorageStateChanged { // Volume that ran out of storage. optional string volume_description = 1; enum State { UNKNOWN = 0; OFF = 1; ON = 2; } optional State state = 2; } /** * Logs when an app is downgraded. * Logged from: * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java */ message AppDowngraded { optional string package_name = 1; // Size of the package (all data) before being downgraded. optional int64 size_in_bytes_before = 2; // Size of the package (all data) after being downgraded. optional int64 size_in_bytes_after = 3; optional bool aggressive = 4; } /** * Logs when an app is optimized after being downgraded. * Logged from: * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java */ message AppOptimizedAfterDowngraded { optional string package_name = 1; } /** * Logs when an app crashes. * Logged from: Loading services/core/java/com/android/server/pm/BackgroundDexOptService.java +180 −69 Original line number Diff line number Diff line Loading @@ -27,24 +27,30 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.os.BatteryManager; import android.os.Environment; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; import com.android.server.pm.dex.DexManager; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PinnerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import java.io.File; import java.nio.file.Paths; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; /** * {@hide} Loading @@ -52,7 +58,7 @@ import java.util.concurrent.TimeUnit; public class BackgroundDexOptService extends JobService { private static final String TAG = "BackgroundDexOptService"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int JOB_IDLE_OPTIMIZE = 800; private static final int JOB_POST_BOOT_UPDATE = 801; Loading Loading @@ -97,7 +103,6 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); Loading Loading @@ -270,106 +275,145 @@ public class BackgroundDexOptService extends JobService { long lowStorageThreshold = getLowStorageThreshold(context); // Optimize primary apks. int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true, sFailedPackageNamesPrimary); int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*isForPrimaryDex=*/ true); if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return result; } if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) { if (supportSecondaryDex()) { result = reconcileSecondaryDexFiles(pm.getDexManager()); if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return result; } result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false, sFailedPackageNamesSecondary); result = optimizePackages(pm, pkgs, lowStorageThreshold, /*isForPrimaryDex=*/ false); } return result; } /** * Get the size of the directory. It uses recursion to go over all files. * @param f * @return */ private long getDirectorySize(File f) { long size = 0; if (f.isDirectory()) { for (File file: f.listFiles()) { size += getDirectorySize(file); } } else { size = f.length(); } return size; } /** * Get the size of a package. * @param pkg */ private long getPackageSize(PackageManagerService pm, String pkg) { PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); long size = 0; if (info != null && info.applicationInfo != null) { File path = Paths.get(info.applicationInfo.sourceDir).toFile(); if (path.isFile()) { path = path.getParentFile(); } size += getDirectorySize(path); if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { path = Paths.get(splitSourceDir).toFile(); if (path.isFile()) { path = path.getParentFile(); } size += getDirectorySize(path); } } return size; } return 0; } private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, long lowStorageThreshold, boolean is_for_primary_dex, ArraySet<String> failedPackageNames) { long lowStorageThreshold, boolean isForPrimaryDex) { ArraySet<String> updatedPackages = new ArraySet<>(); Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); // 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); Log.d(TAG, "Should Downgrade " + shouldDowngrade); boolean dex_opt_performed = false; for (String pkg : pkgs) { int abort_code = abortIdleOptimizations(lowStorageThreshold); if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return abort_code; } synchronized (failedPackageNames) { if (failedPackageNames.contains(pkg)) { // Skip previously failing package // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex); } else { if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) { // can't dexopt because of low space. continue; } dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex); } if (dex_opt_performed) { updatedPackages.add(pkg); } } int reason; boolean downgrade; // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { notifyPinService(updatedPackages); return OPTIMIZE_PROCESSED; } /** * Try to downgrade the package to a smaller compilation filter. * eg. if the package is in speed-profile the package will be downgraded to verify. * @param pm PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean downgradePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { Log.d(TAG, "Downgrading " + pkg); boolean dex_opt_performed = false; int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | DexoptOptions.DEXOPT_DOWNGRADE; long package_size_before = getPackageSize(pm, pkg); if (isForPrimaryDex) { // 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)) { if (!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; dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } } 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; dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } 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; int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; if (is_for_primary_dex) { int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)); success = result != PackageDexOptimizer.DEX_OPT_FAILED; if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); } } else { success = pm.performDexOpt(new DexoptOptions(pkg, reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX)); } if (success) { // Dexopt succeeded, remove package from the list of failing ones. synchronized (failedPackageNames) { failedPackageNames.remove(pkg); if (dex_opt_performed) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, getPackageSize(pm, pkg), /*aggressive=*/ false); } return dex_opt_performed; } } notifyPinService(updatedPackages); return OPTIMIZE_PROCESSED; private boolean supportSecondaryDex() { return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)); } private int reconcileSecondaryDexFiles(DexManager dm) { Loading @@ -383,6 +427,73 @@ public class BackgroundDexOptService extends JobService { return OPTIMIZE_PROCESSED; } /** * * Optimize package if needed. Note that there can be no race between * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. * @param pm An instance of PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean optimizePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { int reason = PackageManagerService.REASON_BACKGROUND_DEXOPT; int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; return isForPrimaryDex ? performDexOptPrimary(pm, pkg, reason, dexoptFlags) : performDexOptSecondary(pm, pkg, reason, dexoptFlags); } private boolean performDexOptPrimary(PackageManagerService pm, String pkg, int reason, int dexoptFlags) { int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false, () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags))); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; } private boolean performDexOptSecondary(PackageManagerService pm, String pkg, int reason, int dexoptFlags) { DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX); int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true, () -> pm.performDexOpt(dexoptOptions) ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED ); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; } /** * Execute the dexopt wrapper and make sure that if performDexOpt wrapper fails * the package is added to the list of failed packages. * Return one of following result: * {@link PackageDexOptimizer#DEX_OPT_SKIPPED} * {@link PackageDexOptimizer#DEX_OPT_PERFORMED} * {@link PackageDexOptimizer#DEX_OPT_FAILED} */ private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex, Supplier<Integer> performDexOptWrapper) { ArraySet<String> sFailedPackageNames = isForPrimaryDex ? sFailedPackageNamesPrimary : sFailedPackageNamesSecondary; synchronized (sFailedPackageNames) { if (sFailedPackageNames.contains(pkg)) { // Skip previously failing package return PackageDexOptimizer.DEX_OPT_SKIPPED; } sFailedPackageNames.add(pkg); } int result = performDexOptWrapper.get(); if (result != PackageDexOptimizer.DEX_OPT_FAILED) { synchronized (sFailedPackageNames) { sFailedPackageNames.remove(pkg); } } return result; } // Evaluate whether or not idle optimizations should continue. private int abortIdleOptimizations(long lowStorageThreshold) { if (mAbortIdleOptimization.get()) { Loading services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +7 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.TrafficStats; import android.os.Binder; import android.os.Environment; import android.os.FileObserver; Loading @@ -42,13 +41,13 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.DataUnit; import android.util.Slog; import android.util.StatsLog; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; import com.android.server.IoThread; import com.android.server.SystemService; import com.android.server.pm.InstructionSets; import com.android.server.pm.PackageManagerService; Loading Loading @@ -499,9 +498,15 @@ public class DeviceStorageMonitorService extends SystemService { notification.flags |= Notification.FLAG_NO_CLEAR; mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, notification, UserHandle.ALL); StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, Objects.toString(vol.getDescription()), StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON); } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) { mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, UserHandle.ALL); StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, Objects.toString(vol.getDescription()), StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF); } } Loading Loading
cmds/statsd/src/atoms.proto +44 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,9 @@ message Atom { BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125; BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126; BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127; AppDowngraded app_downgraded = 128; AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129; LowStorageStateChanged low_storage_state_changed = 130; NfcErrorOccurred nfc_error_occurred = 134; NfcStateChanged nfc_state_changed = 135; NfcBeamOccurred nfc_beam_occurred = 136; Loading Loading @@ -2006,6 +2009,47 @@ message ActivityForegroundStateChanged { optional State state = 4; } /** * Logs when a volume entered low Storage state. * Logged from: * frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java */ message LowStorageStateChanged { // Volume that ran out of storage. optional string volume_description = 1; enum State { UNKNOWN = 0; OFF = 1; ON = 2; } optional State state = 2; } /** * Logs when an app is downgraded. * Logged from: * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java */ message AppDowngraded { optional string package_name = 1; // Size of the package (all data) before being downgraded. optional int64 size_in_bytes_before = 2; // Size of the package (all data) after being downgraded. optional int64 size_in_bytes_after = 3; optional bool aggressive = 4; } /** * Logs when an app is optimized after being downgraded. * Logged from: * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java */ message AppOptimizedAfterDowngraded { optional string package_name = 1; } /** * Logs when an app crashes. * Logged from: Loading
services/core/java/com/android/server/pm/BackgroundDexOptService.java +180 −69 Original line number Diff line number Diff line Loading @@ -27,24 +27,30 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.os.BatteryManager; import android.os.Environment; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; import com.android.server.pm.dex.DexManager; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PinnerService; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import java.io.File; import java.nio.file.Paths; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; /** * {@hide} Loading @@ -52,7 +58,7 @@ import java.util.concurrent.TimeUnit; public class BackgroundDexOptService extends JobService { private static final String TAG = "BackgroundDexOptService"; private static final boolean DEBUG = false; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int JOB_IDLE_OPTIMIZE = 800; private static final int JOB_POST_BOOT_UPDATE = 801; Loading Loading @@ -97,7 +103,6 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); Loading Loading @@ -270,106 +275,145 @@ public class BackgroundDexOptService extends JobService { long lowStorageThreshold = getLowStorageThreshold(context); // Optimize primary apks. int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true, sFailedPackageNamesPrimary); int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*isForPrimaryDex=*/ true); if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return result; } if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) { if (supportSecondaryDex()) { result = reconcileSecondaryDexFiles(pm.getDexManager()); if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return result; } result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false, sFailedPackageNamesSecondary); result = optimizePackages(pm, pkgs, lowStorageThreshold, /*isForPrimaryDex=*/ false); } return result; } /** * Get the size of the directory. It uses recursion to go over all files. * @param f * @return */ private long getDirectorySize(File f) { long size = 0; if (f.isDirectory()) { for (File file: f.listFiles()) { size += getDirectorySize(file); } } else { size = f.length(); } return size; } /** * Get the size of a package. * @param pkg */ private long getPackageSize(PackageManagerService pm, String pkg) { PackageInfo info = pm.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); long size = 0; if (info != null && info.applicationInfo != null) { File path = Paths.get(info.applicationInfo.sourceDir).toFile(); if (path.isFile()) { path = path.getParentFile(); } size += getDirectorySize(path); if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { path = Paths.get(splitSourceDir).toFile(); if (path.isFile()) { path = path.getParentFile(); } size += getDirectorySize(path); } } return size; } return 0; } private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, long lowStorageThreshold, boolean is_for_primary_dex, ArraySet<String> failedPackageNames) { long lowStorageThreshold, boolean isForPrimaryDex) { ArraySet<String> updatedPackages = new ArraySet<>(); Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); // 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); Log.d(TAG, "Should Downgrade " + shouldDowngrade); boolean dex_opt_performed = false; for (String pkg : pkgs) { int abort_code = abortIdleOptimizations(lowStorageThreshold); if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { return abort_code; } synchronized (failedPackageNames) { if (failedPackageNames.contains(pkg)) { // Skip previously failing package // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex); } else { if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) { // can't dexopt because of low space. continue; } dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex); } if (dex_opt_performed) { updatedPackages.add(pkg); } } int reason; boolean downgrade; // Downgrade unused packages. if (unusedPackages.contains(pkg) && shouldDowngrade) { notifyPinService(updatedPackages); return OPTIMIZE_PROCESSED; } /** * Try to downgrade the package to a smaller compilation filter. * eg. if the package is in speed-profile the package will be downgraded to verify. * @param pm PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean downgradePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { Log.d(TAG, "Downgrading " + pkg); boolean dex_opt_performed = false; int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | DexoptOptions.DEXOPT_DOWNGRADE; long package_size_before = getPackageSize(pm, pkg); if (isForPrimaryDex) { // 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)) { if (!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; dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); } } 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; dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } 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; int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; if (is_for_primary_dex) { int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)); success = result != PackageDexOptimizer.DEX_OPT_FAILED; if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); } } else { success = pm.performDexOpt(new DexoptOptions(pkg, reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX)); } if (success) { // Dexopt succeeded, remove package from the list of failing ones. synchronized (failedPackageNames) { failedPackageNames.remove(pkg); if (dex_opt_performed) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, getPackageSize(pm, pkg), /*aggressive=*/ false); } return dex_opt_performed; } } notifyPinService(updatedPackages); return OPTIMIZE_PROCESSED; private boolean supportSecondaryDex() { return (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)); } private int reconcileSecondaryDexFiles(DexManager dm) { Loading @@ -383,6 +427,73 @@ public class BackgroundDexOptService extends JobService { return OPTIMIZE_PROCESSED; } /** * * Optimize package if needed. Note that there can be no race between * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. * @param pm An instance of PackageManagerService * @param pkg The package to be downgraded. * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean optimizePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex) { int reason = PackageManagerService.REASON_BACKGROUND_DEXOPT; int dexoptFlags = DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB; return isForPrimaryDex ? performDexOptPrimary(pm, pkg, reason, dexoptFlags) : performDexOptSecondary(pm, pkg, reason, dexoptFlags); } private boolean performDexOptPrimary(PackageManagerService pm, String pkg, int reason, int dexoptFlags) { int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false, () -> pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags))); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; } private boolean performDexOptSecondary(PackageManagerService pm, String pkg, int reason, int dexoptFlags) { DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX); int result = trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true, () -> pm.performDexOpt(dexoptOptions) ? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED ); return result == PackageDexOptimizer.DEX_OPT_PERFORMED; } /** * Execute the dexopt wrapper and make sure that if performDexOpt wrapper fails * the package is added to the list of failed packages. * Return one of following result: * {@link PackageDexOptimizer#DEX_OPT_SKIPPED} * {@link PackageDexOptimizer#DEX_OPT_PERFORMED} * {@link PackageDexOptimizer#DEX_OPT_FAILED} */ private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex, Supplier<Integer> performDexOptWrapper) { ArraySet<String> sFailedPackageNames = isForPrimaryDex ? sFailedPackageNamesPrimary : sFailedPackageNamesSecondary; synchronized (sFailedPackageNames) { if (sFailedPackageNames.contains(pkg)) { // Skip previously failing package return PackageDexOptimizer.DEX_OPT_SKIPPED; } sFailedPackageNames.add(pkg); } int result = performDexOptWrapper.get(); if (result != PackageDexOptimizer.DEX_OPT_FAILED) { synchronized (sFailedPackageNames) { sFailedPackageNames.remove(pkg); } } return result; } // Evaluate whether or not idle optimizations should continue. private int abortIdleOptimizations(long lowStorageThreshold) { if (mAbortIdleOptimization.get()) { Loading
services/core/java/com/android/server/storage/DeviceStorageMonitorService.java +7 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.TrafficStats; import android.os.Binder; import android.os.Environment; import android.os.FileObserver; Loading @@ -42,13 +41,13 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.DataUnit; import android.util.Slog; import android.util.StatsLog; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; import com.android.server.IoThread; import com.android.server.SystemService; import com.android.server.pm.InstructionSets; import com.android.server.pm.PackageManagerService; Loading Loading @@ -499,9 +498,15 @@ public class DeviceStorageMonitorService extends SystemService { notification.flags |= Notification.FLAG_NO_CLEAR; mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, notification, UserHandle.ALL); StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, Objects.toString(vol.getDescription()), StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON); } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) { mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE, UserHandle.ALL); StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED, Objects.toString(vol.getDescription()), StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF); } } Loading